@invopop/popui 0.1.35 → 0.1.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BaseButton.svelte +4 -0
- package/dist/BaseDropdown.svelte +42 -3
- package/dist/BaseDropdown.svelte.d.ts +1 -0
- package/dist/BaseTableHeaderOrderBy.svelte +35 -12
- package/dist/ButtonSearch.svelte +82 -0
- package/dist/ButtonSearch.svelte.d.ts +4 -0
- package/dist/ButtonUuidCopy.svelte +1 -0
- package/dist/DatePicker.svelte +96 -27
- package/dist/DatePicker.svelte.d.ts +5 -1
- package/dist/DrawerContext.svelte +443 -34
- package/dist/DrawerContextItem.svelte +36 -29
- package/dist/DropdownSelect.svelte +68 -18
- package/dist/DropdownSelect.svelte.d.ts +4 -1
- package/dist/DropdownSelectGroup.svelte +15 -0
- package/dist/DropdownSelectGroup.svelte.d.ts +7 -0
- package/dist/EmptyState.svelte +6 -2
- package/dist/InputSearch.svelte +45 -5
- package/dist/InputSelect.svelte +12 -3
- package/dist/InputText.svelte +25 -8
- package/dist/InputToggle.svelte +23 -6
- package/dist/StepIcon.svelte +35 -0
- package/dist/StepIcon.svelte.d.ts +4 -0
- package/dist/StepIconList.svelte +24 -31
- package/dist/TagStatus.svelte +1 -1
- package/dist/button/button.svelte +34 -3
- package/dist/button/button.svelte.d.ts +29 -0
- package/dist/clickOutside.d.ts +5 -2
- package/dist/clickOutside.js +9 -3
- package/dist/data-table/cells/boolean-cell.svelte +29 -0
- package/dist/data-table/cells/boolean-cell.svelte.d.ts +8 -0
- package/dist/data-table/cells/cell-skeleton.svelte +35 -0
- package/dist/data-table/cells/cell-skeleton.svelte.d.ts +4 -0
- package/dist/data-table/cells/currency-cell.svelte +10 -0
- package/dist/data-table/cells/currency-cell.svelte.d.ts +8 -0
- package/dist/data-table/cells/date-cell.svelte +10 -0
- package/dist/data-table/cells/date-cell.svelte.d.ts +8 -0
- package/dist/data-table/cells/tag-cell.svelte +12 -0
- package/dist/data-table/cells/tag-cell.svelte.d.ts +8 -0
- package/dist/data-table/cells/text-cell.svelte +10 -0
- package/dist/data-table/cells/text-cell.svelte.d.ts +8 -0
- package/dist/data-table/cells/uuid-cell.svelte +17 -0
- package/dist/data-table/cells/uuid-cell.svelte.d.ts +8 -0
- package/dist/data-table/column-definitions.d.ts +12 -0
- package/dist/data-table/column-definitions.js +42 -0
- package/dist/data-table/column-sizing-helpers.d.ts +6 -0
- package/dist/data-table/column-sizing-helpers.js +24 -0
- package/dist/data-table/create-columns.d.ts +3 -0
- package/dist/data-table/create-columns.js +67 -0
- package/dist/data-table/data-table-cell.svelte +94 -0
- package/dist/data-table/data-table-cell.svelte.d.ts +25 -0
- package/dist/data-table/data-table-header-cell.svelte +188 -0
- package/dist/data-table/data-table-header-cell.svelte.d.ts +25 -0
- package/dist/data-table/data-table-helpers.d.ts +10 -0
- package/dist/data-table/data-table-helpers.js +124 -0
- package/dist/data-table/data-table-pagination.svelte +221 -0
- package/dist/data-table/data-table-pagination.svelte.d.ts +4 -0
- package/dist/data-table/data-table-row.svelte +57 -0
- package/dist/data-table/data-table-row.svelte.d.ts +25 -0
- package/dist/data-table/data-table-svelte.svelte.d.ts +40 -0
- package/dist/data-table/data-table-svelte.svelte.js +115 -0
- package/dist/data-table/data-table-toolbar.svelte +19 -0
- package/dist/data-table/data-table-toolbar.svelte.d.ts +32 -0
- package/dist/data-table/data-table-types.d.ts +196 -0
- package/dist/data-table/data-table-types.js +1 -0
- package/dist/data-table/data-table-view-options.svelte +126 -0
- package/dist/data-table/data-table-view-options.svelte.d.ts +29 -0
- package/dist/data-table/data-table.svelte +437 -0
- package/dist/data-table/data-table.svelte.d.ts +25 -0
- package/dist/data-table/flex-render.svelte +40 -0
- package/dist/data-table/flex-render.svelte.d.ts +33 -0
- package/dist/data-table/index.d.ts +13 -0
- package/dist/data-table/index.js +13 -0
- package/dist/data-table/render-helpers.d.ts +90 -0
- package/dist/data-table/render-helpers.js +99 -0
- package/dist/data-table/table-setup.d.ts +39 -0
- package/dist/data-table/table-setup.js +151 -0
- package/dist/data-table/table-styles.d.ts +17 -0
- package/dist/data-table/table-styles.js +70 -0
- package/dist/drawer-dnd-helpers.d.ts +30 -0
- package/dist/drawer-dnd-helpers.js +72 -0
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.js +3 -0
- package/dist/index.d.ts +15 -3
- package/dist/index.js +28 -5
- package/dist/skeleton/index.d.ts +5 -0
- package/dist/skeleton/index.js +7 -0
- package/dist/skeleton/skeleton-avatar.svelte +14 -0
- package/dist/skeleton/skeleton-avatar.svelte.d.ts +7 -0
- package/dist/skeleton/skeleton-card.svelte +22 -0
- package/dist/skeleton/skeleton-card.svelte.d.ts +9 -0
- package/dist/skeleton/skeleton-list.svelte +25 -0
- package/dist/skeleton/skeleton-list.svelte.d.ts +8 -0
- package/dist/skeleton/skeleton.svelte +17 -0
- package/dist/skeleton/skeleton.svelte.d.ts +5 -0
- package/dist/svg/IconDelivery.svelte +1 -1
- package/dist/svg/IconOrder.svelte +1 -1
- package/dist/svg/IconPayment.svelte +1 -1
- package/dist/table/table-cell.svelte +4 -2
- package/dist/table/table-head.svelte +4 -2
- package/dist/table/table-header.svelte +1 -1
- package/dist/table/table-row.svelte +4 -2
- package/dist/table/table.svelte +2 -2
- package/dist/tailwind.theme.css +30 -6
- package/dist/tooltip/index.d.ts +2 -1
- package/dist/tooltip/index.js +3 -2
- package/dist/tooltip/tooltip-auto-hide.svelte +31 -0
- package/dist/tooltip/tooltip-auto-hide.svelte.d.ts +7 -0
- package/dist/types.d.ts +51 -73
- package/package.json +14 -8
- package/dist/BaseTable.svelte +0 -391
- package/dist/BaseTable.svelte.d.ts +0 -4
- package/dist/BaseTableCellContent.svelte +0 -58
- package/dist/BaseTableCellContent.svelte.d.ts +0 -4
- package/dist/BaseTableCheckbox.svelte +0 -33
- package/dist/BaseTableCheckbox.svelte.d.ts +0 -4
- package/dist/BaseTableHeaderContent.svelte +0 -67
- package/dist/BaseTableHeaderContent.svelte.d.ts +0 -4
- package/dist/BaseTableRow.svelte +0 -127
- package/dist/BaseTableRow.svelte.d.ts +0 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { DrawerContextProps, DrawerOption } from './types.ts'
|
|
2
|
+
import type { DrawerContextProps, DrawerOption, AnyProp } from './types.ts'
|
|
3
3
|
import DrawerContextItem from './DrawerContextItem.svelte'
|
|
4
4
|
import DrawerContextSeparator from './DrawerContextSeparator.svelte'
|
|
5
5
|
import EmptyState from './EmptyState.svelte'
|
|
@@ -7,17 +7,46 @@
|
|
|
7
7
|
import { Icon } from '@steeze-ui/svelte-icon'
|
|
8
8
|
import { ChevronRight } from '@steeze-ui/heroicons'
|
|
9
9
|
import { slide } from 'svelte/transition'
|
|
10
|
+
import { flip } from 'svelte/animate'
|
|
11
|
+
import {
|
|
12
|
+
draggable as makeDraggable,
|
|
13
|
+
dropTargetForElements
|
|
14
|
+
} from '@atlaskit/pragmatic-drag-and-drop/element/adapter'
|
|
15
|
+
import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine'
|
|
16
|
+
import { autoScrollForElements } from '@atlaskit/pragmatic-drag-and-drop-auto-scroll/element'
|
|
17
|
+
import { reorder } from '@atlaskit/pragmatic-drag-and-drop/reorder'
|
|
18
|
+
import {
|
|
19
|
+
attachClosestEdge,
|
|
20
|
+
extractClosestEdge,
|
|
21
|
+
type Edge
|
|
22
|
+
} from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge'
|
|
23
|
+
import { onMount, onDestroy, untrack } from 'svelte'
|
|
24
|
+
import {
|
|
25
|
+
shouldShowDropIndicator,
|
|
26
|
+
reorderItems,
|
|
27
|
+
moveItemBetweenGroups,
|
|
28
|
+
type DropIndicatorState,
|
|
29
|
+
type DndItem as DndItemType
|
|
30
|
+
} from './drawer-dnd-helpers'
|
|
31
|
+
|
|
32
|
+
const flipDurationMs = 150
|
|
10
33
|
|
|
11
34
|
let {
|
|
12
35
|
items = $bindable([]),
|
|
13
36
|
multiple = false,
|
|
37
|
+
draggable = false,
|
|
14
38
|
widthClass = 'w-60',
|
|
39
|
+
collapsibleGroups = true,
|
|
15
40
|
onclick,
|
|
16
41
|
onselect,
|
|
42
|
+
onreorder,
|
|
43
|
+
ondropitem,
|
|
17
44
|
children,
|
|
18
45
|
groups
|
|
19
46
|
}: DrawerContextProps = $props()
|
|
20
47
|
|
|
48
|
+
type DndItem = DrawerOption & { id: string }
|
|
49
|
+
|
|
21
50
|
let selectedItems = $derived(items.filter((i) => i.selected))
|
|
22
51
|
let hasGroups = $derived(groups && groups.length > 0)
|
|
23
52
|
let { groupedItems, ungroupedItems } = $derived.by(() => {
|
|
@@ -42,7 +71,79 @@
|
|
|
42
71
|
})
|
|
43
72
|
|
|
44
73
|
let openGroups = $state<Record<string, boolean>>({})
|
|
74
|
+
let groupDndItems = $state<Record<string, DndItem[]>>({})
|
|
75
|
+
let ungroupedDndItems = $state<DndItem[]>([])
|
|
76
|
+
let mounted = $state(false)
|
|
77
|
+
let itemsCache = $state<DrawerOption[]>([])
|
|
78
|
+
let isDragging = $state(false)
|
|
79
|
+
let draggedItemId = $state<string | null>(null)
|
|
80
|
+
let emitTimeout: number | undefined
|
|
81
|
+
let draggedOverGroup = $state<string | null>(null)
|
|
82
|
+
let dropIndicator = $state<DropIndicatorState>(null)
|
|
83
|
+
let cleanupFunctions: (() => void)[] = []
|
|
84
|
+
|
|
85
|
+
// Build internal DND items from external items
|
|
86
|
+
function buildListIn() {
|
|
87
|
+
if (hasGroups) {
|
|
88
|
+
// Build DND items for each group
|
|
89
|
+
groups!.forEach((group) => {
|
|
90
|
+
const groupItems = groupedItems.get(group.slug) || []
|
|
91
|
+
groupDndItems[group.slug] = groupItems.map((item: DrawerOption, i: number) => ({
|
|
92
|
+
...item,
|
|
93
|
+
id: `${group.slug}-${item.value}-${i}`
|
|
94
|
+
}))
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Build DND items for ungrouped
|
|
99
|
+
ungroupedDndItems = ungroupedItems.map((item, i) => ({
|
|
100
|
+
...item,
|
|
101
|
+
id: `ungrouped-${item.value}-${i}`
|
|
102
|
+
}))
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Build external items from internal DND items
|
|
106
|
+
function buildListOut() {
|
|
107
|
+
const newItems: DrawerOption[] = []
|
|
108
|
+
const used = new Set<AnyProp>()
|
|
109
|
+
|
|
110
|
+
// Add all grouped items
|
|
111
|
+
if (hasGroups) {
|
|
112
|
+
groups!.forEach((group) => {
|
|
113
|
+
const dndItems = groupDndItems[group.slug] || []
|
|
114
|
+
dndItems.forEach((dndItem) => {
|
|
115
|
+
if (!used.has(dndItem.value)) {
|
|
116
|
+
const { id, ...item } = dndItem
|
|
117
|
+
newItems.push({ ...item, groupBy: group.slug })
|
|
118
|
+
used.add(dndItem.value)
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Add ungrouped items
|
|
125
|
+
ungroupedDndItems.forEach((dndItem) => {
|
|
126
|
+
if (!used.has(dndItem.value)) {
|
|
127
|
+
const { id, ...item } = dndItem
|
|
128
|
+
newItems.push(item)
|
|
129
|
+
used.add(dndItem.value)
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
return newItems
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Sync items when they change from outside
|
|
137
|
+
$effect(() => {
|
|
138
|
+
if (items && mounted && !isDragging) {
|
|
139
|
+
if (JSON.stringify(items) !== JSON.stringify(itemsCache)) {
|
|
140
|
+
buildListIn()
|
|
141
|
+
itemsCache = JSON.parse(JSON.stringify(items))
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
})
|
|
45
145
|
|
|
146
|
+
// Open group with selected item on mount
|
|
46
147
|
$effect(() => {
|
|
47
148
|
if (hasGroups) {
|
|
48
149
|
const selectedItem = items.find((i) => i.selected)
|
|
@@ -52,10 +153,243 @@
|
|
|
52
153
|
}
|
|
53
154
|
})
|
|
54
155
|
|
|
156
|
+
// Notify parent of selection changes
|
|
55
157
|
$effect(() => {
|
|
56
158
|
onselect?.(selectedItems)
|
|
57
159
|
})
|
|
58
160
|
|
|
161
|
+
onMount(() => {
|
|
162
|
+
itemsCache = JSON.parse(JSON.stringify(items))
|
|
163
|
+
buildListIn()
|
|
164
|
+
mounted = true
|
|
165
|
+
|
|
166
|
+
// Set up auto-scroll
|
|
167
|
+
const autoScrollCleanup = autoScrollForElements({
|
|
168
|
+
element: document.documentElement
|
|
169
|
+
})
|
|
170
|
+
cleanupFunctions.push(autoScrollCleanup)
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
onDestroy(() => {
|
|
174
|
+
cleanupFunctions.forEach((cleanup) => cleanup())
|
|
175
|
+
cleanupFunctions = []
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
function emitGroupDistribution() {
|
|
179
|
+
if (ondropitem && hasGroups) {
|
|
180
|
+
// Clear any pending emit
|
|
181
|
+
if (emitTimeout) {
|
|
182
|
+
clearTimeout(emitTimeout)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Debounce the emit to avoid duplicate calls when dragging between groups
|
|
186
|
+
emitTimeout = window.setTimeout(() => {
|
|
187
|
+
const groupsDistribution: Record<string, DrawerOption[]> = {}
|
|
188
|
+
groups!.forEach((group) => {
|
|
189
|
+
const dndItems = groupDndItems[group.slug] || []
|
|
190
|
+
groupsDistribution[group.slug] = dndItems.map(({ id, ...item }) => item)
|
|
191
|
+
})
|
|
192
|
+
ondropitem(groupsDistribution)
|
|
193
|
+
}, 0)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Setup draggable item (Svelte action)
|
|
198
|
+
function setupDraggableItem(element: HTMLElement, params: [DndItem, string]) {
|
|
199
|
+
const [dndItem, groupSlug] = params
|
|
200
|
+
if (!element || dndItem.locked) return
|
|
201
|
+
|
|
202
|
+
const cleanup = makeDraggable({
|
|
203
|
+
element,
|
|
204
|
+
getInitialData: () => ({ id: dndItem.id, groupSlug, type: 'drawer-item' }),
|
|
205
|
+
onDragStart: () => {
|
|
206
|
+
isDragging = true
|
|
207
|
+
draggedItemId = dndItem.id
|
|
208
|
+
dropIndicator = null
|
|
209
|
+
},
|
|
210
|
+
onDrop: () => {
|
|
211
|
+
isDragging = false
|
|
212
|
+
draggedItemId = null
|
|
213
|
+
dropIndicator = null
|
|
214
|
+
}
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
destroy() {
|
|
219
|
+
cleanup()
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Setup drop zone for a container (group or ungrouped) (Svelte action)
|
|
225
|
+
function setupDropZone(element: HTMLElement, groupSlug: string) {
|
|
226
|
+
if (!element) return
|
|
227
|
+
|
|
228
|
+
const cleanup = dropTargetForElements({
|
|
229
|
+
element,
|
|
230
|
+
getData: () => {
|
|
231
|
+
const items = groupSlug === 'ungrouped' ? ungroupedDndItems : groupDndItems[groupSlug] || []
|
|
232
|
+
return { groupSlug, items: items.map((i) => i.id) }
|
|
233
|
+
},
|
|
234
|
+
canDrop: ({ source }) => source.data.type === 'drawer-item',
|
|
235
|
+
onDragEnter: () => {
|
|
236
|
+
draggedOverGroup = groupSlug
|
|
237
|
+
},
|
|
238
|
+
onDragLeave: () => {
|
|
239
|
+
if (draggedOverGroup === groupSlug) {
|
|
240
|
+
draggedOverGroup = null
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
onDrop: ({ source, location }) => {
|
|
244
|
+
draggedOverGroup = null
|
|
245
|
+
dropIndicator = null
|
|
246
|
+
const sourceId = source.data.id as string
|
|
247
|
+
const sourceGroup = source.data.groupSlug as string
|
|
248
|
+
const targetGroup = groupSlug
|
|
249
|
+
|
|
250
|
+
// Get source and target arrays
|
|
251
|
+
const sourceItems =
|
|
252
|
+
sourceGroup === 'ungrouped' ? ungroupedDndItems : groupDndItems[sourceGroup] || []
|
|
253
|
+
const targetItems =
|
|
254
|
+
targetGroup === 'ungrouped' ? ungroupedDndItems : groupDndItems[targetGroup] || []
|
|
255
|
+
|
|
256
|
+
// Find the dragged item
|
|
257
|
+
const sourceIndex = sourceItems.findIndex((item) => item.id === sourceId)
|
|
258
|
+
if (sourceIndex === -1) return
|
|
259
|
+
|
|
260
|
+
const draggedItem = sourceItems[sourceIndex]
|
|
261
|
+
|
|
262
|
+
// If moving within the same group, we need to handle reordering
|
|
263
|
+
if (sourceGroup === targetGroup) {
|
|
264
|
+
// Check if we're dropping on another item
|
|
265
|
+
const dropTargets = location.current.dropTargets
|
|
266
|
+
const itemDropTarget = dropTargets.find((target) => target.data.itemId)
|
|
267
|
+
|
|
268
|
+
if (itemDropTarget) {
|
|
269
|
+
const targetItemId = itemDropTarget.data.itemId as string
|
|
270
|
+
const edge = extractClosestEdge(itemDropTarget.data)
|
|
271
|
+
|
|
272
|
+
if (edge) {
|
|
273
|
+
const newItems = reorderItems(targetItems, sourceId, targetItemId, edge)
|
|
274
|
+
|
|
275
|
+
if (targetGroup === 'ungrouped') {
|
|
276
|
+
ungroupedDndItems = newItems
|
|
277
|
+
} else {
|
|
278
|
+
groupDndItems[targetGroup] = newItems
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
} else {
|
|
283
|
+
// Moving between groups
|
|
284
|
+
const dropTargets = location.current.dropTargets
|
|
285
|
+
const itemDropTarget = dropTargets.find((target) => target.data.itemId)
|
|
286
|
+
|
|
287
|
+
let targetItemId: string | undefined
|
|
288
|
+
let edge: Edge | undefined
|
|
289
|
+
|
|
290
|
+
if (itemDropTarget) {
|
|
291
|
+
targetItemId = itemDropTarget.data.itemId as string
|
|
292
|
+
edge = extractClosestEdge(itemDropTarget.data) || undefined
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const { newSourceItems, newTargetItems } = moveItemBetweenGroups(
|
|
296
|
+
sourceItems,
|
|
297
|
+
targetItems,
|
|
298
|
+
sourceId,
|
|
299
|
+
targetItemId,
|
|
300
|
+
edge
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
if (sourceGroup === 'ungrouped') {
|
|
304
|
+
ungroupedDndItems = newSourceItems
|
|
305
|
+
} else {
|
|
306
|
+
groupDndItems[sourceGroup] = newSourceItems
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (targetGroup === 'ungrouped') {
|
|
310
|
+
ungroupedDndItems = newTargetItems
|
|
311
|
+
} else {
|
|
312
|
+
groupDndItems[targetGroup] = newTargetItems
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Update items and notify
|
|
317
|
+
const newItems = buildListOut()
|
|
318
|
+
items = newItems
|
|
319
|
+
itemsCache = JSON.parse(JSON.stringify(items))
|
|
320
|
+
onreorder?.(newItems)
|
|
321
|
+
emitGroupDistribution()
|
|
322
|
+
}
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
destroy() {
|
|
327
|
+
cleanup()
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Setup drop zone for individual items (for reordering within same group) (Svelte action)
|
|
333
|
+
function setupItemDropZone(element: HTMLElement, params: [DndItem, string]) {
|
|
334
|
+
const [dndItem, groupSlug] = params
|
|
335
|
+
if (!element) return
|
|
336
|
+
|
|
337
|
+
const cleanup = dropTargetForElements({
|
|
338
|
+
element,
|
|
339
|
+
getData: ({ input }) => {
|
|
340
|
+
const data = { itemId: dndItem.id, groupSlug }
|
|
341
|
+
return attachClosestEdge(data, {
|
|
342
|
+
element,
|
|
343
|
+
input,
|
|
344
|
+
allowedEdges: ['top', 'bottom']
|
|
345
|
+
})
|
|
346
|
+
},
|
|
347
|
+
canDrop: ({ source }) => {
|
|
348
|
+
return source.data.type === 'drawer-item' && source.data.id !== dndItem.id
|
|
349
|
+
},
|
|
350
|
+
onDragEnter: ({ self, source }) => {
|
|
351
|
+
const edge = extractClosestEdge(self.data)
|
|
352
|
+
if (!edge) return
|
|
353
|
+
|
|
354
|
+
const sourceId = source.data.id as string
|
|
355
|
+
const sourceGroup = source.data.groupSlug as string
|
|
356
|
+
const items = groupSlug === 'ungrouped' ? ungroupedDndItems : groupDndItems[groupSlug] || []
|
|
357
|
+
|
|
358
|
+
if (shouldShowDropIndicator(sourceId, dndItem.id, sourceGroup, groupSlug, edge, items)) {
|
|
359
|
+
dropIndicator = { itemId: dndItem.id, edge }
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
onDrag: ({ self, source }) => {
|
|
363
|
+
const edge = extractClosestEdge(self.data)
|
|
364
|
+
if (!edge) return
|
|
365
|
+
|
|
366
|
+
const sourceId = source.data.id as string
|
|
367
|
+
const sourceGroup = source.data.groupSlug as string
|
|
368
|
+
const items = groupSlug === 'ungrouped' ? ungroupedDndItems : groupDndItems[groupSlug] || []
|
|
369
|
+
|
|
370
|
+
if (shouldShowDropIndicator(sourceId, dndItem.id, sourceGroup, groupSlug, edge, items)) {
|
|
371
|
+
dropIndicator = { itemId: dndItem.id, edge }
|
|
372
|
+
} else {
|
|
373
|
+
dropIndicator = null
|
|
374
|
+
}
|
|
375
|
+
},
|
|
376
|
+
onDragLeave: () => {
|
|
377
|
+
if (dropIndicator?.itemId === dndItem.id) {
|
|
378
|
+
dropIndicator = null
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
onDrop: () => {
|
|
382
|
+
dropIndicator = null
|
|
383
|
+
}
|
|
384
|
+
})
|
|
385
|
+
|
|
386
|
+
return {
|
|
387
|
+
destroy() {
|
|
388
|
+
cleanup()
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
59
393
|
function updateItem(item: DrawerOption) {
|
|
60
394
|
items = items.map((i) => {
|
|
61
395
|
if (i.value === item.value) return item
|
|
@@ -72,7 +406,7 @@
|
|
|
72
406
|
{#if item.separator}
|
|
73
407
|
<DrawerContextSeparator />
|
|
74
408
|
{:else}
|
|
75
|
-
<div class:px-1={!item.groupBy}>
|
|
409
|
+
<div class:px-1={!item.groupBy} class:cursor-grab={draggable && !item.locked}>
|
|
76
410
|
<DrawerContextItem {item} {multiple} {onclick} onchange={updateItem} />
|
|
77
411
|
</div>
|
|
78
412
|
{/if}
|
|
@@ -83,67 +417,142 @@
|
|
|
83
417
|
>
|
|
84
418
|
{@render children?.()}
|
|
85
419
|
|
|
86
|
-
{#if hasGroups}
|
|
420
|
+
{#if hasGroups && groups}
|
|
87
421
|
{#each groups as group, index}
|
|
88
422
|
{@const groupItems = groupedItems.get(group.slug) || []}
|
|
89
423
|
{@const isLastGroup = index === groups!.length - 1}
|
|
90
|
-
{@const isOpen = openGroups[group.slug]}
|
|
91
|
-
{@const hasOpenGroup = Object.values(openGroups).some((v) => v)}
|
|
424
|
+
{@const isOpen = collapsibleGroups ? openGroups[group.slug] : true}
|
|
425
|
+
{@const hasOpenGroup = collapsibleGroups ? Object.values(openGroups).some((v) => v) : true}
|
|
92
426
|
<div
|
|
93
427
|
class="px-1"
|
|
94
|
-
class:flex-1={isOpen}
|
|
428
|
+
class:flex-1={isOpen && collapsibleGroups}
|
|
95
429
|
class:flex={isOpen}
|
|
96
430
|
class:flex-col={isOpen}
|
|
97
|
-
class:min-h-0={isOpen}
|
|
98
|
-
class:flex-shrink-0={!isOpen && hasOpenGroup}
|
|
431
|
+
class:min-h-0={isOpen && collapsibleGroups}
|
|
432
|
+
class:flex-shrink-0={!isOpen && hasOpenGroup && collapsibleGroups}
|
|
99
433
|
>
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
434
|
+
{#if collapsibleGroups}
|
|
435
|
+
<button
|
|
436
|
+
class="cursor-pointer flex items-center justify-between h-8 pl-2.5 pr-2.5 py-2.5 text-base font-medium text-foreground-default-secondary w-full hover:bg-background-default-secondary rounded-lg overflow-clip flex-shrink-0"
|
|
437
|
+
onclick={() => toggleGroup(group.slug)}
|
|
438
|
+
>
|
|
439
|
+
<div class="flex items-center gap-1.5">
|
|
440
|
+
<span>{group.label}</span>
|
|
441
|
+
<Icon
|
|
442
|
+
src={ChevronRight}
|
|
443
|
+
class="size-3 text-icon-default-secondary transition-all transform {isOpen
|
|
444
|
+
? 'rotate-90'
|
|
445
|
+
: ''}"
|
|
446
|
+
/>
|
|
447
|
+
</div>
|
|
448
|
+
{#if groupItems.length && !group.hideCounter}
|
|
449
|
+
<BaseCounter value={groupItems.length} />
|
|
450
|
+
{/if}
|
|
451
|
+
</button>
|
|
452
|
+
{:else}
|
|
453
|
+
<div
|
|
454
|
+
class="flex items-center justify-between h-8 pl-2.5 pr-2.5 py-2.5 text-base font-medium text-foreground-default-secondary w-full overflow-clip flex-shrink-0"
|
|
455
|
+
>
|
|
105
456
|
<span>{group.label}</span>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
? 'rotate-90'
|
|
110
|
-
: ''}"
|
|
111
|
-
/>
|
|
457
|
+
{#if groupItems.length && !group.hideCounter}
|
|
458
|
+
<BaseCounter value={groupItems.length} />
|
|
459
|
+
{/if}
|
|
112
460
|
</div>
|
|
113
|
-
|
|
114
|
-
<BaseCounter value={groupItems.length} />
|
|
115
|
-
{/if}
|
|
116
|
-
</button>
|
|
461
|
+
{/if}
|
|
117
462
|
|
|
118
463
|
{#if isOpen}
|
|
119
|
-
<div
|
|
120
|
-
{
|
|
464
|
+
<div
|
|
465
|
+
class="w-full overflow-y-auto {collapsibleGroups ? 'flex-1 min-h-0' : ''}"
|
|
466
|
+
transition:slide={{ duration: collapsibleGroups ? 200 : 0 }}
|
|
467
|
+
>
|
|
468
|
+
{#if draggable}
|
|
469
|
+
<div use:setupDropZone={group.slug} class="min-h-[40px]">
|
|
470
|
+
{#if !groupItems.length}
|
|
471
|
+
<div class="px-1 pt-1 pb-5">
|
|
472
|
+
<EmptyState
|
|
473
|
+
iconSource={group.emptyIcon}
|
|
474
|
+
title={group.emptyTitle}
|
|
475
|
+
description={group.emptyDescription}
|
|
476
|
+
/>
|
|
477
|
+
</div>
|
|
478
|
+
{:else}
|
|
479
|
+
{#each groupDndItems[group.slug] || [] as dndItem (dndItem.id)}
|
|
480
|
+
{@const showTopIndicator =
|
|
481
|
+
dropIndicator?.itemId === dndItem.id && dropIndicator?.edge === 'top'}
|
|
482
|
+
{@const showBottomIndicator =
|
|
483
|
+
dropIndicator?.itemId === dndItem.id && dropIndicator?.edge === 'bottom'}
|
|
484
|
+
{@const isBeingDragged = draggedItemId === dndItem.id}
|
|
485
|
+
<div
|
|
486
|
+
animate:flip={{ duration: isDragging ? flipDurationMs : 0 }}
|
|
487
|
+
use:setupDraggableItem={[dndItem, group.slug]}
|
|
488
|
+
use:setupItemDropZone={[dndItem, group.slug]}
|
|
489
|
+
class:border-t-2={showTopIndicator}
|
|
490
|
+
class:border-t-accent-50={showTopIndicator}
|
|
491
|
+
class:border-b-2={showBottomIndicator}
|
|
492
|
+
class:border-b-accent-50={showBottomIndicator}
|
|
493
|
+
class:opacity-40={isBeingDragged}
|
|
494
|
+
class="transition-opacity"
|
|
495
|
+
>
|
|
496
|
+
{@render drawerItem(dndItem)}
|
|
497
|
+
</div>
|
|
498
|
+
{/each}
|
|
499
|
+
{/if}
|
|
500
|
+
</div>
|
|
501
|
+
{:else if !groupItems.length}
|
|
121
502
|
<div class="px-1 pt-1 pb-5">
|
|
122
503
|
<EmptyState
|
|
123
504
|
iconSource={group.emptyIcon}
|
|
124
|
-
title={group.emptyTitle
|
|
125
|
-
description={group.emptyDescription
|
|
505
|
+
title={group.emptyTitle}
|
|
506
|
+
description={group.emptyDescription}
|
|
126
507
|
/>
|
|
127
508
|
</div>
|
|
128
509
|
{:else}
|
|
129
|
-
{#each groupItems as item}
|
|
510
|
+
{#each groupItems as item (item.value)}
|
|
130
511
|
{@render drawerItem(item)}
|
|
131
512
|
{/each}
|
|
132
513
|
{/if}
|
|
133
514
|
</div>
|
|
134
515
|
{/if}
|
|
135
516
|
</div>
|
|
136
|
-
{#if !isLastGroup}
|
|
517
|
+
{#if !isLastGroup && collapsibleGroups}
|
|
137
518
|
<DrawerContextSeparator />
|
|
138
519
|
{/if}
|
|
139
520
|
{/each}
|
|
140
521
|
{/if}
|
|
141
522
|
|
|
142
523
|
{#if ungroupedItems.length}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
524
|
+
{#if draggable}
|
|
525
|
+
<div
|
|
526
|
+
class="flex-shrink-0 overflow-y-auto max-h-[564px] min-h-[40px]"
|
|
527
|
+
use:setupDropZone={'ungrouped'}
|
|
528
|
+
>
|
|
529
|
+
{#each ungroupedDndItems as dndItem (dndItem.id)}
|
|
530
|
+
{@const showTopIndicator =
|
|
531
|
+
dropIndicator?.itemId === dndItem.id && dropIndicator?.edge === 'top'}
|
|
532
|
+
{@const showBottomIndicator =
|
|
533
|
+
dropIndicator?.itemId === dndItem.id && dropIndicator?.edge === 'bottom'}
|
|
534
|
+
{@const isBeingDragged = draggedItemId === dndItem.id}
|
|
535
|
+
<div
|
|
536
|
+
animate:flip={{ duration: isDragging ? flipDurationMs : 0 }}
|
|
537
|
+
use:setupDraggableItem={[dndItem, 'ungrouped']}
|
|
538
|
+
use:setupItemDropZone={[dndItem, 'ungrouped']}
|
|
539
|
+
class:border-t-2={showTopIndicator}
|
|
540
|
+
class:border-t-accent-50={showTopIndicator}
|
|
541
|
+
class:border-b-2={showBottomIndicator}
|
|
542
|
+
class:border-b-accent-50={showBottomIndicator}
|
|
543
|
+
class:opacity-40={isBeingDragged}
|
|
544
|
+
class="transition-opacity"
|
|
545
|
+
>
|
|
546
|
+
{@render drawerItem(dndItem)}
|
|
547
|
+
</div>
|
|
548
|
+
{/each}
|
|
549
|
+
</div>
|
|
550
|
+
{:else}
|
|
551
|
+
<div class="flex-shrink-0 overflow-y-auto max-h-[564px]">
|
|
552
|
+
{#each ungroupedItems as item (item.value)}
|
|
553
|
+
{@render drawerItem(item)}
|
|
554
|
+
{/each}
|
|
555
|
+
</div>
|
|
556
|
+
{/if}
|
|
148
557
|
{/if}
|
|
149
558
|
</div>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import type { DrawerContextItemProps } from './types.ts'
|
|
3
3
|
import InputCheckbox from './InputCheckbox.svelte'
|
|
4
4
|
import { Icon } from '@steeze-ui/svelte-icon'
|
|
5
|
-
import { onMount } from 'svelte'
|
|
5
|
+
import { onMount, onDestroy } from 'svelte'
|
|
6
6
|
import { Success } from '@invopop/ui-icons'
|
|
7
7
|
import clsx from 'clsx'
|
|
8
8
|
import BaseFlag from './BaseFlag.svelte'
|
|
@@ -23,32 +23,33 @@
|
|
|
23
23
|
let styles = $derived(
|
|
24
24
|
clsx(
|
|
25
25
|
'px-2 py-1.5 space-x-1.5',
|
|
26
|
-
{ 'bg-background-selected': item
|
|
26
|
+
{ 'bg-background-selected': item?.selected && !multiple },
|
|
27
27
|
{
|
|
28
28
|
'group-hover:bg-background-default-secondary':
|
|
29
|
-
(!item
|
|
29
|
+
(!item?.selected && !item?.disabled) || multiple
|
|
30
30
|
}
|
|
31
31
|
)
|
|
32
32
|
)
|
|
33
|
+
|
|
33
34
|
let labelStyles = $derived(
|
|
34
35
|
clsx(
|
|
35
|
-
{ 'text-foreground-critical': item
|
|
36
|
-
{ 'text-foreground': !item
|
|
37
|
-
{ 'opacity-30': item
|
|
36
|
+
{ 'text-foreground-critical': item?.destructive },
|
|
37
|
+
{ 'text-foreground': !item?.destructive },
|
|
38
|
+
{ 'opacity-30': item?.locked }
|
|
38
39
|
)
|
|
39
40
|
)
|
|
40
|
-
let title = $derived(item.label.length > 25 ? item.label : undefined)
|
|
41
|
+
let title = $derived(item?.label && item.label.length > 25 ? item.label : undefined)
|
|
41
42
|
|
|
42
43
|
onMount(() => {
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
if (item.selected) {
|
|
44
|
+
if (scrollIfSelected && item?.selected) {
|
|
46
45
|
el?.scrollIntoView()
|
|
47
46
|
}
|
|
48
47
|
})
|
|
49
48
|
|
|
50
49
|
function handleClick(event: MouseEvent) {
|
|
51
50
|
event.stopPropagation()
|
|
51
|
+
if (!item) return
|
|
52
|
+
|
|
52
53
|
if (multiple) {
|
|
53
54
|
item.selected = !item.selected
|
|
54
55
|
onchange?.(item)
|
|
@@ -61,49 +62,55 @@
|
|
|
61
62
|
<button
|
|
62
63
|
bind:this={el}
|
|
63
64
|
class="cursor-pointer w-full disabled:opacity-30 group"
|
|
64
|
-
disabled={item
|
|
65
|
+
disabled={item?.disabled}
|
|
65
66
|
onclick={handleClick}
|
|
66
67
|
>
|
|
67
|
-
<div class="
|
|
68
|
-
{
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
<div class="bg-background rounded-md">
|
|
69
|
+
<div class="{styles} rounded-md pr-2 flex items-center justify-start w-full">
|
|
70
|
+
{#if item?.useAvatar}
|
|
71
|
+
<ProfileAvatar name={item?.label || ''} picture={item?.picture || ''} variant="sm" />
|
|
72
|
+
{:else if item?.picture}
|
|
73
|
+
<ProfileAvatar name={item?.label || ''} picture={item?.picture} variant="sm" />
|
|
74
|
+
{:else if item?.icon}
|
|
73
75
|
<Icon
|
|
74
76
|
src={item.icon}
|
|
75
|
-
class="w-4 h-4 {item
|
|
77
|
+
class="w-4 h-4 {item?.destructive
|
|
76
78
|
? 'text-icon-critical'
|
|
77
|
-
: item
|
|
79
|
+
: item?.iconClass || 'text-icon'} {item?.locked ? 'opacity-30' : ''}"
|
|
78
80
|
/>
|
|
79
81
|
{/if}
|
|
80
82
|
<div class="whitespace-nowrap flex-1 text-left flex items-center space-x-1.5 truncate" {title}>
|
|
81
|
-
{#if item
|
|
83
|
+
{#if item?.color}
|
|
82
84
|
<TagStatus status={item.color} dot />
|
|
83
85
|
{/if}
|
|
84
|
-
<span class="{labelStyles} text-base font-medium truncate">{item
|
|
86
|
+
<span class="{labelStyles} text-base font-medium truncate">{item?.label || ''}</span>
|
|
85
87
|
|
|
86
|
-
{#if item
|
|
88
|
+
{#if item?.country}
|
|
87
89
|
<BaseFlag country={item.country} />
|
|
88
90
|
<span class="text-xs font-medium text-foreground-default-secondary uppercase">
|
|
89
91
|
{item.country}
|
|
90
92
|
</span>
|
|
91
93
|
{/if}
|
|
92
94
|
</div>
|
|
93
|
-
{#if item
|
|
94
|
-
|
|
95
|
+
{#if item?.action}
|
|
96
|
+
<div class="no-drag !cursor-default">
|
|
97
|
+
{@render item.action(item)}
|
|
98
|
+
</div>
|
|
95
99
|
{:else if multiple}
|
|
96
100
|
<InputCheckbox
|
|
97
|
-
checked={item
|
|
101
|
+
checked={item?.selected ?? false}
|
|
98
102
|
onchange={(value) => {
|
|
99
|
-
item
|
|
100
|
-
|
|
103
|
+
if (item) {
|
|
104
|
+
item.selected = value
|
|
105
|
+
onchange?.(item)
|
|
106
|
+
}
|
|
101
107
|
}}
|
|
102
108
|
/>
|
|
103
|
-
{:else if item
|
|
109
|
+
{:else if item?.selected}
|
|
104
110
|
<Icon src={Success} class="size-4 text-icon-selected" />
|
|
105
|
-
{:else if item
|
|
111
|
+
{:else if item?.rightIcon}
|
|
106
112
|
<Icon src={item.rightIcon} class="size-4 text-icon-default-secondary" />
|
|
107
113
|
{/if}
|
|
114
|
+
</div>
|
|
108
115
|
</div>
|
|
109
116
|
</button>
|