Skip to content
Snippets Groups Projects
column.vue 8.38 KiB
Newer Older
syuilo's avatar
syuilo committed
<template>
<!-- sectionを利用しているのは、deck.vue側でcolumnに対してfirst-of-typeを効かせるため -->
<section class="dnpfarvg _panel _narrow_" :class="{ paged: isMainColumn, naked, _inContainer_: !isMainColumn, active, isStacked, draghover, dragging, dropready }"
syuilo's avatar
syuilo committed
	@dragover.prevent.stop="onDragover"
	@dragleave="onDragleave"
	@drop.prevent.stop="onDrop"
	v-hotkey="keymap"
	:style="{ width: `${width}px` }"
>
	<header :class="{ indicated }"
		draggable="true"
		@click="goTop"
		@dragstart="onDragstart"
		@dragend="onDragend"
		@contextmenu.prevent.stop="onContextmenu"
	>
		<button class="toggleActive _button" @click="toggleActive" v-if="isStacked">
syuilo's avatar
syuilo committed
			<template v-if="active"><Fa :icon="faAngleUp"/></template>
			<template v-else><Fa :icon="faAngleDown"/></template>
syuilo's avatar
syuilo committed
		</button>
		<div class="action">
			<slot name="action"></slot>
		</div>
		<span class="header"><slot name="header"></slot></span>
syuilo's avatar
syuilo committed
		<button v-if="!isMainColumn" class="menu _button" ref="menu" @click.stop="showMenu"><Fa :icon="faCaretDown"/></button>
syuilo's avatar
syuilo committed
	</header>
	<div ref="body" v-show="active">
		<slot></slot>
	</div>
</section>
</template>

<script lang="ts">
syuilo's avatar
syuilo committed
import { defineComponent } from 'vue';
import { faArrowUp, faArrowDown, faAngleUp, faAngleDown, faCaretDown, faArrowRight, faArrowLeft, faPencilAlt } from '@fortawesome/free-solid-svg-icons';
syuilo's avatar
syuilo committed
import { faWindowMaximize, faTrashAlt, faWindowRestore } from '@fortawesome/free-regular-svg-icons';
syuilo's avatar
syuilo committed
import * as os from '@/os';
syuilo's avatar
syuilo committed
import { renameColumn, swapLeftColumn, swapRightColumn, swapUpColumn, swapDownColumn, stackLeftColumn, popRightColumn, removeColumn, swapColumn } from './deck-store';
syuilo's avatar
syuilo committed

