@x33025/sveltely 0.1.2 → 0.1.4

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 (76) 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.svelte +21 -13
  4. package/dist/components/Library/Calendar/Calendar.svelte +16 -16
  5. package/dist/components/Library/Checkbox/Checkbox.svelte +13 -14
  6. package/dist/components/Library/ChipInput/ChipInput.demo.svelte +1 -1
  7. package/dist/components/Library/ChipInput/ChipInput.svelte +7 -4
  8. package/dist/components/Library/Dropdown/Action.svelte +60 -0
  9. package/dist/components/Library/Dropdown/Action.svelte.d.ts +11 -0
  10. package/dist/components/Library/Dropdown/Divider.svelte +5 -0
  11. package/dist/components/Library/Dropdown/Divider.svelte.d.ts +19 -0
  12. package/dist/components/Library/Dropdown/Dropdown.demo.svelte +182 -72
  13. package/dist/components/Library/Dropdown/Dropdown.demo.svelte.d.ts +2 -1
  14. package/dist/components/Library/Dropdown/Dropdown.svelte +78 -267
  15. package/dist/components/Library/Dropdown/Dropdown.svelte.d.ts +17 -16
  16. package/dist/components/Library/Dropdown/Item.svelte +73 -0
  17. package/dist/components/Library/Dropdown/Item.svelte.d.ts +31 -0
  18. package/dist/components/Library/Dropdown/Section.svelte +34 -0
  19. package/dist/components/Library/Dropdown/Section.svelte.d.ts +8 -0
  20. package/dist/components/Library/Dropdown/context.d.ts +34 -0
  21. package/dist/components/Library/Dropdown/context.js +6 -0
  22. package/dist/components/Library/Dropdown/index.d.ts +13 -2
  23. package/dist/components/Library/Dropdown/index.js +11 -1
  24. package/dist/components/Library/Floating/Floating.svelte +10 -7
  25. package/dist/components/Library/ImageMask/BrushPreview.svelte +6 -6
  26. package/dist/components/Library/ImageMask/ImageMask.demo.svelte +7 -7
  27. package/dist/components/Library/ImageMask/MaskLayer.svelte +3 -3
  28. package/dist/components/Library/Label/Label.svelte +2 -4
  29. package/dist/components/Library/NavigationStack/NavigationStack.svelte +17 -7
  30. package/dist/components/Library/NavigationStack/Toolbar.svelte +7 -2
  31. package/dist/components/Library/NumberField/NumberField.svelte +14 -9
  32. package/dist/components/Library/Pagination/Pagination.svelte +16 -20
  33. package/dist/components/Library/Popover/Popover.demo.svelte +2 -2
  34. package/dist/components/Library/Popover/Popover.svelte +7 -4
  35. package/dist/components/Library/ScrollView/ScrollView.svelte +140 -3
  36. package/dist/components/Library/ScrollView/ScrollView.svelte.d.ts +28 -0
  37. package/dist/components/Library/ScrollView/index.d.ts +1 -0
  38. package/dist/components/Library/{SearchInput/SearchInput.demo.svelte → SearchField/SearchField.demo.svelte} +4 -4
  39. package/dist/components/Library/SearchField/SearchField.demo.svelte.d.ts +8 -0
  40. package/dist/components/Library/{SearchInput/SearchInput.svelte → SearchField/SearchField.svelte} +26 -30
  41. package/dist/components/Library/{SearchInput/SearchInput.svelte.d.ts → SearchField/SearchField.svelte.d.ts} +3 -3
  42. package/dist/components/Library/SearchField/index.d.ts +1 -0
  43. package/dist/components/Library/SearchField/index.js +1 -0
  44. package/dist/components/Library/SegmentedPicker/SegmentedPicker.demo.svelte +1 -1
  45. package/dist/components/Library/SegmentedPicker/SegmentedPicker.svelte +9 -9
  46. package/dist/components/Library/Sheet/Sheet.demo.svelte +1 -1
  47. package/dist/components/Library/Sheet/Sheet.svelte +8 -5
  48. package/dist/components/Library/Slider/Slider.demo.svelte +1 -1
  49. package/dist/components/Library/Slider/Slider.svelte +11 -10
  50. package/dist/components/Library/Spinner/Spinner.demo.svelte +1 -1
  51. package/dist/components/Library/Switch/Switch.svelte +6 -11
  52. package/dist/components/Library/TextField/TextField.svelte +14 -9
  53. package/dist/components/Library/{TokenSearchInput/TokenSearchInput.demo.svelte → TokenSearchField/TokenSearchField.demo.svelte} +4 -4
  54. package/dist/components/Library/TokenSearchField/TokenSearchField.demo.svelte.d.ts +9 -0
  55. package/dist/components/Library/{TokenSearchInput/TokenSearchInput.svelte → TokenSearchField/TokenSearchField.svelte} +70 -66
  56. package/dist/components/Library/{TokenSearchInput/TokenSearchInput.svelte.d.ts → TokenSearchField/TokenSearchField.svelte.d.ts} +3 -3
  57. package/dist/components/Library/TokenSearchField/index.d.ts +1 -0
  58. package/dist/components/Library/TokenSearchField/index.js +1 -0
  59. package/dist/components/Library/WheelPicker/WheelColumn.svelte +4 -10
  60. package/dist/components/Library/WheelPicker/WheelPicker.svelte +5 -9
  61. package/dist/components/Local/HeroCard.svelte +5 -3
  62. package/dist/components/Local/StyleControls.svelte +58 -27
  63. package/dist/components/Local/StyleControls.svelte.d.ts +3 -1
  64. package/dist/index.d.ts +3 -2
  65. package/dist/index.js +2 -2
  66. package/dist/style/index.css +9 -5
  67. package/dist/style.css +60 -29
  68. package/package.json +1 -1
  69. package/dist/components/Library/Dropdown/types.d.ts +0 -30
  70. package/dist/components/Library/Dropdown/types.js +0 -1
  71. package/dist/components/Library/SearchInput/SearchInput.demo.svelte.d.ts +0 -8
  72. package/dist/components/Library/SearchInput/index.d.ts +0 -1
  73. package/dist/components/Library/SearchInput/index.js +0 -1
  74. package/dist/components/Library/TokenSearchInput/TokenSearchInput.demo.svelte.d.ts +0 -9
  75. package/dist/components/Library/TokenSearchInput/index.d.ts +0 -1
  76. package/dist/components/Library/TokenSearchInput/index.js +0 -1
