@x33025/sveltely 0.1.1 → 0.1.3

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.
Files changed (156) hide show
  1. package/dist/components/Library/AnimatedNumber/AnimatedNumber.demo.svelte +1 -1
  2. package/dist/components/Library/AsyncButton/AsyncButton.svelte +42 -16
  3. package/dist/components/Library/Button/Button.demo.svelte +5 -3
  4. package/dist/components/Library/Button/Button.demo.svelte.d.ts +1 -0
  5. package/dist/components/Library/Button/Button.svelte +21 -13
  6. package/dist/components/Library/Calendar/Calendar.demo.svelte +2 -14
  7. package/dist/components/Library/Calendar/Calendar.svelte +69 -65
  8. package/dist/components/Library/Checkbox/Checkbox.svelte +13 -14
  9. package/dist/components/Library/ChipInput/ChipInput.demo.svelte +1 -1
  10. package/dist/components/Library/ChipInput/ChipInput.svelte +7 -4
  11. package/dist/components/Library/Divider/Divider.svelte +10 -0
  12. package/dist/components/Library/Divider/Divider.svelte.d.ts +26 -0
  13. package/dist/components/Library/Divider/index.d.ts +1 -0
  14. package/dist/components/Library/Divider/index.js +1 -0
  15. package/dist/components/Library/Dropdown/Action.svelte +60 -0
  16. package/dist/components/Library/Dropdown/Action.svelte.d.ts +11 -0
  17. package/dist/components/Library/Dropdown/Divider.svelte +5 -0
  18. package/dist/components/Library/Dropdown/Divider.svelte.d.ts +19 -0
  19. package/dist/components/Library/Dropdown/Dropdown.demo.svelte +182 -65
  20. package/dist/components/Library/Dropdown/Dropdown.demo.svelte.d.ts +2 -1
  21. package/dist/components/Library/Dropdown/Dropdown.svelte +95 -250
  22. package/dist/components/Library/Dropdown/Dropdown.svelte.d.ts +17 -16
  23. package/dist/components/Library/Dropdown/Item.svelte +68 -0
  24. package/dist/components/Library/Dropdown/Item.svelte.d.ts +31 -0
  25. package/dist/components/Library/Dropdown/Section.svelte +34 -0
  26. package/dist/components/Library/Dropdown/Section.svelte.d.ts +8 -0
  27. package/dist/components/Library/Dropdown/context.d.ts +34 -0
  28. package/dist/components/Library/Dropdown/context.js +6 -0
  29. package/dist/components/Library/Dropdown/index.d.ts +13 -2
  30. package/dist/components/Library/Dropdown/index.js +11 -1
  31. package/dist/components/Library/Floating/Floating.svelte +44 -7
  32. package/dist/components/Library/ForEach/ForEach.svelte +14 -0
  33. package/dist/components/Library/ForEach/ForEach.svelte.d.ts +28 -0
  34. package/dist/components/Library/ForEach/index.d.ts +1 -0
  35. package/dist/components/Library/ForEach/index.js +1 -0
  36. package/dist/components/Library/Grid/Grid.svelte +74 -0
  37. package/dist/components/Library/Grid/Grid.svelte.d.ts +13 -0
  38. package/dist/components/Library/Grid/index.d.ts +1 -0
  39. package/dist/components/Library/Grid/index.js +1 -0
  40. package/dist/components/Library/GridItem/GridItem.svelte +65 -0
  41. package/dist/components/Library/GridItem/GridItem.svelte.d.ts +14 -0
  42. package/dist/components/Library/GridItem/index.d.ts +1 -0
  43. package/dist/components/Library/GridItem/index.js +1 -0
  44. package/dist/components/Library/HStack/HStack.svelte +45 -0
  45. package/dist/components/Library/HStack/HStack.svelte.d.ts +9 -0
  46. package/dist/components/Library/HStack/index.d.ts +1 -0
  47. package/dist/components/Library/HStack/index.js +1 -0
  48. package/dist/components/Library/Image/Image.demo.svelte +18 -0
  49. package/dist/components/Library/Image/Image.demo.svelte.d.ts +23 -0
  50. package/dist/components/Library/Image/Image.svelte +57 -0
  51. package/dist/components/Library/Image/Image.svelte.d.ts +17 -0
  52. package/dist/components/Library/Image/ImagePlaceholder.svelte +202 -0
  53. package/dist/components/Library/Image/ImagePlaceholder.svelte.d.ts +7 -0
  54. package/dist/components/Library/Image/index.d.ts +1 -0
  55. package/dist/components/Library/Image/index.js +1 -0
  56. package/dist/components/Library/ImageMask/BrushPreview.svelte +119 -0
  57. package/dist/components/Library/ImageMask/BrushPreview.svelte.d.ts +11 -0
  58. package/dist/components/Library/ImageMask/ImageMask.demo.svelte +117 -0
  59. package/dist/components/Library/ImageMask/ImageMask.demo.svelte.d.ts +10 -0
  60. package/dist/components/Library/ImageMask/ImageMask.svelte +46 -0
  61. package/dist/components/Library/ImageMask/ImageMask.svelte.d.ts +20 -0
  62. package/dist/components/Library/ImageMask/MaskLayer.svelte +341 -0
  63. package/dist/components/Library/ImageMask/MaskLayer.svelte.d.ts +12 -0
  64. package/dist/components/Library/ImageMask/contour.d.ts +11 -0
  65. package/dist/components/Library/ImageMask/contour.js +152 -0
  66. package/dist/components/Library/ImageMask/index.d.ts +2 -0
  67. package/dist/components/Library/ImageMask/index.js +1 -0
  68. package/dist/components/Library/ImageMask/marchingAnts.d.ts +8 -0
  69. package/dist/components/Library/ImageMask/marchingAnts.js +29 -0
  70. package/dist/components/Library/ImageMask/maskSurface.d.ts +5 -0
  71. package/dist/components/Library/ImageMask/maskSurface.js +94 -0
  72. package/dist/components/Library/ImageMask/types.d.ts +23 -0
  73. package/dist/components/Library/Label/Label.demo.svelte +28 -0
  74. package/dist/components/Library/Label/Label.demo.svelte.d.ts +9 -0
  75. package/dist/components/Library/Label/Label.svelte +175 -0
  76. package/dist/components/Library/Label/Label.svelte.d.ts +18 -0
  77. package/dist/components/Library/Label/index.d.ts +1 -0
  78. package/dist/components/Library/Label/index.js +1 -0
  79. package/dist/components/Library/NavigationStack/NavigationStack.svelte +17 -7
  80. package/dist/components/Library/NavigationStack/Toolbar.svelte +7 -2
  81. package/dist/components/Library/NumberField/NumberField.demo.svelte +21 -0
  82. package/dist/components/Library/NumberField/NumberField.demo.svelte.d.ts +8 -0
  83. package/dist/components/Library/NumberField/NumberField.svelte +199 -0
  84. package/dist/components/Library/NumberField/NumberField.svelte.d.ts +21 -0
  85. package/dist/components/Library/NumberField/index.d.ts +1 -0
  86. package/dist/components/Library/NumberField/index.js +1 -0
  87. package/dist/components/Library/Pagination/Pagination.svelte +16 -20
  88. package/dist/components/Library/Popover/Popover.demo.svelte +2 -2
  89. package/dist/components/Library/Popover/Popover.svelte +7 -4
  90. package/dist/components/Library/ScrollView/ScrollView.svelte +165 -12
  91. package/dist/components/Library/ScrollView/ScrollView.svelte.d.ts +32 -4
  92. package/dist/components/Library/ScrollView/index.d.ts +1 -0
  93. package/dist/components/Library/{SearchInput/SearchInput.demo.svelte → SearchField/SearchField.demo.svelte} +4 -4
  94. package/dist/components/Library/SearchField/SearchField.demo.svelte.d.ts +8 -0
  95. package/dist/components/Library/{SearchInput/SearchInput.svelte → SearchField/SearchField.svelte} +26 -30
  96. package/dist/components/Library/{SearchInput/SearchInput.svelte.d.ts → SearchField/SearchField.svelte.d.ts} +3 -3
  97. package/dist/components/Library/SearchField/index.d.ts +1 -0
  98. package/dist/components/Library/SearchField/index.js +1 -0
  99. package/dist/components/Library/SegmentedPicker/SegmentedPicker.demo.svelte +1 -1
  100. package/dist/components/Library/SegmentedPicker/SegmentedPicker.svelte +9 -9
  101. package/dist/components/Library/Sheet/Sheet.demo.svelte +1 -1
  102. package/dist/components/Library/Sheet/Sheet.svelte +8 -5
  103. package/dist/components/Library/Slider/Slider.demo.svelte +1 -1
  104. package/dist/components/Library/Slider/Slider.svelte +11 -10
  105. package/dist/components/Library/Spacer/Spacer.svelte +7 -0
  106. package/dist/components/Library/Spacer/Spacer.svelte.d.ts +26 -0
  107. package/dist/components/Library/Spacer/index.d.ts +1 -0
  108. package/dist/components/Library/Spacer/index.js +1 -0
  109. package/dist/components/Library/Spinner/Spinner.demo.svelte +1 -1
  110. package/dist/components/Library/Switch/Switch.svelte +6 -11
  111. package/dist/components/Library/TextField/TextField.demo.svelte +14 -0
  112. package/dist/components/Library/TextField/TextField.demo.svelte.d.ts +8 -0
  113. package/dist/components/Library/TextField/TextField.svelte +154 -0
  114. package/dist/components/Library/TextField/TextField.svelte.d.ts +19 -0
  115. package/dist/components/Library/TextField/index.d.ts +1 -0
  116. package/dist/components/Library/TextField/index.js +1 -0
  117. package/dist/components/Library/{TokenSearchInput/TokenSearchInput.demo.svelte → TokenSearchField/TokenSearchField.demo.svelte} +4 -4
  118. package/dist/components/Library/TokenSearchField/TokenSearchField.demo.svelte.d.ts +9 -0
  119. package/dist/components/Library/{TokenSearchInput/TokenSearchInput.svelte → TokenSearchField/TokenSearchField.svelte} +70 -66
  120. package/dist/components/Library/{TokenSearchInput/TokenSearchInput.svelte.d.ts → TokenSearchField/TokenSearchField.svelte.d.ts} +3 -3
  121. package/dist/components/Library/TokenSearchField/index.d.ts +1 -0
  122. package/dist/components/Library/TokenSearchField/index.js +1 -0
  123. package/dist/components/Library/VStack/VStack.svelte +45 -0
  124. package/dist/components/Library/VStack/VStack.svelte.d.ts +9 -0
  125. package/dist/components/Library/VStack/index.d.ts +1 -0
  126. package/dist/components/Library/VStack/index.js +1 -0
  127. package/dist/components/Library/WheelPicker/WheelColumn.svelte +4 -10
  128. package/dist/components/Library/WheelPicker/WheelPicker.svelte +5 -9
  129. package/dist/components/Local/ComponentGrid.svelte +15 -31
  130. package/dist/components/Local/HeroCard.svelte +30 -38
  131. package/dist/components/Local/HeroCard.svelte.d.ts +0 -2
  132. package/dist/components/Local/StyleControls.svelte +58 -27
  133. package/dist/components/Local/StyleControls.svelte.d.ts +3 -1
  134. package/dist/index.d.ts +26 -2
  135. package/dist/index.js +19 -2
  136. package/dist/style/index.css +35 -20
  137. package/dist/style/label.d.ts +6 -0
  138. package/dist/style/label.js +4 -0
  139. package/dist/style/layout.d.ts +57 -0
  140. package/dist/style/layout.js +128 -0
  141. package/dist/style/media.d.ts +12 -0
  142. package/dist/style/media.js +8 -0
  143. package/dist/style/scroll.d.ts +7 -0
  144. package/dist/style/scroll.js +5 -0
  145. package/dist/style/text-editor.d.ts +34 -0
  146. package/dist/style/text-editor.js +29 -0
  147. package/dist/style.css +112 -58
  148. package/package.json +1 -1
  149. package/dist/components/Library/Dropdown/types.d.ts +0 -27
  150. package/dist/components/Library/SearchInput/SearchInput.demo.svelte.d.ts +0 -8
  151. package/dist/components/Library/SearchInput/index.d.ts +0 -1
  152. package/dist/components/Library/SearchInput/index.js +0 -1
  153. package/dist/components/Library/TokenSearchInput/TokenSearchInput.demo.svelte.d.ts +0 -9
  154. package/dist/components/Library/TokenSearchInput/index.d.ts +0 -1
  155. package/dist/components/Library/TokenSearchInput/index.js +0 -1
  156. /package/dist/components/Library/{Dropdown → ImageMask}/types.js +0 -0