syuilo's avatar
syuilo committed
export default defineComponent({
syuilo's avatar
syuilo committed
	props: {
		column: {
			type: Object,
			required: false,
			default: null
		},
		isStacked: {
			type: Boolean,
			required: false,
			default: false
		},
		menu: {
			type: Array,
			required: false,
			default: null
		},
		naked: {
			type: Boolean,
			required: false,
			default: false
		},
		indicated: {
			type: Boolean,
			required: false,
			default: false
		},
	},

	data() {
		return {
			active: true,
			dragging: false,
			draghover: false,
			dropready: false,
syuilo's avatar
syuilo committed
			faArrowUp, faArrowDown, faAngleUp, faAngleDown, faCaretDown,
syuilo's avatar
syuilo committed
		};
	},

	computed: {
		isMainColumn(): boolean {
			return this.column == null;
		},

		width(): number {
			return this.isMainColumn ? 350 : this.column.width;
		},

		keymap(): any {
			return {
syuilo's avatar
syuilo committed
				'shift+up': () => this.$parent.$emit('parent-focus', 'up'),
				'shift+down': () => this.$parent.$emit('parent-focus', 'down'),
				'shift+left': () => this.$parent.$emit('parent-focus', 'left'),
				'shift+right': () => this.$parent.$emit('parent-focus', 'right'),
syuilo's avatar
syuilo committed
			};
		}
	},

	watch: {
		active(v) {
			this.$emit('change-active-state', v);
		},

		dragging(v) {
syuilo's avatar
syuilo committed
			os.deckGlobalEvents.emit(v ? 'column.dragStart' : 'column.dragEnd');
syuilo's avatar
syuilo committed
		}
	},

	mounted() {
		if (!this.isMainColumn) {
syuilo's avatar
syuilo committed
			os.deckGlobalEvents.on('column.dragStart', this.onOtherDragStart);
			os.deckGlobalEvents.on('column.dragEnd', this.onOtherDragEnd);
syuilo's avatar
syuilo committed
		}
	},

syuilo's avatar
syuilo committed
	beforeUnmount() {
syuilo's avatar
syuilo committed
		if (!this.isMainColumn) {
syuilo's avatar
syuilo committed
			os.deckGlobalEvents.off('column.dragStart', this.onOtherDragStart);
			os.deckGlobalEvents.off('column.dragEnd', this.onOtherDragEnd);
syuilo's avatar
syuilo committed
		}
	},

	methods: {
		onOtherDragStart() {
			this.dropready = true;
		},

		onOtherDragEnd() {
			this.dropready = false;
		},

		toggleActive() {
			if (!this.isStacked) return;
			this.active = !this.active;
		},

		getMenu() {
			const items = [{
				icon: faPencilAlt,
				text: this.$t('rename'),
				action: () => {
syuilo's avatar
syuilo committed
					os.dialog({
syuilo's avatar
syuilo committed
						title: this.$t('rename'),
						input: {
							default: this.column.name,
							allowEmpty: false
						}
					}).then(({ canceled, result: name }) => {
						if (canceled) return;
syuilo's avatar
syuilo committed
						renameColumn(this.column.id, name);
syuilo's avatar
syuilo committed
					});
				}
			}, null, {
				icon: faArrowLeft,
tamaina's avatar
tamaina committed
				text: this.$t('_deck.swapLeft'),
syuilo's avatar
syuilo committed
				action: () => {
syuilo's avatar
syuilo committed
					swapLeftColumn(this.column.id);
syuilo's avatar
syuilo committed
				}
			}, {
				icon: faArrowRight,
tamaina's avatar
tamaina committed
				text: this.$t('_deck.swapRight'),
syuilo's avatar
syuilo committed
				action: () => {
syuilo's avatar
syuilo committed
					swapRightColumn(this.column.id);
syuilo's avatar
syuilo committed
				}
			}, this.isStacked ? {
				icon: faArrowUp,
tamaina's avatar
tamaina committed
				text: this.$t('_deck.swapUp'),
syuilo's avatar
syuilo committed
				action: () => {
syuilo's avatar
syuilo committed
					swapUpColumn(this.column.id);
syuilo's avatar
syuilo committed
				}
			} : undefined, this.isStacked ? {
				icon: faArrowDown,
tamaina's avatar
tamaina committed
				text: this.$t('_deck.swapDown'),
syuilo's avatar
syuilo committed
				action: () => {
syuilo's avatar
syuilo committed
					swapDownColumn(this.column.id);
syuilo's avatar
syuilo committed
				}
			} : undefined, null, {
				icon: faWindowRestore,
tamaina's avatar
tamaina committed
				text: this.$t('_deck.stackLeft'),
syuilo's avatar
syuilo committed
				action: () => {
syuilo's avatar
syuilo committed
					stackLeftColumn(this.column.id);
syuilo's avatar
syuilo committed
				}
			}, this.isStacked ? {
				icon: faWindowMaximize,
tamaina's avatar
tamaina committed
				text: this.$t('_deck.popRight'),
syuilo's avatar
syuilo committed
				action: () => {
syuilo's avatar
syuilo committed
					popRightColumn(this.column.id);
syuilo's avatar
syuilo committed
				}
			} : undefined, null, {
				icon: faTrashAlt,
				text: this.$t('remove'),
				action: () => {
syuilo's avatar
syuilo committed
					removeColumn(this.column.id);
syuilo's avatar
syuilo committed
				}
			}];

			if (this.menu) {
				for (const i of this.menu.reverse()) {
					items.unshift(i);
				}
			}

			return items;
		},

		onContextmenu(e) {
			if (this.isMainColumn) return;
			this.showMenu();
		},

		showMenu() {
syuilo's avatar
syuilo committed
			os.modalMenu(this.getMenu(), this.$refs.menu);
syuilo's avatar
syuilo committed
		},

		goTop() {
			this.$refs.body.scrollTo({
				top: 0,
				behavior: 'smooth'
			});
		},

		onDragstart(e) {
			// メインカラムはドラッグさせない
			if (this.isMainColumn) {
				e.preventDefault();
				return;
			}

			e.dataTransfer.effectAllowed = 'move';
syuilo's avatar
syuilo committed
			e.dataTransfer.setData(_DATA_TRANSFER_DECK_COLUMN_, this.column.id);
syuilo's avatar
syuilo committed
			this.dragging = true;
		},

		onDragend(e) {
			this.dragging = false;
		},

		onDragover(e) {
			// メインカラムにはドロップさせない
			if (this.isMainColumn) {
				e.dataTransfer.dropEffect = 'none';
				return;
			}

			// 自分自身がドラッグされている場合
			if (this.dragging) {
				// 自分自身にはドロップさせない
				e.dataTransfer.dropEffect = 'none';
				return;
			}

syuilo's avatar
syuilo committed
			const isDeckColumn = e.dataTransfer.types[0] == _DATA_TRANSFER_DECK_COLUMN_;
syuilo's avatar
syuilo committed

			e.dataTransfer.dropEffect = isDeckColumn ? 'move' : 'none';

			if (!this.dragging && isDeckColumn) this.draghover = true;
		},

		onDragleave() {
			this.draghover = false;
		},

		onDrop(e) {
			this.draghover = false;
syuilo's avatar
syuilo committed
			os.deckGlobalEvents.emit('column.dragEnd');
syuilo's avatar
syuilo committed

syuilo's avatar
syuilo committed
			const id = e.dataTransfer.getData(_DATA_TRANSFER_DECK_COLUMN_);
syuilo's avatar
syuilo committed
			if (id != null && id != '') {
syuilo's avatar
syuilo committed
				swapColumn(this.column.id, id);
syuilo's avatar
syuilo committed
			}
		}
	}
});
</script>

