intelliwaketssveltekitv25 1.0.81 → 1.0.83

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 (53) hide show
  1. package/INTEGRATION.md +574 -0
  2. package/README.md +199 -45
  3. package/dist/ArrayTable.stories.js +215 -0
  4. package/dist/ArrayTable.svelte +46 -0
  5. package/dist/ArrayTable.svelte.d.ts +44 -0
  6. package/dist/DropDown.stories.d.ts +96 -0
  7. package/dist/DropDown.stories.js +192 -0
  8. package/dist/DropDown.svelte +32 -0
  9. package/dist/DropDown.svelte.d.ts +30 -0
  10. package/dist/InputNumber.stories.d.ts +122 -0
  11. package/dist/InputNumber.stories.js +186 -0
  12. package/dist/InputNumber.svelte +52 -0
  13. package/dist/InputNumber.svelte.d.ts +27 -0
  14. package/dist/Modal.stories.d.ts +114 -0
  15. package/dist/Modal.stories.js +139 -0
  16. package/dist/Modal.svelte +34 -3
  17. package/dist/Modal.svelte.d.ts +35 -3
  18. package/dist/MultiSelect.stories.js +338 -0
  19. package/dist/MultiSelect.svelte +81 -0
  20. package/dist/MultiSelect.svelte.d.ts +38 -0
  21. package/dist/Switch.stories.d.ts +81 -0
  22. package/dist/Switch.stories.js +99 -0
  23. package/dist/Switch.svelte +40 -0
  24. package/dist/Switch.svelte.d.ts +26 -0
  25. package/dist/TextArea.stories.d.ts +180 -0
  26. package/dist/TextArea.stories.js +216 -0
  27. package/dist/TextArea.svelte +32 -0
  28. package/dist/TextArea.svelte.d.ts +24 -0
  29. package/dist/app.css +1 -1
  30. package/docs/DateRangePicker.md +272 -0
  31. package/docs/DisplayHTML.md +249 -0
  32. package/docs/DropDown.md +269 -0
  33. package/docs/Functions.md +796 -0
  34. package/docs/Home.md +109 -0
  35. package/docs/Icon.md +203 -0
  36. package/docs/Importer.md +328 -0
  37. package/docs/ImporterAnalysis.md +249 -0
  38. package/docs/ImporterLoad.md +288 -0
  39. package/docs/InputNumber.md +159 -0
  40. package/docs/Integration.md +215 -0
  41. package/docs/Modal.md +207 -0
  42. package/docs/MultiSelect.md +304 -0
  43. package/docs/Paginator.md +332 -0
  44. package/docs/Search.md +364 -0
  45. package/docs/SlideDown.md +358 -0
  46. package/docs/Svelte-5-Patterns.md +364 -0
  47. package/docs/Switch.md +107 -0
  48. package/docs/TabHeader.md +333 -0
  49. package/docs/TabHref.md +370 -0
  50. package/docs/TextArea.md +118 -0
  51. package/docs/_Sidebar.md +38 -0
  52. package/llms.txt +113 -0
  53. package/package.json +7 -2
