@svelte-atoms/core 1.0.0-alpha.31 → 1.0.0-alpha.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +289 -853
- package/dist/attachments/index.d.ts +1 -0
- package/dist/attachments/index.js +1 -0
- package/dist/components/accordion/accordion-root.svelte +65 -65
- package/dist/components/accordion/accordion.stories.svelte +70 -70
- package/dist/components/accordion/item/accordion-item-body.svelte +44 -44
- package/dist/components/accordion/item/accordion-item-header.svelte +51 -51
- package/dist/components/accordion/item/accordion-item-indicator.svelte +51 -51
- package/dist/components/accordion/item/accordion-item-root.svelte +66 -66
- package/dist/components/alert/alert-close-button.svelte +66 -66
- package/dist/components/alert/alert-description.svelte +42 -42
- package/dist/components/alert/alert-root.svelte +68 -68
- package/dist/components/atom/html-atom.svelte +26 -194
- package/dist/components/atom/types.d.ts +3 -2
- package/dist/components/atom/utils.d.ts +37 -0
- package/dist/components/atom/utils.js +208 -0
- package/dist/components/breadcrumb/breadcrumb-item.svelte +1 -1
- package/dist/components/breadcrumb/breadcrumb-separator.svelte +5 -1
- package/dist/components/breadcrumb/breadcrumb.stories.svelte +16 -16
- package/dist/components/calendar/calendar-day.svelte +101 -101
- package/dist/components/checkbox/checkbox.svelte +159 -159
- package/dist/components/collapsible/bond.svelte.js +2 -1
- package/dist/components/collapsible/collapsible-body.svelte +3 -2
- package/dist/components/collapsible/motion.svelte.d.ts +6 -0
- package/dist/components/collapsible/motion.svelte.js +15 -0
- package/dist/components/combobox/atoms.d.ts +3 -3
- package/dist/components/combobox/atoms.js +3 -3
- package/dist/components/combobox/bond.svelte.d.ts +6 -6
- package/dist/components/combobox/bond.svelte.js +3 -26
- package/dist/components/combobox/combobox-control.svelte +52 -52
- package/dist/components/combobox/{compobox-item.svelte → combobox-item.svelte} +62 -68
- package/dist/components/combobox/combobox-item.svelte.d.ts +12 -0
- package/dist/components/combobox/combobox.stories.svelte +50 -0
- package/dist/components/combobox/combobox.stories.svelte.d.ts +3 -0
- package/dist/components/datagrid/tr/datagrid-tr.svelte +90 -90
- package/dist/components/date-picker/bond.svelte.d.ts +15 -5
- package/dist/components/date-picker/bond.svelte.js +5 -11
- package/dist/components/date-picker/date-picker-calendar.svelte +67 -67
- package/dist/components/dialog/bond.svelte.js +5 -20
- package/dist/components/dialog/dialog-content.svelte +44 -44
- package/dist/components/dialog/dialog-root.svelte +91 -91
- package/dist/components/drawer/bond.svelte.d.ts +18 -16
- package/dist/components/drawer/bond.svelte.js +8 -18
- package/dist/components/drawer/drawer-content.svelte +49 -49
- package/dist/components/drawer/drawer-root.svelte +5 -4
- package/dist/components/drawer/drawer.stories.svelte +141 -144
- package/dist/components/drawer/motion.js +1 -1
- package/dist/components/dropdown/atoms.d.ts +1 -1
- package/dist/components/dropdown/atoms.js +1 -1
- package/dist/components/dropdown/bond.svelte.d.ts +21 -22
- package/dist/components/dropdown/bond.svelte.js +29 -53
- package/dist/components/dropdown/dropdown-root.svelte +65 -59
- package/dist/components/dropdown/dropdown-values.svelte +17 -17
- package/dist/components/dropdown/dropdown-values.svelte.d.ts +1 -2
- package/dist/components/dropdown/dropdown.stories.svelte +83 -80
- package/dist/components/dropdown/index.d.ts +1 -0
- package/dist/components/dropdown/index.js +1 -0
- package/dist/components/dropdown/item/attachments.svelte.d.ts +2 -2
- package/dist/components/dropdown/item/attachments.svelte.js +2 -2
- package/dist/components/dropdown/item/controller.svelte.d.ts +34 -0
- package/dist/components/dropdown/item/controller.svelte.js +82 -0
- package/dist/components/dropdown/item/dropdown-item.svelte +109 -102
- package/dist/components/dropdown/item/dropdown-item.svelte.d.ts +13 -28
- package/dist/components/dropdown/item/index.d.ts +3 -0
- package/dist/components/dropdown/item/index.js +3 -0
- package/dist/components/dropdown/item/types.d.ts +29 -0
- package/dist/components/dropdown/item/types.js +1 -0
- package/dist/components/list/list-item.svelte +20 -20
- package/dist/components/menu/atoms.d.ts +8 -3
- package/dist/components/menu/atoms.js +8 -3
- package/dist/components/menu/bond.svelte.d.ts +54 -0
- package/dist/components/menu/bond.svelte.js +132 -0
- package/dist/components/menu/index.d.ts +1 -0
- package/dist/components/menu/index.js +1 -0
- package/dist/components/menu/item/controller.svelte.d.ts +26 -0
- package/dist/components/menu/item/controller.svelte.js +69 -0
- package/dist/components/menu/item/index.d.ts +2 -0
- package/dist/components/menu/item/index.js +2 -0
- package/dist/components/menu/item/menu-item.svelte +103 -0
- package/dist/components/menu/item/menu-item.svelte.d.ts +31 -0
- package/dist/components/menu/item/types.d.ts +62 -0
- package/dist/components/menu/item/types.js +1 -0
- package/dist/components/menu/{menu-list.svelte → menu-content.svelte} +40 -40
- package/dist/components/menu/{menu-list.svelte.d.ts → menu-content.svelte.d.ts} +3 -3
- package/dist/components/menu/menu-root.svelte +15 -0
- package/dist/components/menu/menu-root.svelte.d.ts +8 -0
- package/dist/components/menu/menu.stories.svelte +33 -33
- package/dist/components/menu/types.d.ts +0 -7
- package/dist/components/popover/bond.svelte.d.ts +11 -14
- package/dist/components/popover/bond.svelte.js +27 -44
- package/dist/components/popover/popover-content.svelte +137 -137
- package/dist/components/popover/popover.stories.svelte +37 -49
- package/dist/components/portal/active-portal.svelte +29 -29
- package/dist/components/portal/portal-root.svelte +76 -76
- package/dist/components/portal/teleport.svelte +49 -49
- package/dist/components/radio/radio.svelte +109 -109
- package/dist/components/root/index.d.ts +1 -0
- package/dist/components/root/index.js +1 -0
- package/dist/components/root/l0-portal.svelte +8 -0
- package/dist/components/root/l0-portal.svelte.d.ts +26 -0
- package/dist/components/root/l1-portal.svelte +7 -0
- package/dist/components/root/l1-portal.svelte.d.ts +26 -0
- package/dist/components/root/root.css +119 -119
- package/dist/components/root/root.svelte +17 -18
- package/dist/components/root/root.svelte.d.ts +2 -6
- package/dist/components/root/toasts-portal.svelte +7 -0
- package/dist/components/root/toasts-portal.svelte.d.ts +26 -0
- package/dist/components/root/types.d.ts +17 -0
- package/dist/components/sidebar/motion.svelte.js +3 -3
- package/dist/components/sidebar/sidebar-content.svelte +40 -40
- package/dist/components/textarea/textarea-input.svelte +9 -9
- package/dist/components/textarea/textarea-root.svelte +9 -9
- package/dist/components/tooltip/tooltip-trigger.svelte +39 -39
- package/dist/components/tree/index.d.ts +1 -0
- package/dist/components/tree/index.js +1 -0
- package/dist/components/tree/motion.svelte.d.ts +6 -0
- package/dist/components/tree/motion.svelte.js +14 -0
- package/dist/components/tree/tree-body.svelte +4 -3
- package/dist/context/preset.svelte.d.ts +3 -1
- package/dist/icons/icon-copy.svelte +6 -6
- package/dist/utils/dom.svelte.d.ts +2 -0
- package/dist/utils/dom.svelte.js +21 -0
- package/dist/utils/function.d.ts +1 -1
- package/dist/utils/promise.svelte.d.ts +5 -0
- package/dist/utils/promise.svelte.js +20 -0
- package/package.json +4 -2
- package/dist/components/combobox/compobox-item.svelte.d.ts +0 -34
- package/dist/components/combobox/compobox.stories.svelte +0 -51
- package/dist/components/combobox/compobox.stories.svelte.d.ts +0 -3
- package/dist/components/dropdown/item/bond.svelte.d.ts +0 -42
- package/dist/components/dropdown/item/bond.svelte.js +0 -99
- package/dist/components/menu/menu-item.svelte +0 -69
- package/dist/components/menu/menu-item.svelte.d.ts +0 -37
- package/dist/utils/markdown-to-llm.d.ts +0 -28
- package/dist/utils/markdown-to-llm.js +0 -76
package/README.md
CHANGED
|
@@ -1,853 +1,289 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
> A modern, modular, and accessible Svelte 5 UI component library built with composability at its core.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
[![
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
###
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
###
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
###
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
<Dialog.
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
let open = $state(false);
|
|
292
|
-
// Filter items based on search query
|
|
293
|
-
const dd = filter(
|
|
294
|
-
() => data,
|
|
295
|
-
(query, item) => item.text.toLowerCase().includes(query.toLowerCase())
|
|
296
|
-
);
|
|
297
|
-
</script>
|
|
298
|
-
|
|
299
|
-
<Root class="items-center justify-center p-4">
|
|
300
|
-
<!-- Multi-select dropdown with search functionality -->
|
|
301
|
-
<Dropdown.Root
|
|
302
|
-
bind:open
|
|
303
|
-
multiple
|
|
304
|
-
keys={data.map((item) => item.value)}
|
|
305
|
-
onquerychange={(q) => (dd.query = q)}
|
|
306
|
-
>
|
|
307
|
-
{#snippet children({ dropdown })}
|
|
308
|
-
<!-- Compose Dropdown.Trigger with Input.Root for a custom trigger -->
|
|
309
|
-
<Dropdown.Trigger
|
|
310
|
-
base={Input.Root}
|
|
311
|
-
class="h-auto min-h-12 max-w-sm min-w-sm items-center gap-2 rounded-sm px-4 transition-colors duration-200"
|
|
312
|
-
onclick={(ev) => {
|
|
313
|
-
ev.preventDefault();
|
|
314
|
-
|
|
315
|
-
dropdown.state.open();
|
|
316
|
-
}}
|
|
317
|
-
>
|
|
318
|
-
<!-- Display selected values with animation -->
|
|
319
|
-
{#each dropdown?.state?.selectedItems ?? [] as item (item.id)}
|
|
320
|
-
<div animate:flip={{ duration: 200 }}>
|
|
321
|
-
<ADropdown.Value value={item.value} class="text-foreground/80">
|
|
322
|
-
{item.text}
|
|
323
|
-
</ADropdown.Value>
|
|
324
|
-
</div>
|
|
325
|
-
{/each}
|
|
326
|
-
|
|
327
|
-
<!-- Inline search input within the trigger -->
|
|
328
|
-
<Dropdown.Query class="flex-1 px-1" placeholder="Search for fruits..." />
|
|
329
|
-
</Dropdown.Trigger>
|
|
330
|
-
|
|
331
|
-
<!-- Dropdown list with filtered items -->
|
|
332
|
-
<Dropdown.List>
|
|
333
|
-
{#each dd.current as item (item.id)}
|
|
334
|
-
<div animate:flip={{ duration: 200 }}>
|
|
335
|
-
<Dropdown.Item value={item.value}>{item.text}</Dropdown.Item>
|
|
336
|
-
</div>
|
|
337
|
-
{/each}
|
|
338
|
-
</Dropdown.List>
|
|
339
|
-
{/snippet}
|
|
340
|
-
</Dropdown.Root>
|
|
341
|
-
</Root>
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
**Key composition features demonstrated:**
|
|
345
|
-
|
|
346
|
-
- **Component Fusion**: Using `base={Input.Root}` to compose Dropdown.Trigger with Input styling and behavior
|
|
347
|
-
- **Snippet Patterns**: Accessing internal state through snippets for custom rendering
|
|
348
|
-
- **Reactive Filtering**: Combining search query state with reactive effects for real-time filtering
|
|
349
|
-
- **Smooth Animations**: Using Svelte's `flip` animation for seamless list transitions
|
|
350
|
-
- **Multi-Select State**: Managing complex selection state through the Bond pattern
|
|
351
|
-
|
|
352
|
-
### Creating Custom Variants
|
|
353
|
-
|
|
354
|
-
@svelte-atoms/core provides a powerful variant system using `defineVariants()` that allows you to create type-safe, reusable component variations with support for compound variants, defaults, and bond state integration.
|
|
355
|
-
|
|
356
|
-
#### Basic Variant Definition
|
|
357
|
-
|
|
358
|
-
```typescript
|
|
359
|
-
import { defineVariants, type VariantPropsType } from '@svelte-atoms/core/utils';
|
|
360
|
-
|
|
361
|
-
const buttonVariants = defineVariants({
|
|
362
|
-
class: 'inline-flex items-center justify-center rounded-md font-medium transition-colors',
|
|
363
|
-
variants: {
|
|
364
|
-
variant: {
|
|
365
|
-
primary: 'bg-blue-500 text-white hover:bg-blue-600',
|
|
366
|
-
secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300',
|
|
367
|
-
ghost: 'hover:bg-gray-100'
|
|
368
|
-
},
|
|
369
|
-
size: {
|
|
370
|
-
sm: 'h-8 px-3 text-sm',
|
|
371
|
-
md: 'h-10 px-4',
|
|
372
|
-
lg: 'h-12 px-6 text-lg'
|
|
373
|
-
}
|
|
374
|
-
},
|
|
375
|
-
compounds: [
|
|
376
|
-
{
|
|
377
|
-
variant: 'primary',
|
|
378
|
-
size: 'lg',
|
|
379
|
-
class: 'shadow-md font-semibold'
|
|
380
|
-
}
|
|
381
|
-
],
|
|
382
|
-
defaults: {
|
|
383
|
-
variant: 'primary',
|
|
384
|
-
size: 'md'
|
|
385
|
-
}
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
// Extract type-safe props
|
|
389
|
-
type ButtonVariantProps = VariantPropsType<typeof buttonVariants>;
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
#### Local vs Global Variants
|
|
393
|
-
|
|
394
|
-
**Local Variants** - Define variants directly in your component:
|
|
395
|
-
|
|
396
|
-
```svelte
|
|
397
|
-
<script lang="ts">
|
|
398
|
-
import { HtmlAtom } from '@svelte-atoms/core';
|
|
399
|
-
import { defineVariants, type VariantPropsType } from '@svelte-atoms/core/utils';
|
|
400
|
-
|
|
401
|
-
const buttonVariants = defineVariants({
|
|
402
|
-
class: 'rounded-md font-medium',
|
|
403
|
-
variants: {
|
|
404
|
-
variant: {
|
|
405
|
-
primary: 'bg-blue-500 text-white',
|
|
406
|
-
secondary: 'bg-gray-500 text-white'
|
|
407
|
-
},
|
|
408
|
-
size: {
|
|
409
|
-
sm: 'px-2 py-1 text-sm',
|
|
410
|
-
md: 'px-4 py-2 text-base'
|
|
411
|
-
}
|
|
412
|
-
},
|
|
413
|
-
defaults: {
|
|
414
|
-
variant: 'primary',
|
|
415
|
-
size: 'md'
|
|
416
|
-
}
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
type ButtonProps = VariantPropsType<typeof buttonVariants> & {
|
|
420
|
-
disabled?: boolean;
|
|
421
|
-
class?: string;
|
|
422
|
-
};
|
|
423
|
-
|
|
424
|
-
let { variant, size, disabled = false, class: klass = '', ...props }: ButtonProps = $props();
|
|
425
|
-
|
|
426
|
-
const variantProps = $derived(buttonVariants(null, { variant, size }));
|
|
427
|
-
</script>
|
|
428
|
-
|
|
429
|
-
<HtmlAtom
|
|
430
|
-
as="button"
|
|
431
|
-
variants={variantProps}
|
|
432
|
-
{disabled}
|
|
433
|
-
class={[variantProps.class, klass]}
|
|
434
|
-
{...props}
|
|
435
|
-
>
|
|
436
|
-
{@render children?.()}
|
|
437
|
-
</HtmlAtom>
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
**Global Variants** - Define variants in your theme/preset configuration:
|
|
441
|
-
|
|
442
|
-
```typescript
|
|
443
|
-
// +layout.svelte or theme configuration
|
|
444
|
-
import { setPreset } from '@svelte-atoms/core/context';
|
|
445
|
-
|
|
446
|
-
setPreset({
|
|
447
|
-
button: () => ({
|
|
448
|
-
class: 'inline-flex items-center justify-center rounded-md font-medium transition-colors',
|
|
449
|
-
variants: {
|
|
450
|
-
variant: {
|
|
451
|
-
default: {
|
|
452
|
-
class: 'bg-primary text-primary-foreground hover:bg-primary/90'
|
|
453
|
-
},
|
|
454
|
-
destructive: {
|
|
455
|
-
class: 'bg-destructive text-destructive-foreground hover:bg-destructive/90'
|
|
456
|
-
},
|
|
457
|
-
outline: {
|
|
458
|
-
class: 'border border-input bg-background hover:bg-accent'
|
|
459
|
-
}
|
|
460
|
-
},
|
|
461
|
-
size: {
|
|
462
|
-
default: 'h-10 px-4 py-2',
|
|
463
|
-
sm: 'h-9 px-3',
|
|
464
|
-
lg: 'h-11 px-8'
|
|
465
|
-
}
|
|
466
|
-
},
|
|
467
|
-
compounds: [
|
|
468
|
-
{
|
|
469
|
-
variant: 'default',
|
|
470
|
-
size: 'lg',
|
|
471
|
-
class: 'text-base font-semibold'
|
|
472
|
-
}
|
|
473
|
-
],
|
|
474
|
-
defaults: {
|
|
475
|
-
variant: 'default',
|
|
476
|
-
size: 'default'
|
|
477
|
-
}
|
|
478
|
-
})
|
|
479
|
-
});
|
|
480
|
-
```
|
|
481
|
-
|
|
482
|
-
#### Extending Global Variants
|
|
483
|
-
|
|
484
|
-
Combine global presets with local extensions:
|
|
485
|
-
|
|
486
|
-
```svelte
|
|
487
|
-
<script lang="ts">
|
|
488
|
-
import { HtmlAtom } from '@svelte-atoms/core';
|
|
489
|
-
import { defineVariants } from '@svelte-atoms/core/utils';
|
|
490
|
-
|
|
491
|
-
// Extend preset variants with local additions
|
|
492
|
-
const extendedVariants = defineVariants({
|
|
493
|
-
variants: {
|
|
494
|
-
variant: {
|
|
495
|
-
// Add new variants not in preset
|
|
496
|
-
gradient: {
|
|
497
|
-
class: 'bg-gradient-to-r from-purple-500 to-pink-500 text-white'
|
|
498
|
-
},
|
|
499
|
-
neon: {
|
|
500
|
-
class: 'bg-black text-green-400 border-2 border-green-400'
|
|
501
|
-
}
|
|
502
|
-
},
|
|
503
|
-
// Add new variant dimension
|
|
504
|
-
animated: {
|
|
505
|
-
true: 'animate-pulse',
|
|
506
|
-
false: ''
|
|
507
|
-
}
|
|
508
|
-
},
|
|
509
|
-
defaults: {
|
|
510
|
-
animated: false
|
|
511
|
-
}
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
let { variant, size, animated, ...props } = $props();
|
|
515
|
-
</script>
|
|
516
|
-
|
|
517
|
-
<HtmlAtom
|
|
518
|
-
preset="button"
|
|
519
|
-
variants={extendedVariants}
|
|
520
|
-
as="button"
|
|
521
|
-
{variant}
|
|
522
|
-
{size}
|
|
523
|
-
{animated}
|
|
524
|
-
{...props}
|
|
525
|
-
>
|
|
526
|
-
{@render children?.()}
|
|
527
|
-
</HtmlAtom>
|
|
528
|
-
```
|
|
529
|
-
|
|
530
|
-
#### Bond-Reactive Variants
|
|
531
|
-
|
|
532
|
-
Variants can react to component state through the Bond pattern:
|
|
533
|
-
|
|
534
|
-
```typescript
|
|
535
|
-
const accordionVariants = defineVariants({
|
|
536
|
-
class: 'border rounded-md transition-all',
|
|
537
|
-
variants: {
|
|
538
|
-
state: {
|
|
539
|
-
open: (bond) => ({
|
|
540
|
-
class: bond?.state?.isOpen ? 'bg-blue-50 border-blue-200' : 'bg-white',
|
|
541
|
-
'aria-expanded': bond?.state?.isOpen,
|
|
542
|
-
'data-state': bond?.state?.isOpen ? 'open' : 'closed'
|
|
543
|
-
})
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
});
|
|
547
|
-
|
|
548
|
-
// Usage with bond
|
|
549
|
-
const bond = AccordionBond.get();
|
|
550
|
-
const variantProps = $derived(accordionVariants(bond, { state: 'open' }));
|
|
551
|
-
```
|
|
552
|
-
|
|
553
|
-
**Variant Features:**
|
|
554
|
-
|
|
555
|
-
- ✅ **Type Safety** - Automatic TypeScript inference
|
|
556
|
-
- ✅ **Compound Variants** - Apply styles when multiple conditions match
|
|
557
|
-
- ✅ **Default Values** - Specify fallback variant values
|
|
558
|
-
- ✅ **Bond Integration** - Access component state for reactive styling
|
|
559
|
-
- ✅ **Return Attributes** - Not just classes, any HTML attributes
|
|
560
|
-
- ✅ **Extensible** - Combine global presets with local variants
|
|
561
|
-
|
|
562
|
-
---
|
|
563
|
-
|
|
564
|
-
## 📖 Documentation
|
|
565
|
-
|
|
566
|
-
### Component Examples
|
|
567
|
-
|
|
568
|
-
#### Dropdown with Multiple Selection
|
|
569
|
-
|
|
570
|
-
```svelte
|
|
571
|
-
<script lang="ts">
|
|
572
|
-
import { Dropdown } from '@svelte-atoms/core';
|
|
573
|
-
|
|
574
|
-
let selectedValues = ['option1'];
|
|
575
|
-
const options = [
|
|
576
|
-
{ value: 'option1', label: 'Option 1' },
|
|
577
|
-
{ value: 'option2', label: 'Option 2' },
|
|
578
|
-
{ value: 'option3', label: 'Option 3' }
|
|
579
|
-
];
|
|
580
|
-
</script>
|
|
581
|
-
|
|
582
|
-
<Dropdown.Root multiple bind:values={selectedValues}>
|
|
583
|
-
<!-- Access internal bond -->
|
|
584
|
-
{#snippet children({ dropdown })}
|
|
585
|
-
<Dropdown.Trigger>
|
|
586
|
-
Select options ({selectedValues.length} selected)
|
|
587
|
-
</Dropdown.Trigger>
|
|
588
|
-
|
|
589
|
-
<Dropdown.Content>
|
|
590
|
-
{#each options as option}
|
|
591
|
-
<Dropdown.Item value={option.value}>
|
|
592
|
-
{option.label}
|
|
593
|
-
</Dropdown.Item>
|
|
594
|
-
{/each}
|
|
595
|
-
</Dropdown.Content>
|
|
596
|
-
{/snippet}
|
|
597
|
-
</Dropdown.Root>
|
|
598
|
-
```
|
|
599
|
-
|
|
600
|
-
#### Form with Validation
|
|
601
|
-
|
|
602
|
-
```svelte
|
|
603
|
-
<script lang="ts">
|
|
604
|
-
import { Form, Input, Button } from '@svelte-atoms/core';
|
|
605
|
-
import { z } from 'zod';
|
|
606
|
-
|
|
607
|
-
const schema = z.object({
|
|
608
|
-
email: z.string().email('Invalid email address'),
|
|
609
|
-
password: z.string().min(8, 'Password must be at least 8 characters')
|
|
610
|
-
});
|
|
611
|
-
|
|
612
|
-
let formData = { email: '', password: '' };
|
|
613
|
-
let errors = {};
|
|
614
|
-
</script>
|
|
615
|
-
|
|
616
|
-
<Form {schema} bind:value={formData} bind:errors>
|
|
617
|
-
<Field name="email">
|
|
618
|
-
<Field.Label>Email</Field.Label>
|
|
619
|
-
<Field.Control>
|
|
620
|
-
<Input.Root type="email" placeholder="Enter your email" bind:value={formData.email} />
|
|
621
|
-
</Field.Control>
|
|
622
|
-
{#if errors.email}
|
|
623
|
-
<Form.Error>{errors.email}</Form.Error>
|
|
624
|
-
{/if}
|
|
625
|
-
</.Field>
|
|
626
|
-
|
|
627
|
-
<Field name="password">
|
|
628
|
-
<Field.Label>Password</Field.Label>
|
|
629
|
-
<Field.Control>
|
|
630
|
-
<Input.Root
|
|
631
|
-
type="password"
|
|
632
|
-
placeholder="Enter your password"
|
|
633
|
-
bind:value={formData.password}
|
|
634
|
-
/>
|
|
635
|
-
</Field.Control>
|
|
636
|
-
{#if errors.password}
|
|
637
|
-
<Field.Error>{errors.password}</Field.Error>
|
|
638
|
-
{/if}
|
|
639
|
-
</.Field>
|
|
640
|
-
|
|
641
|
-
<Button type="submit">Submit</Button>
|
|
642
|
-
</Form>
|
|
643
|
-
```
|
|
644
|
-
|
|
645
|
-
#### Data Grid with Sorting and Selection
|
|
646
|
-
|
|
647
|
-
```svelte
|
|
648
|
-
<script lang="ts">
|
|
649
|
-
import { DataGrid, Checkbox } from '@svelte-atoms/core';
|
|
650
|
-
|
|
651
|
-
let data = [
|
|
652
|
-
{ id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin' },
|
|
653
|
-
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
|
|
654
|
-
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'Editor' }
|
|
655
|
-
];
|
|
656
|
-
|
|
657
|
-
let selectedRows = [];
|
|
658
|
-
</script>
|
|
659
|
-
|
|
660
|
-
<DataGrid.Root {data} bind:selectedRows multiple>
|
|
661
|
-
<DataGrid.Header>
|
|
662
|
-
<DataGrid.Tr>
|
|
663
|
-
<DataGrid.Th>
|
|
664
|
-
<Checkbox />
|
|
665
|
-
</DataGrid.Th>
|
|
666
|
-
<DataGrid.Th sortable="name">Name</DataGrid.Th>
|
|
667
|
-
<DataGrid.Th sortable="email">Email</DataGrid.Th>
|
|
668
|
-
<DataGrid.Th>Role</DataGrid.Th>
|
|
669
|
-
</DataGrid.Tr>
|
|
670
|
-
</DataGrid.Header>
|
|
671
|
-
|
|
672
|
-
<DataGrid.Body>
|
|
673
|
-
{#each data as row}
|
|
674
|
-
<DataGrid.Tr value={row.id}>
|
|
675
|
-
<DataGrid.Td>
|
|
676
|
-
<Checkbox.Root value={row.id} />
|
|
677
|
-
</DataGrid.Td>
|
|
678
|
-
<DataGrid.Td>{row.name}</DataGrid.Td>
|
|
679
|
-
<DataGrid.Td>{row.email}</DataGrid.Td>
|
|
680
|
-
<DataGrid.Td>{row.role}</DataGrid.Td>
|
|
681
|
-
</DataGrid.Tr>
|
|
682
|
-
{/each}
|
|
683
|
-
</DataGrid.Body>
|
|
684
|
-
</DataGrid.Root>
|
|
685
|
-
```
|
|
686
|
-
|
|
687
|
-
---
|
|
688
|
-
|
|
689
|
-
## 🎨 Styling
|
|
690
|
-
|
|
691
|
-
@svelte-atoms/core is completely headless, giving you full control over styling. Here are some approaches:
|
|
692
|
-
|
|
693
|
-
### Using Vanilla CSS
|
|
694
|
-
|
|
695
|
-
```css
|
|
696
|
-
/* Default button styles */
|
|
697
|
-
.btn {
|
|
698
|
-
@apply rounded-md px-4 py-2 font-medium transition-colors;
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
.btn-primary {
|
|
702
|
-
@apply bg-blue-600 text-white hover:bg-blue-700;
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
.btn-secondary {
|
|
706
|
-
@apply bg-gray-200 text-gray-900 hover:bg-gray-300;
|
|
707
|
-
}
|
|
708
|
-
```
|
|
709
|
-
|
|
710
|
-
### Using Tailwind CSS
|
|
711
|
-
|
|
712
|
-
```svelte
|
|
713
|
-
<Button class="rounded-md bg-blue-600 px-4 py-2 text-white hover:bg-blue-700">Styled Button</Button>
|
|
714
|
-
```
|
|
715
|
-
|
|
716
|
-
---
|
|
717
|
-
|
|
718
|
-
## 🧪 Development
|
|
719
|
-
|
|
720
|
-
### Setup
|
|
721
|
-
|
|
722
|
-
1. **Clone the repository:**
|
|
723
|
-
|
|
724
|
-
```bash
|
|
725
|
-
git clone https://github.com/ryu-man/svelte-atoms.git
|
|
726
|
-
cd svelte-atoms
|
|
727
|
-
```
|
|
728
|
-
|
|
729
|
-
2. **Install dependencies:**
|
|
730
|
-
|
|
731
|
-
```bash
|
|
732
|
-
bun install
|
|
733
|
-
```
|
|
734
|
-
|
|
735
|
-
3. **Start development server:**
|
|
736
|
-
|
|
737
|
-
```bash
|
|
738
|
-
bun dev
|
|
739
|
-
```
|
|
740
|
-
|
|
741
|
-
4. **Run Storybook:**
|
|
742
|
-
```bash
|
|
743
|
-
bun run storybook:dev
|
|
744
|
-
```
|
|
745
|
-
|
|
746
|
-
### Building
|
|
747
|
-
|
|
748
|
-
```bash
|
|
749
|
-
# Build library
|
|
750
|
-
bun run build
|
|
751
|
-
|
|
752
|
-
# Build Storybook
|
|
753
|
-
bun run storybook:build
|
|
754
|
-
```
|
|
755
|
-
|
|
756
|
-
---
|
|
757
|
-
|
|
758
|
-
<!-- ## 🤝 Contributing
|
|
759
|
-
|
|
760
|
-
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details. -->
|
|
761
|
-
|
|
762
|
-
<!-- ### Development Workflow
|
|
763
|
-
|
|
764
|
-
1. Fork the repository
|
|
765
|
-
2. Create a feature branch: `git checkout -b feature/amazing-feature`
|
|
766
|
-
3. Make your changes and add tests
|
|
767
|
-
4. Run the test suite: `bun test`
|
|
768
|
-
5. Commit your changes: `git commit -m 'Add amazing feature'`
|
|
769
|
-
6. Push to the branch: `git push origin feature/amazing-feature`
|
|
770
|
-
7. Open a Pull Request -->
|
|
771
|
-
|
|
772
|
-
### Creating New Components
|
|
773
|
-
|
|
774
|
-
When adding new components, follow these guidelines:
|
|
775
|
-
|
|
776
|
-
1. **Create the bond structure:**
|
|
777
|
-
|
|
778
|
-
```
|
|
779
|
-
src/lib/atoms/my-component/
|
|
780
|
-
├── bond.svelte.ts # Core bond logic (Bond + BondState classes)
|
|
781
|
-
├── index.ts # Public exports
|
|
782
|
-
├── atoms.ts # Component exports
|
|
783
|
-
├── my-component-root.svelte # Use namespace pattern when building complex component
|
|
784
|
-
├── my-component-content.svelte
|
|
785
|
-
└── README.md # Component documentation
|
|
786
|
-
```
|
|
787
|
-
|
|
788
|
-
2. **Implement accessibility features:**
|
|
789
|
-
- ARIA attributes
|
|
790
|
-
- Keyboard navigation
|
|
791
|
-
- Focus management
|
|
792
|
-
- Screen reader support
|
|
793
|
-
|
|
794
|
-
3. **Add comprehensive tests:**
|
|
795
|
-
- Unit tests for bond logic
|
|
796
|
-
- Component integration tests
|
|
797
|
-
- Accessibility tests
|
|
798
|
-
|
|
799
|
-
4. **Create Storybook stories:**
|
|
800
|
-
- Basic usage examples
|
|
801
|
-
- Advanced configurations
|
|
802
|
-
- Interactive demos
|
|
803
|
-
|
|
804
|
-
---
|
|
805
|
-
|
|
806
|
-
## 📚 Resources
|
|
807
|
-
|
|
808
|
-
- **[Documentation](https://svelte-atoms.dev)** - Comprehensive documentation
|
|
809
|
-
- **[Storybook](https://storybook.svelte-atoms.dev/)** - Interactive component documentation
|
|
810
|
-
- **[GitHub](https://github.com/ryu-man/svelte-atoms)** - Source code and issues
|
|
811
|
-
- **[@svelte-atoms/alchemist](../alchimist)** - Data visualization companion library
|
|
812
|
-
|
|
813
|
-
---
|
|
814
|
-
|
|
815
|
-
## 🗺️ Roadmap
|
|
816
|
-
|
|
817
|
-
### v1.0.0 (Current - Alpha)
|
|
818
|
-
|
|
819
|
-
- ✅ Bond architecture with Svelte 5 runes
|
|
820
|
-
- ✅ 35+ essential components
|
|
821
|
-
- ✅ TypeScript support
|
|
822
|
-
- ✅ Accessibility features
|
|
823
|
-
- ✅ Storybook documentation
|
|
824
|
-
- ✅ Standardized context pattern
|
|
825
|
-
|
|
826
|
-
---
|
|
827
|
-
|
|
828
|
-
## 📄 License
|
|
829
|
-
|
|
830
|
-
MIT License - see the [LICENSE](LICENSE) file for details.
|
|
831
|
-
|
|
832
|
-
---
|
|
833
|
-
|
|
834
|
-
## 🙏 Acknowledgements
|
|
835
|
-
|
|
836
|
-
- [Svelte](https://svelte.dev/) - The amazing framework that powers this library
|
|
837
|
-
- [Motion](https://motion.dev/) - For handling internal default animations
|
|
838
|
-
- [Floating UI](https://floating-ui.com/) - For advanced positioning logic
|
|
839
|
-
- [Tailwind CSS](https://tailwindcss.com/) - For styling
|
|
840
|
-
- [Storybook](https://storybook.js.org/) - For component documentation and testing
|
|
841
|
-
- [Vitest](https://vitest.dev/) - For fast and reliable testing
|
|
842
|
-
- [Playwright](https://playwright.dev/) - For end-to-end testing
|
|
843
|
-
|
|
844
|
-
---
|
|
845
|
-
|
|
846
|
-
<div align="center">
|
|
847
|
-
<p>Built with ❤️ by the Svelte Atoms team</p>
|
|
848
|
-
<!-- <p>
|
|
849
|
-
<a href="https://github.com/ryu-man/svelte-atoms">GitHub</a> •
|
|
850
|
-
<a href="https://svelte-atoms.dev">Documentation</a> •
|
|
851
|
-
<a href="https://storybook.svelte-atoms.dev">Storybook</a>
|
|
852
|
-
</p> -->
|
|
853
|
-
</div>
|
|
1
|
+
# @svelte-atoms/core
|
|
2
|
+
|
|
3
|
+
> A modern, modular, and accessible Svelte 5 UI component library built with composability at its core.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@svelte-atoms/core)
|
|
6
|
+
[](https://deepwiki.com/svelte-atoms/core)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Bond Architecture** - Self-contained state management with context-based communication
|
|
12
|
+
- **Accessible** - ARIA attributes, keyboard navigation, and focus management included
|
|
13
|
+
- **Type Safe** - Full TypeScript support with comprehensive type definitions
|
|
14
|
+
- **Headless** - Complete styling control with sensible defaults
|
|
15
|
+
- **Composable** - Build complex UIs by combining simple, reusable components
|
|
16
|
+
- **Svelte 5 Runes** - Optimized reactivity and performance
|
|
17
|
+
|
|
18
|
+
## Documentation
|
|
19
|
+
|
|
20
|
+
- **[Documentation](https://sacore.netlify.app/)** - Complete guides, API references, and examples
|
|
21
|
+
- **[Storybook](https://statuesque-boba-0fb888.netlify.app/)** - Interactive component playground
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @svelte-atoms/core
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
### Basic Example
|
|
32
|
+
|
|
33
|
+
```svelte
|
|
34
|
+
<script lang="ts">
|
|
35
|
+
import { Button, Dialog, Input } from '@svelte-atoms/core';
|
|
36
|
+
|
|
37
|
+
let dialogOpen = $state(false);
|
|
38
|
+
let inputValue = '';
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<Button onclick={() => (dialogOpen = true)}>Open Dialog</Button>
|
|
42
|
+
|
|
43
|
+
<Dialog.Root bind:open={dialogOpen}>
|
|
44
|
+
<Dialog.Content>
|
|
45
|
+
<Dialog.Header>
|
|
46
|
+
<Dialog.Title>Enter your name</Dialog.Title>
|
|
47
|
+
</Dialog.Header>
|
|
48
|
+
<Dialog.Body>
|
|
49
|
+
<Input.Root>
|
|
50
|
+
<Input.Control bind:value={inputValue} placeholder="Your name..." />
|
|
51
|
+
</Input.Root>
|
|
52
|
+
</Dialog.Body>
|
|
53
|
+
<Dialog.Footer>
|
|
54
|
+
<Button onclick={() => (dialogOpen = false)}>Cancel</Button>
|
|
55
|
+
<Button variant="primary" onclick={() => (dialogOpen = false)}>Confirm</Button>
|
|
56
|
+
</Dialog.Footer>
|
|
57
|
+
</Dialog.Content>
|
|
58
|
+
</Dialog.Root>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Composable Components
|
|
62
|
+
|
|
63
|
+
Combine components to create complex UIs:
|
|
64
|
+
|
|
65
|
+
```svelte
|
|
66
|
+
<script lang="ts">
|
|
67
|
+
import { Dropdown, Input, Root, filter } from '@svelte-atoms/core';
|
|
68
|
+
import { flip } from 'svelte/animate';
|
|
69
|
+
|
|
70
|
+
let data = [
|
|
71
|
+
{ id: 1, value: 'apple', text: 'Apple' },
|
|
72
|
+
{ id: 2, value: 'banana', text: 'Banana' },
|
|
73
|
+
{ id: 3, value: 'cherry', text: 'Cherry' }
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
let open = $state(false);
|
|
77
|
+
const dd = filter(
|
|
78
|
+
() => data,
|
|
79
|
+
(query, item) => item.text.toLowerCase().includes(query.toLowerCase())
|
|
80
|
+
);
|
|
81
|
+
</script>
|
|
82
|
+
|
|
83
|
+
<Dropdown.Root
|
|
84
|
+
bind:open
|
|
85
|
+
multiple
|
|
86
|
+
keys={data.map((item) => item.value)}
|
|
87
|
+
onquerychange={(q) => (dd.query = q)}
|
|
88
|
+
>
|
|
89
|
+
{#snippet children({ dropdown })}
|
|
90
|
+
<Dropdown.Trigger
|
|
91
|
+
base={Input.Root}
|
|
92
|
+
onclick={(ev) => {
|
|
93
|
+
ev.preventDefault();
|
|
94
|
+
dropdown.state.open();
|
|
95
|
+
}}
|
|
96
|
+
>
|
|
97
|
+
{#each dropdown?.state?.selectedItems ?? [] as item (item.id)}
|
|
98
|
+
<div animate:flip={{ duration: 200 }}>
|
|
99
|
+
<Dropdown.Value value={item.value}>{item.text}</Dropdown.Value>
|
|
100
|
+
</div>
|
|
101
|
+
{/each}
|
|
102
|
+
<Dropdown.Query placeholder="Search..." />
|
|
103
|
+
</Dropdown.Trigger>
|
|
104
|
+
|
|
105
|
+
<Dropdown.Content>
|
|
106
|
+
{#each dd.current as item (item.id)}
|
|
107
|
+
<div animate:flip={{ duration: 200 }}>
|
|
108
|
+
<Dropdown.Item value={item.value}>{item.text}</Dropdown.Item>
|
|
109
|
+
</div>
|
|
110
|
+
{/each}
|
|
111
|
+
</Dropdown.Content>
|
|
112
|
+
{/snippet}
|
|
113
|
+
</Dropdown.Root>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Extensibility with `as` and `base` Props
|
|
117
|
+
|
|
118
|
+
Transform components into any element or wrap them with custom logic:
|
|
119
|
+
|
|
120
|
+
```svelte
|
|
121
|
+
<script lang="ts">
|
|
122
|
+
import { Button, Popover, Input } from '@svelte-atoms/core';
|
|
123
|
+
</script>
|
|
124
|
+
|
|
125
|
+
<!-- Use Button as Popover trigger -->
|
|
126
|
+
<Popover.Root>
|
|
127
|
+
<Popover.Trigger base={Button} variant="outline">Open Popover</Popover.Trigger>
|
|
128
|
+
<Popover.Content class="p-4">
|
|
129
|
+
<h4 class="font-semibold">Settings</h4>
|
|
130
|
+
<p class="text-sm">Configure your preferences here.</p>
|
|
131
|
+
</Popover.Content>
|
|
132
|
+
</Popover.Root>
|
|
133
|
+
|
|
134
|
+
<!-- Use Input.Root as Popover trigger -->
|
|
135
|
+
<Popover.Root>
|
|
136
|
+
<Popover.Trigger base={Input.Root}>
|
|
137
|
+
<Input.Control placeholder="Click to open popover..." readonly />
|
|
138
|
+
</Popover.Trigger>
|
|
139
|
+
<Popover.Content class="p-4">
|
|
140
|
+
<p class="text-sm">Popover triggered by an input field</p>
|
|
141
|
+
</Popover.Content>
|
|
142
|
+
</Popover.Root>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Working with Animations
|
|
146
|
+
|
|
147
|
+
Seamlessly integrate with Svelte's animation system:
|
|
148
|
+
|
|
149
|
+
```svelte
|
|
150
|
+
<script lang="ts">
|
|
151
|
+
import { List, Button } from '@svelte-atoms/core';
|
|
152
|
+
import { flip } from 'svelte/animate';
|
|
153
|
+
import { slide } from 'svelte/transition';
|
|
154
|
+
|
|
155
|
+
let items = $state([
|
|
156
|
+
{ id: 1, text: 'Task 1' },
|
|
157
|
+
{ id: 2, text: 'Task 2' },
|
|
158
|
+
{ id: 3, text: 'Task 3' }
|
|
159
|
+
]);
|
|
160
|
+
|
|
161
|
+
function removeItem(id: number) {
|
|
162
|
+
items = items.filter((item) => item.id !== id);
|
|
163
|
+
}
|
|
164
|
+
</script>
|
|
165
|
+
|
|
166
|
+
<List.Root>
|
|
167
|
+
{#each items as item (item.id)}
|
|
168
|
+
<div animate:flip={{ duration: 300 }}>
|
|
169
|
+
<List.Item>
|
|
170
|
+
<span>{item.text}</span>
|
|
171
|
+
<Button size="sm" onclick={() => removeItem(item.id)}>Remove</Button>
|
|
172
|
+
</List.Item>
|
|
173
|
+
</div>
|
|
174
|
+
{/each}
|
|
175
|
+
</List.Root>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Advanced animation with lifecycle hooks using Motion:
|
|
179
|
+
|
|
180
|
+
```svelte
|
|
181
|
+
<script lang="ts">
|
|
182
|
+
import { Dialog, Button, toTransitionConfig } from '@svelte-atoms/core';
|
|
183
|
+
import { animate } from 'motion';
|
|
184
|
+
|
|
185
|
+
let open = $state(false);
|
|
186
|
+
</script>
|
|
187
|
+
|
|
188
|
+
<Button onclick={() => (open = true)}>Open Dialog</Button>
|
|
189
|
+
|
|
190
|
+
<Dialog.Root bind:open>
|
|
191
|
+
<Dialog.Overlay
|
|
192
|
+
initial={(node) => {
|
|
193
|
+
node.style.opacity = '0';
|
|
194
|
+
}}
|
|
195
|
+
enter={(node) => {
|
|
196
|
+
const duration = 0.2;
|
|
197
|
+
const animation = animate(node, { opacity: 1 }, { duration });
|
|
198
|
+
return { duration };
|
|
199
|
+
}}
|
|
200
|
+
exit={(node) => {
|
|
201
|
+
const duration = 0.1;
|
|
202
|
+
const animation = animate(node, { opacity: 0 }, { duration: 0.1 });
|
|
203
|
+
return { duration };
|
|
204
|
+
}}
|
|
205
|
+
/>
|
|
206
|
+
<Dialog.Content
|
|
207
|
+
initial={(node) => {
|
|
208
|
+
node.style.opacity = '0';
|
|
209
|
+
node.style.scale = '0.95';
|
|
210
|
+
}}
|
|
211
|
+
enter={(node) => {
|
|
212
|
+
const duration = 0.3;
|
|
213
|
+
const animation = animate(node, { opacity: 1, scale: 1 }, { duration, easing: 'ease-out' });
|
|
214
|
+
return { duration };
|
|
215
|
+
}}
|
|
216
|
+
exit={(node) => {
|
|
217
|
+
const animation = animate(node, { opacity: 0, scale: 0.95 }, { duration, easing: 'ease-in' });
|
|
218
|
+
return { duration };
|
|
219
|
+
}}
|
|
220
|
+
>
|
|
221
|
+
<Dialog.Header>
|
|
222
|
+
<Dialog.Title>Animated Dialog</Dialog.Title>
|
|
223
|
+
</Dialog.Header>
|
|
224
|
+
<Dialog.Body>
|
|
225
|
+
<p>This dialog animates with custom Motion transitions using lifecycle hooks.</p>
|
|
226
|
+
</Dialog.Body>
|
|
227
|
+
<Dialog.Footer>
|
|
228
|
+
<Button onclick={() => (open = false)}>Close</Button>
|
|
229
|
+
</Dialog.Footer>
|
|
230
|
+
</Dialog.Content>
|
|
231
|
+
</Dialog.Root>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Styling with Variants
|
|
235
|
+
|
|
236
|
+
Variants can be created locally at component level or globally in the preset configuration:
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
import { defineVariants } from '@svelte-atoms/core/utils';
|
|
240
|
+
|
|
241
|
+
const buttonVariants = defineVariants({
|
|
242
|
+
class: 'inline-flex items-center justify-center rounded-md font-medium',
|
|
243
|
+
variants: {
|
|
244
|
+
variant: {
|
|
245
|
+
primary: 'bg-blue-500 text-white hover:bg-blue-600',
|
|
246
|
+
secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300'
|
|
247
|
+
},
|
|
248
|
+
size: {
|
|
249
|
+
sm: 'h-8 px-3 text-sm',
|
|
250
|
+
md: 'h-10 px-4',
|
|
251
|
+
lg: 'h-12 px-6 text-lg'
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
defaults: {
|
|
255
|
+
variant: 'primary',
|
|
256
|
+
size: 'md'
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Development
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
# Clone repository
|
|
265
|
+
git clone https://github.com/svelte-atoms/core.git
|
|
266
|
+
cd svelte-atoms
|
|
267
|
+
|
|
268
|
+
# Install dependencies
|
|
269
|
+
bun install
|
|
270
|
+
|
|
271
|
+
# Start dev server
|
|
272
|
+
bun dev
|
|
273
|
+
|
|
274
|
+
# Run tests
|
|
275
|
+
bun test
|
|
276
|
+
|
|
277
|
+
# Build library
|
|
278
|
+
bun run build
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## License
|
|
282
|
+
|
|
283
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
<div align="center">
|
|
288
|
+
<p>Built with ❤️ by the Svelte Atoms team</p>
|
|
289
|
+
</div>
|