@insymetri/styleguide 0.1.35 → 0.1.37
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/IIContextMenu/IIContextMenu.svelte +223 -98
- package/dist/IIContextMenu/IIContextMenu.svelte.d.ts +13 -0
- package/dist/IIContextMenu/IIContextMenuStories.svelte +8 -0
- package/dist/IIContextMenu/IIContextMenuSub.svelte +131 -0
- package/dist/IIContextMenu/IIContextMenuSub.svelte.d.ts +13 -0
- package/dist/IIContextMenu/IIContextMenuSubSimple.svelte +137 -0
- package/dist/IIContextMenu/IIContextMenuSubSimple.svelte.d.ts +13 -0
- package/dist/IIDropdownInput/IIDropdownInput.svelte +161 -60
- package/dist/IIDropdownInput/IIDropdownInput.svelte.d.ts +6 -9
- package/dist/IIDropdownMenu/IIDropdownMenu.svelte +278 -89
- package/dist/IIDropdownMenu/IIDropdownMenu.svelte.d.ts +4 -17
- package/dist/IIDropdownMenu/IIDropdownMenuStories.svelte +20 -0
- package/dist/IIDropdownMenu/IIDropdownMenuSub.svelte +131 -0
- package/dist/IIDropdownMenu/IIDropdownMenuSub.svelte.d.ts +13 -0
- package/dist/IIDropdownMenu/IIDropdownMenuSubSimple.svelte +134 -0
- package/dist/IIDropdownMenu/IIDropdownMenuSubSimple.svelte.d.ts +13 -0
- package/dist/style/colors.css +1 -1
- package/dist/style/tailwind/animations.js +1 -1
- package/dist/style/tailwind/shadows.d.ts +1 -0
- package/dist/style/tailwind/shadows.js +3 -1
- package/dist/utils/menu/MenuItemContent.svelte +21 -0
- package/dist/utils/menu/MenuItemContent.svelte.d.ts +9 -0
- package/dist/utils/menu/MenuNoResults.svelte +4 -0
- package/dist/utils/menu/MenuNoResults.svelte.d.ts +18 -0
- package/dist/utils/menu/MenuSearchInput.svelte +25 -0
- package/dist/utils/menu/MenuSearchInput.svelte.d.ts +9 -0
- package/dist/utils/menu/index.d.ts +8 -0
- package/dist/utils/menu/index.js +8 -0
- package/dist/utils/menu/menu-search.svelte.d.ts +53 -0
- package/dist/utils/menu/menu-search.svelte.js +138 -0
- package/dist/utils/menu/menu-styles.d.ts +16 -0
- package/dist/utils/menu/menu-styles.js +19 -0
- package/dist/utils/menu/use-click-outside.d.ts +9 -0
- package/dist/utils/menu/use-click-outside.js +45 -0
- package/dist/utils/menu/use-floating.svelte.d.ts +13 -0
- package/dist/utils/menu/use-floating.svelte.js +55 -0
- package/dist/utils/menu/use-portal.d.ts +3 -0
- package/dist/utils/menu/use-portal.js +8 -0
- package/package.json +8 -1
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type {Snippet} from 'svelte'
|
|
3
|
+
import {isItem, isGroup, isSub, isSeparator, menuStyles, menuItemClass, type MenuItem, type MenuEntry, type SubEntry} from '../utils/menu'
|
|
4
|
+
import {useFloating, portal} from '../utils/menu'
|
|
5
|
+
import {cn} from '../utils/cn'
|
|
6
|
+
import {IIIcon} from '../IIIcon'
|
|
7
|
+
import MenuItemContent from '../utils/menu/MenuItemContent.svelte'
|
|
8
|
+
|
|
9
|
+
type Props = {
|
|
10
|
+
items: MenuEntry[]
|
|
11
|
+
onSelect: (value: string) => void
|
|
12
|
+
onClose: () => void
|
|
13
|
+
triggerEl: HTMLElement | null
|
|
14
|
+
renderItemContent: Snippet<[MenuItem]>
|
|
15
|
+
renderSubMenu: Snippet<[SubEntry]>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
items,
|
|
20
|
+
onSelect,
|
|
21
|
+
onClose,
|
|
22
|
+
triggerEl,
|
|
23
|
+
renderItemContent,
|
|
24
|
+
renderSubMenu,
|
|
25
|
+
}: Props = $props()
|
|
26
|
+
|
|
27
|
+
let floatingEl = $state<HTMLElement | null>(null)
|
|
28
|
+
|
|
29
|
+
useFloating({
|
|
30
|
+
reference: () => triggerEl,
|
|
31
|
+
floating: () => floatingEl,
|
|
32
|
+
placement: 'right-start',
|
|
33
|
+
offset: 4,
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
function activate() {
|
|
37
|
+
requestAnimationFrame(() => {
|
|
38
|
+
const first = floatingEl?.querySelector<HTMLElement>('[role="menuitem"]:not([data-disabled])')
|
|
39
|
+
first?.focus()
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getMenuItems(): HTMLElement[] {
|
|
44
|
+
if (!floatingEl) return []
|
|
45
|
+
return Array.from(floatingEl.querySelectorAll<HTMLElement>(':scope > [role="menuitem"]:not([data-disabled]), :scope > [role="group"] > [role="menuitem"]:not([data-disabled])'))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function focusItem(items: HTMLElement[], currentIndex: number, direction: 1 | -1) {
|
|
49
|
+
if (items.length === 0) return
|
|
50
|
+
const nextIndex = direction === 1
|
|
51
|
+
? (currentIndex >= items.length - 1 ? 0 : currentIndex + 1)
|
|
52
|
+
: (currentIndex <= 0 ? items.length - 1 : currentIndex - 1)
|
|
53
|
+
items[nextIndex]?.focus()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function handleKeydown(e: KeyboardEvent) {
|
|
57
|
+
const menuItems = getMenuItems()
|
|
58
|
+
const currentIndex = menuItems.indexOf(document.activeElement as HTMLElement)
|
|
59
|
+
|
|
60
|
+
switch (e.key) {
|
|
61
|
+
case 'ArrowDown':
|
|
62
|
+
e.preventDefault()
|
|
63
|
+
focusItem(menuItems, currentIndex, 1)
|
|
64
|
+
break
|
|
65
|
+
case 'ArrowUp':
|
|
66
|
+
e.preventDefault()
|
|
67
|
+
focusItem(menuItems, currentIndex, -1)
|
|
68
|
+
break
|
|
69
|
+
case 'ArrowLeft':
|
|
70
|
+
case 'Escape':
|
|
71
|
+
e.preventDefault()
|
|
72
|
+
onClose()
|
|
73
|
+
break
|
|
74
|
+
case 'Enter':
|
|
75
|
+
case ' ':
|
|
76
|
+
e.preventDefault()
|
|
77
|
+
;(document.activeElement as HTMLElement)?.click()
|
|
78
|
+
break
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
</script>
|
|
82
|
+
|
|
83
|
+
<div use:portal>
|
|
84
|
+
<div
|
|
85
|
+
bind:this={floatingEl}
|
|
86
|
+
role="menu"
|
|
87
|
+
data-menu-content
|
|
88
|
+
class={menuStyles.subContent}
|
|
89
|
+
onkeydown={handleKeydown}
|
|
90
|
+
onpointerenter={() => activate()}
|
|
91
|
+
onpointerleave={(e) => e.stopImmediatePropagation()}
|
|
92
|
+
>
|
|
93
|
+
{#each items as entry, i (i)}
|
|
94
|
+
{#if isSeparator(entry)}
|
|
95
|
+
<div role="separator" class={menuStyles.separator}></div>
|
|
96
|
+
{:else if isGroup(entry)}
|
|
97
|
+
{@const headingId = `sub-group-${i}`}
|
|
98
|
+
<div role="group" aria-labelledby={entry.heading ? headingId : undefined}>
|
|
99
|
+
{#if entry.heading}
|
|
100
|
+
<div id={headingId} role="presentation" class={menuStyles.groupHeading}>
|
|
101
|
+
{entry.heading}
|
|
102
|
+
</div>
|
|
103
|
+
{/if}
|
|
104
|
+
{#each entry.items as groupItem (isItem(groupItem) ? groupItem.value : groupItem.label)}
|
|
105
|
+
{#if isSub(groupItem)}
|
|
106
|
+
{@render renderSubMenu(groupItem)}
|
|
107
|
+
{:else if isItem(groupItem)}
|
|
108
|
+
<div
|
|
109
|
+
role="menuitem"
|
|
110
|
+
tabindex="-1"
|
|
111
|
+
data-disabled={groupItem.disabled ? '' : undefined}
|
|
112
|
+
class={menuItemClass({variant: groupItem.variant})}
|
|
113
|
+
onclick={() => onSelect(groupItem.value)}
|
|
114
|
+
onpointerenter={(e) => (e.currentTarget as HTMLElement).focus()}
|
|
115
|
+
>
|
|
116
|
+
{@render renderItemContent(groupItem)}
|
|
117
|
+
</div>
|
|
118
|
+
{/if}
|
|
119
|
+
{/each}
|
|
120
|
+
</div>
|
|
121
|
+
{:else if isSub(entry)}
|
|
122
|
+
{@render renderSubMenu(entry)}
|
|
123
|
+
{:else if isItem(entry)}
|
|
124
|
+
<div
|
|
125
|
+
role="menuitem"
|
|
126
|
+
tabindex="-1"
|
|
127
|
+
data-disabled={entry.disabled ? '' : undefined}
|
|
128
|
+
class={menuItemClass({variant: entry.variant})}
|
|
129
|
+
onclick={() => onSelect(entry.value)}
|
|
130
|
+
onpointerenter={(e) => (e.currentTarget as HTMLElement).focus()}
|
|
131
|
+
>
|
|
132
|
+
{@render renderItemContent(entry)}
|
|
133
|
+
</div>
|
|
134
|
+
{/if}
|
|
135
|
+
{/each}
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import { type MenuItem, type MenuEntry, type SubEntry } from '../utils/menu';
|
|
3
|
+
type Props = {
|
|
4
|
+
items: MenuEntry[];
|
|
5
|
+
onSelect: (value: string) => void;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
triggerEl: HTMLElement | null;
|
|
8
|
+
renderItemContent: Snippet<[MenuItem]>;
|
|
9
|
+
renderSubMenu: Snippet<[SubEntry]>;
|
|
10
|
+
};
|
|
11
|
+
declare const IIContextMenuSubSimple: import("svelte").Component<Props, {}, "">;
|
|
12
|
+
type IIContextMenuSubSimple = ReturnType<typeof IIContextMenuSubSimple>;
|
|
13
|
+
export default IIContextMenuSubSimple;
|
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type {Snippet} from 'svelte'
|
|
3
|
-
import {DropdownMenu} from 'bits-ui'
|
|
4
3
|
import {IIIcon} from '../IIIcon'
|
|
5
4
|
import {cn} from '../utils/cn'
|
|
6
5
|
import {useDensity} from '../density'
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
icon?: Snippet
|
|
12
|
-
disabled?: boolean
|
|
13
|
-
}
|
|
6
|
+
import {createMenuSearch, type MenuItem} from '../utils/menu'
|
|
7
|
+
import {useFloating, portal, clickOutside} from '../utils/menu'
|
|
8
|
+
import MenuSearchInput from '../utils/menu/MenuSearchInput.svelte'
|
|
9
|
+
import MenuNoResults from '../utils/menu/MenuNoResults.svelte'
|
|
14
10
|
|
|
15
11
|
type Props = {
|
|
16
|
-
items:
|
|
12
|
+
items: MenuItem[]
|
|
17
13
|
value?: string | undefined
|
|
18
14
|
placeholder?: string
|
|
19
15
|
label?: string
|
|
20
16
|
disabled?: boolean
|
|
21
17
|
onSelect?: (value: string) => void
|
|
22
18
|
matchTriggerWidth?: boolean
|
|
23
|
-
renderItem?: Snippet<[item:
|
|
24
|
-
renderSelected?: Snippet<[item:
|
|
19
|
+
renderItem?: Snippet<[item: MenuItem, selected: boolean]>
|
|
20
|
+
renderSelected?: Snippet<[item: MenuItem]>
|
|
21
|
+
searchable?: boolean
|
|
22
|
+
searchPlaceholder?: string
|
|
25
23
|
class?: string
|
|
26
24
|
}
|
|
27
25
|
|
|
@@ -35,6 +33,8 @@
|
|
|
35
33
|
matchTriggerWidth = false,
|
|
36
34
|
renderItem,
|
|
37
35
|
renderSelected,
|
|
36
|
+
searchable = false,
|
|
37
|
+
searchPlaceholder = 'Search...',
|
|
38
38
|
class: className,
|
|
39
39
|
}: Props = $props()
|
|
40
40
|
|
|
@@ -56,31 +56,113 @@
|
|
|
56
56
|
|
|
57
57
|
let open = $state(false)
|
|
58
58
|
let triggerEl = $state<HTMLElement | null>(null)
|
|
59
|
-
let
|
|
59
|
+
let floatingEl = $state<HTMLElement | null>(null)
|
|
60
60
|
|
|
61
61
|
const selectedItem = $derived(items.find(i => i.value === value))
|
|
62
62
|
const selectedLabel = $derived(selectedItem?.label ?? placeholder)
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
const search = createMenuSearch({
|
|
65
|
+
getItems: () => items,
|
|
66
|
+
onSelect: handleSelectValue,
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
useFloating({
|
|
70
|
+
reference: () => triggerEl,
|
|
71
|
+
floating: () => floatingEl,
|
|
72
|
+
placement: 'bottom-start',
|
|
73
|
+
offset: 2,
|
|
74
|
+
matchWidth: matchTriggerWidth,
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
function handleSelect(item: MenuItem) {
|
|
65
78
|
value = item.value
|
|
66
79
|
open = false
|
|
67
80
|
onSelect?.(item.value)
|
|
81
|
+
triggerEl?.focus()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function handleSelectValue(val: string) {
|
|
85
|
+
const item = items.find(i => i.value === val)
|
|
86
|
+
if (item) handleSelect(item)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function toggle() {
|
|
90
|
+
if (disabled) return
|
|
91
|
+
open = !open
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function close() {
|
|
95
|
+
open = false
|
|
96
|
+
triggerEl?.focus()
|
|
68
97
|
}
|
|
69
98
|
|
|
70
99
|
$effect(() => {
|
|
71
|
-
if (
|
|
72
|
-
|
|
100
|
+
if (open && searchable) {
|
|
101
|
+
search.resetAndFocus()
|
|
73
102
|
}
|
|
74
103
|
})
|
|
104
|
+
|
|
105
|
+
// Focus first item when opened (non-searchable)
|
|
106
|
+
$effect(() => {
|
|
107
|
+
if (open && floatingEl && !searchable) {
|
|
108
|
+
requestAnimationFrame(() => {
|
|
109
|
+
const first = floatingEl?.querySelector<HTMLElement>('[role="option"]:not([data-disabled])')
|
|
110
|
+
first?.focus()
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
function getOptionItems(): HTMLElement[] {
|
|
116
|
+
if (!floatingEl) return []
|
|
117
|
+
return Array.from(floatingEl.querySelectorAll<HTMLElement>('[role="option"]:not([data-disabled])'))
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function focusItem(optionItems: HTMLElement[], currentIndex: number, direction: 1 | -1) {
|
|
121
|
+
if (optionItems.length === 0) return
|
|
122
|
+
const nextIndex = direction === 1
|
|
123
|
+
? (currentIndex >= optionItems.length - 1 ? 0 : currentIndex + 1)
|
|
124
|
+
: (currentIndex <= 0 ? optionItems.length - 1 : currentIndex - 1)
|
|
125
|
+
optionItems[nextIndex]?.focus()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function handleListKeydown(e: KeyboardEvent) {
|
|
129
|
+
if (searchable) return
|
|
130
|
+
|
|
131
|
+
const optionItems = getOptionItems()
|
|
132
|
+
const currentIndex = optionItems.indexOf(document.activeElement as HTMLElement)
|
|
133
|
+
|
|
134
|
+
switch (e.key) {
|
|
135
|
+
case 'ArrowDown':
|
|
136
|
+
e.preventDefault()
|
|
137
|
+
focusItem(optionItems, currentIndex, 1)
|
|
138
|
+
break
|
|
139
|
+
case 'ArrowUp':
|
|
140
|
+
e.preventDefault()
|
|
141
|
+
focusItem(optionItems, currentIndex, -1)
|
|
142
|
+
break
|
|
143
|
+
case 'Enter':
|
|
144
|
+
case ' ':
|
|
145
|
+
e.preventDefault()
|
|
146
|
+
;(document.activeElement as HTMLElement)?.click()
|
|
147
|
+
break
|
|
148
|
+
case 'Escape':
|
|
149
|
+
e.preventDefault()
|
|
150
|
+
close()
|
|
151
|
+
break
|
|
152
|
+
}
|
|
153
|
+
}
|
|
75
154
|
</script>
|
|
76
155
|
|
|
77
156
|
<div class="flex flex-col">
|
|
78
157
|
{#if label}
|
|
79
158
|
<span class="text-small-emphasis text-secondary mb-4">{label}</span>
|
|
80
159
|
{/if}
|
|
81
|
-
<
|
|
82
|
-
|
|
83
|
-
|
|
160
|
+
<button
|
|
161
|
+
bind:this={triggerEl}
|
|
162
|
+
type="button"
|
|
163
|
+
role="combobox"
|
|
164
|
+
aria-haspopup="listbox"
|
|
165
|
+
aria-expanded={open}
|
|
84
166
|
{disabled}
|
|
85
167
|
class={cn(
|
|
86
168
|
'flex items-center justify-between gap-4 py-5 pl-12 pr-8 border bg-input-bg cursor-default text-button-secondary box-border appearance-none font-inherit outline-none transition-colors duration-base ease-in-out hover:text-button-secondary-hover hover:border-button-secondary-hover focus:border-accent focus:ring-3 focus:ring-primary disabled:opacity-50 disabled:cursor-not-allowed',
|
|
@@ -88,6 +170,7 @@
|
|
|
88
170
|
open ? 'border-button-secondary-hover' : 'border-button-secondary',
|
|
89
171
|
className
|
|
90
172
|
)}
|
|
173
|
+
onclick={toggle}
|
|
91
174
|
>
|
|
92
175
|
{#if renderSelected && selectedItem}
|
|
93
176
|
{@render renderSelected(selectedItem)}
|
|
@@ -95,50 +178,68 @@
|
|
|
95
178
|
<span>{selectedLabel}</span>
|
|
96
179
|
{/if}
|
|
97
180
|
<IIIcon iconName="caret-down" class="w-14 h-14 shrink-0" />
|
|
98
|
-
</
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
181
|
+
</button>
|
|
182
|
+
|
|
183
|
+
{#if open}
|
|
184
|
+
<div use:portal use:clickOutside={{onClose: close}}>
|
|
185
|
+
<div
|
|
186
|
+
bind:this={floatingEl}
|
|
187
|
+
role="listbox"
|
|
188
|
+
data-menu-content
|
|
189
|
+
class="min-w-100 bg-dropdown-bg border-[0.5px] border-dropdown-border rounded-10 shadow-dropdown p-4 z-16 outline-none"
|
|
190
|
+
onkeydown={handleListKeydown}
|
|
191
|
+
>
|
|
192
|
+
{#if searchable}
|
|
193
|
+
<MenuSearchInput
|
|
194
|
+
bind:inputEl={search.inputEl}
|
|
195
|
+
bind:searchQuery={search.searchQuery}
|
|
196
|
+
placeholder={searchPlaceholder}
|
|
197
|
+
onkeydown={(e) => search.handleKeydown(e, '[data-menu-content]')}
|
|
198
|
+
/>
|
|
199
|
+
{/if}
|
|
200
|
+
<div class="max-h-300 overflow-y-auto">
|
|
201
|
+
{#each (searchable ? search.filteredItems as MenuItem[] : items) as item (item.value)}
|
|
202
|
+
{@const index = searchable ? search.getItemIndex(item) : -1}
|
|
203
|
+
<div
|
|
204
|
+
role="option"
|
|
205
|
+
tabindex="-1"
|
|
206
|
+
aria-selected={value === item.value}
|
|
207
|
+
data-disabled={item.disabled ? '' : undefined}
|
|
208
|
+
class={cn(
|
|
209
|
+
'flex items-center justify-between gap-12 px-12 rounded-6 text-dropdown-item cursor-default outline-none data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed',
|
|
210
|
+
itemDensityClasses[density.value],
|
|
211
|
+
value === item.value && 'text-dropdown-item-selected',
|
|
212
|
+
!searchable && 'focus:bg-dropdown-item-hover',
|
|
213
|
+
searchable && index === search.highlightedIndex && 'bg-dropdown-item-hover'
|
|
214
|
+
)}
|
|
215
|
+
data-search-item={searchable ? '' : undefined}
|
|
216
|
+
onclick={() => handleSelect(item)}
|
|
217
|
+
onfocus={searchable ? search.refocusInput : undefined}
|
|
218
|
+
onpointermove={searchable ? () => search.setHighlight(index) : undefined}
|
|
219
|
+
onpointerenter={!searchable ? (e) => (e.currentTarget as HTMLElement).focus() : undefined}
|
|
220
|
+
>
|
|
221
|
+
{#if renderItem}
|
|
222
|
+
{@render renderItem(item, value === item.value)}
|
|
223
|
+
{:else}
|
|
224
|
+
<span class="flex items-center gap-8">
|
|
225
|
+
{#if item.icon}
|
|
226
|
+
<span class="w-16 h-16 flex items-center justify-center shrink-0 [&_svg]:w-16 [&_svg]:h-16">
|
|
227
|
+
{@render item.icon()}
|
|
130
228
|
</span>
|
|
131
|
-
{#if value === item.value}
|
|
132
|
-
<IIIcon iconName="check" class="w-14 h-14 text-accent shrink-0" />
|
|
133
|
-
{/if}
|
|
134
229
|
{/if}
|
|
135
|
-
|
|
136
|
-
|
|
230
|
+
{item.label}
|
|
231
|
+
</span>
|
|
232
|
+
{#if value === item.value}
|
|
233
|
+
<IIIcon iconName="check" class="w-14 h-14 text-accent shrink-0" />
|
|
234
|
+
{/if}
|
|
235
|
+
{/if}
|
|
137
236
|
</div>
|
|
138
|
-
|
|
237
|
+
{/each}
|
|
238
|
+
{#if searchable && search.filteredItems.length === 0}
|
|
239
|
+
<MenuNoResults />
|
|
240
|
+
{/if}
|
|
139
241
|
</div>
|
|
140
|
-
|
|
141
|
-
</
|
|
142
|
-
|
|
143
|
-
</DropdownMenu.Root>
|
|
242
|
+
</div>
|
|
243
|
+
</div>
|
|
244
|
+
{/if}
|
|
144
245
|
</div>
|
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
|
-
type
|
|
3
|
-
label: string;
|
|
4
|
-
value: string;
|
|
5
|
-
icon?: Snippet;
|
|
6
|
-
disabled?: boolean;
|
|
7
|
-
};
|
|
2
|
+
import { type MenuItem } from '../utils/menu';
|
|
8
3
|
type Props = {
|
|
9
|
-
items:
|
|
4
|
+
items: MenuItem[];
|
|
10
5
|
value?: string | undefined;
|
|
11
6
|
placeholder?: string;
|
|
12
7
|
label?: string;
|
|
13
8
|
disabled?: boolean;
|
|
14
9
|
onSelect?: (value: string) => void;
|
|
15
10
|
matchTriggerWidth?: boolean;
|
|
16
|
-
renderItem?: Snippet<[item:
|
|
17
|
-
renderSelected?: Snippet<[item:
|
|
11
|
+
renderItem?: Snippet<[item: MenuItem, selected: boolean]>;
|
|
12
|
+
renderSelected?: Snippet<[item: MenuItem]>;
|
|
13
|
+
searchable?: boolean;
|
|
14
|
+
searchPlaceholder?: string;
|
|
18
15
|
class?: string;
|
|
19
16
|
};
|
|
20
17
|
declare const IIDropdownInput: import("svelte").Component<Props, {}, "value">;
|