@x33025/sveltely 0.0.58 → 0.1.0

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 (188) hide show
  1. package/dist/actions/tooltip.d.ts +10 -0
  2. package/dist/actions/tooltip.js +255 -0
  3. package/dist/components/{AnimatedNumber.demo.svelte → Library/AnimatedNumber/AnimatedNumber.demo.svelte} +6 -10
  4. package/dist/components/{AnimatedNumber.demo.svelte.d.ts → Library/AnimatedNumber/AnimatedNumber.demo.svelte.d.ts} +0 -1
  5. package/dist/components/Library/AnimatedNumber/AnimatedNumber.svelte +29 -0
  6. package/dist/components/Library/AnimatedNumber/AnimatedNumber.svelte.d.ts +9 -0
  7. package/dist/components/Library/AnimatedNumber/index.d.ts +1 -0
  8. package/dist/components/Library/AnimatedNumber/index.js +1 -0
  9. package/dist/components/{AsyncButton.demo.svelte → Library/AsyncButton/AsyncButton.demo.svelte} +0 -1
  10. package/dist/components/Library/AsyncButton/AsyncButton.svelte +178 -0
  11. package/dist/components/{AsyncButton.svelte.d.ts → Library/AsyncButton/AsyncButton.svelte.d.ts} +7 -2
  12. package/dist/components/Library/AsyncButton/index.d.ts +1 -0
  13. package/dist/components/Library/AsyncButton/index.js +1 -0
  14. package/dist/components/Library/Button/Button.demo.svelte +17 -0
  15. package/dist/components/Library/Button/Button.demo.svelte.d.ts +23 -0
  16. package/dist/components/Library/Button/Button.svelte +134 -0
  17. package/dist/components/Library/Button/Button.svelte.d.ts +17 -0
  18. package/dist/components/Library/Button/index.d.ts +1 -0
  19. package/dist/components/Library/Button/index.js +1 -0
  20. package/dist/components/Library/Calendar/Calendar.demo.svelte +30 -0
  21. package/dist/components/Library/Calendar/Calendar.demo.svelte.d.ts +10 -0
  22. package/dist/components/Library/Calendar/Calendar.svelte +310 -0
  23. package/dist/components/Library/Calendar/Calendar.svelte.d.ts +10 -0
  24. package/dist/components/Library/Calendar/index.d.ts +1 -0
  25. package/dist/components/Library/Calendar/index.js +1 -0
  26. package/dist/components/Library/Checkbox/Checkbox.demo.svelte +20 -0
  27. package/dist/components/Library/Checkbox/Checkbox.demo.svelte.d.ts +8 -0
  28. package/dist/components/Library/Checkbox/Checkbox.svelte +134 -0
  29. package/dist/components/Library/Checkbox/Checkbox.svelte.d.ts +11 -0
  30. package/dist/components/Library/Checkbox/index.d.ts +1 -0
  31. package/dist/components/Library/Checkbox/index.js +1 -0
  32. package/dist/components/{ChipInput.demo.svelte → Library/ChipInput/ChipInput.demo.svelte} +1 -2
  33. package/dist/components/{ChipInput.demo.svelte.d.ts → Library/ChipInput/ChipInput.demo.svelte.d.ts} +0 -1
  34. package/dist/components/{ChipInput.svelte → Library/ChipInput/ChipInput.svelte} +77 -3
  35. package/dist/components/{ChipInput.svelte.d.ts → Library/ChipInput/ChipInput.svelte.d.ts} +2 -1
  36. package/dist/components/Library/ChipInput/index.d.ts +1 -0
  37. package/dist/components/Library/ChipInput/index.js +1 -0
  38. package/dist/components/Library/Dropdown/Dropdown.demo.svelte +54 -0
  39. package/dist/components/Library/Dropdown/Dropdown.demo.svelte.d.ts +8 -0
  40. package/dist/components/Library/Dropdown/Dropdown.svelte +346 -0
  41. package/dist/components/Library/Dropdown/Dropdown.svelte.d.ts +40 -0
  42. package/dist/components/Library/Dropdown/index.d.ts +2 -0
  43. package/dist/components/Library/Dropdown/index.js +1 -0
  44. package/dist/components/Library/Dropdown/types.d.ts +27 -0
  45. package/dist/components/Library/Dropdown/types.js +1 -0
  46. package/dist/components/{Popover/Popover.svelte → Library/Floating/Floating.svelte} +155 -101
  47. package/dist/components/Library/Floating/Floating.svelte.d.ts +30 -0
  48. package/dist/components/Library/Floating/registry.svelte.d.ts +6 -0
  49. package/dist/components/{Popover → Library/Floating}/registry.svelte.js +2 -23
  50. package/dist/components/{GlowEffect.svelte → Library/GlowEffect/GlowEffect.svelte} +15 -6
  51. package/dist/components/{GlowEffect.svelte.d.ts → Library/GlowEffect/GlowEffect.svelte.d.ts} +3 -2
  52. package/dist/components/Library/GlowEffect/index.d.ts +1 -0
  53. package/dist/components/Library/GlowEffect/index.js +1 -0
  54. package/dist/components/Library/NavigationStack/NavigationStack.svelte +242 -0
  55. package/dist/components/Library/NavigationStack/NavigationStack.svelte.d.ts +15 -0
  56. package/dist/components/Library/NavigationStack/SidebarToggle.svelte +52 -0
  57. package/dist/components/Library/NavigationStack/SidebarToggle.svelte.d.ts +10 -0
  58. package/dist/components/Library/NavigationStack/Toolbar.svelte +59 -0
  59. package/dist/components/Library/NavigationStack/Toolbar.svelte.d.ts +11 -0
  60. package/dist/components/{Pagination.demo.svelte → Library/Pagination/Pagination.demo.svelte} +0 -1
  61. package/dist/components/{Pagination.demo.svelte.d.ts → Library/Pagination/Pagination.demo.svelte.d.ts} +0 -1
  62. package/dist/components/Library/Pagination/Pagination.svelte +227 -0
  63. package/dist/components/{Pagination.svelte.d.ts → Library/Pagination/Pagination.svelte.d.ts} +3 -3
  64. package/dist/components/Library/Pagination/index.d.ts +1 -0
  65. package/dist/components/Library/Pagination/index.js +1 -0
  66. package/dist/components/Library/Popover/Popover.demo.svelte +21 -0
  67. package/dist/components/Library/Popover/Popover.svelte +92 -0
  68. package/dist/components/Library/Popover/Popover.svelte.d.ts +14 -0
  69. package/dist/components/{Popover → Library/Popover}/PopoverDebugOverlay.svelte +1 -1
  70. package/dist/components/Library/Popover/index.d.ts +1 -0
  71. package/dist/components/Library/Popover/index.js +1 -0
  72. package/dist/components/Library/ScrollView/ScrollView.svelte +89 -0
  73. package/dist/components/Library/ScrollView/ScrollView.svelte.d.ts +12 -0
  74. package/dist/components/Library/ScrollView/index.d.ts +1 -0
  75. package/dist/components/Library/ScrollView/index.js +1 -0
  76. package/dist/components/{SearchInput.demo.svelte → Library/SearchInput/SearchInput.demo.svelte} +2 -3
  77. package/dist/components/{SearchInput.demo.svelte.d.ts → Library/SearchInput/SearchInput.demo.svelte.d.ts} +0 -1
  78. package/dist/components/Library/SearchInput/SearchInput.svelte +88 -0
  79. package/dist/components/{SearchInput.svelte.d.ts → Library/SearchInput/SearchInput.svelte.d.ts} +3 -3
  80. package/dist/components/Library/SearchInput/index.d.ts +1 -0
  81. package/dist/components/Library/SearchInput/index.js +1 -0
  82. package/dist/components/{SegmentedPicker.demo.svelte → Library/SegmentedPicker/SegmentedPicker.demo.svelte} +1 -2
  83. package/dist/components/{SegmentedPicker.demo.svelte.d.ts → Library/SegmentedPicker/SegmentedPicker.demo.svelte.d.ts} +0 -1
  84. package/dist/components/Library/SegmentedPicker/SegmentedPicker.svelte +106 -0
  85. package/dist/components/{SegmentedPicker.svelte.d.ts → Library/SegmentedPicker/SegmentedPicker.svelte.d.ts} +2 -2
  86. package/dist/components/Library/SegmentedPicker/index.d.ts +1 -0
  87. package/dist/components/Library/SegmentedPicker/index.js +1 -0
  88. package/dist/components/{Sheet → Library/Sheet}/Sheet.demo.svelte +4 -17
  89. package/dist/components/Library/Sheet/Sheet.demo.svelte.d.ts +23 -0
  90. package/dist/components/Library/Sheet/Sheet.svelte +113 -0
  91. package/dist/components/{Sheet → Library/Sheet}/Sheet.svelte.d.ts +4 -2
  92. package/dist/components/{Slider.demo.svelte → Library/Slider/Slider.demo.svelte} +2 -3
  93. package/dist/components/{Slider.demo.svelte.d.ts → Library/Slider/Slider.demo.svelte.d.ts} +0 -1
  94. package/dist/components/Library/Slider/Slider.svelte +122 -0
  95. package/dist/components/{Slider.svelte.d.ts → Library/Slider/Slider.svelte.d.ts} +2 -3
  96. package/dist/components/Library/Slider/index.d.ts +1 -0
  97. package/dist/components/Library/Slider/index.js +1 -0
  98. package/dist/components/{Spinner.demo.svelte → Library/Spinner/Spinner.demo.svelte} +3 -1
  99. package/dist/components/Library/Spinner/Spinner.svelte +32 -0
  100. package/dist/components/{Spinner.svelte.d.ts → Library/Spinner/Spinner.svelte.d.ts} +3 -2
  101. package/dist/components/Library/Spinner/index.d.ts +1 -0
  102. package/dist/components/Library/Spinner/index.js +1 -0
  103. package/dist/components/Library/Switch/Switch.demo.svelte +20 -0
  104. package/dist/components/Library/Switch/Switch.demo.svelte.d.ts +8 -0
  105. package/dist/components/Library/Switch/Switch.svelte +168 -0
  106. package/dist/components/Library/Switch/Switch.svelte.d.ts +11 -0
  107. package/dist/components/Library/Switch/index.d.ts +1 -0
  108. package/dist/components/Library/Switch/index.js +1 -0
  109. package/dist/components/Library/TextShimmer/TextShimmer.demo.svelte +14 -0
  110. package/dist/components/Library/TextShimmer/TextShimmer.demo.svelte.d.ts +23 -0
  111. package/dist/components/Library/TextShimmer/TextShimmer.svelte +88 -0
  112. package/dist/components/Library/TextShimmer/TextShimmer.svelte.d.ts +11 -0
  113. package/dist/components/Library/TextShimmer/index.d.ts +1 -0
  114. package/dist/components/Library/TextShimmer/index.js +1 -0
  115. package/dist/components/Library/TimePicker/TimePicker.demo.svelte +18 -0
  116. package/dist/components/Library/TimePicker/TimePicker.demo.svelte.d.ts +10 -0
  117. package/dist/components/Library/TimePicker/TimePicker.svelte +143 -0
  118. package/dist/components/Library/TimePicker/TimePicker.svelte.d.ts +13 -0
  119. package/dist/components/Library/TimePicker/index.d.ts +2 -0
  120. package/dist/components/Library/TimePicker/index.js +1 -0
  121. package/dist/components/{TokenSearchInput.demo.svelte → Library/TokenSearchInput/TokenSearchInput.demo.svelte} +1 -2
  122. package/dist/components/{TokenSearchInput.demo.svelte.d.ts → Library/TokenSearchInput/TokenSearchInput.demo.svelte.d.ts} +0 -1
  123. package/dist/components/Library/TokenSearchInput/TokenSearchInput.svelte +230 -0
  124. package/dist/components/{TokenSearchInput.svelte.d.ts → Library/TokenSearchInput/TokenSearchInput.svelte.d.ts} +2 -3
  125. package/dist/components/Library/TokenSearchInput/index.d.ts +1 -0
  126. package/dist/components/Library/TokenSearchInput/index.js +1 -0
  127. package/dist/components/Library/Tooltip/Tooltip.demo.svelte +14 -0
  128. package/dist/components/{Tooltip.demo.svelte.d.ts → Library/Tooltip/Tooltip.demo.svelte.d.ts} +0 -1
  129. package/dist/components/Library/Tooltip/index.d.ts +2 -0
  130. package/dist/components/Library/Tooltip/index.js +1 -0
  131. package/dist/components/Library/WheelPicker/WheelColumn.svelte +302 -0
  132. package/dist/components/Library/WheelPicker/WheelColumn.svelte.d.ts +29 -0
  133. package/dist/components/Library/WheelPicker/WheelPicker.svelte +114 -0
  134. package/dist/components/Library/WheelPicker/WheelPicker.svelte.d.ts +9 -0
  135. package/dist/components/Library/WheelPicker/index.d.ts +2 -0
  136. package/dist/components/Library/WheelPicker/index.js +1 -0
  137. package/dist/components/Library/WheelPicker/types.d.ts +10 -0
  138. package/dist/components/Library/WheelPicker/types.js +1 -0
  139. package/dist/components/Local/ComponentGrid.svelte +7 -6
  140. package/dist/components/Local/ComponentGrid.svelte.d.ts +1 -1
  141. package/dist/components/Local/HeroCard.svelte +18 -8
  142. package/dist/components/Local/HeroCard.svelte.d.ts +1 -1
  143. package/dist/components/Local/StyleControls.svelte +119 -0
  144. package/dist/components/Local/StyleControls.svelte.d.ts +15 -0
  145. package/dist/index.d.ts +24 -15
  146. package/dist/index.js +23 -15
  147. package/dist/style/index.css +61 -325
  148. package/dist/style/surface.d.ts +17 -0
  149. package/dist/style/surface.js +54 -0
  150. package/dist/style.css +86 -601
  151. package/dist/utils/positioning.d.ts +3 -2
  152. package/dist/utils/positioning.js +9 -5
  153. package/package.json +1 -1
  154. package/dist/components/AnimatedNumber.svelte +0 -18
  155. package/dist/components/AnimatedNumber.svelte.d.ts +0 -8
  156. package/dist/components/AsyncButton.svelte +0 -93
  157. package/dist/components/NavigationStack/NavigationStack.svelte +0 -76
  158. package/dist/components/NavigationStack/NavigationStack.svelte.d.ts +0 -10
  159. package/dist/components/NavigationStack/SidebarToggle.svelte +0 -36
  160. package/dist/components/NavigationStack/SidebarToggle.svelte.d.ts +0 -9
  161. package/dist/components/NavigationStack/Toolbar.svelte +0 -25
  162. package/dist/components/NavigationStack/Toolbar.svelte.d.ts +0 -9
  163. package/dist/components/Pagination.svelte +0 -144
  164. package/dist/components/Popover/Popover.demo.svelte +0 -35
  165. package/dist/components/Popover/Popover.svelte.d.ts +0 -13
  166. package/dist/components/Popover/index.d.ts +0 -2
  167. package/dist/components/Popover/index.js +0 -2
  168. package/dist/components/Popover/registry.svelte.d.ts +0 -18
  169. package/dist/components/SearchInput.svelte +0 -39
  170. package/dist/components/SegmentedPicker.svelte +0 -51
  171. package/dist/components/Sheet/Sheet.demo.svelte.d.ts +0 -8
  172. package/dist/components/Sheet/Sheet.svelte +0 -60
  173. package/dist/components/Slider.svelte +0 -47
  174. package/dist/components/Spinner.svelte +0 -7
  175. package/dist/components/TextShimmer.svelte +0 -60
  176. package/dist/components/TextShimmer.svelte.d.ts +0 -10
  177. package/dist/components/TokenSearchInput.svelte +0 -124
  178. package/dist/components/Tooltip.demo.svelte +0 -16
  179. package/dist/components/Tooltip.svelte +0 -79
  180. package/dist/components/Tooltip.svelte.d.ts +0 -12
  181. /package/dist/components/{AsyncButton.demo.svelte.d.ts → Library/AsyncButton/AsyncButton.demo.svelte.d.ts} +0 -0
  182. /package/dist/components/{NavigationStack → Library/NavigationStack}/index.d.ts +0 -0
  183. /package/dist/components/{NavigationStack → Library/NavigationStack}/index.js +0 -0
  184. /package/dist/components/{Popover → Library/Popover}/Popover.demo.svelte.d.ts +0 -0
  185. /package/dist/components/{Popover → Library/Popover}/PopoverDebugOverlay.svelte.d.ts +0 -0
  186. /package/dist/components/{Sheet → Library/Sheet}/index.d.ts +0 -0
  187. /package/dist/components/{Sheet → Library/Sheet}/index.js +0 -0
  188. /package/dist/components/{Spinner.demo.svelte.d.ts → Library/Spinner/Spinner.demo.svelte.d.ts} +0 -0
