Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions app/components/Draggable/ExampleOne.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<script setup lang="ts">
import {
DraggableArea,
DraggableItem,
useDraggable,
} from "@/components/draggable"
import { ref } from "vue"

const {
draggableItem,
draggableIndex,
resetDraggable,
} = useDraggable()

const items = ref([
{
id: 1,
value: 'Item 1',
},
{
id: 2,
value: 'Item 2',
},
{
id: 3,
value: 'Item 3',
},
{
id: 4,
value: 'Item 4',
},
{
id: 5,
value: 'Item 5',
},
])

const handleDrop = (e: DragEvent) => {
if (!draggableItem.value || draggableIndex.value == null) return

// Remove from the original
items.value = items.value.filter((m) => m.id !== draggableItem.value.id)

items.value.splice(
draggableIndex.value,
0,
draggableItem.value,
)

resetDraggable()
}
</script>

<template>
<DraggableArea data="area-1" @drop="handleDrop" class="w-full space-y-2 rounded border-2 border-dashed p-4">
<DraggableItem
v-for="(item, idx) in items"
:key="idx"
:data="item"
:index="idx"
class="cursor-move rounded border bg-white p-2"
>
{{ item.value }}
</DraggableItem>
</DraggableArea>
</template>
84 changes: 84 additions & 0 deletions app/components/Draggable/ExampleTwo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<script setup lang="ts">
import {
DraggableArea,
DraggableItem,
useDraggable,
} from "@/components/draggable"
import { ref } from "vue"

const {
draggedFromArea,
draggedToArea,
draggableItem,
draggableIndex,
resetDraggable,
} = useDraggable()

const jobs = ref({
'backlog': [
{
id: 1,
value: 'Item 1',
},
{
id: 2,
value: 'Item 2',
},
],
'in_progress': [
{
id: 3,
value: 'Item 3',
},
{
id: 4,
value: 'Item 4',
},
],
'finished': [
{
id: 5,
value: 'Item 5',
},
{
id: 6,
value: 'Item 6',
},
],
})

const handleDrop = (e: DragEvent) => {
if (!draggableItem.value || draggableIndex.value == null) return

// Remove from the original
jobs.value[draggedFromArea.value] = jobs.value[draggedFromArea.value].filter((m) => m.id !== draggableItem.value.id)

jobs.value[draggedToArea.value].splice(
draggableIndex.value,
0,
draggableItem.value,
)

resetDraggable()
}
</script>

<template>
<div class="flex gap-4">
<DraggableArea v-for="(items, job) in jobs" :key="job" :data="job" @drop="handleDrop" class="w-full space-y-2 rounded border-2 border-dashed p-4">
<span>
{{ job }}
</span>

<DraggableItem
v-for="(item, idx) in items"
:key="idx"
:data="item"
:index="idx"
class="cursor-move rounded border bg-white p-2"
>
{{ item.value }}
</DraggableItem>
</DraggableArea>
</div>
</template>
32 changes: 32 additions & 0 deletions app/pages/components/Draggable.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<script setup lang="ts">
import ExampleOne from "@app/components/Draggable/ExampleOne.vue"
import ExampleTwo from "@app/components/Draggable/ExampleTwo.vue"
</script>

<template>
<div class="space-y-4">
<div>
<h2>
Example One
</h2>

<h3>
Single Draggable Area
</h3>

<ExampleOne />
</div>

<div>
<h2>
Example Two
</h2>

<h3>
Multiple Draggable Areas
</h3>