<style lang="scss" scoped>
.dnpfarvg {
	$header-height: 42px;

syuilo's avatar
syuilo committed
	--section-padding: 10px;

syuilo's avatar
syuilo committed
	height: 100%;
	overflow: hidden;
syuilo's avatar
syuilo committed
	contain: content;
syuilo's avatar
syuilo committed

	&.draghover {
		box-shadow: 0 0 0 2px var(--focus);

		&:after {
			content: "";
			display: block;
			position: absolute;
			z-index: 1000;
			top: 0;
			left: 0;
			width: 100%;
			height: 100%;
			background: var(--focus);
		}
	}

	&.dragging {
		box-shadow: 0 0 0 2px var(--focus);
	}

	&.dropready {
		* {
			pointer-events: none;
		}
	}

	&:not(.active) {
		flex-basis: $header-height;
		min-height: $header-height;

		> header.indicated {
			box-shadow: 4px 0px var(--accent) inset;
		}
	}

	&.naked {
		//background: var(--deckAcrylicColumnBg);
		background: transparent !important;

		> header {
			background: transparent;
			box-shadow: none;

			> button {
				color: var(--fg);
			}
		}
	}

	&.paged {
		> div {
			background: var(--bg);
		}
	}

	> header {
		position: relative;
		display: flex;
		z-index: 2;
		line-height: $header-height;
syuilo's avatar
syuilo committed
		padding: 0 16px;
		font-size: 0.9em;
		color: var(--panelHeaderFg);
		background: var(--panelHeaderBg);
		box-shadow: 0 1px 0 0 var(--panelHeaderDivider);
		cursor: pointer;

		&, * {
			user-select: none;
		}

		&.indicated {
			box-shadow: 0 3px 0 0 var(--accent);
		}

		> .header {
			display: inline-block;
			align-items: center;
			overflow: hidden;
			text-overflow: ellipsis;
			white-space: nowrap;
		}

		> span:only-of-type {
			width: 100%;
		}

		> .toggleActive,
		> .action > *,
syuilo's avatar
syuilo committed
		> .menu {
syuilo's avatar
syuilo committed
			z-index: 1;
			width: $header-height;
			line-height: $header-height;
			font-size: 16px;
			color: var(--faceTextButton);

			&:hover {
				color: var(--faceTextButtonHover);
			}

			&:active {
				color: var(--faceTextButtonActive);
			}
		}

		> .toggleActive, > .action {
			margin-left: -16px;
		}

		> .action {
			z-index: 1;
		}

		> .action:empty {
			display: none;
		}

syuilo's avatar
syuilo committed
		> .menu {
syuilo's avatar
syuilo committed
			margin-left: auto;
			margin-right: -16px;
		}
	}

	> div {
		height: calc(100% - #{$header-height});
		overflow: auto;
		overflow-x: hidden;
		-webkit-overflow-scrolling: touch;
		box-sizing: border-box;
	}
}
</style>