@@ -1,134 +1,71 @@
1
- <script lang="ts" generics="T extends string | number = string">
2
- import { CheckIcon, ChevronDownIcon, ChevronRightIcon } from '@lucide/svelte';
1
+ <script lang="ts" generics="TValue = unknown">
2
+ import { ChevronDownIcon } from '@lucide/svelte';
3
+ import type { Snippet } from 'svelte';
3
4
  import Floating from '../Floating/Floating.svelte';
4
- import SearchInput from '../SearchInput';
5
5
  import { surfaceStyle, type StyleProps } from '../../../style/surface';
6
6
  import type { Anchor } from '../../../utils/positioning';
7
- import type {
8
- DropdownAction,
9
- DropdownEntry,
10
- DropdownGroup,
11
- DropdownItem,
12
- DropdownSubmenu
13
- } from './types';
7
+ import { setDropdownContext, type DropdownTriggerState } from './context';
14
8
 
15
9
  type Props = {
16
- items: DropdownEntry<T>[];
17
- value?: T | null;
10
+ value?: TValue | null;
18
11
  open?: boolean;
19
12
  label?: string;
20
13
  placeholder?: string;
14
+ selectedLabel?: string | null;
21
15
  disabled?: boolean;
22
16
  closeOnSelect?: boolean;
23
17
  showCheck?: boolean;
24
- searchPlaceholder?: string;
25
18
  placement?: Anchor;
26
- onSearch?: (query: string) => void | Promise<void>;
27
- onSelect?: (item: DropdownItem<T>) => void;
19
+ trigger?: Snippet<[DropdownTriggerState<TValue>]>;
20
+ children?: Snippet;
21
+ onValueChange?: (value: TValue) => void;
28
22
  } & StyleProps;