<ExampleTwo />
</div>
</div>
</template>
1 change: 1 addition & 0 deletions app/pages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export { default as Combobox } from "./components/Combobox.vue"
export { default as Command } from "./components/Command.vue"
export { default as ConfirmDialog } from "./components/ConfirmDialog.vue"
export { default as Dialog } from "./components/Dialog.vue"
export { default as Draggable } from "./components/Draggable.vue"
export { default as Drawer } from "./components/Drawer.vue"
export { default as DropdownMenu } from "./components/DropdownMenu.vue"
export { default as Flasher } from "./components/Flasher.vue"
Expand Down
7 changes: 7 additions & 0 deletions app/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
Command,
ConfirmDialog,
Dialog,
Draggable,
Drawer,
DropdownMenu,
Flasher,
Expand Down Expand Up @@ -132,6 +133,12 @@ const routes = [
component: ConfirmDialog,
meta: { layout: ComponentLayout, shadcn: false },
},
{
name: "Draggable",
path: "/components/draggable",
component: Draggable,
meta: { layout: ComponentLayout, shadcn: false },
},
{
name: "Dropdown Menu",
path: "/components/dropdown-menu",
Expand Down
43 changes: 43 additions & 0 deletions src/components/draggable/DraggableArea.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<script lang="ts">
import { createContext } from '@/lib/createContext'

export const [injectDraggableRootContext, provideDraggableRootContext] =
createContext('DraggableRoot')
</script>

<script setup lang="ts">
import { useDraggable } from '.'
import { defineEmits } from 'vue'

const props = defineProps<{
class?: string
data: any
}>()

const emit = defineEmits<{
(e: 'drop', payload: { event: DragEvent; data: any }): void
}>()

const draggable = useDraggable()

provideDraggableRootContext(draggable)

const handleDrop = (event: DragEvent) => emit('drop', event)

const allowDrop = (event: DragEvent) => {
event.preventDefault()
draggable.draggedToArea.value = props.data
}

const onDragStart = () => (draggable.draggedFromArea.value = props.data)
</script>

<template>
<div
:class="props.class"
@drop="handleDrop"
@dragstart="onDragStart"
@dragover="allowDrop">
<slot />
</div>
</template>
50 changes: 50 additions & 0 deletions src/components/draggable/DraggableItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<script setup lang="ts">
import { defineProps, defineEmits } from 'vue'
import { injectDraggableRootContext } from './DraggableArea.vue'

const props = defineProps<{
class?: string
data: any
index: number
}>()

const emit = defineEmits<{
(e: 'dragstart', { event: DragEvent, data: any }): void
(e: 'dragend', event: DragEvent): void
(e: 'dragover', index: number): void
}>()

const onDragStart = (event: DragEvent) => {
context.draggableItem.value = props.data
emit('dragstart', {
event: event,
data: props.data,
})
}

const context = injectDraggableRootContext()

const onDragOver = () => {
context.draggableIndex.value = props.index

emit('dragover', props.index)
}

const onDragEnd = (event: DragEvent) => {
context.draggableItem.value = null
context.draggableIndex.value = null

emit('dragend', event)
}
</script>

<template>
<div
:class="props.class"
draggable="true"
@dragstart="onDragStart"
@dragend="onDragEnd"
@dragover="onDragOver">
<slot :item="data" />
</div>
</template>
4 changes: 4 additions & 0 deletions src/components/draggable/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { default as DraggableArea } from './DraggableArea.vue'
export { default as DraggableItem } from './DraggableItem.vue'

export { useDraggable } from './useDraggable'
31 changes: 31 additions & 0 deletions src/components/draggable/useDraggable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { ref, type Ref } from 'vue'

export type DraggableContext = {
draggedFromArea: Ref<any>
draggedToArea: Ref<any>
draggableItem: Ref<any | null>
draggableIndex: Ref<number | null>
resetDraggable: () => void
}

const draggedFromArea = ref<any>(null)
const draggedToArea = ref<any>(null)
const draggableItem = ref<any | null>(null)
const draggableIndex = ref<number | null>(null)

const resetDraggable = () => {
draggedFromArea.value = null
draggedToArea.value = null
draggableItem.value = null
draggableIndex.value = null
}

export function useDraggable(): DraggableContext {
return {
draggedFromArea,
draggedToArea,
draggableItem,
draggableIndex,
resetDraggable,
}
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export * from "@/components/collapsible"
export * from "@/components/combobox"
export * from "@/components/command"
export * from "@/components/dialog"
export * from "@/components/draggable"
export * from "@/components/drawer"
export * from "@/components/dropdown-menu"
export * from "@/components/input"
Expand Down