@@ -1,89 +1,199 @@
1
1
  <script module lang="ts">
2
2
  export const demo = {
3
3
  name: 'Dropdown',
4
- description: 'Structured dropdown menu with grouped items and automatic close on selection.'
4
+ description: 'Composable dropdown menu with declarative items, sections, and actions.',
5
+ columnSpan: 2
5
6
  };
6
7
  </script>
7
8
 
8
9
  <script lang="ts">
9
- import Dropdown from './Dropdown.svelte';
10
- import type { DropdownEntry } from './types';
10
+ import { CheckIcon, ChevronDownIcon, PlusIcon, UserRoundXIcon } from '@lucide/svelte';
11
+ import SearchField from '../SearchField';
12
+ import ScrollView, { type ScrollGeometry } from '../ScrollView';
13
+ import Dropdown from './index';
11
14
 
12
- const allItems: DropdownEntry<string>[] = [
13
- {
14
- type: 'group' as const,
15
- label: 'Publishing',
16
- items: [
17
- { label: 'Draft', value: 'draft' },
18
- { label: 'Scheduled', value: 'scheduled' },
19
- { type: 'divider' },
20
- {
21
- type: 'submenu' as const,
22
- label: 'Advanced',
23
- items: [
24
- { label: 'Review queue', value: 'review' },
25
- { type: 'divider' },
26
- {
27
- type: 'action' as const,
28
- label: 'Open workflow',
29
- onSelect: () => {
30
- console.log('Open workflow');
31
- }
32
- }
33
- ]
34
- },
35
- {
36
- type: 'action' as const,
37
- label: 'Create new status',
38
- onSelect: () => {
39
- console.log('Create new status');
40
- }
41
- }
42
- ]
43
- },
44
- {
45
- type: 'group' as const,
46
- label: 'Visibility',
47
- items: [
48
- { label: 'Public', value: 'public' },
49
- { label: 'Private', value: 'private', disabled: true }
50
- ]
51
- }
52
- ];
15
+ type Website = {
16
+ id: string;
17
+ name: string;
18
+ domain: string | null;
19
+ };
53
20
 