29
23
 
30
- const isGroup = (entry: DropdownEntry<T>): entry is DropdownGroup<T> =>
31
- 'type' in entry && entry.type === 'group';
32
- const isAction = (entry: DropdownItem<T> | DropdownAction): entry is DropdownAction =>
33
- 'type' in entry && entry.type === 'action';
34
- const isSubmenu = (entry: DropdownEntry<T>): entry is DropdownSubmenu<T> =>
35
- 'type' in entry && entry.type === 'submenu';
36
-
37
24
  let {
38
- items,
39
- value = $bindable<T | null>(null),
25
+ value = $bindable<TValue | null>(null),
40
26
  open = $bindable(false),
41
27
  label = 'Select option',
42
28
  placeholder = 'Select option',
29
+ selectedLabel = null,
43
30
  disabled = false,
44
31
  closeOnSelect = true,
45
32
  showCheck = true,
46
- searchPlaceholder = 'Search',
47
33
  placement = 'bottom',
48
- onSearch,
49
- onSelect,
34
+ trigger: triggerContent,
35
+ children,
36
+ onValueChange,
50
37
  ...styleProps
51
38
  }: Props = $props();
52
39
 
53
40
  const dropdownStyle = $derived.by(() => surfaceStyle(styleProps, 'dropdown'));
