@invopop/popui 0.1.4-beta.21 → 0.1.4-beta.23
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 +10 -4
- package/dist/BaseTableHeaderContent.svelte +1 -1
- package/dist/BaseTableHeaderOrderBy.svelte +5 -3
- package/dist/DrawerContext.svelte +230 -120
- package/dist/EmptyState.svelte +6 -2
- package/dist/data-table/data-table-toolbar.svelte +1 -1
- package/dist/data-table/data-table-types.d.ts +4 -0
- package/dist/data-table/data-table.svelte +157 -75
- package/dist/table/table-row.svelte +1 -1
- package/dist/types.d.ts +5 -0
- package/package.json +3 -4
package/dist/BaseDropdown.svelte
CHANGED
|
@@ -10,12 +10,16 @@
|
|
|
10
10
|
fullWidth = false,
|
|
11
11
|
placement = 'bottom-start',
|
|
12
12
|
matchParentWidth = false,
|
|
13
|
+
usePortal = true,
|
|
13
14
|
class: className = '',
|
|
14
15
|
trigger,
|
|
15
16
|
children,
|
|
16
17
|
...rest
|
|
17
18
|
}: BaseDropdownProps = $props()
|
|
18
19
|
|
|
20
|
+
// Conditional portal action - noop if disabled
|
|
21
|
+
const conditionalPortal = usePortal ? portal : () => {}
|
|
22
|
+
|
|
19
23
|
const middleware = [offset(6), flip(), shift()]
|
|
20
24
|
|
|
21
25
|
if (matchParentWidth) {
|
|
@@ -31,14 +35,16 @@
|
|
|
31
35
|
)
|
|
32
36
|
}
|
|
33
37
|
|
|
38
|
+
let closedFromClickOutside = $state(false)
|
|
39
|
+
|
|
40
|
+
// Create floating actions with strategy based on usePortal
|
|
41
|
+
const strategy = usePortal ? 'absolute' : 'fixed'
|
|
34
42
|
const [floatingRef, floatingContent] = createFloatingActions({
|
|
35
|
-
strategy
|
|
43
|
+
strategy,
|
|
36
44
|
placement,
|
|
37
45
|
middleware
|
|
38
46
|
})
|
|
39
47
|
|
|
40
|
-
let closedFromClickOutside = $state(false)
|
|
41
|
-
|
|
42
48
|
export const toggle = () => {
|
|
43
49
|
isOpen = !isOpen
|
|
44
50
|
}
|
|
@@ -62,7 +68,7 @@
|
|
|
62
68
|
{#if isOpen}
|
|
63
69
|
<div
|
|
64
70
|
class="max-h-40 absolute z-1001"
|
|
65
|
-
use:
|
|
71
|
+
use:conditionalPortal
|
|
66
72
|
use:floatingContent
|
|
67
73
|
use:clickOutside
|
|
68
74
|
onclick_outside={() => {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import type { TableSortBy, DrawerOption, BaseTableHeaderOrderByProps } from './types.js'
|
|
4
4
|
import DrawerContext from './DrawerContext.svelte'
|
|
5
5
|
|
|
6
|
-
let { isActive = false, sortDirection, onOrderBy, onHide, onFilter, onFreeze, isFrozen = false, showSortOptions = true }: BaseTableHeaderOrderByProps = $props()
|
|
6
|
+
let { isActive = false, sortDirection, onOrderBy, onHide, onFilter, onFreeze, isFrozen = false, showSortOptions = true, showFilterOption = true }: BaseTableHeaderOrderByProps = $props()
|
|
7
7
|
|
|
8
8
|
let items = $derived([
|
|
9
9
|
...(showSortOptions ? [
|
|
@@ -21,8 +21,10 @@
|
|
|
21
21
|
},
|
|
22
22
|
{ label: '', value: 'sep-1', separator: true }
|
|
23
23
|
] : []),
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
...(showFilterOption ? [
|
|
25
|
+
{ icon: Filter, label: 'Filter by column', value: 'filter' },
|
|
26
|
+
{ label: '', value: 'sep-2', separator: true }
|
|
27
|
+
] : []),
|
|
26
28
|
{ icon: Lock, label: isFrozen ? 'Unfreeze column' : 'Freeze column', value: 'freeze' },
|
|
27
29
|
{ label: '', value: 'sep-3', separator: true },
|
|
28
30
|
{ icon: Preview, label: 'Hide column', value: 'hide' }
|
|
@@ -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,21 +7,28 @@
|
|
|
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
|
|
10
|
+
import { flip } from 'svelte/animate'
|
|
11
|
+
import { dndzone } from 'svelte-dnd-action'
|
|
11
12
|
import { onMount } from 'svelte'
|
|
12
13
|
|
|
14
|
+
const flipDurationMs = 150
|
|
15
|
+
|
|
13
16
|
let {
|
|
14
17
|
items = $bindable([]),
|
|
15
18
|
multiple = false,
|
|
16
19
|
draggable = false,
|
|
17
20
|
widthClass = 'w-60',
|
|
21
|
+
collapsibleGroups = true,
|
|
18
22
|
onclick,
|
|
19
23
|
onselect,
|
|
20
24
|
onreorder,
|
|
25
|
+
ondropitem,
|
|
21
26
|
children,
|
|
22
27
|
groups
|
|
23
28
|
}: DrawerContextProps = $props()
|
|
24
29
|
|
|
30
|
+
type DndItem = DrawerOption & { id: string }
|
|
31
|
+
|
|
25
32
|
let selectedItems = $derived(items.filter((i) => i.selected))
|
|
26
33
|
let hasGroups = $derived(groups && groups.length > 0)
|
|
27
34
|
let { groupedItems, ungroupedItems } = $derived.by(() => {
|
|
@@ -46,9 +53,75 @@
|
|
|
46
53
|
})
|
|
47
54
|
|
|
48
55
|
let openGroups = $state<Record<string, boolean>>({})
|
|
49
|
-
let
|
|
50
|
-
let
|
|
56
|
+
let groupDndItems = $state<Record<string, DndItem[]>>({})
|
|
57
|
+
let ungroupedDndItems = $state<DndItem[]>([])
|
|
58
|
+
let mounted = $state(false)
|
|
59
|
+
let itemsCache = $state<DrawerOption[]>([])
|
|
60
|
+
let isDragging = $state(false)
|
|
61
|
+
let emitTimeout: number | undefined
|
|
62
|
+
|
|
63
|
+
// Build internal DND items from external items
|
|
64
|
+
function buildListIn() {
|
|
65
|
+
if (hasGroups) {
|
|
66
|
+
// Build DND items for each group
|
|
67
|
+
groups!.forEach((group) => {
|
|
68
|
+
const groupItems = groupedItems.get(group.slug) || []
|
|
69
|
+
groupDndItems[group.slug] = groupItems.map((item: DrawerOption, i: number) => ({
|
|
70
|
+
...item,
|
|
71
|
+
id: `${group.slug}-${item.value}-${i}`
|
|
72
|
+
}))
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Build DND items for ungrouped
|
|
77
|
+
ungroupedDndItems = ungroupedItems.map((item, i) => ({
|
|
78
|
+
...item,
|
|
79
|
+
id: `ungrouped-${item.value}-${i}`
|
|
80
|
+
}))
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Build external items from internal DND items
|
|
84
|
+
function buildListOut() {
|
|
85
|
+
const newItems: DrawerOption[] = []
|
|
86
|
+
const used = new Set<AnyProp>()
|
|
87
|
+
|
|
88
|
+
// Add all grouped items
|
|
89
|
+
if (hasGroups) {
|
|
90
|
+
groups!.forEach((group) => {
|
|
91
|
+
const dndItems = groupDndItems[group.slug] || []
|
|
92
|
+
dndItems.forEach((dndItem) => {
|
|
93
|
+
if (!used.has(dndItem.value)) {
|
|
94
|
+
const { id, ...item } = dndItem
|
|
95
|
+
newItems.push({ ...item, groupBy: group.slug })
|
|
96
|
+
used.add(dndItem.value)
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
}
|
|
51
101
|
|
|
102
|
+
// Add ungrouped items
|
|
103
|
+
ungroupedDndItems.forEach((dndItem) => {
|
|
104
|
+
if (!used.has(dndItem.value)) {
|
|
105
|
+
const { id, ...item } = dndItem
|
|
106
|
+
newItems.push(item)
|
|
107
|
+
used.add(dndItem.value)
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
return newItems
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Sync items when they change from outside
|
|
115
|
+
$effect(() => {
|
|
116
|
+
if (items && mounted && !isDragging) {
|
|
117
|
+
if (JSON.stringify(items) !== JSON.stringify(itemsCache)) {
|
|
118
|
+
buildListIn()
|
|
119
|
+
itemsCache = JSON.parse(JSON.stringify(items))
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
// Open group with selected item on mount
|
|
52
125
|
$effect(() => {
|
|
53
126
|
if (hasGroups) {
|
|
54
127
|
const selectedItem = items.find((i) => i.selected)
|
|
@@ -58,95 +131,78 @@
|
|
|
58
131
|
}
|
|
59
132
|
})
|
|
60
133
|
|
|
134
|
+
// Notify parent of selection changes
|
|
61
135
|
$effect(() => {
|
|
62
136
|
onselect?.(selectedItems)
|
|
63
137
|
})
|
|
64
138
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
ghostClass: 'opacity-10',
|
|
76
|
-
dragClass: 'cursor-grabbing',
|
|
77
|
-
forceFallback: true,
|
|
78
|
-
onMove: (event) => {
|
|
79
|
-
// Prevent moving items above locked items
|
|
80
|
-
return !event.related.classList.contains('no-drag')
|
|
81
|
-
},
|
|
82
|
-
onEnd: (event) => {
|
|
83
|
-
if (event.oldIndex !== undefined && event.newIndex !== undefined) {
|
|
84
|
-
const newItems = [...items]
|
|
85
|
-
const ungroupedIndices = items
|
|
86
|
-
.map((item, index) => (!item.groupBy ? index : -1))
|
|
87
|
-
.filter((i) => i !== -1)
|
|
88
|
-
|
|
89
|
-
const fromIndex = ungroupedIndices[event.oldIndex]
|
|
90
|
-
const toIndex = ungroupedIndices[event.newIndex]
|
|
91
|
-
|
|
92
|
-
const [removed] = newItems.splice(fromIndex, 1)
|
|
93
|
-
newItems.splice(toIndex, 0, removed)
|
|
94
|
-
|
|
95
|
-
items = newItems
|
|
96
|
-
onreorder?.(newItems)
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
})
|
|
139
|
+
onMount(() => {
|
|
140
|
+
itemsCache = JSON.parse(JSON.stringify(items))
|
|
141
|
+
buildListIn()
|
|
142
|
+
mounted = true
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
function transformDraggedElement(draggedEl: HTMLElement | undefined) {
|
|
146
|
+
if (draggedEl) {
|
|
147
|
+
draggedEl.style.border = 'none'
|
|
148
|
+
draggedEl.style.outline = 'none'
|
|
100
149
|
}
|
|
150
|
+
}
|
|
101
151
|
|
|
102
|
-
|
|
103
|
-
if (
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
152
|
+
function emitGroupDistribution() {
|
|
153
|
+
if (ondropitem && hasGroups) {
|
|
154
|
+
// Clear any pending emit
|
|
155
|
+
if (emitTimeout) {
|
|
156
|
+
clearTimeout(emitTimeout)
|
|
157
|
+
}
|
|
107
158
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
onMove: (event) => {
|
|
118
|
-
// Prevent moving items above locked items
|
|
119
|
-
return !event.related.classList.contains('no-drag')
|
|
120
|
-
},
|
|
121
|
-
onEnd: (event) => {
|
|
122
|
-
if (event.oldIndex !== undefined && event.newIndex !== undefined) {
|
|
123
|
-
const newItems = [...items]
|
|
124
|
-
const groupedIndices = items
|
|
125
|
-
.map((item, index) => (item.groupBy === group.slug ? index : -1))
|
|
126
|
-
.filter((i) => i !== -1)
|
|
127
|
-
|
|
128
|
-
const fromIndex = groupedIndices[event.oldIndex]
|
|
129
|
-
const toIndex = groupedIndices[event.newIndex]
|
|
130
|
-
|
|
131
|
-
const [removed] = newItems.splice(fromIndex, 1)
|
|
132
|
-
newItems.splice(toIndex, 0, removed)
|
|
133
|
-
|
|
134
|
-
items = newItems
|
|
135
|
-
onreorder?.(newItems)
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
})
|
|
139
|
-
}
|
|
140
|
-
})
|
|
159
|
+
// Debounce the emit to avoid duplicate calls when dragging between groups
|
|
160
|
+
emitTimeout = window.setTimeout(() => {
|
|
161
|
+
const groupsDistribution: Record<string, DrawerOption[]> = {}
|
|
162
|
+
groups!.forEach((group) => {
|
|
163
|
+
const dndItems = groupDndItems[group.slug] || []
|
|
164
|
+
groupsDistribution[group.slug] = dndItems.map(({ id, ...item }) => item)
|
|
165
|
+
})
|
|
166
|
+
ondropitem(groupsDistribution)
|
|
167
|
+
}, 0)
|
|
141
168
|
}
|
|
142
169
|
}
|
|
143
170
|
|
|
144
|
-
|
|
145
|
-
if (
|
|
146
|
-
|
|
147
|
-
setTimeout(initializeSortable, 100)
|
|
171
|
+
function handleDndConsider(groupSlug: string, e: CustomEvent<any>) {
|
|
172
|
+
if (!isDragging) {
|
|
173
|
+
isDragging = true
|
|
148
174
|
}
|
|
149
|
-
|
|
175
|
+
groupDndItems[groupSlug] = e.detail.items
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function handleDndFinalize(groupSlug: string, e: CustomEvent<any>) {
|
|
179
|
+
isDragging = false
|
|
180
|
+
groupDndItems[groupSlug] = e.detail.items
|
|
181
|
+
|
|
182
|
+
const newItems = buildListOut()
|
|
183
|
+
items = newItems
|
|
184
|
+
itemsCache = JSON.parse(JSON.stringify(items))
|
|
185
|
+
onreorder?.(newItems)
|
|
186
|
+
emitGroupDistribution()
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function handleUngroupedDndConsider(e: CustomEvent<any>) {
|
|
190
|
+
if (!isDragging) {
|
|
191
|
+
isDragging = true
|
|
192
|
+
}
|
|
193
|
+
ungroupedDndItems = e.detail.items
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function handleUngroupedDndFinalize(e: CustomEvent<any>) {
|
|
197
|
+
isDragging = false
|
|
198
|
+
ungroupedDndItems = e.detail.items
|
|
199
|
+
|
|
200
|
+
const newItems = buildListOut()
|
|
201
|
+
items = newItems
|
|
202
|
+
itemsCache = JSON.parse(JSON.stringify(items))
|
|
203
|
+
onreorder?.(newItems)
|
|
204
|
+
emitGroupDistribution()
|
|
205
|
+
}
|
|
150
206
|
|
|
151
207
|
function updateItem(item: DrawerOption) {
|
|
152
208
|
items = items.map((i) => {
|
|
@@ -157,11 +213,6 @@
|
|
|
157
213
|
|
|
158
214
|
function toggleGroup(groupSlug: string) {
|
|
159
215
|
openGroups = openGroups[groupSlug] ? {} : { [groupSlug]: true }
|
|
160
|
-
|
|
161
|
-
// Reinitialize sortable when a group is toggled
|
|
162
|
-
if (draggable) {
|
|
163
|
-
setTimeout(initializeSortable, 100)
|
|
164
|
-
}
|
|
165
216
|
}
|
|
166
217
|
</script>
|
|
167
218
|
|
|
@@ -169,7 +220,7 @@
|
|
|
169
220
|
{#if item.separator}
|
|
170
221
|
<DrawerContextSeparator />
|
|
171
222
|
{:else}
|
|
172
|
-
<div class:px-1={!item.groupBy} class:
|
|
223
|
+
<div class:px-1={!item.groupBy} class:cursor-grab={draggable && !item.locked}>
|
|
173
224
|
<DrawerContextItem {item} {multiple} {onclick} onchange={updateItem} />
|
|
174
225
|
</div>
|
|
175
226
|
{/if}
|
|
@@ -184,50 +235,88 @@
|
|
|
184
235
|
{#each groups as group, index}
|
|
185
236
|
{@const groupItems = groupedItems.get(group.slug) || []}
|
|
186
237
|
{@const isLastGroup = index === groups!.length - 1}
|
|
187
|
-
{@const isOpen = openGroups[group.slug]}
|
|
188
|
-
{@const hasOpenGroup = Object.values(openGroups).some((v) => v)}
|
|
238
|
+
{@const isOpen = collapsibleGroups ? openGroups[group.slug] : true}
|
|
239
|
+
{@const hasOpenGroup = collapsibleGroups ? Object.values(openGroups).some((v) => v) : true}
|
|
189
240
|
<div
|
|
190
241
|
class="px-1"
|
|
191
|
-
class:flex-1={isOpen}
|
|
242
|
+
class:flex-1={isOpen && collapsibleGroups}
|
|
192
243
|
class:flex={isOpen}
|
|
193
244
|
class:flex-col={isOpen}
|
|
194
|
-
class:min-h-0={isOpen}
|
|
195
|
-
class:flex-shrink-0={!isOpen && hasOpenGroup}
|
|
245
|
+
class:min-h-0={isOpen && collapsibleGroups}
|
|
246
|
+
class:flex-shrink-0={!isOpen && hasOpenGroup && collapsibleGroups}
|
|
196
247
|
>
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
248
|
+
{#if collapsibleGroups}
|
|
249
|
+
<button
|
|
250
|
+
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"
|
|
251
|
+
onclick={() => toggleGroup(group.slug)}
|
|
252
|
+
>
|
|
253
|
+
<div class="flex items-center gap-1.5">
|
|
254
|
+
<span>{group.label}</span>
|
|
255
|
+
<Icon
|
|
256
|
+
src={ChevronRight}
|
|
257
|
+
class="size-3 text-icon-default-secondary transition-all transform {isOpen
|
|
258
|
+
? 'rotate-90'
|
|
259
|
+
: ''}"
|
|
260
|
+
/>
|
|
261
|
+
</div>
|
|
262
|
+
{#if groupItems.length && !group.hideCounter}
|
|
263
|
+
<BaseCounter value={groupItems.length} />
|
|
264
|
+
{/if}
|
|
265
|
+
</button>
|
|
266
|
+
{:else}
|
|
267
|
+
<div
|
|
268
|
+
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"
|
|
269
|
+
>
|
|
202
270
|
<span>{group.label}</span>
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
? 'rotate-90'
|
|
207
|
-
: ''}"
|
|
208
|
-
/>
|
|
271
|
+
{#if groupItems.length && !group.hideCounter}
|
|
272
|
+
<BaseCounter value={groupItems.length} />
|
|
273
|
+
{/if}
|
|
209
274
|
</div>
|
|
210
|
-
|
|
211
|
-
<BaseCounter value={groupItems.length} />
|
|
212
|
-
{/if}
|
|
213
|
-
</button>
|
|
275
|
+
{/if}
|
|
214
276
|
|
|
215
277
|
{#if isOpen}
|
|
216
278
|
<div
|
|
217
|
-
class="w-full overflow-y-auto flex-1 min-h-0"
|
|
218
|
-
transition:slide={{ duration: 200 }}
|
|
219
|
-
bind:this={groupContainers[group.slug]}
|
|
279
|
+
class="w-full overflow-y-auto {collapsibleGroups ? 'flex-1 min-h-0' : ''}"
|
|
280
|
+
transition:slide={{ duration: collapsibleGroups ? 200 : 0 }}
|
|
220
281
|
>
|
|
221
|
-
{#if
|
|
282
|
+
{#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
|
+
>
|
|
294
|
+
{#if !groupItems.length}
|
|
295
|
+
<div class="px-1 pt-1 pb-5">
|
|
296
|
+
<EmptyState
|
|
297
|
+
iconSource={group.emptyIcon}
|
|
298
|
+
title={group.emptyTitle}
|
|
299
|
+
description={group.emptyDescription}
|
|
300
|
+
/>
|
|
301
|
+
</div>
|
|
302
|
+
{:else}
|
|
303
|
+
{#each groupDndItems[group.slug] || [] as dndItem (dndItem.id)}
|
|
304
|
+
<div animate:flip={{ duration: flipDurationMs }}>
|
|
305
|
+
{@render drawerItem(dndItem)}
|
|
306
|
+
</div>
|
|
307
|
+
{/each}
|
|
308
|
+
{/if}
|
|
309
|
+
</div>
|
|
310
|
+
{:else if !groupItems.length}
|
|
222
311
|
<div class="px-1 pt-1 pb-5">
|
|
223
312
|
<EmptyState
|
|
224
313
|
iconSource={group.emptyIcon}
|
|
225
|
-
title={group.emptyTitle
|
|
226
|
-
description={group.emptyDescription
|
|
314
|
+
title={group.emptyTitle}
|
|
315
|
+
description={group.emptyDescription}
|
|
227
316
|
/>
|
|
228
317
|
</div>
|
|
229
318
|
{:else}
|
|
230
|
-
{#each groupItems as item}
|
|
319
|
+
{#each groupItems as item (item.value)}
|
|
231
320
|
{@render drawerItem(item)}
|
|
232
321
|
{/each}
|
|
233
322
|
{/if}
|
|
@@ -241,10 +330,31 @@
|
|
|
241
330
|
{/if}
|
|
242
331
|
|
|
243
332
|
{#if ungroupedItems.length}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
333
|
+
{#if draggable}
|
|
334
|
+
<div
|
|
335
|
+
class="flex-shrink-0 overflow-y-auto max-h-[564px]"
|
|
336
|
+
use:dndzone={{
|
|
337
|
+
items: ungroupedDndItems,
|
|
338
|
+
flipDurationMs,
|
|
339
|
+
dropTargetStyle: {},
|
|
340
|
+
type: 'drawer-item',
|
|
341
|
+
transformDraggedElement
|
|
342
|
+
}}
|
|
343
|
+
onconsider={handleUngroupedDndConsider}
|
|
344
|
+
onfinalize={handleUngroupedDndFinalize}
|
|
345
|
+
>
|
|
346
|
+
{#each ungroupedDndItems as dndItem (dndItem.id)}
|
|
347
|
+
<div animate:flip={{ duration: flipDurationMs }}>
|
|
348
|
+
{@render drawerItem(dndItem)}
|
|
349
|
+
</div>
|
|
350
|
+
{/each}
|
|
351
|
+
</div>
|
|
352
|
+
{:else}
|
|
353
|
+
<div class="flex-shrink-0 overflow-y-auto max-h-[564px]">
|
|
354
|
+
{#each ungroupedItems as item (item.value)}
|
|
355
|
+
{@render drawerItem(item)}
|
|
356
|
+
{/each}
|
|
357
|
+
</div>
|
|
358
|
+
{/if}
|
|
249
359
|
{/if}
|
|
250
360
|
</div>
|
package/dist/EmptyState.svelte
CHANGED
|
@@ -31,8 +31,12 @@
|
|
|
31
31
|
</div>
|
|
32
32
|
{/if}
|
|
33
33
|
<div class="flex flex-col items-center gap-0.5 text-center">
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
{#if title}
|
|
35
|
+
<h4 class="font-medium text-foreground text-base">{title}</h4>
|
|
36
|
+
{/if}
|
|
37
|
+
{#if description}
|
|
38
|
+
<p class="text-foreground-default-secondary text-base">{description}</p>
|
|
39
|
+
{/if}
|
|
36
40
|
</div>
|
|
37
41
|
{#if children}
|
|
38
42
|
<div class="mt-4">
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
let { table, filters, frozenColumns }: { table: Table<TData>; filters?: Snippet; frozenColumns: Set<string> } = $props()
|
|
7
7
|
</script>
|
|
8
8
|
|
|
9
|
-
<div class="flex items-center justify-between px-4 py-
|
|
9
|
+
<div class="flex items-center justify-between px-4 py-3">
|
|
10
10
|
{#if filters}
|
|
11
11
|
<div class="flex-1">
|
|
12
12
|
{@render filters()}
|
|
@@ -53,11 +53,15 @@ export interface DataTableProps<TData> {
|
|
|
53
53
|
columns: DataTableColumn<TData>[];
|
|
54
54
|
disableSelection?: boolean;
|
|
55
55
|
disablePagination?: boolean;
|
|
56
|
+
disableKeyboardNavigation?: boolean;
|
|
56
57
|
rowActions?: TableAction[];
|
|
57
58
|
getRowActions?: (row: TData) => TableAction[];
|
|
58
59
|
onRowAction?: (action: AnyProp, row: TData) => void;
|
|
59
60
|
initialPageSize?: number;
|
|
60
61
|
initialPage?: number;
|
|
62
|
+
initialSortColumn?: string;
|
|
63
|
+
initialSortDirection?: 'asc' | 'desc';
|
|
64
|
+
initialFrozenColumns?: string[];
|
|
61
65
|
pageSizeOptions?: number[];
|
|
62
66
|
emptyState?: Omit<EmptyStateProps, 'children' | 'check'>;
|
|
63
67
|
onRowClick?: (row: TData) => void;
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
type VisibilityState,
|
|
11
11
|
type Column
|
|
12
12
|
} from '@tanstack/table-core'
|
|
13
|
+
import { onMount, onDestroy } from 'svelte'
|
|
13
14
|
import DataTableToolbar from './data-table-toolbar.svelte'
|
|
14
15
|
import DataTablePagination from './data-table-pagination.svelte'
|
|
15
16
|
import FlexRender from './flex-render.svelte'
|
|
@@ -33,11 +34,15 @@
|
|
|
33
34
|
columns: columnConfig,
|
|
34
35
|
disableSelection = false,
|
|
35
36
|
disablePagination = false,
|
|
37
|
+
disableKeyboardNavigation = false,
|
|
36
38
|
rowActions = [],
|
|
37
39
|
getRowActions,
|
|
38
40
|
onRowAction,
|
|
39
41
|
initialPageSize = 10,
|
|
40
42
|
initialPage = 0,
|
|
43
|
+
initialSortColumn,
|
|
44
|
+
initialSortDirection,
|
|
45
|
+
initialFrozenColumns = [],
|
|
41
46
|
emptyState = {
|
|
42
47
|
iconSource: Search,
|
|
43
48
|
title: 'No results',
|
|
@@ -64,7 +69,11 @@
|
|
|
64
69
|
|
|
65
70
|
let rowSelection = $state<RowSelectionState>({})
|
|
66
71
|
let columnVisibility = $state<VisibilityState>({})
|
|
67
|
-
let sorting = $state<SortingState>(
|
|
72
|
+
let sorting = $state<SortingState>(
|
|
73
|
+
initialSortColumn && initialSortDirection
|
|
74
|
+
? [{ id: initialSortColumn, desc: initialSortDirection === 'desc' }]
|
|
75
|
+
: []
|
|
76
|
+
)
|
|
68
77
|
let pagination = $state<PaginationState>({ pageIndex: initialPage, pageSize: initialPageSize })
|
|
69
78
|
let columnSizing = $state<ColumnSizingState>({})
|
|
70
79
|
let columnSizingInfo = $state<ColumnSizingInfoState>({
|
|
@@ -78,7 +87,9 @@
|
|
|
78
87
|
let columnOrder = $state<ColumnOrderState>([])
|
|
79
88
|
let containerRef = $state<HTMLDivElement | null>(null)
|
|
80
89
|
let columnDropdowns: Record<string, BaseDropdown> = {}
|
|
81
|
-
let frozenColumns = $state<Set<string>>(new Set())
|
|
90
|
+
let frozenColumns = $state<Set<string>>(new Set(initialFrozenColumns))
|
|
91
|
+
let focusedRowIndex = $state<number>(-1)
|
|
92
|
+
let tableBodyRef: HTMLTableSectionElement | null = null
|
|
82
93
|
|
|
83
94
|
// Build TanStack columns from config
|
|
84
95
|
const columns = $derived.by(() =>
|
|
@@ -106,6 +117,13 @@
|
|
|
106
117
|
pagination.pageIndex = initialPage
|
|
107
118
|
})
|
|
108
119
|
|
|
120
|
+
// Reorder initial frozen columns on mount
|
|
121
|
+
$effect(() => {
|
|
122
|
+
if (initialFrozenColumns.length > 0 && columnOrder.length === 0) {
|
|
123
|
+
initialFrozenColumns.forEach(columnId => reorderFrozenColumn(columnId))
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
|
|
109
127
|
// Track selection changes
|
|
110
128
|
$effect(() => {
|
|
111
129
|
if (onSelectionChange) {
|
|
@@ -140,49 +158,44 @@
|
|
|
140
158
|
setColumnOrder: (value) => (columnOrder = value)
|
|
141
159
|
})
|
|
142
160
|
|
|
161
|
+
function reorderFrozenColumn(columnId: string) {
|
|
162
|
+
const currentOrder = table.getState().columnOrder.length > 0
|
|
163
|
+
? table.getState().columnOrder
|
|
164
|
+
: table.getAllLeafColumns().map(col => col.id)
|
|
165
|
+
|
|
166
|
+
const newOrder = [...currentOrder]
|
|
167
|
+
const columnIndex = newOrder.indexOf(columnId)
|
|
168
|
+
|
|
169
|
+
if (columnIndex > -1) {
|
|
170
|
+
newOrder.splice(columnIndex, 1)
|
|
171
|
+
|
|
172
|
+
const selectIndex = newOrder.indexOf('select')
|
|
173
|
+
const insertIndex = selectIndex >= 0 ? selectIndex + 1 : 0
|
|
174
|
+
|
|
175
|
+
let lastFrozenIndex = insertIndex
|
|
176
|
+
for (let i = insertIndex; i < newOrder.length; i++) {
|
|
177
|
+
if (frozenColumns.has(newOrder[i])) {
|
|
178
|
+
lastFrozenIndex = i + 1
|
|
179
|
+
} else {
|
|
180
|
+
break
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
newOrder.splice(lastFrozenIndex, 0, columnId)
|
|
185
|
+
table.setColumnOrder(newOrder)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
143
189
|
function handleFreezeColumn(columnId: string) {
|
|
144
190
|
const isFrozen = frozenColumns.has(columnId)
|
|
145
191
|
|
|
146
192
|
if (isFrozen) {
|
|
147
|
-
// Unfreeze
|
|
148
193
|
frozenColumns.delete(columnId)
|
|
149
194
|
frozenColumns = new Set(frozenColumns)
|
|
150
195
|
} else {
|
|
151
|
-
// Freeze
|
|
152
196
|
frozenColumns.add(columnId)
|
|
153
197
|
frozenColumns = new Set(frozenColumns)
|
|
154
|
-
|
|
155
|
-
// Reorder columns to move frozen column to the left
|
|
156
|
-
const currentOrder = table.getState().columnOrder.length > 0
|
|
157
|
-
? table.getState().columnOrder
|
|
158
|
-
: table.getAllLeafColumns().map(col => col.id)
|
|
159
|
-
|
|
160
|
-
const newOrder = [...currentOrder]
|
|
161
|
-
const columnIndex = newOrder.indexOf(columnId)
|
|
162
|
-
|
|
163
|
-
if (columnIndex > -1) {
|
|
164
|
-
// Remove from current position
|
|
165
|
-
newOrder.splice(columnIndex, 1)
|
|
166
|
-
|
|
167
|
-
// Find position to insert (after select column if present, otherwise at start)
|
|
168
|
-
const selectIndex = newOrder.indexOf('select')
|
|
169
|
-
const insertIndex = selectIndex >= 0 ? selectIndex + 1 : 0
|
|
170
|
-
|
|
171
|
-
// Find the last frozen column position
|
|
172
|
-
let lastFrozenIndex = insertIndex
|
|
173
|
-
for (let i = insertIndex; i < newOrder.length; i++) {
|
|
174
|
-
if (frozenColumns.has(newOrder[i])) {
|
|
175
|
-
lastFrozenIndex = i + 1
|
|
176
|
-
} else {
|
|
177
|
-
break
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Insert after the last frozen column
|
|
182
|
-
newOrder.splice(lastFrozenIndex, 0, columnId)
|
|
183
|
-
|
|
184
|
-
table.setColumnOrder(newOrder)
|
|
185
|
-
}
|
|
198
|
+
reorderFrozenColumn(columnId)
|
|
186
199
|
}
|
|
187
200
|
}
|
|
188
201
|
|
|
@@ -202,6 +215,88 @@
|
|
|
202
215
|
|
|
203
216
|
return offset
|
|
204
217
|
}
|
|
218
|
+
|
|
219
|
+
function handleKeydown(event: KeyboardEvent) {
|
|
220
|
+
const rows = table.getRowModel().rows
|
|
221
|
+
if (rows.length === 0) return
|
|
222
|
+
|
|
223
|
+
// Ignore if user is typing in an input or has a dropdown open
|
|
224
|
+
if ((event.target as HTMLElement).tagName === 'INPUT' ||
|
|
225
|
+
(event.target as HTMLElement).tagName === 'TEXTAREA') {
|
|
226
|
+
return
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
switch (event.key) {
|
|
230
|
+
case 'ArrowDown':
|
|
231
|
+
event.preventDefault()
|
|
232
|
+
if (focusedRowIndex === -1 && rows.length > 0) {
|
|
233
|
+
// No row focused, focus first row
|
|
234
|
+
focusedRowIndex = 0
|
|
235
|
+
scrollToFocusedRow()
|
|
236
|
+
if (event.shiftKey && enableSelection) {
|
|
237
|
+
rows[focusedRowIndex].toggleSelected(true)
|
|
238
|
+
}
|
|
239
|
+
} else if (focusedRowIndex < rows.length - 1) {
|
|
240
|
+
// Move down
|
|
241
|
+
focusedRowIndex++
|
|
242
|
+
scrollToFocusedRow()
|
|
243
|
+
if (event.shiftKey && enableSelection) {
|
|
244
|
+
// Always select when going down
|
|
245
|
+
rows[focusedRowIndex].toggleSelected(true)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
break
|
|
249
|
+
case 'ArrowUp':
|
|
250
|
+
event.preventDefault()
|
|
251
|
+
if (event.shiftKey && enableSelection && focusedRowIndex >= 0) {
|
|
252
|
+
// Deselect current row first when going up with shift
|
|
253
|
+
rows[focusedRowIndex].toggleSelected(false)
|
|
254
|
+
}
|
|
255
|
+
if (focusedRowIndex === -1 && rows.length > 0) {
|
|
256
|
+
// No row focused, focus first row
|
|
257
|
+
focusedRowIndex = 0
|
|
258
|
+
scrollToFocusedRow()
|
|
259
|
+
} else if (focusedRowIndex > 0) {
|
|
260
|
+
// Move up
|
|
261
|
+
focusedRowIndex--
|
|
262
|
+
scrollToFocusedRow()
|
|
263
|
+
}
|
|
264
|
+
break
|
|
265
|
+
case ' ':
|
|
266
|
+
case 'Enter':
|
|
267
|
+
event.preventDefault()
|
|
268
|
+
if (focusedRowIndex >= 0 && focusedRowIndex < rows.length && enableSelection) {
|
|
269
|
+
const row = rows[focusedRowIndex]
|
|
270
|
+
row.toggleSelected()
|
|
271
|
+
}
|
|
272
|
+
break
|
|
273
|
+
case 'Escape':
|
|
274
|
+
focusedRowIndex = -1
|
|
275
|
+
break
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function scrollToFocusedRow() {
|
|
280
|
+
if (focusedRowIndex >= 0 && tableBodyRef) {
|
|
281
|
+
const rowElement = tableBodyRef.querySelector(`[data-row-index="${focusedRowIndex}"]`)
|
|
282
|
+
if (rowElement) {
|
|
283
|
+
rowElement.scrollIntoView({ block: 'nearest', behavior: 'smooth' })
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Add global keyboard navigation
|
|
289
|
+
onMount(() => {
|
|
290
|
+
if (!disableKeyboardNavigation) {
|
|
291
|
+
document.addEventListener('keydown', handleKeydown)
|
|
292
|
+
}
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
onDestroy(() => {
|
|
296
|
+
if (!disableKeyboardNavigation) {
|
|
297
|
+
document.removeEventListener('keydown', handleKeydown)
|
|
298
|
+
}
|
|
299
|
+
})
|
|
205
300
|
</script>
|
|
206
301
|
|
|
207
302
|
{#snippet StickyCellWrapper({
|
|
@@ -219,7 +314,7 @@
|
|
|
219
314
|
})}
|
|
220
315
|
<div
|
|
221
316
|
class={cn(
|
|
222
|
-
'absolute inset-0 flex items-center bg-background group-hover/row:bg-background-default-secondary group-data-[state=selected]/row:bg-background-selected px-3',
|
|
317
|
+
'absolute inset-0 flex items-center bg-background group-hover/row:bg-background-default-secondary group-data-[state=selected]/row:bg-background-selected group-data-[focused=true]/row:bg-background-default-secondary px-3',
|
|
223
318
|
align === 'right' ? 'justify-end' : '',
|
|
224
319
|
{ 'pl-4': isFirst, 'pr-4': isLast, 'border-r border-border': isFrozen }
|
|
225
320
|
)}
|
|
@@ -249,7 +344,7 @@
|
|
|
249
344
|
}: { column: Column<TData>; title?: string } & HTMLAttributes<HTMLDivElement>)}
|
|
250
345
|
{@const isCurrency = column.columnDef.meta?.cellType === 'currency'}
|
|
251
346
|
<div class={cn('flex items-center w-full', className)} {...restProps}>
|
|
252
|
-
<BaseDropdown bind:this={columnDropdowns[column.id]} fullWidth>
|
|
347
|
+
<BaseDropdown bind:this={columnDropdowns[column.id]} fullWidth usePortal={false}>
|
|
253
348
|
{#snippet trigger()}
|
|
254
349
|
<button
|
|
255
350
|
class={clsx('data-[state=open]:bg-accent w-full flex items-center gap-1 py-2.5', {
|
|
@@ -272,6 +367,7 @@
|
|
|
272
367
|
isActive={column.getIsSorted() !== false}
|
|
273
368
|
isFrozen={frozenColumns.has(column.id)}
|
|
274
369
|
showSortOptions={column.getCanSort()}
|
|
370
|
+
showFilterOption={!column.columnDef.disableColumnFilter}
|
|
275
371
|
onOrderBy={(direction) => {
|
|
276
372
|
column.toggleSorting(direction === 'desc')
|
|
277
373
|
// Reset to first page when sorting changes (same as page size change)
|
|
@@ -305,15 +401,24 @@
|
|
|
305
401
|
<div class="flex flex-col h-full">
|
|
306
402
|
<DataTableToolbar {table} {filters} {frozenColumns} />
|
|
307
403
|
<div class="flex-1 overflow-hidden flex flex-col">
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
404
|
+
{#if data.length === 0}
|
|
405
|
+
<div class="flex-1 flex items-center justify-center bg-background">
|
|
406
|
+
<EmptyState
|
|
407
|
+
iconSource={emptyState.iconSource}
|
|
408
|
+
title={emptyState.title}
|
|
409
|
+
description={emptyState.description}
|
|
410
|
+
/>
|
|
411
|
+
</div>
|
|
412
|
+
{:else}
|
|
413
|
+
<div
|
|
414
|
+
bind:this={containerRef}
|
|
415
|
+
class="relative bg-background flex-1 overflow-auto"
|
|
416
|
+
style="overscroll-behavior-x: none;"
|
|
417
|
+
>
|
|
418
|
+
<Table.Root>
|
|
419
|
+
<Table.Header>
|
|
420
|
+
{#each table.getHeaderGroups() as headerGroup (headerGroup.id)}
|
|
421
|
+
<Table.Row class="hover:!bg-transparent border-t border-b border-border">
|
|
317
422
|
{#each headerGroup.headers as header, index (header.id)}
|
|
318
423
|
{@const isLastScrollable = index === headerGroup.headers.length - 2}
|
|
319
424
|
{@const isFirstHeader = index === 0}
|
|
@@ -341,13 +446,6 @@
|
|
|
341
446
|
{/if}
|
|
342
447
|
<!-- Left resize handler (resizes previous column) -->
|
|
343
448
|
{#if prevHeader && prevHeader.column.getCanResize()}
|
|
344
|
-
<!-- Always visible vertical border on left -->
|
|
345
|
-
<div
|
|
346
|
-
class={cn(
|
|
347
|
-
'absolute left-0 top-1/2 -translate-y-1/2 h-3 w-px bg-background-default-tertiary',
|
|
348
|
-
prevHeader.column.getIsResizing() && 'opacity-0'
|
|
349
|
-
)}
|
|
350
|
-
></div>
|
|
351
449
|
<!-- Left resize handler -->
|
|
352
450
|
<div
|
|
353
451
|
role="button"
|
|
@@ -366,13 +464,6 @@
|
|
|
366
464
|
</div>
|
|
367
465
|
{/if}
|
|
368
466
|
{#if header.column.getCanResize()}
|
|
369
|
-
<!-- Always visible vertical border -->
|
|
370
|
-
<div
|
|
371
|
-
class={cn(
|
|
372
|
-
'absolute right-0 top-1/2 -translate-y-1/2 h-3 w-px bg-background-default-tertiary',
|
|
373
|
-
header.column.getIsResizing() && 'opacity-0'
|
|
374
|
-
)}
|
|
375
|
-
></div>
|
|
376
467
|
<!-- Resize handler (larger interactive area, enhanced on hover) -->
|
|
377
468
|
<div
|
|
378
469
|
role="button"
|
|
@@ -395,10 +486,12 @@
|
|
|
395
486
|
</Table.Row>
|
|
396
487
|
{/each}
|
|
397
488
|
</Table.Header>
|
|
398
|
-
<Table.Body>
|
|
399
|
-
{#each table.getRowModel().rows as row (row.id)}
|
|
489
|
+
<Table.Body bind:ref={tableBodyRef}>
|
|
490
|
+
{#each table.getRowModel().rows as row, rowIndex (row.id)}
|
|
400
491
|
<Table.Row
|
|
401
492
|
data-state={row.getIsSelected() ? 'selected' : undefined}
|
|
493
|
+
data-row-index={rowIndex}
|
|
494
|
+
data-focused={focusedRowIndex === rowIndex ? 'true' : undefined}
|
|
402
495
|
class={cn('border-b border-border', getRowClassName?.(row.original as TData))}
|
|
403
496
|
onclick={() => onRowClick?.(row.original as TData)}
|
|
404
497
|
>
|
|
@@ -457,22 +550,11 @@
|
|
|
457
550
|
</Table.Cell>
|
|
458
551
|
{/each}
|
|
459
552
|
</Table.Row>
|
|
460
|
-
{:else}
|
|
461
|
-
<Table.Row class="hover:!bg-transparent h-full">
|
|
462
|
-
<Table.Cell colspan={columns.length} class="h-full !p-0">
|
|
463
|
-
<div class="flex items-center justify-center h-full w-full">
|
|
464
|
-
<EmptyState
|
|
465
|
-
iconSource={emptyState.iconSource}
|
|
466
|
-
title={emptyState.title}
|
|
467
|
-
description={emptyState.description}
|
|
468
|
-
/>
|
|
469
|
-
</div>
|
|
470
|
-
</Table.Cell>
|
|
471
|
-
</Table.Row>
|
|
472
553
|
{/each}
|
|
473
554
|
</Table.Body>
|
|
474
555
|
</Table.Root>
|
|
475
556
|
</div>
|
|
557
|
+
{/if}
|
|
476
558
|
{#if enablePagination}
|
|
477
559
|
<DataTablePagination
|
|
478
560
|
{table}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
bind:this={ref}
|
|
20
20
|
data-slot="table-row"
|
|
21
21
|
class={cn(
|
|
22
|
-
'group/row data-[state=selected]:bg-background-selected data-[state=checked]:bg-background-selected h-10 data-[state=selected]:hover:bg-background-selected data-[state=checked]:hover:bg-background-selected',
|
|
22
|
+
'group/row data-[state=selected]:bg-background-selected data-[state=checked]:bg-background-selected h-10 data-[state=selected]:hover:bg-background-selected data-[state=checked]:hover:bg-background-selected data-[focused=true]:bg-background-default-secondary',
|
|
23
23
|
className
|
|
24
24
|
)}
|
|
25
25
|
{oncontextmenu}
|
package/dist/types.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export type DrawerGroup = {
|
|
|
23
23
|
emptyIcon?: IconSource;
|
|
24
24
|
emptyTitle?: string;
|
|
25
25
|
emptyDescription?: string;
|
|
26
|
+
hideCounter?: boolean;
|
|
26
27
|
};
|
|
27
28
|
export type DrawerOption = SelectOption & {
|
|
28
29
|
separator?: boolean;
|
|
@@ -217,6 +218,7 @@ export interface BaseDropdownProps {
|
|
|
217
218
|
fullWidth?: boolean;
|
|
218
219
|
placement?: Placement;
|
|
219
220
|
matchParentWidth?: boolean;
|
|
221
|
+
usePortal?: boolean;
|
|
220
222
|
trigger?: Snippet;
|
|
221
223
|
children?: Snippet;
|
|
222
224
|
[key: string]: any;
|
|
@@ -286,6 +288,7 @@ export interface BaseTableHeaderOrderByProps {
|
|
|
286
288
|
onFreeze?: () => void;
|
|
287
289
|
isFrozen?: boolean;
|
|
288
290
|
showSortOptions?: boolean;
|
|
291
|
+
showFilterOption?: boolean;
|
|
289
292
|
}
|
|
290
293
|
export interface BaseTableRowProps {
|
|
291
294
|
row: TableDataRow;
|
|
@@ -389,9 +392,11 @@ export interface DrawerContextProps {
|
|
|
389
392
|
multiple?: boolean;
|
|
390
393
|
draggable?: boolean;
|
|
391
394
|
widthClass?: string;
|
|
395
|
+
collapsibleGroups?: boolean;
|
|
392
396
|
onclick?: (value: AnyProp) => void;
|
|
393
397
|
onselect?: (selected: DrawerOption[]) => void;
|
|
394
398
|
onreorder?: (items: DrawerOption[]) => void;
|
|
399
|
+
ondropitem?: (groups: Record<string, DrawerOption[]>) => void;
|
|
395
400
|
children?: Snippet;
|
|
396
401
|
groups?: DrawerGroup[];
|
|
397
402
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@invopop/popui",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "0.1.4-beta.
|
|
4
|
+
"version": "0.1.4-beta.23",
|
|
5
5
|
"repository": {
|
|
6
|
-
"url":
|
|
6
|
+
"url": "https://github.com/invopop/popui"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
9
|
"dev": "vite dev",
|
|
@@ -55,7 +55,6 @@
|
|
|
55
55
|
"@tailwindcss/forms": "^0.5.9",
|
|
56
56
|
"@tailwindcss/typography": "^0.5.15",
|
|
57
57
|
"@types/lodash-es": "^4.17.12",
|
|
58
|
-
"@types/sortablejs": "^1.15.9",
|
|
59
58
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
60
59
|
"@typescript-eslint/parser": "^6.0.0",
|
|
61
60
|
"eslint": "^8.28.0",
|
|
@@ -98,7 +97,7 @@
|
|
|
98
97
|
"inter-ui": "^3.19.3",
|
|
99
98
|
"lodash-es": "^4.17.21",
|
|
100
99
|
"mode-watcher": "^1.1.0",
|
|
101
|
-
"
|
|
100
|
+
"svelte-dnd-action": "^0.9.69",
|
|
102
101
|
"svelte-floating-ui": "^1.5.8",
|
|
103
102
|
"svelte-headlessui": "^0.0.46",
|
|
104
103
|
"svelte-intersection-observer-action": "^0.0.4",
|