@spaethtech/svelte-ui 0.3.1-dev.6.5d93a69 → 0.3.1-dev.8.7f749c3
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/.claude/skills/svelte-ui/SKILL.md +116 -0
- package/.claude/skills/themes/SKILL.md +303 -0
- package/CLAUDE.md +248 -0
- package/dist/components/Card.svelte +3 -2
- package/dist/components/CardFooter.svelte +5 -4
- package/dist/components/CardHeader.svelte +5 -4
- package/docs/components.md +240 -0
- package/docs/examples.md +323 -0
- package/docs/paradigm.md +240 -0
- package/docs/themes.md +170 -0
- package/docs/usage.md +427 -0
- package/package.json +6 -2
package/docs/usage.md
ADDED
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
# Usage Examples
|
|
2
|
+
|
|
3
|
+
Common usage patterns for svelte-ui components. For the full, live gallery, run the demo and
|
|
4
|
+
browse every component at **http://localhost:5173**.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
Published to npm:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @spaethtech/svelte-ui
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
During active co-development you can instead link a local checkout with a `file:` dependency:
|
|
15
|
+
|
|
16
|
+
```jsonc
|
|
17
|
+
// package.json
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@spaethtech/svelte-ui": "file:../svelte-ui"
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Peer dependencies:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install svelte@^5 tailwindcss@^4 unplugin-icons@^23 @iconify-json/mdi@^1
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Setup
|
|
30
|
+
|
|
31
|
+
**1. Vite — add the icon resolver** (`vite.config.ts`):
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import Icons from "unplugin-icons/vite";
|
|
35
|
+
|
|
36
|
+
export default defineConfig({
|
|
37
|
+
plugins: [/* sveltekit(), tailwindcss(), */ Icons({ compiler: "svelte" })],
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**2. CSS — import Tailwind + the theme and scan the dist** (`app.css`, Tailwind v4):
|
|
42
|
+
|
|
43
|
+
```css
|
|
44
|
+
@import "tailwindcss";
|
|
45
|
+
@import "@spaethtech/svelte-ui/theme.css";
|
|
46
|
+
|
|
47
|
+
/* Generate the utility classes the components use (path relative to this file). */
|
|
48
|
+
@source "../node_modules/@spaethtech/svelte-ui/dist";
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Basic Import
|
|
52
|
+
|
|
53
|
+
```svelte
|
|
54
|
+
<script>
|
|
55
|
+
import { Button, Input, Select } from "@spaethtech/svelte-ui";
|
|
56
|
+
</script>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Icons
|
|
60
|
+
|
|
61
|
+
There is **no `Icon` component**. Import an MDI icon component from `~icons/mdi/*` and render it
|
|
62
|
+
into a component's `icon` snippet — the library sizes and colors it:
|
|
63
|
+
|
|
64
|
+
```svelte
|
|
65
|
+
<script>
|
|
66
|
+
import { Button, Input } from "@spaethtech/svelte-ui";
|
|
67
|
+
import IconHome from "~icons/mdi/home";
|
|
68
|
+
import IconSearch from "~icons/mdi/magnify";
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<Button title="Home">{#snippet icon()}<IconHome />{/snippet}</Button>
|
|
72
|
+
|
|
73
|
+
<Input placeholder="Search…">
|
|
74
|
+
{#snippet icon()}<IconSearch />{/snippet}
|
|
75
|
+
</Input>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Component Examples
|
|
79
|
+
|
|
80
|
+
### Button
|
|
81
|
+
|
|
82
|
+
```svelte
|
|
83
|
+
<script>
|
|
84
|
+
import { Button } from "@spaethtech/svelte-ui";
|
|
85
|
+
import IconContentSave from "~icons/mdi/content-save";
|
|
86
|
+
import IconPencil from "~icons/mdi/pencil";
|
|
87
|
+
import IconDelete from "~icons/mdi/delete";
|
|
88
|
+
</script>
|
|
89
|
+
|
|
90
|
+
<!-- Text button -->
|
|
91
|
+
<Button text="Click me" variant="primary" onclick={() => alert("Clicked!")} />
|
|
92
|
+
|
|
93
|
+
<!-- Button with icon -->
|
|
94
|
+
<Button text="Save" variant="success">
|
|
95
|
+
{#snippet icon()}<IconContentSave />{/snippet}
|
|
96
|
+
</Button>
|
|
97
|
+
|
|
98
|
+
<!-- Icon-only button (title → themed tooltip + aria-label) -->
|
|
99
|
+
<Button title="Edit" variant="plain" size="sm">
|
|
100
|
+
{#snippet icon()}<IconPencil />{/snippet}
|
|
101
|
+
</Button>
|
|
102
|
+
|
|
103
|
+
<!-- Button as link -->
|
|
104
|
+
<Button href="/home" text="Home" variant="ghost" />
|
|
105
|
+
|
|
106
|
+
<!-- Danger -->
|
|
107
|
+
<Button text="Delete" variant="danger">
|
|
108
|
+
{#snippet icon()}<IconDelete />{/snippet}
|
|
109
|
+
</Button>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Input
|
|
113
|
+
|
|
114
|
+
```svelte
|
|
115
|
+
<script>
|
|
116
|
+
import { Input } from "@spaethtech/svelte-ui";
|
|
117
|
+
import IconAccount from "~icons/mdi/account";
|
|
118
|
+
|
|
119
|
+
let username = $state("");
|
|
120
|
+
let validated = $state("");
|
|
121
|
+
</script>
|
|
122
|
+
|
|
123
|
+
<!-- Basic -->
|
|
124
|
+
<Input bind:value={username} placeholder="Username" />
|
|
125
|
+
|
|
126
|
+
<!-- With a leading icon -->
|
|
127
|
+
<Input bind:value={username} placeholder="Enter username" iconPosition="left">
|
|
128
|
+
{#snippet icon()}<IconAccount />{/snippet}
|
|
129
|
+
</Input>
|
|
130
|
+
|
|
131
|
+
<!-- With validation -->
|
|
132
|
+
<Input
|
|
133
|
+
bind:value={validated}
|
|
134
|
+
validate={(v) => (v.length >= 3 ? true : "Minimum 3 characters")}
|
|
135
|
+
placeholder="Validated input"
|
|
136
|
+
/>
|
|
137
|
+
|
|
138
|
+
<!-- Clickable trailing icon -->
|
|
139
|
+
<Input bind:value={username} iconClickable onIconClick={() => console.log("clicked")}>
|
|
140
|
+
{#snippet icon()}<IconAccount />{/snippet}
|
|
141
|
+
</Input>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Select
|
|
145
|
+
|
|
146
|
+
```svelte
|
|
147
|
+
<script>
|
|
148
|
+
import { Select } from "@spaethtech/svelte-ui";
|
|
149
|
+
|
|
150
|
+
let selected = $state("");
|
|
151
|
+
const options = [
|
|
152
|
+
{ value: "1", label: "Option 1" },
|
|
153
|
+
{ value: "2", label: "Option 2" },
|
|
154
|
+
{ value: "3", label: "Option 3" },
|
|
155
|
+
];
|
|
156
|
+
</script>
|
|
157
|
+
|
|
158
|
+
<!-- Static select -->
|
|
159
|
+
<Select bind:value={selected} {options} placeholder="Choose option" />
|
|
160
|
+
|
|
161
|
+
<!-- API-driven select with search -->
|
|
162
|
+
<Select
|
|
163
|
+
bind:value={selected}
|
|
164
|
+
url="/api/items"
|
|
165
|
+
searchParam="q"
|
|
166
|
+
placeholder="Search items..."
|
|
167
|
+
transform={(data) => data.results.map((r) => ({ value: r.id, label: r.name }))}
|
|
168
|
+
/>
|
|
169
|
+
|
|
170
|
+
<!-- Custom item rendering -->
|
|
171
|
+
<Select bind:value={selected} {options}>
|
|
172
|
+
{#snippet itemSnippet(option)}
|
|
173
|
+
<div class="flex items-center gap-2">
|
|
174
|
+
<span class="font-bold">{option.value}</span>
|
|
175
|
+
<span>{option.label}</span>
|
|
176
|
+
</div>
|
|
177
|
+
{/snippet}
|
|
178
|
+
</Select>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### List
|
|
182
|
+
|
|
183
|
+
```svelte
|
|
184
|
+
<script>
|
|
185
|
+
import { List } from "@spaethtech/svelte-ui";
|
|
186
|
+
|
|
187
|
+
let selected = $state([]);
|
|
188
|
+
const options = [
|
|
189
|
+
{ value: "1", label: "Item 1" },
|
|
190
|
+
{ value: "2", label: "Item 2" },
|
|
191
|
+
{ value: "3", label: "Item 3" },
|
|
192
|
+
];
|
|
193
|
+
</script>
|
|
194
|
+
|
|
195
|
+
<!-- Single select -->
|
|
196
|
+
<List bind:value={selected} {options} />
|
|
197
|
+
|
|
198
|
+
<!-- Multi-select with checkboxes -->
|
|
199
|
+
<List bind:value={selected} {options} multiSelect showCheckboxes />
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Checkbox
|
|
203
|
+
|
|
204
|
+
```svelte
|
|
205
|
+
<script>
|
|
206
|
+
import { Checkbox } from "@spaethtech/svelte-ui";
|
|
207
|
+
let checked = $state(false);
|
|
208
|
+
</script>
|
|
209
|
+
|
|
210
|
+
<Checkbox bind:checked />
|
|
211
|
+
<Checkbox checked indeterminate />
|
|
212
|
+
<Checkbox disabled />
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Dialog
|
|
216
|
+
|
|
217
|
+
```svelte
|
|
218
|
+
<script>
|
|
219
|
+
import { Dialog, Button, Input } from "@spaethtech/svelte-ui";
|
|
220
|
+
|
|
221
|
+
let showDialog = $state(false);
|
|
222
|
+
let showDanger = $state(false);
|
|
223
|
+
</script>
|
|
224
|
+
|
|
225
|
+
<!-- Basic confirmation -->
|
|
226
|
+
<Button text="Show Dialog" onclick={() => (showDialog = true)} />
|
|
227
|
+
<Dialog
|
|
228
|
+
bind:isOpen={showDialog}
|
|
229
|
+
title="Confirm Action"
|
|
230
|
+
message="Are you sure you want to proceed?"
|
|
231
|
+
onConfirm={() => console.log("Confirmed")}
|
|
232
|
+
/>
|
|
233
|
+
|
|
234
|
+
<!-- Danger variant -->
|
|
235
|
+
<Dialog
|
|
236
|
+
bind:isOpen={showDanger}
|
|
237
|
+
title="Delete Item"
|
|
238
|
+
message="This action cannot be undone!"
|
|
239
|
+
variant="danger"
|
|
240
|
+
confirmText="Delete"
|
|
241
|
+
onConfirm={() => console.log("Deleted")}
|
|
242
|
+
/>
|
|
243
|
+
|
|
244
|
+
<!-- Custom content -->
|
|
245
|
+
<Dialog bind:isOpen={showDialog} title="Custom Content">
|
|
246
|
+
<div class="space-y-2">
|
|
247
|
+
<p>This is custom content</p>
|
|
248
|
+
<Input placeholder="Enter value" />
|
|
249
|
+
</div>
|
|
250
|
+
</Dialog>
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Badge
|
|
254
|
+
|
|
255
|
+
```svelte
|
|
256
|
+
<script>
|
|
257
|
+
import { Badge } from "@spaethtech/svelte-ui";
|
|
258
|
+
import IconCheck from "~icons/mdi/check";
|
|
259
|
+
|
|
260
|
+
let showBadge = $state(true);
|
|
261
|
+
</script>
|
|
262
|
+
|
|
263
|
+
<!-- Basic badges -->
|
|
264
|
+
<Badge text="New" variant="primary" />
|
|
265
|
+
<Badge text="Beta" variant="warning" size="sm" />
|
|
266
|
+
<Badge text="v1.0.0" variant="success" size="lg" />
|
|
267
|
+
|
|
268
|
+
<!-- Dismissible -->
|
|
269
|
+
{#if showBadge}
|
|
270
|
+
<Badge text="Alert" variant="danger" dismissible on:dismiss={() => (showBadge = false)} />
|
|
271
|
+
{/if}
|
|
272
|
+
|
|
273
|
+
<!-- Custom content -->
|
|
274
|
+
<Badge variant="success">
|
|
275
|
+
<IconCheck />
|
|
276
|
+
<span>Verified</span>
|
|
277
|
+
</Badge>
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### TextArea
|
|
281
|
+
|
|
282
|
+
```svelte
|
|
283
|
+
<script>
|
|
284
|
+
import { TextArea } from "@spaethtech/svelte-ui";
|
|
285
|
+
|
|
286
|
+
let notes = $state("");
|
|
287
|
+
let code = $state("");
|
|
288
|
+
</script>
|
|
289
|
+
|
|
290
|
+
<!-- Auto-resizing -->
|
|
291
|
+
<TextArea bind:value={notes} placeholder="Enter notes..." minRows={3} maxRows={10} />
|
|
292
|
+
|
|
293
|
+
<!-- Fixed height -->
|
|
294
|
+
<TextArea bind:value={code} minRows={5} maxRows={5} placeholder="Paste code here..." />
|
|
295
|
+
|
|
296
|
+
<!-- Read-only with copy control -->
|
|
297
|
+
<TextArea value="Read-only content" readonly allowCopy />
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Specialized Inputs
|
|
301
|
+
|
|
302
|
+
```svelte
|
|
303
|
+
<script>
|
|
304
|
+
import { PasswordInput, EmailInput, SearchInput, NumberInput } from "@spaethtech/svelte-ui";
|
|
305
|
+
|
|
306
|
+
let password = $state("");
|
|
307
|
+
let email = $state("");
|
|
308
|
+
let search = $state("");
|
|
309
|
+
let quantity = $state(1);
|
|
310
|
+
</script>
|
|
311
|
+
|
|
312
|
+
<PasswordInput bind:value={password} placeholder="Enter password" />
|
|
313
|
+
<EmailInput bind:value={email} required placeholder="user@example.com" />
|
|
314
|
+
<SearchInput bind:value={search} placeholder="Search..." />
|
|
315
|
+
<NumberInput bind:value={quantity} min={1} max={100} />
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Tooltips (use:tooltip action)
|
|
319
|
+
|
|
320
|
+
There is no `Tooltip` component — tooltips are a Svelte action built on the `anchored` engine.
|
|
321
|
+
|
|
322
|
+
```svelte
|
|
323
|
+
<script>
|
|
324
|
+
import { Button, tooltip } from "@spaethtech/svelte-ui";
|
|
325
|
+
</script>
|
|
326
|
+
|
|
327
|
+
<!-- Button drives the action from its `title` automatically -->
|
|
328
|
+
<Button title="Delete" variant="danger" />
|
|
329
|
+
|
|
330
|
+
<!-- Apply the action directly to any element -->
|
|
331
|
+
<span use:tooltip={{ text: "Any element", side: "top" }}>hover me</span>
|
|
332
|
+
|
|
333
|
+
<!-- Falls back to the element's native title (which it then suppresses) -->
|
|
334
|
+
<a href="/help" title="Get help" use:tooltip>?</a>
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Popup & Menu
|
|
338
|
+
|
|
339
|
+
```svelte
|
|
340
|
+
<script>
|
|
341
|
+
import { Popup, Menu, Button } from "@spaethtech/svelte-ui";
|
|
342
|
+
import IconPencil from "~icons/mdi/pencil";
|
|
343
|
+
import IconDelete from "~icons/mdi/delete";
|
|
344
|
+
|
|
345
|
+
let trigger = $state();
|
|
346
|
+
let popupOpen = $state(false);
|
|
347
|
+
let menuOpen = $state(false);
|
|
348
|
+
|
|
349
|
+
const items = [
|
|
350
|
+
{ label: "Edit", icon: IconPencil, onclick: () => console.log("edit") },
|
|
351
|
+
{ label: "Delete", icon: IconDelete, onclick: () => console.log("delete") },
|
|
352
|
+
];
|
|
353
|
+
</script>
|
|
354
|
+
|
|
355
|
+
<!-- Popover (arbitrary content) -->
|
|
356
|
+
<Button bind:element={trigger} text="Open" onclick={() => (popupOpen = !popupOpen)} />
|
|
357
|
+
<Popup anchor={trigger} bind:open={popupOpen} side="bottom" align="start">
|
|
358
|
+
<div class="p-3">…your content…</div>
|
|
359
|
+
</Popup>
|
|
360
|
+
|
|
361
|
+
<!-- Action menu -->
|
|
362
|
+
<Button bind:element={trigger} text="Actions" onclick={() => (menuOpen = !menuOpen)} />
|
|
363
|
+
<Menu anchor={trigger} bind:open={menuOpen} {items} align="end" />
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### DataTable & Query (@spaethtech/svelte-ui/data)
|
|
367
|
+
|
|
368
|
+
The data components are driven by the headless layer. Build a grid with `createGrid` and a filter
|
|
369
|
+
bar with `Query` over a `DataSet`.
|
|
370
|
+
|
|
371
|
+
```svelte
|
|
372
|
+
<script>
|
|
373
|
+
import { DataTable, Query } from "@spaethtech/svelte-ui";
|
|
374
|
+
import { createGrid } from "@spaethtech/svelte-ui/data";
|
|
375
|
+
import IconPencil from "~icons/mdi/pencil";
|
|
376
|
+
|
|
377
|
+
const grid = createGrid({
|
|
378
|
+
columns: [
|
|
379
|
+
{ id: "name", label: "Name" },
|
|
380
|
+
{ id: "email", label: "Email" },
|
|
381
|
+
{ id: "status", label: "Status", sortKey: "status" },
|
|
382
|
+
],
|
|
383
|
+
fetch: (request) => /* a reactive query returning { rows, total } */ myQuery(request),
|
|
384
|
+
});
|
|
385
|
+
</script>
|
|
386
|
+
|
|
387
|
+
<DataTable
|
|
388
|
+
{grid}
|
|
389
|
+
selectable
|
|
390
|
+
rowActions={(row) => [{ label: "Edit", icon: IconPencil, onclick: () => edit(row) }]}
|
|
391
|
+
/>
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
See the live demo for the full DataTable + Query walkthrough — http://localhost:5173.
|
|
395
|
+
|
|
396
|
+
## Theme Integration
|
|
397
|
+
|
|
398
|
+
### Using CSS Variables
|
|
399
|
+
|
|
400
|
+
```svelte
|
|
401
|
+
<style>
|
|
402
|
+
.custom-component {
|
|
403
|
+
background: var(--ui-color-background);
|
|
404
|
+
color: var(--ui-color-text);
|
|
405
|
+
height: var(--ui-height);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.custom-button {
|
|
409
|
+
background: var(--ui-color-primary);
|
|
410
|
+
&:hover {
|
|
411
|
+
background: color-mix(in srgb, var(--ui-color-primary), black 12%);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
/* For neutral surfaces (rows, ghost buttons, section bands) use the shared tints instead:
|
|
415
|
+
var(--ui-color-hover) / var(--ui-color-surface) / var(--ui-color-active). */
|
|
416
|
+
</style>
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Theme Selector
|
|
420
|
+
|
|
421
|
+
```svelte
|
|
422
|
+
<script>
|
|
423
|
+
import { ThemeSelector } from "@spaethtech/svelte-ui";
|
|
424
|
+
</script>
|
|
425
|
+
|
|
426
|
+
<ThemeSelector />
|
|
427
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spaethtech/svelte-ui",
|
|
3
|
-
"version": "0.3.1-dev.
|
|
3
|
+
"version": "0.3.1-dev.8.7f749c3",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/spaethtech/svelte-ui.git"
|
|
@@ -33,7 +33,11 @@
|
|
|
33
33
|
"files": [
|
|
34
34
|
"dist",
|
|
35
35
|
"!dist/**/*.test.*",
|
|
36
|
-
"!dist/**/*.spec.*"
|
|
36
|
+
"!dist/**/*.spec.*",
|
|
37
|
+
"CLAUDE.md",
|
|
38
|
+
"docs",
|
|
39
|
+
".claude/skills/themes",
|
|
40
|
+
".claude/skills/svelte-ui"
|
|
37
41
|
],
|
|
38
42
|
"sideEffects": [
|
|
39
43
|
"**/*.css"
|