@@ -0,0 +1,11 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLInputAttributes } from 'svelte/elements';
3
+ import { type StyleProps } from '../../../style/surface';
4
+ type Props = {
5
+ checked?: boolean;
6
+ label?: string;
7
+ children?: Snippet;
8
+ } & StyleProps & Omit<HTMLInputAttributes, 'type' | 'children' | 'class' | 'style' | 'checked'>;
9
+ declare const Checkbox: import("svelte").Component<Props, {}, "checked">;
10
+ type Checkbox = ReturnType<typeof Checkbox>;
11
+ export default Checkbox;
@@ -0,0 +1 @@
1
+ export { default } from './Checkbox.svelte';
@@ -0,0 +1 @@
1
+ export { default } from './Checkbox.svelte';
@@ -1,8 +1,7 @@
1
1
  <script module lang="ts">
2
2
  export const demo = {
3
3
  name: 'ChipInput',
4
- description: 'Editable chip list with bindable tags.',
5
- isProminent: false
4
+ description: 'Editable chip list with bindable tags.'
6
5
  };
7
6
  </script>
8
7
 
@@ -1,7 +1,6 @@
1
1
  export declare const demo: {
2
2
  name: string;
3
3
  description: string;
4
- isProminent: boolean;
5
4
  };
6
5
  import ChipInput from './ChipInput.svelte';
