@invopop/popui 0.1.4-beta.46 → 0.1.4-beta.48
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/BaseDropdown.svelte +29 -2
- package/dist/ButtonSearch.svelte +3 -3
- package/dist/DrawerContext.svelte +255 -57
- package/dist/data-table/data-table-types.d.ts +1 -0
- package/dist/data-table/data-table.svelte +6 -4
- package/dist/drawer-dnd-helpers.d.ts +30 -0
- package/dist/drawer-dnd-helpers.js +72 -0
- package/package.json +5 -2
package/dist/BaseDropdown.svelte
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
import { createFloatingActions } from 'svelte-floating-ui'
|
|
4
4
|
import { clickOutside } from './clickOutside.js'
|
|
5
5
|
import { portal } from 'svelte-portal'
|
|
6
|
-
import { slide } from 'svelte/transition'
|
|
7
6
|
import type { BaseDropdownProps } from './types.js'
|
|
7
|
+
import type { TransitionConfig } from 'svelte/transition'
|
|
8
8
|
|
|
9
9
|
let {
|
|
10
10
|
isOpen = $bindable(false),
|
|
@@ -46,6 +46,33 @@
|
|
|
46
46
|
middleware
|
|
47
47
|
})
|
|
48
48
|
|
|
49
|
+
// Custom transition that mimics shadcn style
|
|
50
|
+
function dropdownTransition(
|
|
51
|
+
node: HTMLElement,
|
|
52
|
+
{ duration = 150 }: { duration?: number } = {}
|
|
53
|
+
): TransitionConfig {
|
|
54
|
+
const side = placement.split('-')[0]
|
|
55
|
+
|
|
56
|
+
// Calculate slide direction
|
|
57
|
+
let slideY = 0
|
|
58
|
+
let slideX = 0
|
|
59
|
+
if (side === 'bottom') slideY = -8
|
|
60
|
+
if (side === 'top') slideY = 8
|
|
61
|
+
if (side === 'left') slideX = 8
|
|
62
|
+
if (side === 'right') slideX = -8
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
duration,
|
|
66
|
+
css: (t) => {
|
|
67
|
+
const eased = t * (2 - t) // ease-out
|
|
68
|
+
return `
|
|
69
|
+
opacity: ${eased};
|
|
70
|
+
transform: scale(${0.95 + eased * 0.05}) translate(${slideX * (1 - eased)}px, ${slideY * (1 - eased)}px);
|
|
71
|
+
`
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
49
76
|
export const toggle = () => {
|
|
50
77
|
isOpen = !isOpen
|
|
51
78
|
}
|
|
@@ -83,7 +110,7 @@
|
|
|
83
110
|
}, 100)
|
|
84
111
|
isOpen = false
|
|
85
112
|
}}
|
|
86
|
-
transition:
|
|
113
|
+
transition:dropdownTransition={{ duration: 150 }}
|
|
87
114
|
>
|
|
88
115
|
{@render children?.()}
|
|
89
116
|
</div>
|
package/dist/ButtonSearch.svelte
CHANGED
|
@@ -45,14 +45,14 @@
|
|
|
45
45
|
</script>
|
|
46
46
|
|
|
47
47
|
<div
|
|
48
|
-
class="overflow-hidden transition-all duration-
|
|
48
|
+
class="overflow-hidden transition-all duration-150 ease-in-out relative rounded-md"
|
|
49
49
|
class:w-[280px]={expanded}
|
|
50
50
|
class:w-10={!expanded}
|
|
51
51
|
use:clickOutside
|
|
52
52
|
onclick_outside={handleClickOutside}
|
|
53
53
|
>
|
|
54
54
|
<div
|
|
55
|
-
class="w-[280px] transition-opacity duration-
|
|
55
|
+
class="w-[280px] transition-opacity duration-100 absolute left-0 top-0"
|
|
56
56
|
class:opacity-0={!expanded}
|
|
57
57
|
class:opacity-100={expanded}
|
|
58
58
|
class:pointer-events-none={!expanded}
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
/>
|
|
69
69
|
</div>
|
|
70
70
|
<div
|
|
71
|
-
class="transition-opacity duration-
|
|
71
|
+
class="transition-opacity duration-100"
|
|
72
72
|
class:opacity-0={expanded}
|
|
73
73
|
class:opacity-100={!expanded}
|
|
74
74
|
class:pointer-events-none={expanded}
|
|
@@ -8,8 +8,26 @@
|
|
|
8
8
|
import { ChevronRight } from '@steeze-ui/heroicons'
|
|
9
9
|
import { slide } from 'svelte/transition'
|
|
10
10
|
import { flip } from 'svelte/animate'
|
|
11
|
-
import {
|
|
12
|
-
|
|
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'
|
|
13
31
|
|
|
14
32
|
const flipDurationMs = 150
|
|
15
33
|
|
|
@@ -58,7 +76,11 @@
|
|
|
58
76
|
let mounted = $state(false)
|
|
59
77
|
let itemsCache = $state<DrawerOption[]>([])
|
|
60
78
|
let isDragging = $state(false)
|
|
79
|
+
let draggedItemId = $state<string | null>(null)
|
|
61
80
|
let emitTimeout: number | undefined
|
|
81
|
+
let draggedOverGroup = $state<string | null>(null)
|
|
82
|
+
let dropIndicator = $state<DropIndicatorState>(null)
|
|
83
|
+
let cleanupFunctions: (() => void)[] = []
|
|
62
84
|
|
|
63
85
|
// Build internal DND items from external items
|
|
64
86
|
function buildListIn() {
|
|
@@ -140,14 +162,18 @@
|
|
|
140
162
|
itemsCache = JSON.parse(JSON.stringify(items))
|
|
141
163
|
buildListIn()
|
|
142
164
|
mounted = true
|
|
165
|
+
|
|
166
|
+
// Set up auto-scroll
|
|
167
|
+
const autoScrollCleanup = autoScrollForElements({
|
|
168
|
+
element: document.documentElement
|
|
169
|
+
})
|
|
170
|
+
cleanupFunctions.push(autoScrollCleanup)
|
|
143
171
|
})
|
|
144
172
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
}
|
|
173
|
+
onDestroy(() => {
|
|
174
|
+
cleanupFunctions.forEach((cleanup) => cleanup())
|
|
175
|
+
cleanupFunctions = []
|
|
176
|
+
})
|
|
151
177
|
|
|
152
178
|
function emitGroupDistribution() {
|
|
153
179
|
if (ondropitem && hasGroups) {
|
|
@@ -168,40 +194,200 @@
|
|
|
168
194
|
}
|
|
169
195
|
}
|
|
170
196
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
+
}
|
|
174
221
|
}
|
|
175
|
-
groupDndItems[groupSlug] = e.detail.items
|
|
176
222
|
}
|
|
177
223
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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)
|
|
181
286
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
+
}
|
|
188
308
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
+
}
|
|
192
329
|
}
|
|
193
|
-
ungroupedDndItems = e.detail.items
|
|
194
330
|
}
|
|
195
331
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
+
})
|
|
199
385
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
386
|
+
return {
|
|
387
|
+
destroy() {
|
|
388
|
+
cleanup()
|
|
389
|
+
}
|
|
390
|
+
}
|
|
205
391
|
}
|
|
206
392
|
|
|
207
393
|
function updateItem(item: DrawerOption) {
|
|
@@ -231,7 +417,7 @@
|
|
|
231
417
|
>
|
|
232
418
|
{@render children?.()}
|
|
233
419
|
|
|
234
|
-
{#if hasGroups}
|
|
420
|
+
{#if hasGroups && groups}
|
|
235
421
|
{#each groups as group, index}
|
|
236
422
|
{@const groupItems = groupedItems.get(group.slug) || []}
|
|
237
423
|
{@const isLastGroup = index === groups!.length - 1}
|
|
@@ -280,17 +466,7 @@
|
|
|
280
466
|
transition:slide={{ duration: collapsibleGroups ? 200 : 0 }}
|
|
281
467
|
>
|
|
282
468
|
{#if draggable}
|
|
283
|
-
<div
|
|
284
|
-
use:dndzone={{
|
|
285
|
-
items: groupDndItems[group.slug] || [],
|
|
286
|
-
flipDurationMs,
|
|
287
|
-
dropTargetStyle: {},
|
|
288
|
-
type: 'drawer-item',
|
|
289
|
-
transformDraggedElement
|
|
290
|
-
}}
|
|
291
|
-
onconsider={(e) => handleDndConsider(group.slug, e)}
|
|
292
|
-
onfinalize={(e) => handleDndFinalize(group.slug, e)}
|
|
293
|
-
>
|
|
469
|
+
<div use:setupDropZone={group.slug} class="min-h-[40px]">
|
|
294
470
|
{#if !groupItems.length}
|
|
295
471
|
<div class="px-1 pt-1 pb-5">
|
|
296
472
|
<EmptyState
|
|
@@ -301,7 +477,22 @@
|
|
|
301
477
|
</div>
|
|
302
478
|
{:else}
|
|
303
479
|
{#each groupDndItems[group.slug] || [] as dndItem (dndItem.id)}
|
|
304
|
-
|
|
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
|
+
>
|
|
305
496
|
{@render drawerItem(dndItem)}
|
|
306
497
|
</div>
|
|
307
498
|
{/each}
|
|
@@ -332,19 +523,26 @@
|
|
|
332
523
|
{#if ungroupedItems.length}
|
|
333
524
|
{#if draggable}
|
|
334
525
|
<div
|
|
335
|
-
class="flex-shrink-0 overflow-y-auto max-h-[564px]"
|
|
336
|
-
use:
|
|
337
|
-
items: ungroupedDndItems,
|
|
338
|
-
flipDurationMs,
|
|
339
|
-
dropTargetStyle: {},
|
|
340
|
-
type: 'drawer-item',
|
|
341
|
-
transformDraggedElement
|
|
342
|
-
}}
|
|
343
|
-
onconsider={handleUngroupedDndConsider}
|
|
344
|
-
onfinalize={handleUngroupedDndFinalize}
|
|
526
|
+
class="flex-shrink-0 overflow-y-auto max-h-[564px] min-h-[40px]"
|
|
527
|
+
use:setupDropZone={'ungrouped'}
|
|
345
528
|
>
|
|
346
529
|
{#each ungroupedDndItems as dndItem (dndItem.id)}
|
|
347
|
-
|
|
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
|
+
>
|
|
348
546
|
{@render drawerItem(dndItem)}
|
|
349
547
|
</div>
|
|
350
548
|
{/each}
|
|
@@ -79,6 +79,7 @@ export interface DataTableProps<TData> {
|
|
|
79
79
|
initialFrozenColumns?: string[];
|
|
80
80
|
initialColumnOrder?: string[];
|
|
81
81
|
initialColumnVisibility?: Record<string, boolean>;
|
|
82
|
+
initialColumnSizing?: Record<string, number>;
|
|
82
83
|
pageSizeOptions?: number[];
|
|
83
84
|
emptyState?: Omit<EmptyStateProps, 'children' | 'check'>;
|
|
84
85
|
onRowClick?: (row: TData) => void;
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
initialFrozenColumns = [],
|
|
47
47
|
initialColumnOrder = [],
|
|
48
48
|
initialColumnVisibility = {},
|
|
49
|
+
initialColumnSizing = {},
|
|
49
50
|
emptyState = {
|
|
50
51
|
iconSource: Search,
|
|
51
52
|
title: 'No results',
|
|
@@ -83,7 +84,7 @@
|
|
|
83
84
|
: []
|
|
84
85
|
)
|
|
85
86
|
let pagination = $state<PaginationState>({ pageIndex: initialPage, pageSize: initialPageSize })
|
|
86
|
-
let columnSizing = $state<ColumnSizingState>(
|
|
87
|
+
let columnSizing = $state<ColumnSizingState>(initialColumnSizing)
|
|
87
88
|
let columnSizingInfo = $state<ColumnSizingInfoState>({
|
|
88
89
|
columnSizingStart: [],
|
|
89
90
|
deltaOffset: null,
|
|
@@ -272,8 +273,9 @@
|
|
|
272
273
|
if (header.id === columnId) {
|
|
273
274
|
break
|
|
274
275
|
}
|
|
275
|
-
//
|
|
276
|
-
|
|
276
|
+
// Only add width of previous frozen columns that are visible (or select column)
|
|
277
|
+
const isVisible = header.column?.getIsVisible?.() ?? true
|
|
278
|
+
if (isVisible && (frozenColumns.has(header.id) || header.id === 'select')) {
|
|
277
279
|
offset += header.getSize()
|
|
278
280
|
}
|
|
279
281
|
}
|
|
@@ -432,7 +434,7 @@
|
|
|
432
434
|
hasSelectColumn = false,
|
|
433
435
|
...restProps
|
|
434
436
|
}: { column: Column<TData>; title?: string; isFirst?: boolean; hasSelectColumn?: boolean } & HTMLAttributes<HTMLDivElement>)}
|
|
435
|
-
{@const isCurrency = column.columnDef.
|
|
437
|
+
{@const isCurrency = column.columnDef.cellType === 'currency'}
|
|
436
438
|
{@const needsEdgePadding = isFirst && !hasSelectColumn}
|
|
437
439
|
<div
|
|
438
440
|
class={cn('flex items-center w-full [th[data-last-frozen=true]_&]:border-r [th[data-last-frozen=true]_&]:border-border', className)}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Edge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
|
|
2
|
+
export type DndItem = {
|
|
3
|
+
id: string;
|
|
4
|
+
locked?: boolean;
|
|
5
|
+
[key: string]: any;
|
|
6
|
+
};
|
|
7
|
+
export type DropIndicatorState = {
|
|
8
|
+
itemId: string;
|
|
9
|
+
edge: Edge;
|
|
10
|
+
} | null;
|
|
11
|
+
/**
|
|
12
|
+
* Checks if dropping an item would result in no position change
|
|
13
|
+
* (i.e., dropping right next to its current position)
|
|
14
|
+
*/
|
|
15
|
+
export declare function shouldShowDropIndicator(sourceId: string, targetId: string, sourceGroup: string, targetGroup: string, edge: Edge, items: DndItem[]): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Calculates the new index where a dragged item should be inserted
|
|
18
|
+
*/
|
|
19
|
+
export declare function calculateInsertIndex(sourceIndex: number, targetIndex: number, edge: Edge): number;
|
|
20
|
+
/**
|
|
21
|
+
* Reorders items within the same group
|
|
22
|
+
*/
|
|
23
|
+
export declare function reorderItems<T extends DndItem>(items: T[], sourceId: string, targetId: string, edge: Edge): T[];
|
|
24
|
+
/**
|
|
25
|
+
* Moves an item from one group to another
|
|
26
|
+
*/
|
|
27
|
+
export declare function moveItemBetweenGroups<T extends DndItem>(sourceItems: T[], targetItems: T[], sourceId: string, targetId?: string, edge?: Edge): {
|
|
28
|
+
newSourceItems: T[];
|
|
29
|
+
newTargetItems: T[];
|
|
30
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if dropping an item would result in no position change
|
|
3
|
+
* (i.e., dropping right next to its current position)
|
|
4
|
+
*/
|
|
5
|
+
export function shouldShowDropIndicator(sourceId, targetId, sourceGroup, targetGroup, edge, items) {
|
|
6
|
+
// Always show for cross-group drops
|
|
7
|
+
if (sourceGroup !== targetGroup) {
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
// Check if dropping would result in no position change
|
|
11
|
+
const sourceIndex = items.findIndex((item) => item.id === sourceId);
|
|
12
|
+
const targetIndex = items.findIndex((item) => item.id === targetId);
|
|
13
|
+
// Don't show indicator if dropping adjacent to current position
|
|
14
|
+
const isAdjacentTop = edge === 'top' && targetIndex === sourceIndex + 1;
|
|
15
|
+
const isAdjacentBottom = edge === 'bottom' && targetIndex === sourceIndex - 1;
|
|
16
|
+
return !isAdjacentTop && !isAdjacentBottom;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Calculates the new index where a dragged item should be inserted
|
|
20
|
+
*/
|
|
21
|
+
export function calculateInsertIndex(sourceIndex, targetIndex, edge) {
|
|
22
|
+
let insertIndex = targetIndex;
|
|
23
|
+
// Adjust target index if source was before target (array shifts after removal)
|
|
24
|
+
if (sourceIndex < targetIndex) {
|
|
25
|
+
insertIndex = targetIndex - 1;
|
|
26
|
+
}
|
|
27
|
+
// If dropping on bottom edge, insert after
|
|
28
|
+
if (edge === 'bottom') {
|
|
29
|
+
insertIndex = insertIndex + 1;
|
|
30
|
+
}
|
|
31
|
+
return insertIndex;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Reorders items within the same group
|
|
35
|
+
*/
|
|
36
|
+
export function reorderItems(items, sourceId, targetId, edge) {
|
|
37
|
+
const sourceIndex = items.findIndex((item) => item.id === sourceId);
|
|
38
|
+
const targetIndex = items.findIndex((item) => item.id === targetId);
|
|
39
|
+
if (sourceIndex === -1 || targetIndex === -1) {
|
|
40
|
+
return items;
|
|
41
|
+
}
|
|
42
|
+
const newItems = [...items];
|
|
43
|
+
const [draggedItem] = newItems.splice(sourceIndex, 1);
|
|
44
|
+
const insertIndex = calculateInsertIndex(sourceIndex, targetIndex, edge);
|
|
45
|
+
newItems.splice(insertIndex, 0, draggedItem);
|
|
46
|
+
return newItems;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Moves an item from one group to another
|
|
50
|
+
*/
|
|
51
|
+
export function moveItemBetweenGroups(sourceItems, targetItems, sourceId, targetId, edge) {
|
|
52
|
+
const draggedItem = sourceItems.find((item) => item.id === sourceId);
|
|
53
|
+
if (!draggedItem) {
|
|
54
|
+
return { newSourceItems: sourceItems, newTargetItems: targetItems };
|
|
55
|
+
}
|
|
56
|
+
const newSourceItems = sourceItems.filter((item) => item.id !== sourceId);
|
|
57
|
+
// If no target specified, append to end
|
|
58
|
+
if (!targetId || !edge) {
|
|
59
|
+
const newTargetItems = [...targetItems, draggedItem];
|
|
60
|
+
return { newSourceItems, newTargetItems };
|
|
61
|
+
}
|
|
62
|
+
// Insert at specific position based on target and edge
|
|
63
|
+
const targetIndex = targetItems.findIndex((item) => item.id === targetId);
|
|
64
|
+
if (targetIndex === -1) {
|
|
65
|
+
const newTargetItems = [...targetItems, draggedItem];
|
|
66
|
+
return { newSourceItems, newTargetItems };
|
|
67
|
+
}
|
|
68
|
+
const newTargetItems = [...targetItems];
|
|
69
|
+
const insertIndex = edge === 'bottom' ? targetIndex + 1 : targetIndex;
|
|
70
|
+
newTargetItems.splice(insertIndex, 0, draggedItem);
|
|
71
|
+
return { newSourceItems, newTargetItems };
|
|
72
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@invopop/popui",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "0.1.4-beta.
|
|
4
|
+
"version": "0.1.4-beta.48",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "https://github.com/invopop/popui"
|
|
7
7
|
},
|
|
@@ -81,6 +81,9 @@
|
|
|
81
81
|
"types": "./dist/index.d.ts",
|
|
82
82
|
"type": "module",
|
|
83
83
|
"dependencies": {
|
|
84
|
+
"@atlaskit/pragmatic-drag-and-drop": "^1.7.7",
|
|
85
|
+
"@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^2.1.5",
|
|
86
|
+
"@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.1.0",
|
|
84
87
|
"@floating-ui/core": "^1.5.1",
|
|
85
88
|
"@invopop/ui-icons": "^0.0.78",
|
|
86
89
|
"@steeze-ui/heroicons": "^2.2.3",
|
|
@@ -97,7 +100,6 @@
|
|
|
97
100
|
"inter-ui": "^3.19.3",
|
|
98
101
|
"lodash-es": "^4.17.21",
|
|
99
102
|
"mode-watcher": "^1.1.0",
|
|
100
|
-
"svelte-dnd-action": "^0.9.69",
|
|
101
103
|
"svelte-floating-ui": "^1.5.8",
|
|
102
104
|
"svelte-headlessui": "^0.0.46",
|
|
103
105
|
"svelte-intersection-observer-action": "^0.0.4",
|
|
@@ -106,6 +108,7 @@
|
|
|
106
108
|
"svelte-transition": "^0.0.17",
|
|
107
109
|
"svelte-viewport-info": "^1.0.2",
|
|
108
110
|
"tailwind-variants": "^1.0.0",
|
|
111
|
+
"tailwindcss-animate": "^1.0.7",
|
|
109
112
|
"zod": "^4.3.5"
|
|
110
113
|
}
|
|
111
114
|
}
|