compote-ui 0.62.0 → 0.62.2
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/README.md +6 -0
- package/dist/components/password-input/password-input.svelte +2 -0
- package/dist/components/password-input/password-input.svelte.d.ts +1 -1
- package/dist/components/password-input/types.d.ts +1 -0
- package/package.json +13 -3
- package/skills/component-usage/SKILL.md +202 -0
- package/skills/component-usage/references/buttons.md +27 -0
- package/skills/component-usage/references/component-export-map.md +54 -0
- package/skills/component-usage/references/files-and-images.md +75 -0
- package/skills/component-usage/references/forms-and-inputs.md +94 -0
- package/skills/component-usage/references/interactive-components.md +33 -0
- package/skills/component-usage/references/layout-and-display.md +82 -0
- package/skills/component-usage/references/overlays-and-floating-ui.md +80 -0
- package/skills/data-table/SKILL.md +247 -0
- package/skills/data-table/references/column-options.md +53 -0
- package/skills/data-table/references/table-state-and-reactivity.md +45 -0
- package/skills/data-table/references/virtual-table.md +44 -0
- package/skills/getting-started/SKILL.md +173 -0
- package/skills/getting-started/references/install-and-peers.md +45 -0
- package/skills/production-checklist/SKILL.md +181 -0
- package/skills/production-checklist/references/production-audit.md +40 -0
- package/skills/theming/SKILL.md +147 -0
- package/skills/theming/references/theme-tokens.md +56 -0
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { PhEye, PhEyeSlash } from '../../icons';
|
|
6
6
|
|
|
7
7
|
let {
|
|
8
|
+
value = $bindable(),
|
|
8
9
|
label,
|
|
9
10
|
layout = 'vertical',
|
|
10
11
|
placeholder,
|
|
@@ -29,6 +30,7 @@
|
|
|
29
30
|
<PasswordInput.Control class="relative isolate">
|
|
30
31
|
<PasswordInput.Input
|
|
31
32
|
{placeholder}
|
|
33
|
+
bind:value
|
|
32
34
|
class="h-9 w-full rounded-md border bg-surface-1 px-3 pr-9 text-sm shadow-sm placeholder:text-ink-dim focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-none data-invalid:border-danger data-invalid:focus-visible:ring-danger"
|
|
33
35
|
/>
|
|
34
36
|
<PasswordInput.VisibilityTrigger
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PasswordInput } from '@ark-ui/svelte/password-input';
|
|
2
2
|
import type { PasswordInputProps } from './types';
|
|
3
|
-
declare const PasswordInput: import("svelte").Component<PasswordInputProps, {}, "">;
|
|
3
|
+
declare const PasswordInput: import("svelte").Component<PasswordInputProps, {}, "value">;
|
|
4
4
|
type PasswordInput = ReturnType<typeof PasswordInput>;
|
|
5
5
|
export default PasswordInput;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "compote-ui",
|
|
3
|
-
"version": "0.62.
|
|
3
|
+
"version": "0.62.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite dev --open",
|
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
"files": [
|
|
20
20
|
"dist",
|
|
21
21
|
"!dist/**/*.test.*",
|
|
22
|
-
"!dist/**/*.spec.*"
|
|
22
|
+
"!dist/**/*.spec.*",
|
|
23
|
+
"skills",
|
|
24
|
+
"!skills/_artifacts"
|
|
23
25
|
],
|
|
24
26
|
"sideEffects": [
|
|
25
27
|
"**/*.css"
|
|
@@ -62,6 +64,7 @@
|
|
|
62
64
|
"@sveltejs/package": "^2.5.8",
|
|
63
65
|
"@sveltejs/vite-plugin-svelte": "7.1.2",
|
|
64
66
|
"@tailwindcss/vite": "^4.3.1",
|
|
67
|
+
"@tanstack/intent": "^0.3.2",
|
|
65
68
|
"@tanstack/svelte-table": "^9.0.0-beta.17",
|
|
66
69
|
"@tanstack/svelte-virtual": "^3.13.29",
|
|
67
70
|
"@types/node": "^22.20.0",
|
|
@@ -83,7 +86,8 @@
|
|
|
83
86
|
"vite": "^8.0.16"
|
|
84
87
|
},
|
|
85
88
|
"keywords": [
|
|
86
|
-
"svelte"
|
|
89
|
+
"svelte",
|
|
90
|
+
"tanstack-intent"
|
|
87
91
|
],
|
|
88
92
|
"dependencies": {
|
|
89
93
|
"@ark-ui/svelte": "^5.22.1",
|
|
@@ -93,5 +97,11 @@
|
|
|
93
97
|
"svelte-tel-input": "^4.2.0",
|
|
94
98
|
"tailwind-merge": "^3.6.0",
|
|
95
99
|
"tailwind-variants": "^3.2.2"
|
|
100
|
+
},
|
|
101
|
+
"intent": {
|
|
102
|
+
"skills": [
|
|
103
|
+
"compote-ui",
|
|
104
|
+
"@tanstack/svelte-table"
|
|
105
|
+
]
|
|
96
106
|
}
|
|
97
107
|
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: component-usage
|
|
3
|
+
description: >
|
|
4
|
+
Load when choosing or using compote-ui components and utilities from the root
|
|
5
|
+
package. Covers public exports, single components vs namespaced compound
|
|
6
|
+
components, forms, overlays, layout/display components, files/images, buttons,
|
|
7
|
+
collections, and common component usage mistakes.
|
|
8
|
+
metadata:
|
|
9
|
+
type: core
|
|
10
|
+
library: compote-ui
|
|
11
|
+
library_version: '0.62.1'
|
|
12
|
+
sources:
|
|
13
|
+
- src/lib/index.ts
|
|
14
|
+
- src/lib/utils/collections.ts
|
|
15
|
+
- src/lib/components/button/button.svelte
|
|
16
|
+
- src/lib/components/field/field.svelte
|
|
17
|
+
- src/lib/components/select/select.svelte
|
|
18
|
+
- src/lib/components/dialog/dialog-root.svelte
|
|
19
|
+
- src/lib/components/file-upload/file-upload.svelte
|
|
20
|
+
- src/lib/components/image-cropper/image-cropper.svelte
|
|
21
|
+
- src/lib/utils/image-processing.ts
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# Compote UI — Component Usage
|
|
25
|
+
|
|
26
|
+
Use root exports from `compote-ui` for ordinary components and namespaces for compound component families.
|
|
27
|
+
|
|
28
|
+
## Setup
|
|
29
|
+
|
|
30
|
+
```svelte
|
|
31
|
+
<script lang="ts">
|
|
32
|
+
import { Button, Field, Select, Dialog, createListCollection } from 'compote-ui';
|
|
33
|
+
|
|
34
|
+
const fruits = [
|
|
35
|
+
{ value: 'apple', label: 'Apple' },
|
|
36
|
+
{ value: 'banana', label: 'Banana', disabled: true }
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
let name = $state('');
|
|
40
|
+
let fruit = $state<string | null>(null);
|
|
41
|
+
let open = $state(false);
|
|
42
|
+
const collection = createListCollection(fruits);
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<Field.Root required>
|
|
46
|
+
<Field.Label>Name</Field.Label>
|
|
47
|
+
<Field.Input bind:value={name} />
|
|
48
|
+
</Field.Root>
|
|
49
|
+
|
|
50
|
+
<Select items={fruits} label="Fruit" bind:value={fruit} />
|
|
51
|
+
|
|
52
|
+
<Button onclick={() => (open = true)}>Open</Button>
|
|
53
|
+
<Dialog.Root bind:open>
|
|
54
|
+
<Dialog.Title>Selected fruit</Dialog.Title>
|
|
55
|
+
<p>{fruit}</p>
|
|
56
|
+
</Dialog.Root>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Core Patterns
|
|
60
|
+
|
|
61
|
+
### Import compound components as namespaces
|
|
62
|
+
|
|
63
|
+
```svelte
|
|
64
|
+
<script lang="ts">
|
|
65
|
+
import { Card, Tabs, Menu } from 'compote-ui';
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<Card.Root>
|
|
69
|
+
<Card.Header>
|
|
70
|
+
<Card.Title>Summary</Card.Title>
|
|
71
|
+
</Card.Header>
|
|
72
|
+
<Card.Content>
|
|
73
|
+
<Tabs.Root defaultValue="one">
|
|
74
|
+
<Tabs.List>
|
|
75
|
+
<Tabs.Trigger value="one">One</Tabs.Trigger>
|
|
76
|
+
</Tabs.List>
|
|
77
|
+
<Tabs.Content value="one">Content</Tabs.Content>
|
|
78
|
+
</Tabs.Root>
|
|
79
|
+
</Card.Content>
|
|
80
|
+
</Card.Root>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Use item shapes expected by collections
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import { createListCollection, createTreeCollection } from 'compote-ui';
|
|
87
|
+
|
|
88
|
+
const list = createListCollection([
|
|
89
|
+
{ value: 1, label: 'One' },
|
|
90
|
+
{ value: 2, label: 'Two', disabled: true }
|
|
91
|
+
]);
|
|
92
|
+
|
|
93
|
+
const tree = createTreeCollection([
|
|
94
|
+
{ value: 'src', label: 'src', children: [{ value: 'app', label: 'App.svelte' }] }
|
|
95
|
+
]);
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Prefer wrappers before raw Ark UI primitives
|
|
99
|
+
|
|
100
|
+
```svelte
|
|
101
|
+
<script lang="ts">
|
|
102
|
+
import { Drawer } from 'compote-ui';
|
|
103
|
+
</script>
|
|
104
|
+
|
|
105
|
+
<Drawer.Root>
|
|
106
|
+
<Drawer.Trigger>Open</Drawer.Trigger>
|
|
107
|
+
<Drawer.Content>
|
|
108
|
+
<Drawer.Header>
|
|
109
|
+
<Drawer.Title>Edit record</Drawer.Title>
|
|
110
|
+
</Drawer.Header>
|
|
111
|
+
<Drawer.Body>Content</Drawer.Body>
|
|
112
|
+
</Drawer.Content>
|
|
113
|
+
</Drawer.Root>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Use Ark UI directly only for advanced cases the wrapper does not model, such as nested splitters.
|
|
117
|
+
|
|
118
|
+
## Common Mistakes
|
|
119
|
+
|
|
120
|
+
### HIGH Importing compound parts as root exports
|
|
121
|
+
|
|
122
|
+
Wrong:
|
|
123
|
+
|
|
124
|
+
```svelte
|
|
125
|
+
<script lang="ts">
|
|
126
|
+
import { DialogRoot, DialogTitle } from 'compote-ui';
|
|
127
|
+
</script>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Correct:
|
|
131
|
+
|
|
132
|
+
```svelte
|
|
133
|
+
<script lang="ts">
|
|
134
|
+
import { Dialog } from 'compote-ui';
|
|
135
|
+
</script>
|
|
136
|
+
|
|
137
|
+
<Dialog.Root>
|
|
138
|
+
<Dialog.Title>Title</Dialog.Title>
|
|
139
|
+
</Dialog.Root>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Compound components are exported as namespaces.
|
|
143
|
+
|
|
144
|
+
Source: `src/lib/index.ts`
|
|
145
|
+
|
|
146
|
+
### HIGH Using arbitrary item shapes
|
|
147
|
+
|
|
148
|
+
Wrong:
|
|
149
|
+
|
|
150
|
+
```svelte
|
|
151
|
+
<script lang="ts">
|
|
152
|
+
const items = [{ id: 1, name: 'One' }];
|
|
153
|
+
</script>
|
|
154
|
+
|
|
155
|
+
<Select {items} bind:value />
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Correct:
|
|
159
|
+
|
|
160
|
+
```svelte
|
|
161
|
+
<script lang="ts">
|
|
162
|
+
const items = [{ value: 1, label: 'One' }];
|
|
163
|
+
</script>
|
|
164
|
+
|
|
165
|
+
<Select {items} bind:value />
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
List-based components and collection helpers derive value and display text from `value` and `label`.
|
|
169
|
+
|
|
170
|
+
Source: `src/lib/utils/collections.ts`, `src/lib/components/select/select.svelte`
|
|
171
|
+
|
|
172
|
+
### MEDIUM Icon-only button without label
|
|
173
|
+
|
|
174
|
+
Wrong:
|
|
175
|
+
|
|
176
|
+
```svelte
|
|
177
|
+
<Button size="icon">
|
|
178
|
+
<SettingsIcon />
|
|
179
|
+
</Button>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Correct:
|
|
183
|
+
|
|
184
|
+
```svelte
|
|
185
|
+
<Button size="icon" aria-label="Settings">
|
|
186
|
+
<SettingsIcon class="size-4" />
|
|
187
|
+
</Button>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Icon-only controls need an accessible name.
|
|
191
|
+
|
|
192
|
+
Source: `C:/Users/tihom/.claude/skills/compote-ui/references/button.md`
|
|
193
|
+
|
|
194
|
+
## References
|
|
195
|
+
|
|
196
|
+
- [Buttons](references/buttons.md)
|
|
197
|
+
- [Forms and inputs](references/forms-and-inputs.md)
|
|
198
|
+
- [Overlays and floating UI](references/overlays-and-floating-ui.md)
|
|
199
|
+
- [Layout and display](references/layout-and-display.md)
|
|
200
|
+
- [Files and images](references/files-and-images.md)
|
|
201
|
+
- [Interactive components](references/interactive-components.md)
|
|
202
|
+
- [Component export map](references/component-export-map.md)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Buttons
|
|
2
|
+
|
|
3
|
+
```svelte
|
|
4
|
+
<Button>Default</Button>
|
|
5
|
+
<Button variant="outline">Outline</Button>
|
|
6
|
+
<Button variant="ghost">Ghost</Button>
|
|
7
|
+
<Button size="sm">Small</Button>
|
|
8
|
+
<Button size="lg">Large</Button>
|
|
9
|
+
<Button size="icon" aria-label="Settings">
|
|
10
|
+
<SettingsIcon class="size-4" />
|
|
11
|
+
</Button>
|
|
12
|
+
<Button disabled>Disabled</Button>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`Button` props:
|
|
16
|
+
|
|
17
|
+
- `variant?: 'default' | 'outline' | 'ghost'`
|
|
18
|
+
- `size?: 'sm' | 'default' | 'lg' | 'icon' | 'icon-sm' | 'icon-lg'`
|
|
19
|
+
- `class?: ClassValue`
|
|
20
|
+
- standard button attributes such as `onclick`, `disabled`, `type`
|
|
21
|
+
|
|
22
|
+
Use `LinkButton` for navigation links that should look like buttons:
|
|
23
|
+
|
|
24
|
+
```svelte
|
|
25
|
+
<LinkButton href="/dashboard">Dashboard</LinkButton>
|
|
26
|
+
<LinkButton href="https://example.com" variant="outline" target="_blank">External</LinkButton>
|
|
27
|
+
```
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Component Export Map
|
|
2
|
+
|
|
3
|
+
Root exports:
|
|
4
|
+
|
|
5
|
+
- `Avatar`
|
|
6
|
+
- `Button`, `LinkButton`
|
|
7
|
+
- `Checkbox`, `CheckboxGroup`
|
|
8
|
+
- `Combobox`
|
|
9
|
+
- `DateField`, `DateRangeField`, `DateInput`, `DatePicker`
|
|
10
|
+
- `AlertDialog`
|
|
11
|
+
- `FileUploadDropzone`, `FileUpload`
|
|
12
|
+
- `ImageCropper`, `ImageCropDialog`
|
|
13
|
+
- `JsonTreeView`
|
|
14
|
+
- `NumberInput`, `PasswordInput`, `PhoneInput`
|
|
15
|
+
- `QrCode`
|
|
16
|
+
- `Select`
|
|
17
|
+
- `Splitter`
|
|
18
|
+
- `Switch`
|
|
19
|
+
- `Toggle`
|
|
20
|
+
- `TreeView`
|
|
21
|
+
- `LocaleProvider`, `useLocaleContext`
|
|
22
|
+
- `Portal`
|
|
23
|
+
- `PersistedState`, `Debounced`
|
|
24
|
+
- `cn`
|
|
25
|
+
|
|
26
|
+
Namespace exports:
|
|
27
|
+
|
|
28
|
+
- `Card`
|
|
29
|
+
- `Collapsible`
|
|
30
|
+
- `HoverCard`
|
|
31
|
+
- `ScrollArea`
|
|
32
|
+
- `Carousel`
|
|
33
|
+
- `Dialog`
|
|
34
|
+
- `DataTable`
|
|
35
|
+
- `VirtualDataTable`
|
|
36
|
+
- `Drawer`
|
|
37
|
+
- `Listbox`
|
|
38
|
+
- `Popover`
|
|
39
|
+
- `Tabs`
|
|
40
|
+
- `Toast`
|
|
41
|
+
- `ToggleGroup`
|
|
42
|
+
- `Menu`
|
|
43
|
+
- `Tooltip`
|
|
44
|
+
- `Field`
|
|
45
|
+
- `Fieldset`
|
|
46
|
+
|
|
47
|
+
Utility exports:
|
|
48
|
+
|
|
49
|
+
- `loadImage`
|
|
50
|
+
- `fileToDataUrl`
|
|
51
|
+
- `cropImage`
|
|
52
|
+
- `processImage`
|
|
53
|
+
- `createListCollection`
|
|
54
|
+
- `createTreeCollection`
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Files And Images
|
|
2
|
+
|
|
3
|
+
Use `ImageCropDialog` for the complete upload/crop flow.
|
|
4
|
+
|
|
5
|
+
```svelte
|
|
6
|
+
<script lang="ts">
|
|
7
|
+
import { FileUploadDropzone, ImageCropDialog, fileToDataUrl } from 'compote-ui';
|
|
8
|
+
|
|
9
|
+
let cropOpen = $state(false);
|
|
10
|
+
let imageSrc = $state('');
|
|
11
|
+
let previewUrl = $state<string | undefined>();
|
|
12
|
+
|
|
13
|
+
async function handleFile(file: File) {
|
|
14
|
+
imageSrc = await fileToDataUrl(file);
|
|
15
|
+
cropOpen = true;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function handleConfirm(blob: Blob) {
|
|
19
|
+
cropOpen = false;
|
|
20
|
+
if (previewUrl) URL.revokeObjectURL(previewUrl);
|
|
21
|
+
previewUrl = URL.createObjectURL(blob);
|
|
22
|
+
}
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<FileUploadDropzone
|
|
26
|
+
fileType="image"
|
|
27
|
+
onFileAccept={(details) => {
|
|
28
|
+
if (details.files[0]) handleFile(details.files[0]);
|
|
29
|
+
}}
|
|
30
|
+
/>
|
|
31
|
+
|
|
32
|
+
<ImageCropDialog
|
|
33
|
+
bind:open={cropOpen}
|
|
34
|
+
{imageSrc}
|
|
35
|
+
onConfirm={handleConfirm}
|
|
36
|
+
onCancel={() => (cropOpen = false)}
|
|
37
|
+
/>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Image utilities are browser-only:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { fileToDataUrl, processImage, cropImage } from 'compote-ui';
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`ProcessImageOptions`:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
type ProcessImageOptions = {
|
|
50
|
+
maxWidth?: number;
|
|
51
|
+
maxHeight?: number;
|
|
52
|
+
quality?: number;
|
|
53
|
+
format?: 'image/webp' | 'image/jpeg' | 'image/png';
|
|
54
|
+
trim?: boolean;
|
|
55
|
+
trimThreshold?: number;
|
|
56
|
+
};
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Prefer `getProcessedImage` over Ark UI display-resolution crop output:
|
|
60
|
+
|
|
61
|
+
```svelte
|
|
62
|
+
<script lang="ts">
|
|
63
|
+
let getProcessedImage = $state<((opts?: ProcessImageOptions) => Promise<Blob>) | undefined>();
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<ImageCropper src={imageSrc} bind:getProcessedImage aspectRatio={1} />
|
|
67
|
+
|
|
68
|
+
<Button
|
|
69
|
+
onclick={async () => {
|
|
70
|
+
const blob = await getProcessedImage?.({ maxWidth: 1200 });
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
Save
|
|
74
|
+
</Button>
|
|
75
|
+
```
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Forms And Inputs
|
|
2
|
+
|
|
3
|
+
Use `Field.Root` to propagate `invalid`, `disabled`, `required`, and `readOnly` state.
|
|
4
|
+
|
|
5
|
+
```svelte
|
|
6
|
+
<Field.Root required invalid={!email}>
|
|
7
|
+
<Field.Label>Email</Field.Label>
|
|
8
|
+
<Field.Input bind:value={email} type="email" />
|
|
9
|
+
<Field.ErrorText>Email is required.</Field.ErrorText>
|
|
10
|
+
</Field.Root>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
With a form adapter, `Field.Root` derives invalid/required state and renders the first error:
|
|
14
|
+
|
|
15
|
+
```svelte
|
|
16
|
+
<Field.Root {form} field="email" helperText="We'll never share your email.">
|
|
17
|
+
<Field.Label>Email</Field.Label>
|
|
18
|
+
<Field.Input bind:value={email} type="email" />
|
|
19
|
+
</Field.Root>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
List controls use `{ value, label }` items:
|
|
23
|
+
|
|
24
|
+
```svelte
|
|
25
|
+
<script lang="ts">
|
|
26
|
+
const items = [
|
|
27
|
+
{ value: 'apple', label: 'Apple' },
|
|
28
|
+
{ value: 'banana', label: 'Banana', disabled: true }
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
let value = $state<string | null>(null);
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<Select {items} label="Fruit" bind:value placeholder="Select..." />
|
|
35
|
+
<Combobox {items} label="Fruit" bind:value />
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Use `DateField` as the default accessible date input:
|
|
39
|
+
|
|
40
|
+
```svelte
|
|
41
|
+
<script lang="ts">
|
|
42
|
+
import type { DateValue } from 'compote-ui';
|
|
43
|
+
|
|
44
|
+
let value = $state<DateValue | null>(null);
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<DateField label="Date" bind:value />
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Use `DateRangeField` for ranges:
|
|
51
|
+
|
|
52
|
+
```svelte
|
|
53
|
+
<script lang="ts">
|
|
54
|
+
import type { DateValue } from 'compote-ui';
|
|
55
|
+
|
|
56
|
+
let range = $state<DateValue[]>([]);
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
<DateRangeField label="Range" bind:value={range} />
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Use `DateInput` when binding existing `string`, `Date`, or `DateValue` values without a calendar popup.
|
|
63
|
+
|
|
64
|
+
`PhoneInput` stores E.164 values:
|
|
65
|
+
|
|
66
|
+
```svelte
|
|
67
|
+
<script lang="ts">
|
|
68
|
+
let phone = $state('');
|
|
69
|
+
let country = $state(null);
|
|
70
|
+
let valid = $state(false);
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<PhoneInput label="Phone" bind:value={phone} bind:country bind:valid />
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`Listbox.Content` exposes filtered `items` and grouped `group` snippet data:
|
|
77
|
+
|
|
78
|
+
```svelte
|
|
79
|
+
<Listbox.Root {items} bind:value={selected}>
|
|
80
|
+
<Listbox.Label>Pick items</Listbox.Label>
|
|
81
|
+
<Listbox.Input placeholder="Search..." />
|
|
82
|
+
<Listbox.Content>
|
|
83
|
+
{#snippet items({ items })}
|
|
84
|
+
{#each items as item (item.value)}
|
|
85
|
+
<Listbox.Item {item}>
|
|
86
|
+
<Listbox.ItemText>{item.label}</Listbox.ItemText>
|
|
87
|
+
<Listbox.ItemIndicator />
|
|
88
|
+
</Listbox.Item>
|
|
89
|
+
{/each}
|
|
90
|
+
<Listbox.Empty>No results</Listbox.Empty>
|
|
91
|
+
{/snippet}
|
|
92
|
+
</Listbox.Content>
|
|
93
|
+
</Listbox.Root>
|
|
94
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Interactive Components
|
|
2
|
+
|
|
3
|
+
Use `Toggle` for one standalone binary action:
|
|
4
|
+
|
|
5
|
+
```svelte
|
|
6
|
+
<script lang="ts">
|
|
7
|
+
let favorite = $state(false);
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<Toggle bind:pressed={favorite} aria-label="Toggle favorite">Favorite</Toggle>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Use `ToggleGroup` for toolbar-style groups:
|
|
14
|
+
|
|
15
|
+
```svelte
|
|
16
|
+
<script lang="ts">
|
|
17
|
+
let alignment = $state(['left']);
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<ToggleGroup.Root bind:value={alignment} icon>
|
|
21
|
+
<ToggleGroup.Item value="left">
|
|
22
|
+
<AlignLeft />
|
|
23
|
+
</ToggleGroup.Item>
|
|
24
|
+
<ToggleGroup.Item value="center">
|
|
25
|
+
<AlignCenter />
|
|
26
|
+
</ToggleGroup.Item>
|
|
27
|
+
<ToggleGroup.Item value="right">
|
|
28
|
+
<AlignRight />
|
|
29
|
+
</ToggleGroup.Item>
|
|
30
|
+
</ToggleGroup.Root>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`Toggle` defaults to `variant="ghost"`. `ToggleGroup` defaults to `variant="outline"`.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Layout And Display
|
|
2
|
+
|
|
3
|
+
Tabs:
|
|
4
|
+
|
|
5
|
+
```svelte
|
|
6
|
+
<Tabs.Root bind:value={tab} defaultValue="account">
|
|
7
|
+
<Tabs.List>
|
|
8
|
+
<Tabs.Trigger value="account">Account</Tabs.Trigger>
|
|
9
|
+
<Tabs.Trigger value="settings">Settings</Tabs.Trigger>
|
|
10
|
+
<Tabs.Indicator />
|
|
11
|
+
</Tabs.List>
|
|
12
|
+
<Tabs.Content value="account">Account content</Tabs.Content>
|
|
13
|
+
<Tabs.Content value="settings">Settings content</Tabs.Content>
|
|
14
|
+
</Tabs.Root>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Splitter fills its container:
|
|
18
|
+
|
|
19
|
+
```svelte
|
|
20
|
+
<div class="h-64">
|
|
21
|
+
{#snippet left()}
|
|
22
|
+
<div class="p-3">Left</div>
|
|
23
|
+
{/snippet}
|
|
24
|
+
{#snippet right()}
|
|
25
|
+
<div class="p-3">Right</div>
|
|
26
|
+
{/snippet}
|
|
27
|
+
|
|
28
|
+
<Splitter
|
|
29
|
+
panels={[
|
|
30
|
+
{ id: 'left', minSize: 20, content: left },
|
|
31
|
+
{ id: 'right', minSize: 20, content: right }
|
|
32
|
+
]}
|
|
33
|
+
/>
|
|
34
|
+
</div>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For nested splitters, use Ark UI directly with one shared registry:
|
|
38
|
+
|
|
39
|
+
```svelte
|
|
40
|
+
<script lang="ts">
|
|
41
|
+
import { Splitter } from '@ark-ui/svelte/splitter';
|
|
42
|
+
|
|
43
|
+
const registry = Splitter.createRegistry();
|
|
44
|
+
</script>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
ScrollArea needs explicit size:
|
|
48
|
+
|
|
49
|
+
```svelte
|
|
50
|
+
<ScrollArea.Root class="h-80 w-56">
|
|
51
|
+
<ScrollArea.Viewport>
|
|
52
|
+
<ScrollArea.Content class="p-3">Content</ScrollArea.Content>
|
|
53
|
+
</ScrollArea.Viewport>
|
|
54
|
+
<ScrollArea.Scrollbar orientation="vertical">
|
|
55
|
+
<ScrollArea.Thumb />
|
|
56
|
+
</ScrollArea.Scrollbar>
|
|
57
|
+
</ScrollArea.Root>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Vertical Carousel needs explicit height:
|
|
61
|
+
|
|
62
|
+
```svelte
|
|
63
|
+
<Carousel.Root orientation="vertical" class="h-[512px]" slideCount={images.length}>
|
|
64
|
+
<Carousel.Control>
|
|
65
|
+
<Carousel.ItemGroup>
|
|
66
|
+
{#each images as image, index (image.src)}
|
|
67
|
+
<Carousel.Item {index}>
|
|
68
|
+
<img src={image.src} alt={image.alt} class="h-full w-full object-cover" />
|
|
69
|
+
</Carousel.Item>
|
|
70
|
+
{/each}
|
|
71
|
+
</Carousel.ItemGroup>
|
|
72
|
+
</Carousel.Control>
|
|
73
|
+
</Carousel.Root>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
TreeView fills its container:
|
|
77
|
+
|
|
78
|
+
```svelte
|
|
79
|
+
<div class="h-80 rounded border">
|
|
80
|
+
<TreeView {items} label="Files" bind:selectedValue={selected} />
|
|
81
|
+
</div>
|
|
82
|
+
```
|