54
- const filterEntries = (entries: DropdownEntry<string>[], query: string): DropdownEntry<string>[] => {
55
- const normalizedQuery = query.trim().toLowerCase();
56
- if (!normalizedQuery) return allItems;
21
+ const statusLabels: Record<string, string> = {
22
+ draft: 'Draft',
23
+ scheduled: 'Scheduled',
24
+ public: 'Public',
25
+ private: 'Private'
26
+ };
57
27
 
58
- return entries.flatMap<DropdownEntry<string>>((entry) => {
59
- if (entry.type === 'divider') {
60
- return [];
61
- }
28
+ const websites: Website[] = [
29
+ { id: 'sveltely', name: 'Sveltely', domain: 'sveltely.dev' },
30
+ { id: 'northstar', name: 'Northstar Studio', domain: 'northstar.example' },
31
+ { id: 'atlas', name: 'Atlas Analytics', domain: 'atlas.example' },
32
+ { id: 'folio', name: 'Folio', domain: null },
33
+ { id: 'harbor', name: 'Harbor CMS', domain: 'harbor.example' },
34
+ { id: 'beam', name: 'Beam Commerce', domain: 'beam.example' },
35
+ { id: 'signal', name: 'Signal CRM', domain: 'signal.example' },
36
+ { id: 'lumen', name: 'Lumen Health', domain: 'lumen.example' },
37
+ { id: 'orbit', name: 'Orbit Travel', domain: 'orbit.example' },
38
+ { id: 'canvas', name: 'Canvas Studio', domain: 'canvas.example' },
39
+ { id: 'ledger', name: 'Ledger Works', domain: 'ledger.example' },
40
+ { id: 'summit', name: 'Summit Labs', domain: 'summit.example' },
41
+ { id: 'keystone', name: 'Keystone Legal', domain: 'keystone.example' },
42
+ { id: 'prairie', name: 'Prairie Supply', domain: 'prairie.example' },
43
+ { id: 'fieldnote', name: 'Fieldnote', domain: null },
44
+ { id: 'vertex', name: 'Vertex Systems', domain: 'vertex.example' }
45
+ ];
62
46
 
63
- if (entry.type === 'group') {
64
- const nextItems = filterEntries(entry.items, normalizedQuery);
65
- return nextItems.length > 0 ? [{ ...entry, items: nextItems }] : [];
66
- }
47
+ let status = $state<string | null>('draft');
48
+ let selectedWebsiteId = $state<string | null>('sveltely');
49
+ let websiteQuery = $state('');
50
+ let visibleWebsiteCount = $state(6);
51
+ let actionCount = $state(0);
52
+ let lastWebsiteQuery = '';
53
+ const websiteBatchSize = 4;
67
54
 
68
- if (entry.type === 'submenu') {
69
- const nextItems = filterEntries(entry.items, normalizedQuery);
70
- const matches = entry.label.toLowerCase().includes(normalizedQuery);
71
- if (!matches && nextItems.length === 0) {
72
- return [];
73
- }
74
- return [{ ...entry, items: matches ? entry.items : nextItems }];
75
- }
55
+ const selectedStatusLabel = $derived(status ? statusLabels[status] : null);
56
+ const selectedWebsite = $derived(
57
+ websites.find((website) => website.id === selectedWebsiteId) ?? null
58
+ );
59
+ const filteredWebsites = $derived.by(() => {
60
+ const query = websiteQuery.trim().toLowerCase();
61
+ if (!query) return websites;
62
+ return websites.filter((website) =>
63
+ [website.name, website.domain ?? ''].some((value) => value.toLowerCase().includes(query))
64
+ );
65
+ });
66
+ const visibleWebsites = $derived(filteredWebsites.slice(0, visibleWebsiteCount));
76
67
 
77
- return entry.label.toLowerCase().includes(normalizedQuery) ? [entry] : [];
78
- });
79
- };
68
+ function createWebsite() {
69
+ actionCount += 1;
70
+ }
80
71
 
