Browse Source

feat: added disabling to custom draggable, and handled empty slots

Kristian Vos 2 years ago
parent
commit
b7ffb0592b
2 changed files with 109 additions and 67 deletions
  1. 50 44
      frontend/src/components/AdvancedTable.vue
  2. 59 23
      frontend/src/components/Draggable.vue

+ 50 - 44
frontend/src/components/AdvancedTable.vue

@@ -92,8 +92,7 @@ const columnDragOptions = ref({
 	disabled: false,
 	ghostClass: "draggable-list-ghost",
 	filter: ".ignore-elements",
-	fallbackTolerance: 50,
-	draggable: ".is-draggable"
+	fallbackTolerance: 50
 });
 const editingFilters = ref([]);
 const appliedFilters = ref([]);
@@ -1537,61 +1536,68 @@ watch(selectedRows, (newSelectedRows, oldSelectedRows) => {
 									:attributes="{
 										class: column => ({
 											sortable: column.sortable,
-											'is-draggable': column.draggable,
 											'nav-item': true
 										})
 									}"
+									:disabled="column => !column.draggable"
 									tag="button"
 								>
 									<template #item="{ element: column }">
-										<div
+										<template
 											v-if="
 												column.name !== 'select' &&
 												column.name !== 'placeholder' &&
 												column.name !==
 													'updatedPlaceholder'
 											"
-											@click.prevent="
-												toggleColumnVisibility(column)
-											"
 										>
-											<p
-												class="control is-expanded checkbox-control"
+											<div
+												@click.prevent="
+													toggleColumnVisibility(
+														column
+													)
+												"
 											>
-												<label class="switch">
-													<input
-														type="checkbox"
-														:id="`column-dropdown-checkbox-${column.name}`"
-														:checked="
-															shownColumns.indexOf(
-																column.name
-															) !== -1
-														"
-														@click="
-															toggleColumnVisibility(
-																column
-															)
-														"
-													/>
-													<span
-														:class="{
-															slider: true,
-															round: true,
-															disabled:
-																!column.hidable
-														}"
-													></span>
-												</label>
-												<label
-													:for="`column-dropdown-checkbox-${column.name}`"
+												<p
+													class="control is-expanded checkbox-control"
 												>
-													<span></span>
-													<p>
-														{{ column.displayName }}
-													</p>
-												</label>
-											</p>
-										</div>
+													<label class="switch">
+														<input
+															type="checkbox"
+															:id="`column-dropdown-checkbox-${column.name}`"
+															:checked="
+																shownColumns.indexOf(
+																	column.name
+																) !== -1
+															"
+															@click="
+																toggleColumnVisibility(
+																	column
+																)
+															"
+														/>
+														<span
+															:class="{
+																slider: true,
+																round: true,
+																disabled:
+																	!column.hidable
+															}"
+														></span>
+													</label>
+													<label
+														:for="`column-dropdown-checkbox-${column.name}`"
+													>
+														<span></span>
+														<p>
+															{{
+																column.displayName
+															}}
+														</p>
+													</label>
+												</p>
+											</div>
+										</template>
 									</template>
 								</draggable>
 							</div>
@@ -1630,10 +1636,10 @@ watch(selectedRows, (newSelectedRows, oldSelectedRows) => {
 											: `${column.maxWidth}px`
 									}),
 									class: column => ({
-										sortable: column.sortable,
-										'is-draggable': column.draggable
+										sortable: column.sortable
 									})
 								}"
+								:disabled="column => !column.draggable"
 							>
 								<template #item="{ element: column }">
 									<template

+ 59 - 23
frontend/src/components/Draggable.vue

@@ -9,7 +9,8 @@ const props = defineProps({
 	attributes: { type: Object, default: () => ({}) },
 	options: { type: Object, default: () => ({}) },
 	tag: { type: String, default: "div" },
-	class: { type: String, default: "" }
+	class: { type: String, default: "" },
+	disabled: { type: [Boolean, Function], default: false }
 });
 
 const mounted = ref(false);