package/docs/Search.md ADDED
@@ -0,0 +1,364 @@
1
+ # Search Component
2
+
3
+ **Purpose:** Debounced search input with session storage persistence
4
+
5
+ **When to Use:**
6
+ - Filter lists, tables, or search results
7
+ - Debounced input to reduce API calls
8
+ - Persist search value across page navigation
9
+ - Clean, consistent search UI
10
+
11
+ ## Key Props
12
+
13
+ - `value?: string` ($bindable, default: '') - Search query
14
+ - `delayMS?: number` (default: 500) - Debounce delay in milliseconds
15
+ - `placeholder?: string` (default: 'Search') - Input placeholder text
16
+ - `onChange?: (val: string) => void` - Callback when value changes (after debounce)
17
+ - `sessionKey?: string` - Session storage key for persistence
18
+ - `bordered?: boolean` (default: false) - Show input border
19
+ - `noMagnifyingGlass?: boolean` - Hide search icon
20
+ - `element?: HTMLInputElement` - Bind to input element reference
21
+ - `use?: ActionArray` - Svelte actions to apply
22
+ - `hidden?: boolean` - Hide the component
23
+ - `id?: string` - Input element ID
24
+ - `class?: string` - Additional CSS classes
25
+ - ...HTMLInputAttributes - All standard input props
26
+
27
+ ## Key Features
28
+
29
+ ### Debouncing
30
+ - User types → Wait for `delayMS` → Trigger `onChange` and update `value`
31
+ - Press Enter → Immediate trigger (bypass debounce)
32
+ - Blur input → Immediate trigger
33
+ - Reduces API calls and improves performance
34
+
35
+ ### Session Storage
36
+ When `sessionKey` is provided:
37
+ - Saves search value to `sessionStorage`
38
+ - Restores value on component mount
39
+ - Clears storage when value returns to original state
40
+ - Persists across page refreshes
41
+
42
+ ### Auto-Select on Focus
43
+ Built-in `selectOnFocus` action automatically selects text when input is focused.
44
+
45
+ ## Usage Examples
46
+
47
+ ```svelte
48
+ <script>
49
+ import { Search } from 'intelliwaketssveltekitv25';
50
+
51
+ let searchQuery = $state('');
52
+ </script>
53
+
54
+ <!-- Basic search -->
55
+ <Search bind:value={searchQuery} />
56
+
57
+ <!-- With callback -->
58
+ <Search
59
+ bind:value={searchQuery}
60
+ onChange={(val) => {
61
+ console.log('Search for:', val);
62
+ performSearch(val);
63
+ }}
64
+ />
65
+
66
+ <!-- Custom debounce delay -->
67
+ <Search
68
+ bind:value={searchQuery}
69
+ delayMS={300}
70
+ />
71
+ <!-- Triggers 300ms after typing stops -->
72
+
73
+ <!-- Immediate (no debounce) -->
74
+ <Search
75
+ bind:value={searchQuery}
76
+ delayMS={0}
77
+ />
78
+
79
+ <!-- With session storage -->
80
+ <Search
81
+ bind:value={searchQuery}
82
+ sessionKey="productSearch"
83
+ />
84
+ <!-- Value persists across page refreshes -->
85
+
86
+ <!-- Custom placeholder -->
87
+ <Search
88
+ bind:value={searchQuery}
89
+ placeholder="Search products..."
90
+ />
91
+
92
+ <!-- With border -->
93
+ <Search
94
+ bind:value={searchQuery}
95
+ bordered
96
+ />
97
+
98
+ <!-- Without magnifying glass icon -->
99
+ <Search
100
+ bind:value={searchQuery}
101
+ noMagnifyingGlass
102
+ />
103
+
104
+ <!-- Custom styling -->
105
+ <Search
106
+ bind:value={searchQuery}
107
+ class="w-full md:w-96"
108
+ />
109
+
110
+ <!-- Get input element reference -->
111
+ <script>
112
+ let inputElement: HTMLInputElement | undefined;
113
+ </script>
114
+
115
+ <Search
116
+ bind:value={searchQuery}
117
+ bind:element={inputElement}
118
+ />
119
+ <button onclick={() => inputElement?.focus()}>
120
+ Focus Search
121
+ </button>
122
+
123
+ <!-- With additional input attributes -->
124
+ <Search
125
+ bind:value={searchQuery}
126
+ maxlength={50}
127
+ autocomplete="off"
128
+ autofocus
129
+ />
130
+ ```
131
+
132
+ ## Integration Patterns
133
+
134
+ ### Filter Table Data
135
+ ```svelte
136
+ <script>
137
+ let searchQuery = $state('');
138
+ let allItems = $state([...]);
139
+
140
+ let filteredItems = $derived(
141
+ allItems.filter(item =>
142
+ item.name.toLowerCase().includes(searchQuery.toLowerCase())
143
+ )
144
+ );
145
+ </script>
146
+
147
+ <Search bind:value={searchQuery} />
148
+
149
+ <ArrayTable data={filteredItems} {columns} />
150
+ ```
151
+
152
+ ### API Search with SvelteKit
153
+ ```svelte
154
+ <script>
155
+ let searchQuery = $state('');
156
+
157
+ async function handleSearch(query: string) {
158
+ if (query.length >= 2) {
159
+ await goto(`/search?q=${encodeURIComponent(query)}`);
160
+ }
161
+ }
162
+ </script>
163
+
164
+ <Search
165
+ bind:value={searchQuery}
166
+ onChange={handleSearch}
167
+ delayMS={500}
168
+ sessionKey="globalSearch"
169
+ />
170
+ ```
171
+
172
+ ### With Loading Indicator
173
+ ```svelte
174
+ <script>
175
+ let searchQuery = $state('');
176
+ let searching = $state(false);
177
+
178
+ async function performSearch(query: string) {
179
+ searching = true;
180
+ try {
181
+ const results = await api.search(query);
182
+ // ...
183
+ } finally {
184
+ searching = false;
185
+ }
186
+ }
187
+ </script>
188
+
189
+ <div class="relative">
190
+ <Search
191
+ bind:value={searchQuery}
192
+ onChange={performSearch}
193
+ />
194
+ {#if searching}
195
+ <div class="absolute right-2 top-1/2 -translate-y-1/2">
196
+ <Icon icon={faSpinner} spin />
197
+ </div>
198
+ {/if}
199
+ </div>
200
+ ```
201
+
202
+ ### Multi-Field Search
203
+ ```svelte
204
+ <script>
205
+ let nameSearch = $state('');
206
+ let emailSearch = $state('');
207
+
208
+ let filtered = $derived(
209
+ users.filter(user =>
210
+ user.name.includes(nameSearch) &&
211
+ user.email.includes(emailSearch)
212
+ )
213
+ );
214
+ </script>
215
+
216
+ <div class="grid grid-cols-2 gap-4">
217
+ <Search bind:value={nameSearch} placeholder="Search by name..." />
218
+ <Search bind:value={emailSearch} placeholder="Search by email..." />
219
+ </div>
220
+ ```
221
+
222
+ ### With Clear Button
223
+ ```svelte
224
+ <script>
225
+ let searchQuery = $state('');
226
+ </script>
227
+
228
+ <div class="flex gap-2">
229
+ <Search bind:value={searchQuery} class="flex-1" />
230
+ {#if searchQuery}
231
+ <button onclick={() => searchQuery = ''}>
232
+ Clear
233
+ </button>
234
+ {/if}
235
+ </div>
236
+ ```
237
+
238
+ ## Keyboard Shortcuts
239
+
240
+ - **Enter** - Immediately trigger search (bypass debounce)
241
+ - **Escape** - (Native) Clear input
242
+ - **Focus** - Automatically select all text
243
+
244
+ ## Session Storage Behavior
245
+
246
+ When `sessionKey="mySearch"`:
247
+
248
+ 1. **On mount:** Restore value from `sessionStorage.getItem('mySearch')`
249
+ 2. **On change:** Save to `sessionStorage.setItem('mySearch', value)`
250
+ 3. **On clear:** Remove from `sessionStorage.removeItem('mySearch')`
251
+
252
+ Use cases:
253
+ - Preserve search across tab navigation
254
+ - Remember last search on page refresh
255
+ - Share search state between components
256
+
257
+ ```svelte
258
+ <!-- Page 1 -->
259
+ <Search
260
+ bind:value={query1}
261
+ sessionKey="mainSearch"
262
+ />
263
+
264
+ <!-- Page 2 (preserves the same search) -->
265
+ <Search
266
+ bind:value={query2}
267
+ sessionKey="mainSearch"
268
+ />
269
+ ```
270
+
271
+ ## Common Patterns
272
+
273
+ ### Master-Detail Layout Search
274
+ ```svelte
275
+ <MasterDetailLayout>
276
+ {#snippet list()}
277
+ <Search bind:value={filterQuery} sessionKey="itemFilter" />
278
+ {#each filteredItems as item}
279
+ <ItemCard {item} />
280
+ {/each}
281
+ {/snippet}
282
+ </MasterDetailLayout>
283
+ ```
284
+
285
+ ### Real-Time Filter
286
+ ```svelte
287
+ <script>
288
+ let products = $state([...]);
289
+ let filter = $state('');
290
+
291
+ // Updates immediately as user types (after debounce)
292
+ let visible = $derived(
293
+ products.filter(p =>
294
+ p.name.toLowerCase().includes(filter.toLowerCase()) ||
295
+ p.sku.includes(filter)
296
+ )
297
+ );
298
+ </script>
299
+
300
+ <Search bind:value={filter} delayMS={300} />
301
+
302
+ <div class="text-sm text-gray-600">
303
+ Showing {visible.length} of {products.length} products
304
+ </div>
305
+
306
+ {#each visible as product}
307
+ <ProductCard {product} />
308
+ {/each}
309
+ ```
310
+
311
+ ## Common Mistakes
312
+
313
+ - ❌ Not using `bind:value` for two-way binding
314
+ ✅ Correct: `<Search bind:value={searchQuery} />`
315
+
316
+ - ❌ Using very short debounce delays (< 200ms)
317
+ ✅ Correct: Use 300-500ms for best UX and performance
318
+
319
+ - ❌ Triggering expensive operations in `onChange` without checks
320
+ ✅ Correct: Check minimum query length: `if (query.length >= 2)`
321
+
322
+ - ❌ Not handling empty string in filter logic
323
+ ✅ Correct: `if (!query) return allItems;`
324
+
325
+ - ❌ Using same `sessionKey` for different search contexts
326
+ ✅ Correct: Use unique keys: `sessionKey="userSearch"`, `sessionKey="productSearch"`
327
+
328
+ ## Related Components
329
+
330
+ - **MultiSelect** - Has built-in search functionality
331
+ - **ArrayTable** - Often paired with Search for filtering
332
+ - **MasterDetailLayout** - Common container for search + results
333
+
334
+ ## Props Reference
335
+
336
+ | Prop | Type | Default | Description |
337
+ |------|------|---------|-------------|
338
+ | `value` | `string` | `''` | Search query ($bindable) |
339
+ | `delayMS` | `number` | `500` | Debounce delay (ms) |
340
+ | `placeholder` | `string` | `'Search'` | Input placeholder |
341
+ | `onChange` | `(val: string) => void` | - | Debounced callback |
342
+ | `sessionKey` | `string` | - | Session storage key |
343
+ | `bordered` | `boolean` | `false` | Show border |
344
+ | `noMagnifyingGlass` | `boolean` | `false` | Hide search icon |
345
+ | `element` | `HTMLInputElement` | - | Input element ref |
346
+ | `use` | `ActionArray` | `[]` | Svelte actions |
347
+ | `hidden` | `boolean` | `false` | Hide component |
348
+ | `class` | `string` | `''` | CSS classes |
349
+ | `id` | `string` | - | Input ID |
350
+
351
+ ## Styling
352
+
353
+ - Uses `inputSearch` wrapper class
354
+ - Icon: Absolute positioned left with `text-slate-300`
355
+ - Input: Standard input styling with `ps-6` (left padding) when icon visible
356
+ - Border: Transparent by default, visible when `bordered={true}`
357
+ - Print: Hidden if value is empty (`forcePrintHidden`)
358
+
359
+ ## Performance Tips
360
+
361
+ - Use appropriate `delayMS` (500ms recommended)
362
+ - Implement minimum query length checks in `onChange`
363
+ - Use `$derived` for reactive filtering (efficient)
364
+ - Consider virtualization for large result sets
@@ -0,0 +1,358 @@
1
+ # SlideDown Component
2
+
3
+ **Purpose:** Animated dropdown menu with keyboard navigation and flexible content
4
+
5
+ **When to Use:**
6
+ - Dropdown menus without form input styling
7
+ - Standalone dropdown menus
8
+ - Custom menu implementations
9
+ - Menus with mixed content (actions + custom snippets)
10
+
11
+ ## Key Props
12
+
13
+ - `ddActions: IDDAction[]` (required) - Menu items array
14
+ - `show?: boolean` ($bindable, default: false) - Control open/closed state
15
+ - `button?: Snippet` - Custom button content
16
+ - `actions?: Snippet` - Custom menu content (in addition to ddActions)
17
+ - `buttonTitle?: string | null` - Button text (if not using button snippet)
18
+ - `buttonClass?: string` (default: '') - CSS classes for button
19
+ - `width?: string` (default: 'auto') - Menu width
20
+ - `maxHeight?: string | null` (default: '60vh') - Max height for scrolling
21
+ - `caret?: boolean` (default: false) - Show dropdown caret icon
22
+ - `use?: ActionArray` - Svelte actions to apply
23
+ - `highlightedIndex?: number` (default: -1) - Keyboard navigation index
24
+
25
+ ## IDDAction Interface
26
+
27
+ Same as **DropDown** component. See [DropDown.md](DropDown.md#iddaction-interface) for full details.
28
+
29
+ ## Key Differences from DropDown
30
+
31
+ | Feature | SlideDown | DropDown |
32
+ |---------|-----------|----------|
33
+ | Styling | Clean, minimal | Input control styling option |
34
+ | Animation | Slide + fade | Slide only |
35
+ | Position | Centered, auto | Configurable position |
36
+ | Use Case | Menus, navigation | Form controls, selects |
37
+ | Complexity | Simpler | More features |
38
+
39
+ ## Keyboard Navigation
40
+
41
+ - **Arrow Down** - Move to next item / Open menu
42
+ - **Arrow Up** - Move to previous item
43
+ - **Enter** - Execute highlighted action
44
+ - **Escape** - Close menu
45
+ - **Click outside** - Close menu
46
+
47
+ ## Usage Examples
48
+
49
+ ```svelte
50
+ <script>
51
+ import { SlideDown } from 'intelliwaketssveltekitv25';
52
+ import { faUser, faCog, faSignOut } from '@fortawesome/free-solid-svg-icons';
53
+
54
+ let showMenu = $state(false);
55
+ </script>
56
+
57
+ <!-- Basic menu -->
58
+ <SlideDown
59
+ bind:show={showMenu}
60
+ buttonTitle="Menu"
61
+ ddActions={[
62
+ {
63
+ title: 'Profile',
64
+ faProps: { icon: faUser },
65
+ action: () => goto('/profile')
66
+ },
67
+ {
68
+ title: 'Settings',
69
+ faProps: { icon: faCog },
70
+ action: () => goto('/settings')
71
+ },
72
+ { divider: true },
73
+ {
74
+ title: 'Logout',
75
+ faProps: { icon: faSignOut },
76
+ action: () => logout()
77
+ }
78
+ ]}
79
+ />
80
+
81
+ <!-- With caret icon -->
82
+ <SlideDown
83
+ bind:show={showMenu}
84
+ buttonTitle="Options"
85
+ ddActions={actions}
86
+ caret
87
+ />
88
+
89
+ <!-- Custom button content -->
90
+ <SlideDown
91
+ bind:show={showMenu}
92
+ ddActions={actions}
93
+ >
94
+ {#snippet button()}
95
+ <div class="flex items-center gap-2">
96
+ <Avatar src={user.avatar} />
97
+ <span>{user.name}</span>
98
+ </div>
99
+ {/snippet}
100
+ </SlideDown>
101
+
102
+ <!-- Custom menu content -->
103
+ <SlideDown
104
+ bind:show={showMenu}
105
+ ddActions={[]}
106
+ >
107
+ {#snippet actions()}
108
+ <div class="p-4">
109
+ <h3>Custom Content</h3>
110
+ <p>Any content here</p>
111
+ </div>
112
+ {/snippet}
113
+ </SlideDown>
114
+
115
+ <!-- Mixed custom + actions -->
116
+ <SlideDown
117
+ bind:show={showMenu}
118
+ ddActions={standardActions}
119
+ >
120
+ {#snippet actions()}
121
+ <div class="p-2 bg-gray-50">
122
+ <p class="text-xs">Recent Activity:</p>
123
+ {#each recentItems as item}
124
+ <div>{item.name}</div>
125
+ {/each}
126
+ </div>
127
+ {/snippet}
128
+ </SlideDown>
129
+
130
+ <!-- Fixed width -->
131
+ <SlideDown
132
+ bind:show={showMenu}
133
+ buttonTitle="Select"
134
+ ddActions={options}
135
+ width="300px"
136
+ />
137
+
138
+ <!-- No max height (full scrollable) -->
139
+ <SlideDown
140
+ bind:show={showMenu}
141
+ buttonTitle="All Items"
142
+ ddActions={manyItems}
143
+ maxHeight={null}
144
+ />
145
+
146
+ <!-- With custom button styling -->
147
+ <SlideDown
148
+ bind:show={showMenu}
149
+ buttonTitle="Actions"
150
+ buttonClass="text-blue-600 font-bold hover:text-blue-800"
151
+ ddActions={actions}
152
+ />
153
+ ```
154
+
155
+ ## Common Patterns
156
+
157
+ ### User Menu
158
+ ```svelte
159
+ <script>
160
+ let userMenuOpen = $state(false);
161
+ </script>
162
+
163
+ <SlideDown
164
+ bind:show={userMenuOpen}
165
+ ddActions={[
166
+ {
167
+ title: user.name,
168
+ header: true
169
+ },
170
+ { divider: true },
171
+ {
172
+ title: 'Profile',
173
+ faProps: { icon: faUser },
174
+ href: '/profile'
175
+ },
176
+ {
177
+ title: 'Settings',
178
+ faProps: { icon: faCog },
179
+ href: '/settings'
180
+ },
181
+ { divider: true },
182
+ {
183
+ title: 'Logout',
184
+ faProps: { icon: faSignOut },
185
+ action: () => handleLogout()
186
+ }
187
+ ]}
188
+ >
189
+ {#snippet button()}
190
+ <Avatar src={user.avatar} size="sm" />
191
+ {/snippet}
192
+ </SlideDown>
193
+ ```
194
+
195
+ ### Context Menu
196
+ ```svelte
197
+ <script>
198
+ let contextMenu = $state(false);
199
+ </script>
200
+
201
+ <div class="relative">
202
+ <button onclick={() => contextMenu = !contextMenu}>
203
+ <Icon icon={faEllipsisV} />
204
+ </button>
205
+
206
+ <SlideDown
207
+ bind:show={contextMenu}
208
+ ddActions={[
209
+ { title: 'Edit', faProps: { icon: faEdit }, action: () => edit() },
210
+ { title: 'Duplicate', faProps: { icon: faCopy }, action: () => duplicate() },
211
+ { divider: true },
212
+ { title: 'Delete', faProps: { icon: faTrash }, action: () => remove() }
213
+ ]}
214
+ >
215
+ {#snippet button()}
216
+ <!-- Empty, controlled by external button -->
217
+ {/snippet}
218
+ </SlideDown>
219
+ </div>
220
+ ```
221
+
222
+ ### Grouped Menu
223
+ ```svelte
224
+ <SlideDown
225
+ buttonTitle="Tools"
226
+ ddActions={[
227
+ { title: 'File Tools', header: true },
228
+ { title: 'New File', action: () => {} },
229
+ { title: 'Open File', action: () => {} },
230
+ { title: 'Save File', action: () => {} },
231
+ { divider: true },
232
+ { title: 'Edit Tools', header: true },
233
+ { title: 'Cut', action: () => {} },
234
+ { title: 'Copy', action: () => {} },
235
+ { title: 'Paste', action: () => {} }
236
+ ]}
237
+ />
238
+ ```
239
+
240
+ ### With Active State
241
+ ```svelte
242
+ <script>
243
+ let selectedOption = $state('option1');
244
+ </script>
245
+
246
+ <SlideDown
247
+ buttonTitle={options.find(o => o.id === selectedOption)?.title}
248
+ ddActions={options.map(opt => ({
249
+ title: opt.title,
250
+ active: opt.id === selectedOption,
251
+ action: () => selectedOption = opt.id
252
+ }))}
253
+ />
254
+ ```
255
+
256
+ ### Navigation Dropdown
257
+ ```svelte
258
+ <SlideDown
259
+ buttonTitle="Products"
260
+ ddActions={[
261
+ { title: 'All Products', href: '/products' },
262
+ { title: 'Electronics', href: '/products/electronics' },
263
+ { title: 'Clothing', href: '/products/clothing' },
264
+ { title: 'Food', href: '/products/food' }
265
+ ]}
266
+ />
267
+ ```
268
+
269
+ ## Advanced Features
270
+
271
+ ### Auto-Scroll to Active Item
272
+ When menu opens, automatically scrolls to the active item.
273
+
274
+ ### Indentation Support
275
+ ```svelte
276
+ <SlideDown
277
+ buttonTitle="Nested Menu"
278
+ ddActions={[
279
+ { title: 'Parent Item', action: () => {} },
280
+ { title: 'Child Item 1', indented: true, action: () => {} },
281
+ { title: 'Child Item 2', indented: true, action: () => {} },
282
+ { title: 'Another Parent', action: () => {} }
283
+ ]}
284
+ />
285
+ ```
286
+
287
+ ### Auto Headers/Dividers
288
+ ```svelte
289
+ <!-- Auto-adds headers when headerGroup changes -->
290
+ <SlideDown
291
+ buttonTitle="Grouped"
292
+ ddActions={[
293
+ { title: 'Item A1', headerGroup: 'Group A', action: () => {} },
294
+ { title: 'Item A2', headerGroup: 'Group A', action: () => {} },
295
+ { title: 'Item B1', headerGroup: 'Group B', action: () => {} },
296
+ { title: 'Item B2', headerGroup: 'Group B', action: () => {} }
297
+ ]}
298
+ />
299
+ <!-- Renders: Group A (header), Item A1, Item A2, Group B (header), Item B1, Item B2 -->
300
+ ```
301
+
302
+ ## Common Mistakes
303
+
304
+ - ❌ Not providing button content or buttonTitle
305
+ ✅ Correct: Provide buttonTitle or button snippet
306
+
307
+ - ❌ Using SlideDown when DropDown is more appropriate
308
+ ✅ Correct: Use DropDown for form inputs, SlideDown for menus
309
+
310
+ - ❌ Forgetting to bind `show` for controlled state
311
+ ✅ Correct: `bind:show={isOpen}` to control externally
312
+
313
+ - ❌ Not providing action or href for menu items
314
+ ✅ Correct: Every item needs action, href, or be a divider/header
315
+
316
+ - ❌ Very long menus without maxHeight
317
+ ✅ Correct: Keep default maxHeight or set reasonable limit
318
+
319
+ ## Related Components
320
+
321
+ - **DropDown** - More feature-rich dropdown with positioning
322
+ - **DropDownControl** - Lower-level dropdown primitive
323
+ - **Icon** - Used for menu item icons
324
+ - **IDDAction** - Shared action item interface
325
+
326
+ ## Props Reference
327
+
328
+ | Prop | Type | Default | Description |
329
+ |------|------|---------|-------------|
330
+ | `ddActions` | `IDDAction[]` | (required) | Menu items |
331
+ | `show` | `boolean` | `false` | Open state ($bindable) |
332
+ | `button` | `Snippet` | - | Custom button |
333
+ | `actions` | `Snippet` | - | Custom menu content |
334
+ | `buttonTitle` | `string \| null` | `null` | Button text |
335
+ | `buttonClass` | `string` | `''` | Button CSS classes |
336
+ | `width` | `string` | `'auto'` | Menu width |
337
+ | `maxHeight` | `string \| null` | `'60vh'` | Max menu height |
338
+ | `caret` | `boolean` | `false` | Show caret icon |
339
+ | `use` | `ActionArray` | `[]` | Svelte actions |
340
+ | `highlightedIndex` | `number` | `-1` | Keyboard nav index |
341
+
342
+ ## Styling
343
+
344
+ - Clean button styling (no default borders)
345
+ - White background on open (`bg-white`, `dark:bg-slate-700`)
346
+ - Shadow on open (`shadow-lg`)
347
+ - Slide animation (200ms cubic-in-out)
348
+ - Hover: `hover:bg-slate-100` for items
349
+ - Active: `bg-primary-main text-white`
350
+ - Disabled: `text-slate-300`
351
+
352
+ ## Accessibility
353
+
354
+ - Keyboard navigation fully supported
355
+ - `role="button"` and `role="menuitem"` attributes
356
+ - `aria-expanded` on container
357
+ - `aria-haspopup="true"` on button
358
+ - Tab index managed for proper focus flow