81
- let items = $state(allItems);
82
- let value = $state<string | null>('draft');
72
+ function loadMoreWebsites() {
73
+ visibleWebsiteCount = Math.min(filteredWebsites.length, visibleWebsiteCount + websiteBatchSize);
74
+ }
83
75
 
84
- const handleSearch = async (query: string) => {
85
- items = filterEntries(allItems, query);
86
- };
76
+ function handleWebsiteScroll(geometry: ScrollGeometry) {
77
+ if (visibleWebsiteCount >= filteredWebsites.length) return;
78
+ if (geometry.progress.y < 0.8) return;
79
+ loadMoreWebsites();
80
+ }
81
+
82
+ $effect(() => {
83
+ const nextQuery = websiteQuery.trim().toLowerCase();
84
+ if (nextQuery === lastWebsiteQuery) return;
85
+ lastWebsiteQuery = nextQuery;
86
+ visibleWebsiteCount = 6;
87
+ });
87
88
  </script>
88
89
 
89
- <Dropdown {items} bind:value placeholder="Choose status" onSearch={handleSearch} />
90
+ <div class="grid w-full gap-4 md:grid-cols-2">
91
+ <div class="vstack gap-2">
92
+ <p class="text-sm font-medium text-[var(--sveltely-primary-color)]">Default trigger</p>
93
+ <Dropdown bind:value={status} placeholder="Choose status" selectedLabel={selectedStatusLabel}>
94
+ <Dropdown.Section label="Publishing">
95
+ <Dropdown.Item value="draft">Draft</Dropdown.Item>
96
+ <Dropdown.Item value="scheduled">Scheduled</Dropdown.Item>
97
+ <Dropdown.Divider />
98
+ <Dropdown.Action onclick={() => (actionCount += 1)}>Open workflow</Dropdown.Action>
99
+ <Dropdown.Action onclick={() => (actionCount += 1)}>Create new status</Dropdown.Action>
100
+ </Dropdown.Section>
101
+ <Dropdown.Section label="Visibility">
102
+ <Dropdown.Item value="public">Public</Dropdown.Item>
103
+ <Dropdown.Item value="private" disabled>Private</Dropdown.Item>
104
+ </Dropdown.Section>
105
+ </Dropdown>
106
+ <p class="text-xs text-[var(--sveltely-secondary-color)]">
107
+ Selected: {selectedStatusLabel ?? 'none'}
108
+ </p>
109
+ </div>
110
+
111
+ <div class="vstack gap-2">
112
+ <p class="text-sm font-medium text-[var(--sveltely-primary-color)]">
113
+ Custom trigger and rich rows
114
+ </p>
115
+ <Dropdown
116
+ bind:value={selectedWebsiteId}
117
+ selectedLabel={selectedWebsite?.name ?? null}
118
+ showCheck={false}
119
+ >
120
+ {#snippet trigger(dropdown)}
121
+ <button
122
+ use:dropdown.useTrigger
123
+ type="button"
124
+ class="hstack w-full items-center gap-2 rounded-full border px-3 py-2 pr-2 text-left text-sm"
125
+ style="border-color: var(--sveltely-border-color); background: var(--sveltely-background-color);"
126
+ aria-expanded={dropdown.open}
127
+ aria-haspopup="dialog"
128
+ onclick={dropdown.toggle}
129
+ >
130
+ {#if selectedWebsite}
131
+ <span class="min-w-0 flex-1 truncate text-[var(--sveltely-primary-color)]"
132
+ >{selectedWebsite.name}</span
133
+ >
134
+ {#if selectedWebsite.domain}
135
+ <span class="max-w-32 truncate text-xs text-[var(--sveltely-secondary-color)]"
136
+ >{selectedWebsite.domain}</span
137
+ >
138
+ {/if}
139
+ {:else}
140
+ <span class="min-w-0 flex-1 text-[var(--sveltely-secondary-color)]">Select website</span
141
+ >
142
+ {/if}
143
+ <ChevronDownIcon size={14} class="shrink-0 text-[var(--sveltely-secondary-color)]" />
144
+ </button>
145
+ {/snippet}
146
+
147
+ <SearchField bind:value={websiteQuery} placeholder="Search websites..." />
148
+ <Dropdown.Action onclick={createWebsite}>
149
+ <PlusIcon size={14} />
150
+ <span>Create website</span>
151
+ </Dropdown.Action>
152
+ <Dropdown.Divider />
153
+ <Dropdown.Item value={null}>
154
+ <UserRoundXIcon size={14} />
155
+ <span>No website</span>
156
+ </Dropdown.Item>
157
+ <Dropdown.Section label="Websites">
158
+ {#if filteredWebsites.length === 0}
159
+ <div class="px-2 py-1 text-sm text-[var(--sveltely-secondary-color)]">
160
+ No websites found.
161
+ </div>
162
+ {:else}
163
+ <div class="h-36">
164
+ <ScrollView
165
+ contentStyles={{ paddingX: 0, paddingY: 0 }}
166
+ borderRadius="var(--sveltely-border-radius-nested)"
167
+ background="white"
168
+ onScroll={handleWebsiteScroll}
169
+ >
170
+ <div class="vstack gap-1">
171
+ {#each visibleWebsites as website (website.id)}
172
+ <Dropdown.Item value={website.id}>
173
+ {#snippet children({ selected })}
174
+ <div class="vstack min-w-0 gap-0.5">
175
+ <span class="truncate">{website.name}</span>
176
+ {#if website.domain}
177
+ <span class="truncate text-xs text-[var(--sveltely-secondary-color)]"
178
+ >{website.domain}</span
179
+ >
180
+ {/if}
181
+ </div>
182
+ {#if selected}
183
+ <CheckIcon
184
+ size={14}
185
+ class="shrink-0 text-[var(--sveltely-primary-color)]"
186
+ />
187
+ {/if}
188
+ {/snippet}
189
+ </Dropdown.Item>
190
+ {/each}
191
+ </div>
192
+ </ScrollView>
193
+ </div>
194
+ {/if}
195
+ </Dropdown.Section>
196
+ </Dropdown>
197
+ <p class="text-xs text-[var(--sveltely-secondary-color)]">Actions clicked: {actionCount}</p>
198
+ </div>
199
+ </div>
@@ -1,8 +1,9 @@
1
1
  export declare const demo: {
2
2
  name: string;
3
3
  description: string;
4
+ columnSpan: number;
4
5
  };
5
- import Dropdown from './Dropdown.svelte';
6
+ import Dropdown from './index';
6
7
  declare const Dropdown: import("svelte").Component<Record<string, never>, {}, "">;
7
8
  type Dropdown = ReturnType<typeof Dropdown>;
8
9
  export default Dropdown;