@invopop/popui 0.1.74 → 0.1.75
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.
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { slide } from 'svelte/transition'
|
|
10
10
|
import { flip } from 'svelte/animate'
|
|
11
11
|
import clsx from 'clsx'
|
|
12
|
+
import { cn } from './utils.js'
|
|
12
13
|
import {
|
|
13
14
|
draggable as makeDraggable,
|
|
14
15
|
dropTargetForElements
|
|
@@ -22,6 +23,7 @@
|
|
|
22
23
|
type Edge
|
|
23
24
|
} from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge'
|
|
24
25
|
import { onMount, onDestroy, untrack } from 'svelte'
|
|
26
|
+
import { Command } from 'bits-ui'
|
|
25
27
|
import {
|
|
26
28
|
shouldShowDropIndicator,
|
|
27
29
|
reorderItems,
|
|
@@ -29,11 +31,6 @@
|
|
|
29
31
|
type DropIndicatorState,
|
|
30
32
|
type DndItem as DndItemType
|
|
31
33
|
} from './drawer-dnd-helpers'
|
|
32
|
-
import {
|
|
33
|
-
getFocusableItems,
|
|
34
|
-
getNextFocusedIndex,
|
|
35
|
-
selectFocusedItem
|
|
36
|
-
} from './drawer-keyboard-helpers'
|
|
37
34
|
|
|
38
35
|
const flipDurationMs = 150
|
|
39
36
|
|
|
@@ -50,18 +47,18 @@
|
|
|
50
47
|
ondropitem,
|
|
51
48
|
children,
|
|
52
49
|
groups,
|
|
50
|
+
class: className,
|
|
53
51
|
...rest
|
|
54
52
|
}: DrawerContextProps = $props()
|
|
55
53
|
|
|
56
54
|
type DndItem = DrawerOption & { id: string }
|
|
57
55
|
|
|
58
56
|
let containerClasses = $derived(
|
|
59
|
-
|
|
57
|
+
cn(
|
|
60
58
|
widthClass,
|
|
61
|
-
'border border-border rounded-2xl shadow-lg bg-background flex flex-col py-1 max-h-[568px] overflow-y-auto list-none',
|
|
62
|
-
{
|
|
63
|
-
|
|
64
|
-
}
|
|
59
|
+
'border border-border rounded-2xl shadow-lg bg-background flex flex-col py-1 max-h-[568px] overflow-y-auto list-none outline-none',
|
|
60
|
+
clsx({ '[scrollbar-width:none] [&::-webkit-scrollbar]:hidden': draggable }),
|
|
61
|
+
className
|
|
65
62
|
)
|
|
66
63
|
)
|
|
67
64
|
|
|
@@ -88,27 +85,6 @@
|
|
|
88
85
|
return { groupedItems: grouped, ungroupedItems: ungrouped }
|
|
89
86
|
})
|
|
90
87
|
|
|
91
|
-
// Items in display order (matches visual rendering order)
|
|
92
|
-
let itemsInDisplayOrder = $derived.by(() => {
|
|
93
|
-
const displayOrder: DrawerOption[] = []
|
|
94
|
-
|
|
95
|
-
if (hasGroups && groups) {
|
|
96
|
-
// Add grouped items in group order, only if group is open (when collapsible)
|
|
97
|
-
groups.forEach((group) => {
|
|
98
|
-
const isOpen = collapsibleGroups ? openGroups[group.slug] : true
|
|
99
|
-
if (isOpen) {
|
|
100
|
-
const groupItems = groupedItems.get(group.slug) || []
|
|
101
|
-
displayOrder.push(...groupItems)
|
|
102
|
-
}
|
|
103
|
-
})
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Add ungrouped items
|
|
107
|
-
displayOrder.push(...ungroupedItems)
|
|
108
|
-
|
|
109
|
-
return displayOrder
|
|
110
|
-
})
|
|
111
|
-
|
|
112
88
|
let openGroups = $state<Record<string, boolean>>({})
|
|
113
89
|
let groupDndItems = $state<Record<string, DndItem[]>>({})
|
|
114
90
|
let ungroupedDndItems = $state<DndItem[]>([])
|
|
@@ -120,8 +96,6 @@
|
|
|
120
96
|
let draggedOverGroup = $state<string | null>(null)
|
|
121
97
|
let dropIndicator = $state<DropIndicatorState>(null)
|
|
122
98
|
let cleanupFunctions: (() => void)[] = []
|
|
123
|
-
let focusedIndex = $state<number>(-1)
|
|
124
|
-
let containerRef = $state<HTMLDivElement | null>(null)
|
|
125
99
|
|
|
126
100
|
// Build internal DND items from external items
|
|
127
101
|
function buildListIn() {
|
|
@@ -204,19 +178,15 @@
|
|
|
204
178
|
buildListIn()
|
|
205
179
|
mounted = true
|
|
206
180
|
|
|
207
|
-
// Set up auto-scroll
|
|
208
181
|
const autoScrollCleanup = autoScrollForElements({
|
|
209
182
|
element: document.documentElement
|
|
210
183
|
})
|
|
211
184
|
cleanupFunctions.push(autoScrollCleanup)
|
|
212
|
-
|
|
213
|
-
window.addEventListener('keydown', handleKeyDown)
|
|
214
185
|
})
|
|
215
186
|
|
|
216
187
|
onDestroy(() => {
|
|
217
188
|
cleanupFunctions.forEach((cleanup) => cleanup())
|
|
218
189
|
cleanupFunctions = []
|
|
219
|
-
window.removeEventListener('keydown', handleKeyDown)
|
|
220
190
|
})
|
|
221
191
|
|
|
222
192
|
function emitGroupDistribution() {
|
|
@@ -445,37 +415,11 @@
|
|
|
445
415
|
openGroups = openGroups[groupSlug] ? {} : { [groupSlug]: true }
|
|
446
416
|
}
|
|
447
417
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
return null
|
|
454
|
-
})
|
|
455
|
-
|
|
456
|
-
function handleKeyDown(event: KeyboardEvent) {
|
|
457
|
-
// Don't handle if container doesn't exist
|
|
458
|
-
if (!containerRef || !document.body.contains(containerRef)) return
|
|
459
|
-
|
|
460
|
-
const focusableItems = getFocusableItems(itemsInDisplayOrder)
|
|
461
|
-
if (focusableItems.length === 0) return
|
|
462
|
-
|
|
463
|
-
if (event.key === 'ArrowDown') {
|
|
464
|
-
event.preventDefault()
|
|
465
|
-
focusedIndex = getNextFocusedIndex(focusedIndex, 'down', focusableItems.length)
|
|
466
|
-
} else if (event.key === 'ArrowUp') {
|
|
467
|
-
event.preventDefault()
|
|
468
|
-
focusedIndex = getNextFocusedIndex(focusedIndex, 'up', focusableItems.length)
|
|
469
|
-
} else if (event.key === ' ' || event.key === 'Enter') {
|
|
470
|
-
event.preventDefault()
|
|
471
|
-
const result = selectFocusedItem(itemsInDisplayOrder, focusedIndex, multiple)
|
|
472
|
-
if (result) {
|
|
473
|
-
if (result.shouldUpdate) {
|
|
474
|
-
updateItem(result.item)
|
|
475
|
-
} else {
|
|
476
|
-
onclick?.(result.item.value)
|
|
477
|
-
}
|
|
478
|
-
}
|
|
418
|
+
function handleItemSelect(item: DrawerOption) {
|
|
419
|
+
if (multiple) {
|
|
420
|
+
updateItem({ ...item, selected: !item.selected })
|
|
421
|
+
} else {
|
|
422
|
+
onclick?.(item.value)
|
|
479
423
|
}
|
|
480
424
|
}
|
|
481
425
|
</script>
|
|
@@ -487,20 +431,26 @@
|
|
|
487
431
|
{#snippet itemChildren()}
|
|
488
432
|
{@render item.content?.(item)}
|
|
489
433
|
{/snippet}
|
|
490
|
-
<
|
|
434
|
+
<Command.Item
|
|
435
|
+
value={String(item.value)}
|
|
436
|
+
disabled={item.disabled || item.locked}
|
|
437
|
+
onSelect={() => handleItemSelect(item)}
|
|
438
|
+
class={clsx('group outline-none', { 'px-1': !item.groupBy, 'cursor-grab': draggable && !item.locked })}
|
|
439
|
+
>
|
|
491
440
|
<DrawerContextItem
|
|
492
|
-
item={{ ...item,
|
|
441
|
+
item={{ ...item, flagPosition: item.flagPosition || flagPosition }}
|
|
493
442
|
{multiple}
|
|
494
443
|
{onclick}
|
|
495
444
|
onchange={updateItem}
|
|
496
445
|
children={item.content ? itemChildren : undefined}
|
|
497
446
|
/>
|
|
498
|
-
</
|
|
447
|
+
</Command.Item>
|
|
499
448
|
{/if}
|
|
500
449
|
{/snippet}
|
|
501
450
|
|
|
502
|
-
<
|
|
503
|
-
|
|
451
|
+
<Command.Root
|
|
452
|
+
shouldFilter={false}
|
|
453
|
+
loop
|
|
504
454
|
class={containerClasses}
|
|
505
455
|
{...rest}
|
|
506
456
|
>
|
|
@@ -644,4 +594,4 @@
|
|
|
644
594
|
</div>
|
|
645
595
|
{/if}
|
|
646
596
|
{/if}
|
|
647
|
-
</
|
|
597
|
+
</Command.Root>
|
|
@@ -31,8 +31,10 @@
|
|
|
31
31
|
clsx(
|
|
32
32
|
'px-2 py-1.5 space-x-1.5',
|
|
33
33
|
{ 'bg-background-selected': item?.selected && !multiple },
|
|
34
|
-
{
|
|
35
|
-
|
|
34
|
+
{
|
|
35
|
+
'group-hover:bg-background-default-secondary group-data-[selected]:bg-background-default-secondary':
|
|
36
|
+
shouldShowHoverStyle
|
|
37
|
+
}
|
|
36
38
|
)
|
|
37
39
|
)
|
|
38
40
|
|
|
@@ -66,7 +68,7 @@
|
|
|
66
68
|
|
|
67
69
|
<button
|
|
68
70
|
bind:this={el}
|
|
69
|
-
class="cursor-pointer w-full disabled:opacity-30 group"
|
|
71
|
+
class="cursor-pointer w-full disabled:opacity-30 group focus:outline-none"
|
|
70
72
|
disabled={item?.disabled}
|
|
71
73
|
onclick={handleClick}
|
|
72
74
|
>
|
|
@@ -170,6 +170,10 @@
|
|
|
170
170
|
<BaseFlag country={selectedItem.country} />
|
|
171
171
|
{@render label()}
|
|
172
172
|
</div>
|
|
173
|
+
{:else if !multiple && selectedItem?.content}
|
|
174
|
+
<div class="flex items-center gap-1 flex-1 min-w-0">
|
|
175
|
+
{@render selectedItem.content(selectedItem)}
|
|
176
|
+
</div>
|
|
173
177
|
{:else if selectedIcon || resolvedIcon}
|
|
174
178
|
<div class="flex items-center gap-1 flex-1 min-w-0">
|
|
175
179
|
{#if typeof selectedIcon === 'string'}
|
package/dist/types.d.ts
CHANGED
|
@@ -29,7 +29,6 @@ export type DrawerOption = SelectOption & {
|
|
|
29
29
|
separator?: boolean;
|
|
30
30
|
destructive?: boolean;
|
|
31
31
|
selected?: boolean;
|
|
32
|
-
focused?: boolean;
|
|
33
32
|
icon?: IconSource | string | undefined;
|
|
34
33
|
rightIcon?: IconSource | undefined;
|
|
35
34
|
country?: string;
|
|
@@ -352,6 +351,7 @@ export interface DrawerContextProps {
|
|
|
352
351
|
ondropitem?: (groups: Record<string, DrawerOption[]>) => void;
|
|
353
352
|
children?: Snippet;
|
|
354
353
|
groups?: DrawerGroup[];
|
|
354
|
+
class?: string;
|
|
355
355
|
[key: string]: unknown;
|
|
356
356
|
}
|
|
357
357
|
export interface DrawerContextItemProps {
|
package/package.json
CHANGED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { DrawerOption } from './types';
|
|
2
|
-
/**
|
|
3
|
-
* Get all focusable items (non-separator, non-disabled, non-locked)
|
|
4
|
-
*/
|
|
5
|
-
export declare function getFocusableItems(items: DrawerOption[]): DrawerOption[];
|
|
6
|
-
/**
|
|
7
|
-
* Calculate next focused index based on arrow key direction
|
|
8
|
-
*/
|
|
9
|
-
export declare function getNextFocusedIndex(currentIndex: number, direction: 'up' | 'down', itemsCount: number): number;
|
|
10
|
-
/**
|
|
11
|
-
* Handle selection of focused item
|
|
12
|
-
*/
|
|
13
|
-
export declare function selectFocusedItem(items: DrawerOption[], focusedIndex: number, multiple: boolean): {
|
|
14
|
-
item: DrawerOption;
|
|
15
|
-
shouldUpdate: boolean;
|
|
16
|
-
} | null;
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Get all focusable items (non-separator, non-disabled, non-locked)
|
|
3
|
-
*/
|
|
4
|
-
export function getFocusableItems(items) {
|
|
5
|
-
return items.filter((item) => !item.separator && !item.disabled && !item.locked);
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Calculate next focused index based on arrow key direction
|
|
9
|
-
*/
|
|
10
|
-
export function getNextFocusedIndex(currentIndex, direction, itemsCount) {
|
|
11
|
-
if (itemsCount === 0)
|
|
12
|
-
return -1;
|
|
13
|
-
if (direction === 'down') {
|
|
14
|
-
return currentIndex < itemsCount - 1 ? currentIndex + 1 : 0;
|
|
15
|
-
}
|
|
16
|
-
else {
|
|
17
|
-
return currentIndex > 0 ? currentIndex - 1 : itemsCount - 1;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Handle selection of focused item
|
|
22
|
-
*/
|
|
23
|
-
export function selectFocusedItem(items, focusedIndex, multiple) {
|
|
24
|
-
const focusableItems = getFocusableItems(items);
|
|
25
|
-
if (focusedIndex >= 0 && focusedIndex < focusableItems.length) {
|
|
26
|
-
const focusedItem = focusableItems[focusedIndex];
|
|
27
|
-
if (multiple) {
|
|
28
|
-
return {
|
|
29
|
-
item: { ...focusedItem, selected: !focusedItem.selected },
|
|
30
|
-
shouldUpdate: true
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
return {
|
|
35
|
-
item: focusedItem,
|
|
36
|
-
shouldUpdate: false
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return null;
|
|
41
|
-
}
|