@@ -22,7 +23,12 @@ const emit = defineEmits(["update:list", "start", "end", "update"]);
 
 // When an element starts being dragged
 const onDragStart = (itemIndex: number, event: DragEvent) => {
-	// console.log(111, event);
+	const { draggable } = event.target;
+
+	if (props.disabled === true || !draggable) {
+		event.preventDefault();
+		return;
+	}
 
 	// Set the effect of moving an element, which by default is clone. Not being used right now
 	event.dataTransfer.dropEffect = "move";
@@ -43,8 +49,16 @@ const onDragStart = (itemIndex: number, event: DragEvent) => {
 };
 
 // When a dragging element hovers over another draggable element, this gets triggered, usually many times in a second
-const onDragOver = (itemIndex: number) => {
-	// console.log(321, itemIndex);
+const onDragOver = (itemIndex: number, event: DragEvent) => {
+	const getDraggableElement = element =>
+		element.classList.contains("draggable-item")
+			? element
+			: getDraggableElement(element.parentElement);
+	const draggableElement = getDraggableElement(event.target);
+	const { draggable } = draggableElement;
+
+	if (props.disabled === true || !draggable) return;
+
 	// The index and list name of the item that is being dragged, stored in window since it can come from another list as well
 	const fromIndex = window.draggingItemIndex;
 	const fromList = window.draggingItemListName;
@@ -52,8 +66,6 @@ const onDragOver = (itemIndex: number) => {
 	const toIndex = itemIndex;
 	const toList = props.name;
 
-	// console.log(3211, fromIndex, fromList, toIndex, toList);
-
 	// If the item hasn't changed position in the same list, don't continue
 	if (fromIndex === toIndex && fromList === toList) return;
 
@@ -107,26 +119,50 @@ const convertAttributes = item =>
 			typeof value === "function" ? value(item) : value
 		])
 	);
+
+function hasSlotContent(slot: Slot | undefined, slotProps = {}): boolean {
+	if (!slot) return false;
+
+	return slot(slotProps).some((vnode: VNode) => {
+		if (
+			vnode.type === Comment ||
+			vnode.type.toString() === "Symbol(Comment)"
+		)
+			return false;
+
+		if (Array.isArray(vnode.children) && !vnode.children.length)
+			return false;
+
+		return (
+			vnode.type !== Text ||
+			vnode.type.toString() === "Symbol(Text)" ||
+			(typeof vnode.children === "string" && vnode.children.trim() !== "")
+		);
+	});
+}
 </script>
 
 <template>
-	<component
-		:is="tag"
-		v-for="(item, itemIndex) in list"
-		:key="item[itemKey]"
-		draggable="true"
-		@dragstart="onDragStart(itemIndex, $event)"
-		@dragenter.prevent
-		@dragover.prevent="onDragOver(itemIndex)"
-		@dragend="onDragEnd()"
-		@drop.prevent="onDrop()"
-		:data-index="itemIndex"
-		:data-list="name"
-		v-bind="convertAttributes(item)"
-		class="draggable-item"
-	>
-		<slot name="item" :element="item"></slot>
-	</component>
+	<template v-for="(item, itemIndex) in list" :key="item[itemKey]">
+		<component
+			v-if="hasSlotContent($slots.item, { element: item })"
+			:is="tag"
+			:draggable="
+				typeof disabled === 'function' ? !disabled(item) : !disabled
+			"
+			@dragstart="onDragStart(itemIndex, $event)"
+			@dragenter.prevent
+			@dragover.prevent="onDragOver(itemIndex, $event)"
+			@dragend="onDragEnd()"
+			@drop.prevent="onDrop()"
+			:data-index="itemIndex"
+			:data-list="name"
+			class="draggable-item"
+			v-bind="convertAttributes(item)"
+		>
+			<slot name="item" :element="item"></slot>
+		</component>
+	</template>
 </template>
 
 <style>