41
+ const triggerText = $derived(selectedLabel ?? (value === null ? placeholder : String(value)));
54
42
 
55
- let query = $state('');
56
- let lastSearchedQuery = '';
57
-
58
- const searchEnabled = $derived(!!onSearch);
59
-
60
- const flattenItems = (
61
- entries: DropdownEntry<T>[],
62
- inheritedDisabled = false
63
- ): DropdownItem<T>[] =>
64
- entries.flatMap((entry) => {
65
- const nextDisabled = inheritedDisabled || !!entry.disabled;
66
- if (isGroup(entry) || isSubmenu(entry)) {
67
- return flattenItems(entry.items, nextDisabled);
68
- }
69
- if (isAction(entry)) {
70
- return [];
71
- }
72
- return [{ ...entry, disabled: nextDisabled || entry.disabled }];
73
- });
74
-
75
- const findFirstRenderableLabel = (entries: DropdownEntry<T>[]): string | null => {
76
- for (const entry of entries) {
77
- if (isGroup(entry)) {
78
- const nested = findFirstRenderableLabel(entry.items);
79
- if (nested) return nested;
80
- continue;
81
- }
82
- return entry.label;
83
- }
84
- return null;
85
- };
86
-
87
- const flatItems = $derived.by(() => flattenItems(items));
88
-
89
- const selectedItem = $derived.by(
90
- () => flatItems.find((item) => item.value === value) ?? null
91
- );
92
-
93
- const triggerText = $derived(selectedItem?.label ?? placeholder);
94
-
95
- const filteredItems = $derived(items);
96
-
97
- const firstRenderableLabel = $derived.by(() => findFirstRenderableLabel(filteredItems));
98
-
99
- const itemRadiusSourceEnabled = $derived.by(() => {
100
- if (searchEnabled) return false;
101
- const firstEntry = filteredItems[0];
102
- if (!firstEntry) return false;
103
- if (isGroup(firstEntry)) {
104
- return !firstEntry.label;
105
- }
106
- return true;
107
- });
108
-
109
- function handleSelect(item: DropdownItem<T>) {
110
- if (disabled || item.disabled) return;
111
- value = item.value;
112
- onSelect?.(item);
43
+ function select(nextValue: TValue) {
44
+ if (disabled) return;
45
+ value = nextValue;
46
+ onValueChange?.(nextValue);
113
47
  if (closeOnSelect) open = false;
114
48
  }
115
49
 
116
- function handleAction(item: DropdownAction) {
117
- if (disabled || item.disabled) return;
118
- item.onSelect();
119
- if (closeOnSelect) open = false;
50
+ function close() {
51
+ open = false;
120
52
  }
121
53
 
122
- const isEntryDisabled = (entry: { disabled?: boolean }, inheritedDisabled = false) =>
123
- disabled || inheritedDisabled || !!entry.disabled;
124
-
125
- const isRadiusSource = (label: string) => itemRadiusSourceEnabled && label === firstRenderableLabel;
126
-
127
- $effect(() => {
128
- if (!onSearch) return;
129
- if (query === lastSearchedQuery) return;
130
- lastSearchedQuery = query;
131
- void onSearch(query);
54
+ setDropdownContext<TValue>({
55
+ get value() {
56
+ return value;
57
+ },
58
+ get disabled() {
59
+ return disabled;
60
+ },
61
+ get closeOnSelect() {
62
+ return closeOnSelect;
63
+ },
64
+ get showCheck() {
65
+ return showCheck;
66
+ },
67
+ select,
68
+ close
132
69
  });
133
70
  </script>
134
71
 
@@ -142,122 +79,41 @@
142
79
  matchPanelRadiusToSource={true}
143
80
  >
144
81
  {#snippet trigger(floating)}
145
- <button
146
- use:floating.useTrigger
147
- type="button"
148
- class="dropdown-trigger justify-between"
149
- aria-label={label}
150
- aria-disabled={disabled}
151
- disabled={disabled}
152
- style={dropdownStyle}
153
- aria-expanded={floating.open}
154
- aria-haspopup="dialog"
155
- onclick={floating.toggle}
156
- >
157
- <span>{triggerText}</span>
158
- <ChevronDownIcon class="size-4 text-zinc-500" />
159
- </button>
82
+ {#if triggerContent}
83
+ {@render triggerContent({
84
+ useTrigger: floating.useTrigger,
85
+ open: floating.open,
86
+ value,
87
+ selectedLabel: triggerText,
88
+ placeholder,
89
+ disabled,
90
+ toggle: floating.toggle,
91
+ openPanel: floating.openPanel,
92
+ closePanel: floating.closePanel
93
+ })}
94
+ {:else}
95
+ <button
96
+ use:floating.useTrigger
97
+ type="button"
98
+ class="dropdown-trigger justify-between"
99
+ aria-label={label}
100
+ aria-disabled={disabled}
101
+ {disabled}
102
+ style={dropdownStyle}
103
+ aria-expanded={floating.open}
104
+ aria-haspopup="dialog"
105
+ onclick={floating.toggle}
106
+ >
107
+ <span class="dropdown-trigger-label">{triggerText}</span>
108
+ <ChevronDownIcon class="size-4 text-[var(--sveltely-secondary-color)]" />
109
+ </button>
110
+ {/if}
160
111
  {/snippet}
161
112
 
162
- <div
163
- class="dropdown-content vstack"
164
- style={`${dropdownStyle} --dropdown-item-radius: ${searchEnabled ? 'var(--sveltely-border-radius)' : 'var(--sveltely-border-radius-nested)'};`}
165
- >
166
- {#if searchEnabled}
167
- <SearchInput
168
- bind:value={query}
169
- placeholder={searchPlaceholder}
170
- radiusSource={true}
171
- class="w-64"
172
- />
113
+ <div class="dropdown-content vstack" style={dropdownStyle}>
114
+ {#if children}
115
+ {@render children()}
173
116
  {/if}
174
- {#snippet renderEntries(entries: DropdownEntry<T>[], inheritedDisabled = false)}
175
- {#each entries as entry, index (`${index}-${entry.type ?? 'option'}-${entry.label}`)}
176
- {#if isGroup(entry)}
177
- <div class="dropdown-group vstack">
178
- {#if entry.label}
179
- <div class="dropdown-group-label">{entry.label}</div>
180
- {/if}
181
- {@render renderEntries(entry.items, inheritedDisabled || !!entry.disabled)}
182
- </div>
183
- {:else if isSubmenu(entry)}
184
- <Floating
185
- placement={entry.placement ?? 'right'}
186
- rootClass="dropdown-submenu-root relative w-full"
187
- panelClass="dropdown-panel fixed z-50 focus:outline-none"
188
- panelStyle={dropdownStyle}
189
- contentStyle=""
190
- matchPanelRadiusToSource={true}
191
- closeOnPointerLeave={true}
192
- >
193
- {#snippet trigger(floating)}
194
- <button
195
- use:floating.useTrigger
196
- type="button"
197
- class="dropdown-item dropdown-submenu-trigger inline-flex items-center justify-between text-left"
198
- class:dropdown-item-open={floating.open}
199
- data-popover-radius-source={isRadiusSource(entry.label) ? 'true' : undefined}
200
- disabled={isEntryDisabled(entry, inheritedDisabled)}
201
- aria-expanded={floating.open}
202
- aria-haspopup="menu"
203
- onmouseenter={() => {
204
- if (!isEntryDisabled(entry, inheritedDisabled)) {
205
- void floating.openPanel();
206
- }
207
- }}
208
- onclick={(event) => {
209
- event.stopPropagation();
210
- floating.toggle();
211
- }}
212
- >
213
- <span>{entry.label}</span>
214
- <ChevronRightIcon class="size-4 text-zinc-500" />
215
- </button>
216
- {/snippet}
217
-
218
- <div
219
- class="dropdown-content vstack"
220
- style={`${dropdownStyle} --dropdown-item-radius: var(--sveltely-border-radius-nested);`}
221
- >
222
- {@render renderEntries(entry.items, inheritedDisabled || !!entry.disabled)}
223
- </div>
224
- </Floating>
225
- {:else if isAction(entry)}
226
- <button
227
- type="button"
228
- class="dropdown-item inline-flex items-center justify-between text-left"
229
- data-popover-radius-source={isRadiusSource(entry.label) ? 'true' : undefined}
230
- disabled={isEntryDisabled(entry, inheritedDisabled)}
231
- onclick={() =>
232
- handleAction({
233
- ...entry,
234
- disabled: inheritedDisabled || entry.disabled
235
- })}
236
- >
237
- <span>{entry.label}</span>
238
- </button>
239
- {:else}
240
- <button
241
- type="button"
242
- class="dropdown-item inline-flex items-center justify-between text-left"
243
- data-popover-radius-source={isRadiusSource(entry.label) ? 'true' : undefined}
244
- disabled={isEntryDisabled(entry, inheritedDisabled)}
245
- onclick={() =>
246
- handleSelect({
247
- ...entry,
248
- disabled: inheritedDisabled || entry.disabled
249
- })}
250
- >
251
- <span>{entry.label}</span>
252
- {#if showCheck && entry.value === value}
253
- <CheckIcon class="size-4 text-zinc-700" />
254
- {/if}
255
- </button>
256
- {/if}
257
- {/each}
258
- {/snippet}
259
-
260
- {@render renderEntries(filteredItems)}
261
117
  </div>
262
118
  </Floating>
263
119
 
@@ -267,67 +123,56 @@
267
123
  --sveltely-nested-inset: var(--dropdown-inset);
268
124
  border: 1px solid var(--sveltely-border-color);
269
125
  border-radius: var(--sveltely-border-radius);
270
- background: white;
126
+ background: var(--sveltely-background-color);
271
127
  padding: var(--dropdown-inset);
272
128
  }
273
129
 
274
130
  .dropdown-trigger {
275
131
  display: inline-flex;
276
132
  min-width: 8rem;
133
+ max-width: 100%;
277
134
  align-items: center;
278
135
  border: 1px solid var(--sveltely-border-color);
279
136
  border-radius: var(--sveltely-border-radius);
280
- background: white;
281
- color: var(--color-zinc-800);
137
+ background: var(--sveltely-background-color);
138
+ color: var(--sveltely-primary-color);
282
139
  gap: var(--sveltely-gap);
283
140
  padding: calc(var(--sveltely-padding-y) * 0.67) var(--sveltely-padding-x);
284
141
  font-size: 0.875rem;
285
142
  line-height: 1.25rem;
286
- transition: color 150ms, border-color 150ms, background-color 150ms;
143
+ transition:
144
+ color 150ms,
145
+ border-color 150ms,
146
+ background-color 150ms;
287
147
  }
288
148
 
289
- .dropdown-trigger:hover {
290
- background: var(--sveltely-hover-color);
149
+ .dropdown-trigger-label {
150
+ min-width: 0;
151
+ flex: 1 1 auto;
152
+ overflow: hidden;
153
+ text-overflow: ellipsis;
154
+ white-space: nowrap;
291
155
  }
292
156
 
293
- .dropdown-group-label {
294
- padding-inline: 0.25rem;
295
- padding-top: 0.25rem;
296
- color: var(--color-zinc-500);
297
- font-size: 0.75rem;
298
- line-height: 1rem;
299
- font-weight: 500;
300
- }
301
-
302
- .dropdown-content {
303
- gap: var(--sveltely-inset);
304
- }
305
-
306
- .dropdown-group {
307
- gap: var(--sveltely-inset);
157
+ .dropdown-trigger :global(svg) {
158
+ flex: 0 0 auto;
308
159
  }
309
160
 
310
- .dropdown-item {
311
- width: 100%;
312
- gap: calc(var(--sveltely-inset) * 2);
313
- border-radius: var(--dropdown-item-radius, var(--sveltely-border-radius-nested));
314
- padding: calc(var(--sveltely-padding-y) * 0.33) calc(var(--sveltely-padding-x) * 0.67);
315
- }
316
-
317
- .dropdown-item :global(*) {
318
- border-radius: inherit;
319
- }
320
-
321
- .dropdown-item:hover {
161
+ .dropdown-trigger:hover {
322
162
  background: var(--sveltely-hover-color);
323
163
  }
324
164
 
325
- .dropdown-item-open {
326
- background: var(--sveltely-hover-color);
165
+ .dropdown-content {
166
+ --dropdown-item-padding-x: calc(var(--sveltely-padding-x) * 0.67);
167
+ --dropdown-item-padding-y: calc(var(--sveltely-padding-y) * 0.33);
168
+ --dropdown-item-radius: var(--sveltely-border-radius-nested);
169
+ gap: var(--sveltely-inset);
327
170
  }
328
171
 
329
- .dropdown-item:disabled {
330
- cursor: not-allowed;
331
- opacity: 0.5;
172
+ .dropdown-content :global(.divider) {
173
+ --divider-width: auto;
174
+ margin-inline: var(--dropdown-item-padding-x);
175
+ margin-block: calc(var(--sveltely-inset) * 0.5);
176
+ background: var(--sveltely-border-color);
332
177
  }
333
178
  </style>
@@ -1,40 +1,41 @@
1
+ import type { Snippet } from 'svelte';
1
2
  import { type StyleProps } from '../../../style/surface';
2
3
  import type { Anchor } from '../../../utils/positioning';
3
- import type { DropdownEntry, DropdownItem } from './types';
4
- declare function $$render<T extends string | number = string>(): {
4
+ import { type DropdownTriggerState } from './context';
5
+ declare function $$render<TValue = unknown>(): {
5
6
  props: {
6
- items: DropdownEntry<T>[];
7
- value?: T | null;
7
+ value?: TValue | null;
8
8
  open?: boolean;
9
9
  label?: string;
10
10
  placeholder?: string;
11
+ selectedLabel?: string | null;
11
12
  disabled?: boolean;
12
13
  closeOnSelect?: boolean;
13
14
  showCheck?: boolean;
14
- searchPlaceholder?: string;
15
15
  placement?: Anchor;
16
- onSearch?: (query: string) => void | Promise<void>;
17
- onSelect?: (item: DropdownItem<T>) => void;
16
+ trigger?: Snippet<[DropdownTriggerState<TValue>]>;
17
+ children?: Snippet;
18
+ onValueChange?: (value: TValue) => void;
18
19
  } & StyleProps;
19
20
  exports: {};
20
21
  bindings: "value" | "open";
21
22
  slots: {};
22
23
  events: {};
23
24
  };
24
- declare class __sveltets_Render<T extends string | number = string> {
25
- props(): ReturnType<typeof $$render<T>>['props'];
26
- events(): ReturnType<typeof $$render<T>>['events'];
27
- slots(): ReturnType<typeof $$render<T>>['slots'];
25
+ declare class __sveltets_Render<TValue = unknown> {
26
+ props(): ReturnType<typeof $$render<TValue>>['props'];
27
+ events(): ReturnType<typeof $$render<TValue>>['events'];
28
+ slots(): ReturnType<typeof $$render<TValue>>['slots'];
28
29
  bindings(): "value" | "open";
29
30
  exports(): {};
30
31
  }
31
32
  interface $$IsomorphicComponent {
32
- new <T extends string | number = string>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
33
- $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
34
- } & ReturnType<__sveltets_Render<T>['exports']>;
35
- <T extends string | number = string>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
33
+ new <TValue = unknown>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<TValue>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<TValue>['props']>, ReturnType<__sveltets_Render<TValue>['events']>, ReturnType<__sveltets_Render<TValue>['slots']>> & {
34
+ $$bindings?: ReturnType<__sveltets_Render<TValue>['bindings']>;
35
+ } & ReturnType<__sveltets_Render<TValue>['exports']>;
36
+ <TValue = unknown>(internal: unknown, props: ReturnType<__sveltets_Render<TValue>['props']> & {}): ReturnType<__sveltets_Render<TValue>['exports']>;
36
37
  z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
37
38
  }
38
39
  declare const Dropdown: $$IsomorphicComponent;
39
- type Dropdown<T extends string | number = string> = InstanceType<typeof Dropdown<T>>;
40
+ type Dropdown<TValue = unknown> = InstanceType<typeof Dropdown<TValue>>;
40
41
  export default Dropdown;
@@ -0,0 +1,68 @@
1
+ <script lang="ts" generics="TValue = unknown">
2
+ import { CheckIcon } from '@lucide/svelte';
3
+ import type { Snippet } from 'svelte';
4
+ import { getDropdownContext, type DropdownItemState } from './context';
5
+
6
+ type Props = {
7
+ value: TValue;
8
+ disabled?: boolean;
9
+ showCheck?: boolean;
10
+ children?: Snippet<[DropdownItemState<TValue>]>;
11
+ };
12
+
13
+ let { value, disabled = false, showCheck, children }: Props = $props();
14
+
15
+ const dropdown = getDropdownContext<TValue>();
16
+ const selected = $derived(dropdown.value === value);
17
+ const resolvedDisabled = $derived(dropdown.disabled || disabled);
18
+ const resolvedShowCheck = $derived(showCheck ?? dropdown.showCheck);
19
+
20
+ function select() {
21
+ if (resolvedDisabled) return;
22
+ dropdown.select(value);
23
+ }
24
+ </script>
25
+
26
+ <button
27
+ type="button"
28
+ class="dropdown-item inline-flex items-center justify-between text-left"
29
+ data-popover-radius-source
30
+ disabled={resolvedDisabled}
31
+ aria-pressed={selected}
32
+ onclick={select}
33
+ >
34
+ {#if children}
35
+ {@render children({
36
+ value,
37
+ selected,
38
+ disabled: resolvedDisabled,
39
+ close: dropdown.close,
40
+ select
41
+ })}
42
+ {/if}
43
+ {#if resolvedShowCheck && selected}
44
+ <CheckIcon class="size-4 text-[var(--sveltely-primary-color)]" />
45
+ {/if}
46
+ </button>
47
+
48
+ <style>
49
+ .dropdown-item {
50
+ width: 100%;
51
+ gap: calc(var(--sveltely-inset) * 2);
52
+ border-radius: var(--dropdown-item-radius, var(--sveltely-border-radius-nested));
53
+ padding: var(--dropdown-item-padding-y) var(--dropdown-item-padding-x);
54
+ }
55
+
56
+ .dropdown-item :global(*) {
57
+ border-radius: inherit;
58
+ }
59
+
60
+ .dropdown-item:hover {
61
+ background: var(--sveltely-hover-color);
62
+ }
63
+
64
+ .dropdown-item:disabled {
65
+ cursor: not-allowed;
66
+ opacity: 0.5;
67
+ }
68
+ </style>
@@ -0,0 +1,31 @@
1
+ import type { Snippet } from 'svelte';
2
+ import { type DropdownItemState } from './context';
3
+ declare function $$render<TValue = unknown>(): {
4
+ props: {
5
+ value: TValue;
6
+ disabled?: boolean;
7
+ showCheck?: boolean;
8
+ children?: Snippet<[DropdownItemState<TValue>]>;
9
+ };
10
+ exports: {};
11
+ bindings: "";
12
+ slots: {};
13
+ events: {};
14
+ };
15
+ declare class __sveltets_Render<TValue = unknown> {
16
+ props(): ReturnType<typeof $$render<TValue>>['props'];
17
+ events(): ReturnType<typeof $$render<TValue>>['events'];
18
+ slots(): ReturnType<typeof $$render<TValue>>['slots'];
19
+ bindings(): "";
20
+ exports(): {};
21
+ }
22
+ interface $$IsomorphicComponent {
23
+ new <TValue = unknown>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<TValue>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<TValue>['props']>, ReturnType<__sveltets_Render<TValue>['events']>, ReturnType<__sveltets_Render<TValue>['slots']>> & {
24
+ $$bindings?: ReturnType<__sveltets_Render<TValue>['bindings']>;
25
+ } & ReturnType<__sveltets_Render<TValue>['exports']>;
26
+ <TValue = unknown>(internal: unknown, props: ReturnType<__sveltets_Render<TValue>['props']> & {}): ReturnType<__sveltets_Render<TValue>['exports']>;
27
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
28
+ }
29
+ declare const Item: $$IsomorphicComponent;
30
+ type Item<TValue = unknown> = InstanceType<typeof Item<TValue>>;
31
+ export default Item;
@@ -0,0 +1,34 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ type Props = {
5
+ label?: string;
6
+ children?: Snippet;
7
+ };
8
+
9
+ let { label, children }: Props = $props();
10
+ </script>
11
+
12
+ <div class="dropdown-section vstack">
13
+ {#if label}
14
+ <div class="dropdown-section-label">{label}</div>
15
+ {/if}
16
+ {#if children}
17
+ {@render children()}
18
+ {/if}
19
+ </div>
20
+
21
+ <style>
22
+ .dropdown-section {
23
+ gap: var(--sveltely-inset);
24
+ }
25
+
26
+ .dropdown-section-label {
27
+ padding-inline: 0.25rem;
28
+ padding-top: 0.25rem;
29
+ color: var(--sveltely-secondary-color);
30
+ font-size: 0.75rem;
31
+ line-height: 1rem;
32
+ font-weight: 500;
33
+ }
34
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { Snippet } from 'svelte';
2
+ type Props = {
3
+ label?: string;
4
+ children?: Snippet;
5
+ };
6
+ declare const Section: import("svelte").Component<Props, {}, "">;
7
+ type Section = ReturnType<typeof Section>;
8
+ export default Section;
@@ -0,0 +1,34 @@
1
+ import type { Action } from 'svelte/action';
2
+ export type DropdownValue = unknown;
3
+ export type DropdownTriggerState<TValue = DropdownValue> = {
4
+ useTrigger: Action<HTMLElement>;
5
+ open: boolean;
6
+ value: TValue | null;
7
+ selectedLabel: string;
8
+ placeholder: string;
9
+ disabled: boolean;
10
+ toggle: () => void;
11
+ openPanel: () => Promise<void>;
12
+ closePanel: () => void;
13
+ };
14
+ export type DropdownItemState<TValue = DropdownValue> = {
15
+ value: TValue;
16
+ selected: boolean;
17
+ disabled: boolean;
18
+ close: () => void;
19
+ select: () => void;
20
+ };
21
+ export type DropdownActionState = {
22
+ disabled: boolean;
23
+ close: () => void;
24
+ };
25
+ export type DropdownContext<TValue = DropdownValue> = {
26
+ get value(): TValue | null;
27
+ get disabled(): boolean;
28
+ get closeOnSelect(): boolean;
29
+ get showCheck(): boolean;
30
+ select: (value: TValue) => void;
31
+ close: () => void;
32
+ };
33
+ export declare const setDropdownContext: <TValue>(context: DropdownContext<TValue>) => void;
34
+ export declare const getDropdownContext: <TValue>() => DropdownContext<TValue>;
@@ -0,0 +1,6 @@
1
+ import { getContext, setContext } from 'svelte';
2
+ const dropdownContextKey = Symbol('dropdown');
3
+ export const setDropdownContext = (context) => {
4
+ setContext(dropdownContextKey, context);
5
+ };
6
+ export const getDropdownContext = () => getContext(dropdownContextKey);
@@ -1,2 +1,13 @@
1
- export { default } from './Dropdown.svelte';
2
- export type { DropdownAction, DropdownEntry, DropdownGroup, DropdownItem, DropdownSubmenu } from './types';
1
+ import DropdownComponent from './Dropdown.svelte';
2
+ import Action from './Action.svelte';
3
+ import Divider from './Divider.svelte';
4
+ import Item from './Item.svelte';
5
+ import Section from './Section.svelte';
6
+ declare const Dropdown: typeof DropdownComponent & {
7
+ Action: typeof Action;
8
+ Divider: typeof Divider;
9
+ Item: typeof Item;
10
+ Section: typeof Section;
11
+ };
12
+ export default Dropdown;
13
+ export type { DropdownActionState, DropdownItemState, DropdownTriggerState } from './context';