7
6
  declare const ChipInput: import("svelte").Component<Record<string, never>, {}, "">;
@@ -2,6 +2,7 @@
2
2
  import type { Snippet } from 'svelte';
3
3
  import { tick } from 'svelte';
4
4
  import { Plus } from '@lucide/svelte';
5
+ import { extractStyleProps, surfaceStyle, type StyleProps } from '../../../style/surface';
5
6
 
6
7
  type Props = {
7
8
  placeholder?: string;
@@ -9,16 +10,22 @@
9
10
  selection?: string[];
10
11
  disabled?: boolean;
11
12
  action?: Snippet;
12
- };
13
+ } & StyleProps & Record<string, unknown>;
13
14
 
14
15
  let {
15
16
  placeholder = 'Add a tag...',
16
17
  tags = $bindable<string[]>(),
17
18
  selection = $bindable<string[] | undefined>(),
18
19
  disabled = false,
19
- action
20
+ action,
21
+ ...restProps
20
22
  }: Props = $props();
21
23
 
24
+ const extractedStyleProps = $derived.by(() => extractStyleProps(restProps));
25
+ const styleProps = $derived(extractedStyleProps.styleProps);
26
+ const props = $derived(extractedStyleProps.restProps);
27
+ const rootStyle = $derived.by(() => surfaceStyle(styleProps, 'chip-input'));
28
+
22
29
  let inputValue = $state('');
23
30
  let showInput = $state(false);
24
31
  let inputEl = $state<HTMLInputElement | null>(null);
@@ -237,8 +244,10 @@
237
244
  <div
238
245
  bind:this={rootEl}
239
246
  class="chip-input hstack w-full flex-wrap items-center"
247
+ style={rootStyle}
240
248
  class:chip-input-disabled={disabled}
241
249
  aria-disabled={disabled}
250
+ {...props}
242
251
  >
243
252
  {#each tags as tag, index (tag)}
244
253
  {#if editingTag === tag}
@@ -295,7 +304,7 @@
295
304
  {disabled}
296
305
  onclick={openInput}
297
306
  >
298
- <Plus class="size-3" />
307
+ <Plus class="chip-input-action-icon" />
299
308
  </button>
300
309
  {/if}
301
310
 
@@ -303,3 +312,68 @@
303
312
  {@render action()}
304
313
  {/if}
305
314
  </div>
315
+
316
+ <style>
317
+ .chip-input {
318
+ --chip-input-font-size: calc(var(--sveltely-font-size) * 0.857);
319
+ --chip-input-line-height: calc(var(--chip-input-font-size) * 1.333);
320
+ --chip-input-scale: calc(var(--chip-input-font-size) / 0.75rem);
321
+ --chip-input-icon-size: calc(var(--chip-input-font-size) * 1);
322
+ --chip-input-padding-y: calc(var(--sveltely-padding-y) * 0.33 * var(--chip-input-scale));
323
+ --chip-input-padding-x: calc(var(--sveltely-padding-x) * 0.67 * var(--chip-input-scale));
324
+ --chip-input-control-height: calc(var(--chip-input-line-height) + (var(--chip-input-padding-y) * 2) + 2px);
325
+ gap: calc(var(--sveltely-gap) * 0.5);
326
+ }
327
+
328
+ .chip {
329
+ box-sizing: border-box;
330
+ border: 1px solid var(--sveltely-border-color);
331
+ border-radius: var(--sveltely-border-radius);
332
+ background: var(--color-zinc-100);
333
+ color: black;
334
+ min-height: var(--chip-input-control-height);
335
+ padding: var(--chip-input-padding-y) var(--chip-input-padding-x);
336
+ font-size: var(--chip-input-font-size);
337
+ line-height: var(--chip-input-line-height);
338
+ white-space: nowrap;
339
+ }
340
+
341
+ .chip-selected {
342
+ border-color: var(--color-zinc-300);
343
+ }
344
+
345
+ .chip:hover,
346
+ .chip-hovered {
347
+ background: var(--sveltely-hover-color);
348
+ }
349
+
350
+ .chip-input-field:hover {
351
+ background: var(--sveltely-hover-color);
352
+ }
353
+
354
+ .chip-input-disabled {
355
+ pointer-events: none;
356
+ }
357
+
358
+ .chip-input-disabled .chip {
359
+ cursor: not-allowed;
360
+ opacity: 0.5;
361
+ }
362
+
363
+ .chip-input-disabled .chip:hover {
364
+ background: var(--sveltely-hover-color);
365
+ }
366
+
367
+ .chip-input-action {
368
+ padding: 0;
369
+ width: var(--chip-input-control-height);
370
+ min-width: var(--chip-input-control-height);
371
+ height: var(--chip-input-control-height);
372
+ min-height: var(--chip-input-control-height);
373
+ }
374
+
375
+ :global(.chip-input-action-icon) {
376
+ width: var(--chip-input-icon-size);
377
+ height: var(--chip-input-icon-size);
378
+ }
379
+ </style>
@@ -1,11 +1,12 @@
1
1
  import type { Snippet } from 'svelte';
2
+ import { type StyleProps } from '../../../style/surface';
2
3
  type Props = {
3
4
  placeholder?: string;
4
5
  tags: string[];
5
6
  selection?: string[];
6
7
  disabled?: boolean;
7
8
  action?: Snippet;
8
- };
9
+ } & StyleProps & Record<string, unknown>;
9
10
  declare const ChipInput: import("svelte").Component<Props, {}, "tags" | "selection">;
10
11
  type ChipInput = ReturnType<typeof ChipInput>;
11
12
  export default ChipInput;
@@ -0,0 +1 @@
1
+ export { default } from './ChipInput.svelte';
@@ -0,0 +1 @@
1
+ export { default } from './ChipInput.svelte';
@@ -0,0 +1,54 @@
1
+ <script module lang="ts">
2
+ export const demo = {
3
+ name: 'Dropdown',
4
+ description: 'Structured dropdown menu with grouped items and automatic close on selection.'
5
+ };
6
+ </script>
7
+
8
+ <script lang="ts">
9
+ import Dropdown from './Dropdown.svelte';
10
+
11
+ const items = [
12
+ {
13
+ type: 'group' as const,
14
+ label: 'Publishing',
15
+ items: [
16
+ { label: 'Draft', value: 'draft' },
17
+ { label: 'Scheduled', value: 'scheduled' },
18
+ {
19
+ type: 'submenu' as const,
20
+ label: 'Advanced',
21
+ items: [
22
+ { label: 'Review queue', value: 'review' },
23
+ {
24
+ type: 'action' as const,
25
+ label: 'Open workflow',
26
+ onSelect: () => {
27
+ console.log('Open workflow');
28
+ }
29
+ }
30
+ ]
31
+ },
32
+ {
33
+ type: 'action' as const,
34
+ label: 'Create new status',
35
+ onSelect: () => {
36
+ console.log('Create new status');
37
+ }
38
+ }
39
+ ]
40
+ },
41
+ {
42
+ type: 'group' as const,
43
+ label: 'Visibility',
44
+ items: [
45
+ { label: 'Public', value: 'public' },
46
+ { label: 'Private', value: 'private', disabled: true }
47
+ ]
48
+ }
49
+ ];
50
+
51
+ let value = $state<string | null>('draft');
52
+ </script>
53
+
54
+ <Dropdown {items} bind:value placeholder="Choose status" searchable={true} />
@@ -0,0 +1,8 @@
1
+ export declare const demo: {
2
+ name: string;
3
+ description: string;
4
+ };
5
+ import Dropdown from './Dropdown.svelte';
6
+ declare const Dropdown: import("svelte").Component<Record<string, never>, {}, "">;
7
+ type Dropdown = ReturnType<typeof Dropdown>;
8
+ export default Dropdown;
@@ -0,0 +1,346 @@
1
+ <script lang="ts" generics="T extends string | number = string">
2
+ import { CheckIcon, ChevronDownIcon, ChevronRightIcon } from '@lucide/svelte';
3
+ import Floating from '../Floating/Floating.svelte';
4
+ import SearchInput from '../SearchInput';
5
+ import { surfaceStyle, type StyleProps } from '../../../style/surface';
6
+ import type { Anchor } from '../../../utils/positioning';
7
+ import type {
8
+ DropdownAction,
9
+ DropdownEntry,
10
+ DropdownGroup,
11
+ DropdownItem,
12
+ DropdownSubmenu
13
+ } from './types';
14
+
15
+ type Props = {
16
+ items: DropdownEntry<T>[];
17
+ value?: T | null;
18
+ open?: boolean;
19
+ label?: string;
20
+ placeholder?: string;
21
+ disabled?: boolean;
22
+ closeOnSelect?: boolean;
23
+ showCheck?: boolean;
24
+ searchable?: boolean;
25
+ searchPlaceholder?: string;
26
+ placement?: Anchor;
27
+ onSelect?: (item: DropdownItem<T>) => void;
28
+ } & StyleProps;
29
+
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
+ let {
38
+ items,
39
+ value = $bindable<T | null>(null),
40
+ open = $bindable(false),
41
+ label = 'Select option',
42
+ placeholder = 'Select option',
43
+ disabled = false,
44
+ closeOnSelect = true,
45
+ showCheck = true,
46
+ searchable = false,
47
+ searchPlaceholder = 'Search',
48
+ placement = 'bottom',
49
+ onSelect,
50
+ ...styleProps
51
+ }: Props = $props();
52
+
53
+ const dropdownStyle = $derived.by(() => surfaceStyle(styleProps, 'dropdown'));
54
+
55
+ let query = $state('');
56
+
57
+ const flattenItems = (
58
+ entries: DropdownEntry<T>[],
59
+ inheritedDisabled = false
60
+ ): DropdownItem<T>[] =>
61
+ entries.flatMap((entry) => {
62
+ const nextDisabled = inheritedDisabled || !!entry.disabled;
63
+ if (isGroup(entry) || isSubmenu(entry)) {
64
+ return flattenItems(entry.items, nextDisabled);
65
+ }
66
+ if (isAction(entry)) {
67
+ return [];
68
+ }
69
+ return [{ ...entry, disabled: nextDisabled || entry.disabled }];
70
+ });
71
+
72
+ const filterEntries = (entries: DropdownEntry<T>[]): DropdownEntry<T>[] =>
73
+ entries.flatMap((entry) => {
74
+ if (isGroup(entry)) {
75
+ const nextItems = filterEntries(entry.items);
76
+ return nextItems.length > 0 ? [{ ...entry, items: nextItems }] : [];
77
+ }
78
+
79
+ if (isSubmenu(entry)) {
80
+ const nextItems = filterEntries(entry.items);
81
+ const matches = entry.label.toLowerCase().includes(normalizedQuery);
82
+ if (!matches && nextItems.length === 0) {
83
+ return [];
84
+ }
85
+ return [{ ...entry, items: matches ? entry.items : nextItems }];
86
+ }
87
+
88
+ return entry.label.toLowerCase().includes(normalizedQuery) ? [entry] : [];
89
+ });
90
+
91
+ const findFirstRenderableLabel = (entries: DropdownEntry<T>[]): string | null => {
92
+ for (const entry of entries) {
93
+ if (isGroup(entry)) {
94
+ const nested = findFirstRenderableLabel(entry.items);
95
+ if (nested) return nested;
96
+ continue;
97
+ }
98
+ return entry.label;
99
+ }
100
+ return null;
101
+ };
102
+
103
+ const normalizedQuery = $derived(query.trim().toLowerCase());
104
+
105
+ const flatItems = $derived.by(() => flattenItems(items));
106
+
107
+ const selectedItem = $derived.by(
108
+ () => flatItems.find((item) => item.value === value) ?? null
109
+ );
110
+
111
+ const triggerText = $derived(selectedItem?.label ?? placeholder);
112
+
113
+ const filteredItems = $derived.by(() =>
114
+ normalizedQuery ? filterEntries(items) : items
115
+ );
116
+
117
+ const firstRenderableLabel = $derived.by(() => findFirstRenderableLabel(filteredItems));
118
+
119
+ const itemRadiusSourceEnabled = $derived.by(() => {
120
+ if (searchable) return false;
121
+ const firstEntry = filteredItems[0];
122
+ if (!firstEntry) return false;
123
+ if (isGroup(firstEntry)) {
124
+ return !firstEntry.label;
125
+ }
126
+ return true;
127
+ });
128
+
129
+ function handleSelect(item: DropdownItem<T>) {
130
+ if (disabled || item.disabled) return;
131
+ value = item.value;
132
+ onSelect?.(item);
133
+ if (closeOnSelect) open = false;
134
+ }
135
+
136
+ function handleAction(item: DropdownAction) {
137
+ if (disabled || item.disabled) return;
138
+ item.onSelect();
139
+ if (closeOnSelect) open = false;
140
+ }
141
+
142
+ const isEntryDisabled = (entry: { disabled?: boolean }, inheritedDisabled = false) =>
143
+ disabled || inheritedDisabled || !!entry.disabled;
144
+
145
+ const isRadiusSource = (label: string) => itemRadiusSourceEnabled && label === firstRenderableLabel;
146
+ </script>
147
+
148
+ <Floating
149
+ bind:open
150
+ {placement}
151
+ rootClass="relative inline-block text-left"
152
+ panelClass="dropdown-panel fixed z-50 focus:outline-none"
153
+ panelStyle={dropdownStyle}
154
+ contentStyle=""
155
+ matchPanelRadiusToSource={true}
156
+ >
157
+ {#snippet trigger(floating)}
158
+ <button
159
+ use:floating.useTrigger
160
+ type="button"
161
+ class="dropdown-trigger justify-between"
162
+ aria-label={label}
163
+ aria-disabled={disabled}
164
+ disabled={disabled}
165
+ style={dropdownStyle}
166
+ aria-expanded={floating.open}
167
+ aria-haspopup="dialog"
168
+ onclick={floating.toggle}
169
+ >
170
+ <span>{triggerText}</span>
171
+ <ChevronDownIcon class="size-4 text-zinc-500" />
172
+ </button>
173
+ {/snippet}
174
+
175
+ <div
176
+ class="dropdown-content vstack"
177
+ style={`${dropdownStyle} --dropdown-item-radius: ${searchable ? 'var(--sveltely-border-radius)' : 'var(--sveltely-border-radius-nested)'};`}
178
+ >
179
+ {#if searchable}
180
+ <SearchInput
181
+ bind:value={query}
182
+ placeholder={searchPlaceholder}
183
+ radiusSource={true}
184
+ class="w-64"
185
+ />
186
+ {/if}
187
+ {#snippet renderEntries(entries: DropdownEntry<T>[], inheritedDisabled = false)}
188
+ {#each entries as entry, index (`${index}-${entry.type ?? 'option'}-${entry.label}`)}
189
+ {#if isGroup(entry)}
190
+ <div class="dropdown-group vstack">
191
+ {#if entry.label}
192
+ <div class="dropdown-group-label">{entry.label}</div>
193
+ {/if}
194
+ {@render renderEntries(entry.items, inheritedDisabled || !!entry.disabled)}
195
+ </div>
196
+ {:else if isSubmenu(entry)}
197
+ <Floating
198
+ placement={entry.placement ?? 'right'}
199
+ rootClass="dropdown-submenu-root relative w-full"
200
+ panelClass="dropdown-panel fixed z-50 focus:outline-none"
201
+ panelStyle={dropdownStyle}
202
+ contentStyle=""
203
+ matchPanelRadiusToSource={true}
204
+ closeOnPointerLeave={true}
205
+ >
206
+ {#snippet trigger(floating)}
207
+ <button
208
+ use:floating.useTrigger
209
+ type="button"
210
+ class="dropdown-item dropdown-submenu-trigger inline-flex items-center justify-between text-left"
211
+ class:dropdown-item-open={floating.open}
212
+ data-popover-radius-source={isRadiusSource(entry.label) ? 'true' : undefined}
213
+ disabled={isEntryDisabled(entry, inheritedDisabled)}
214
+ aria-expanded={floating.open}
215
+ aria-haspopup="menu"
216
+ onmouseenter={() => {
217
+ if (!isEntryDisabled(entry, inheritedDisabled)) {
218
+ void floating.openPanel();
219
+ }
220
+ }}
221
+ onclick={(event) => {
222
+ event.stopPropagation();
223
+ floating.toggle();
224
+ }}
225
+ >
226
+ <span>{entry.label}</span>
227
+ <ChevronRightIcon class="size-4 text-zinc-500" />
228
+ </button>
229
+ {/snippet}
230
+
231
+ <div
232
+ class="dropdown-content vstack"
233
+ style={`${dropdownStyle} --dropdown-item-radius: var(--sveltely-border-radius-nested);`}
234
+ >
235
+ {@render renderEntries(entry.items, inheritedDisabled || !!entry.disabled)}
236
+ </div>
237
+ </Floating>
238
+ {:else if isAction(entry)}
239
+ <button
240
+ type="button"
241
+ class="dropdown-item inline-flex items-center justify-between text-left"
242
+ data-popover-radius-source={isRadiusSource(entry.label) ? 'true' : undefined}
243
+ disabled={isEntryDisabled(entry, inheritedDisabled)}
244
+ onclick={() =>
245
+ handleAction({
246
+ ...entry,
247
+ disabled: inheritedDisabled || entry.disabled
248
+ })}
249
+ >
250
+ <span>{entry.label}</span>
251
+ </button>
252
+ {:else}
253
+ <button
254
+ type="button"
255
+ class="dropdown-item inline-flex items-center justify-between text-left"
256
+ data-popover-radius-source={isRadiusSource(entry.label) ? 'true' : undefined}
257
+ disabled={isEntryDisabled(entry, inheritedDisabled)}
258
+ onclick={() =>
259
+ handleSelect({
260
+ ...entry,
261
+ disabled: inheritedDisabled || entry.disabled
262
+ })}
263
+ >
264
+ <span>{entry.label}</span>
265
+ {#if showCheck && entry.value === value}
266
+ <CheckIcon class="size-4 text-zinc-700" />
267
+ {/if}
268
+ </button>
269
+ {/if}
270
+ {/each}
271
+ {/snippet}
272
+
273
+ {@render renderEntries(filteredItems)}
274
+ </div>
275
+ </Floating>
276
+
277
+ <style>
278
+ :global(.dropdown-panel) {
279
+ --dropdown-inset: var(--sveltely-inset);
280
+ --sveltely-nested-inset: var(--dropdown-inset);
281
+ border: 1px solid var(--sveltely-border-color);
282
+ border-radius: var(--sveltely-border-radius);
283
+ background: white;
284
+ padding: var(--dropdown-inset);
285
+ }
286
+
287
+ .dropdown-trigger {
288
+ display: inline-flex;
289
+ min-width: 8rem;
290
+ align-items: center;
291
+ border: 1px solid var(--sveltely-border-color);
292
+ border-radius: var(--sveltely-border-radius);
293
+ background: white;
294
+ color: var(--color-zinc-800);
295
+ gap: var(--sveltely-gap);
296
+ padding: calc(var(--sveltely-padding-y) * 0.67) var(--sveltely-padding-x);
297
+ font-size: 0.875rem;
298
+ line-height: 1.25rem;
299
+ transition: color 150ms, border-color 150ms, background-color 150ms;
300
+ }
301
+
302
+ .dropdown-trigger:hover {
303
+ background: var(--sveltely-hover-color);
304
+ }
305
+
306
+ .dropdown-group-label {
307
+ padding-inline: 0.25rem;
308
+ padding-top: 0.25rem;
309
+ color: var(--color-zinc-500);
310
+ font-size: 0.75rem;
311
+ line-height: 1rem;
312
+ font-weight: 500;
313
+ }
314
+
315
+ .dropdown-content {
316
+ gap: var(--sveltely-inset);
317
+ }
318
+
319
+ .dropdown-group {
320
+ gap: var(--sveltely-inset);
321
+ }
322
+
323
+ .dropdown-item {
324
+ width: 100%;
325
+ gap: calc(var(--sveltely-inset) * 2);
326
+ border-radius: var(--dropdown-item-radius, var(--sveltely-border-radius-nested));
327
+ padding: calc(var(--sveltely-padding-y) * 0.33) calc(var(--sveltely-padding-x) * 0.67);
328
+ }
329
+
330
+ .dropdown-item :global(*) {
331
+ border-radius: inherit;
332
+ }
333
+
334
+ .dropdown-item:hover {
335
+ background: var(--sveltely-hover-color);
336
+ }
337
+
338
+ .dropdown-item-open {
339
+ background: var(--sveltely-hover-color);
340
+ }
341
+
342
+ .dropdown-item:disabled {
343
+ cursor: not-allowed;
344
+ opacity: 0.5;
345
+ }
346
+ </style>
@@ -0,0 +1,40 @@
1
+ import { type StyleProps } from '../../../style/surface';
2
+ import type { Anchor } from '../../../utils/positioning';
3
+ import type { DropdownEntry, DropdownItem } from './types';
4
+ declare function $$render<T extends string | number = string>(): {
5
+ props: {
6
+ items: DropdownEntry<T>[];
7
+ value?: T | null;
8
+ open?: boolean;
9
+ label?: string;
10
+ placeholder?: string;
11
+ disabled?: boolean;
12
+ closeOnSelect?: boolean;
13
+ showCheck?: boolean;
14
+ searchable?: boolean;
15
+ searchPlaceholder?: string;
16
+ placement?: Anchor;
17
+ onSelect?: (item: DropdownItem<T>) => void;
18
+ } & StyleProps;
19
+ exports: {};
20
+ bindings: "value" | "open";
21
+ slots: {};
22
+ events: {};
23
+ };
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'];
28
+ bindings(): "value" | "open";
29
+ exports(): {};
30
+ }
31
+ 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']>;
36
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
37
+ }
38
+ declare const Dropdown: $$IsomorphicComponent;
39
+ type Dropdown<T extends string | number = string> = InstanceType<typeof Dropdown<T>>;
40
+ export default Dropdown;
@@ -0,0 +1,2 @@
1
+ export { default } from './Dropdown.svelte';
2
+ export type { DropdownAction, DropdownEntry, DropdownGroup, DropdownItem, DropdownSubmenu } from './types';
@@ -0,0 +1 @@
1
+ export { default } from './Dropdown.svelte';
@@ -0,0 +1,27 @@
1
+ import type { Anchor } from '../../../utils/positioning';
2
+ export type DropdownItem<TValue extends string | number = string> = {
3
+ type?: 'option';
4
+ label: string;
5
+ value: TValue;
6
+ disabled?: boolean;
7
+ };
8
+ export type DropdownAction = {
9
+ type: 'action';
10
+ label: string;
11
+ disabled?: boolean;
12
+ onSelect: () => void;
13
+ };
14
+ export type DropdownSubmenu<TValue extends string | number = string> = {
15
+ type: 'submenu';
16
+ label: string;
17
+ disabled?: boolean;
18
+ items: DropdownEntry<TValue>[];
19
+ placement?: Anchor;
20
+ };
21
+ export type DropdownGroup<TValue extends string | number = string> = {
22
+ type: 'group';
23
+ label?: string;
24
+ disabled?: boolean;
25
+ items: DropdownEntry<TValue>[];
26
+ };
27
+ export type DropdownEntry<TValue extends string | number = string> = DropdownItem<TValue> | DropdownAction | DropdownGroup<TValue> | DropdownSubmenu<TValue>;
@@ -0,0 +1 @@
1
+ export {};