@marianmeres/stuic 2.5.0 → 2.6.0
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/dist/actions/popover/README.md +145 -0
- package/dist/actions/tooltip/README.md +132 -0
- package/dist/components/AlertConfirmPrompt/README.md +139 -0
- package/dist/components/AnimatedElipsis/README.md +42 -0
- package/dist/components/AppShell/README.md +99 -0
- package/dist/components/Backdrop/README.md +81 -0
- package/dist/components/Button/README.md +83 -0
- package/dist/components/ButtonGroupRadio/README.md +94 -0
- package/dist/components/Circle/README.md +70 -0
- package/dist/components/ColorScheme/README.md +64 -0
- package/dist/components/CommandMenu/README.md +97 -0
- package/dist/components/DismissibleMessage/README.md +91 -0
- package/dist/components/Drawer/README.md +110 -0
- package/dist/components/HoverExpandableWidth/README.md +81 -0
- package/dist/components/Input/README.md +192 -0
- package/dist/components/KbdShortcut/README.md +81 -0
- package/dist/components/Modal/README.md +117 -0
- package/dist/components/ModalDialog/README.md +103 -0
- package/dist/components/Notifications/README.md +144 -0
- package/dist/components/Progress/README.md +71 -0
- package/dist/components/SlidingPanels/README.md +123 -0
- package/dist/components/Spinner/README.md +80 -0
- package/dist/components/Switch/README.md +113 -0
- package/dist/components/Thc/README.md +113 -0
- package/dist/components/TwCheck/README.md +54 -0
- package/dist/components/TypeaheadInput/README.md +116 -0
- package/dist/components/X/README.md +69 -0
- package/package.json +1 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Button
|
|
2
|
+
|
|
3
|
+
A flexible button component with style variants, sizes, and optional toggle/switch behavior. Can render as a button or anchor tag.
|
|
4
|
+
|
|
5
|
+
## Props
|
|
6
|
+
|
|
7
|
+
| Prop | Type | Default | Description |
|
|
8
|
+
|------|------|---------|-------------|
|
|
9
|
+
| `variant` | `"primary" \| "secondary" \| string` | - | Style variant |
|
|
10
|
+
| `size` | `"sm" \| "md" \| "lg" \| string` | - | Button size |
|
|
11
|
+
| `muted` | `boolean` | `false` | Reduce text contrast for less emphasis |
|
|
12
|
+
| `noshadow` | `boolean` | `false` | Remove shadow effect |
|
|
13
|
+
| `noborder` | `boolean` | `false` | Remove border |
|
|
14
|
+
| `unstyled` | `boolean` | `false` | Skip all default styling |
|
|
15
|
+
| `inverse` | `boolean` | `false` | Transparent bg, styled on hover |
|
|
16
|
+
| `href` | `string` | - | Render as anchor tag with this URL |
|
|
17
|
+
| `roleSwitch` | `boolean` | `false` | Enable toggle/switch behavior |
|
|
18
|
+
| `checked` | `boolean` | `false` | Toggle state when `roleSwitch` is true (bindable) |
|
|
19
|
+
| `el` | `Element` | - | Element reference (bindable) |
|
|
20
|
+
| `class` | `string` | - | Additional CSS classes |
|
|
21
|
+
|
|
22
|
+
## Snippet Props
|
|
23
|
+
|
|
24
|
+
The `children` snippet receives `{ checked }` when `roleSwitch` is enabled.
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
### Basic Variants
|
|
29
|
+
|
|
30
|
+
```svelte
|
|
31
|
+
<script lang="ts">
|
|
32
|
+
import { Button } from 'stuic';
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<Button>Default</Button>
|
|
36
|
+
<Button variant="primary">Primary</Button>
|
|
37
|
+
<Button variant="secondary">Secondary</Button>
|
|
38
|
+
<Button size="sm">Small</Button>
|
|
39
|
+
<Button size="lg">Large</Button>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Inverse Style (Ghost Button)
|
|
43
|
+
|
|
44
|
+
```svelte
|
|
45
|
+
<Button inverse>
|
|
46
|
+
Hover to see background
|
|
47
|
+
</Button>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### As Link
|
|
51
|
+
|
|
52
|
+
```svelte
|
|
53
|
+
<Button href="/dashboard">
|
|
54
|
+
Go to Dashboard
|
|
55
|
+
</Button>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Toggle Button
|
|
59
|
+
|
|
60
|
+
```svelte
|
|
61
|
+
<script lang="ts">
|
|
62
|
+
import { Button } from 'stuic';
|
|
63
|
+
|
|
64
|
+
let isActive = $state(false);
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<Button roleSwitch bind:checked={isActive}>
|
|
68
|
+
{#snippet children({ checked })}
|
|
69
|
+
{checked ? 'ON' : 'OFF'}
|
|
70
|
+
{/snippet}
|
|
71
|
+
</Button>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Custom Styling
|
|
75
|
+
|
|
76
|
+
```svelte
|
|
77
|
+
<Button
|
|
78
|
+
unstyled
|
|
79
|
+
class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
|
|
80
|
+
>
|
|
81
|
+
Fully Custom
|
|
82
|
+
</Button>
|
|
83
|
+
```
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# ButtonGroupRadio
|
|
2
|
+
|
|
3
|
+
A radio button group styled as a segmented button toggle. Supports keyboard navigation and async validation.
|
|
4
|
+
|
|
5
|
+
## Props
|
|
6
|
+
|
|
7
|
+
| Prop | Type | Default | Description |
|
|
8
|
+
|------|------|---------|-------------|
|
|
9
|
+
| `options` | `(string \| FieldRadiosOption)[]` | - | Array of options |
|
|
10
|
+
| `value` | `string` | - | Selected value (bindable) |
|
|
11
|
+
| `activeIndex` | `number` | - | Selected index (bindable) |
|
|
12
|
+
| `size` | `"sm" \| "md" \| "lg" \| string` | `"md"` | Button size |
|
|
13
|
+
| `disabled` | `boolean` | `false` | Disable all buttons |
|
|
14
|
+
| `tabindex` | `number` | `0` | Tab index for active button |
|
|
15
|
+
| `class` | `string` | - | CSS for container |
|
|
16
|
+
| `classButton` | `string` | - | CSS for all buttons |
|
|
17
|
+
| `classButtonActive` | `string` | - | CSS for active button |
|
|
18
|
+
| `style` | `string` | - | Inline styles for container |
|
|
19
|
+
| `onButtonClick` | `(index, coll) => Promise<boolean> \| boolean` | - | Async validation hook (return `false` to prevent) |
|
|
20
|
+
| `buttonProps` | `(index, coll) => Record<string, any>` | - | Dynamic props per button |
|
|
21
|
+
|
|
22
|
+
## Option Format
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
// Simple string
|
|
26
|
+
'Option A'
|
|
27
|
+
|
|
28
|
+
// Or object
|
|
29
|
+
{
|
|
30
|
+
label: 'Option A',
|
|
31
|
+
value: 'a' // optional, defaults to label
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
### Basic
|
|
38
|
+
|
|
39
|
+
```svelte
|
|
40
|
+
<script lang="ts">
|
|
41
|
+
import { ButtonGroupRadio } from 'stuic';
|
|
42
|
+
|
|
43
|
+
let selected = $state('monthly');
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<ButtonGroupRadio
|
|
47
|
+
options={['daily', 'weekly', 'monthly']}
|
|
48
|
+
bind:value={selected}
|
|
49
|
+
/>
|
|
50
|
+
|
|
51
|
+
<p>Selected: {selected}</p>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### With Object Options
|
|
55
|
+
|
|
56
|
+
```svelte
|
|
57
|
+
<script lang="ts">
|
|
58
|
+
let plan = $state('pro');
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<ButtonGroupRadio
|
|
62
|
+
options={[
|
|
63
|
+
{ label: 'Free', value: 'free' },
|
|
64
|
+
{ label: 'Pro', value: 'pro' },
|
|
65
|
+
{ label: 'Enterprise', value: 'enterprise' }
|
|
66
|
+
]}
|
|
67
|
+
bind:value={plan}
|
|
68
|
+
/>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### With Async Validation
|
|
72
|
+
|
|
73
|
+
```svelte
|
|
74
|
+
<script lang="ts">
|
|
75
|
+
let value = $state('a');
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<ButtonGroupRadio
|
|
79
|
+
options={['a', 'b', 'c']}
|
|
80
|
+
bind:value
|
|
81
|
+
onButtonClick={async (index, coll) => {
|
|
82
|
+
// Return false to prevent selection
|
|
83
|
+
if (index === 2) {
|
|
84
|
+
alert('Option C is disabled');
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}}
|
|
88
|
+
/>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Keyboard Navigation
|
|
92
|
+
|
|
93
|
+
- **Arrow Left/Up**: Select previous option
|
|
94
|
+
- **Arrow Right/Down**: Select next option
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Circle
|
|
2
|
+
|
|
3
|
+
An SVG circle progress indicator with configurable stroke, rotation, and animated transitions.
|
|
4
|
+
|
|
5
|
+
## Props
|
|
6
|
+
|
|
7
|
+
| Prop | Type | Default | Description |
|
|
8
|
+
|------|------|---------|-------------|
|
|
9
|
+
| `completeness` | `number` | `1` | Progress value from 0 to 1 |
|
|
10
|
+
| `strokeWidth` | `number` | `10` | Stroke width in SVG units |
|
|
11
|
+
| `bgStrokeColor` | `string` | - | Background circle stroke color |
|
|
12
|
+
| `roundedEdges` | `boolean` | `true` | Use rounded stroke line caps |
|
|
13
|
+
| `rotate` | `number` | `0` | Rotation in degrees |
|
|
14
|
+
| `strokeWidthRatio` | `number` | `0` | Ratio for background stroke width |
|
|
15
|
+
| `animateCompletenessMs` | `number` | `0` | Transition duration for progress changes (ms) |
|
|
16
|
+
| `class` | `string` | - | CSS for container div |
|
|
17
|
+
| `style` | `string` | - | Inline styles for container |
|
|
18
|
+
| `circleClass` | `string` | - | CSS for SVG circle element |
|
|
19
|
+
| `circleStyle` | `string` | - | Inline styles for circle |
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
### Basic Progress Circle
|
|
24
|
+
|
|
25
|
+
```svelte
|
|
26
|
+
<script lang="ts">
|
|
27
|
+
import { Circle } from 'stuic';
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<Circle completeness={0.75} class="size-16" />
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Animated Progress
|
|
34
|
+
|
|
35
|
+
```svelte
|
|
36
|
+
<script lang="ts">
|
|
37
|
+
import { Circle } from 'stuic';
|
|
38
|
+
|
|
39
|
+
let progress = $state(0);
|
|
40
|
+
|
|
41
|
+
function startProgress() {
|
|
42
|
+
progress = 0;
|
|
43
|
+
const interval = setInterval(() => {
|
|
44
|
+
progress += 0.1;
|
|
45
|
+
if (progress >= 1) clearInterval(interval);
|
|
46
|
+
}, 200);
|
|
47
|
+
}
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<Circle
|
|
51
|
+
completeness={progress}
|
|
52
|
+
animateCompletenessMs={200}
|
|
53
|
+
class="size-20"
|
|
54
|
+
/>
|
|
55
|
+
|
|
56
|
+
<button onclick={startProgress}>Start</button>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Custom Styling
|
|
60
|
+
|
|
61
|
+
```svelte
|
|
62
|
+
<Circle
|
|
63
|
+
completeness={0.5}
|
|
64
|
+
strokeWidth={8}
|
|
65
|
+
rotate={-90}
|
|
66
|
+
bgStrokeColor="rgba(0,0,0,0.1)"
|
|
67
|
+
class="size-24"
|
|
68
|
+
circleClass="stroke-blue-500"
|
|
69
|
+
/>
|
|
70
|
+
```
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# ColorScheme
|
|
2
|
+
|
|
3
|
+
Hydration components for dark mode support. Add/remove the `dark` class on the document root based on localStorage and optionally system preference.
|
|
4
|
+
|
|
5
|
+
## Components
|
|
6
|
+
|
|
7
|
+
### ColorSchemeLocal
|
|
8
|
+
|
|
9
|
+
Uses only the localStorage setting to determine dark mode. Does not check system preference.
|
|
10
|
+
|
|
11
|
+
### ColorSchemeSystemAware
|
|
12
|
+
|
|
13
|
+
Checks localStorage first, then falls back to system preference (`prefers-color-scheme: dark`).
|
|
14
|
+
|
|
15
|
+
## Props
|
|
16
|
+
|
|
17
|
+
Both components have **no props** - they are pure hydration components.
|
|
18
|
+
|
|
19
|
+
## Storage Key
|
|
20
|
+
|
|
21
|
+
Both components use the localStorage key: `stuic-color-scheme`
|
|
22
|
+
|
|
23
|
+
- Value `"dark"` → adds `dark` class to `<html>`
|
|
24
|
+
- Any other value → removes `dark` class
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
### System-Aware (Recommended)
|
|
29
|
+
|
|
30
|
+
```svelte
|
|
31
|
+
<script lang="ts">
|
|
32
|
+
import { ColorSchemeSystemAware } from 'stuic';
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<ColorSchemeSystemAware />
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Local Only
|
|
39
|
+
|
|
40
|
+
```svelte
|
|
41
|
+
<script lang="ts">
|
|
42
|
+
import { ColorSchemeLocal } from 'stuic';
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<ColorSchemeLocal />
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Toggle Dark Mode
|
|
49
|
+
|
|
50
|
+
```svelte
|
|
51
|
+
<script lang="ts">
|
|
52
|
+
import { ColorSchemeSystemAware } from 'stuic';
|
|
53
|
+
|
|
54
|
+
function toggleDarkMode() {
|
|
55
|
+
const root = document.documentElement;
|
|
56
|
+
const isDark = root.classList.toggle('dark');
|
|
57
|
+
localStorage.setItem('stuic-color-scheme', isDark ? 'dark' : 'light');
|
|
58
|
+
}
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<ColorSchemeSystemAware />
|
|
62
|
+
|
|
63
|
+
<button onclick={toggleDarkMode}>Toggle Dark Mode</button>
|
|
64
|
+
```
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# CommandMenu
|
|
2
|
+
|
|
3
|
+
A searchable command palette/menu modal for quick navigation and selection. Supports keyboard navigation, option grouping, and async search.
|
|
4
|
+
|
|
5
|
+
## Props
|
|
6
|
+
|
|
7
|
+
| Prop | Type | Default | Description |
|
|
8
|
+
|------|------|---------|-------------|
|
|
9
|
+
| `value` | `any` | - | Selected item (bindable) |
|
|
10
|
+
| `q` | `string` | `""` | Search query (bindable) |
|
|
11
|
+
| `getOptions` | `(q: string, current: Item[]) => Promise<Item[]>` | - | Async function to fetch options |
|
|
12
|
+
| `renderOptionLabel` | `(item: Item) => string` | - | Custom label renderer |
|
|
13
|
+
| `renderOptionGroup` | `(s: string) => string` | - | Custom group label renderer |
|
|
14
|
+
| `itemIdPropName` | `string` | `"id"` | Property name for item ID |
|
|
15
|
+
| `t` | `TranslateFn` | - | Translation function for i18n |
|
|
16
|
+
| `notifications` | `NotificationsStack` | - | Notifications stack for errors |
|
|
17
|
+
| `searchPlaceholder` | `string` | `"Type to search..."` | Input placeholder |
|
|
18
|
+
| `noScrollLock` | `boolean` | `false` | Disable body scroll lock |
|
|
19
|
+
| `showAllOnEmptyQ` | `boolean` | `false` | Show all options when query is empty |
|
|
20
|
+
| `classOption` | `string` | - | CSS for option buttons |
|
|
21
|
+
| `classOptionActive` | `string` | - | CSS for active option |
|
|
22
|
+
| `input` | `HTMLInputElement` | - | Input element reference (bindable) |
|
|
23
|
+
|
|
24
|
+
## Methods
|
|
25
|
+
|
|
26
|
+
| Method | Description |
|
|
27
|
+
|--------|-------------|
|
|
28
|
+
| `open(opener?)` | Open the command menu |
|
|
29
|
+
| `close()` | Close the command menu |
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
### Basic
|
|
34
|
+
|
|
35
|
+
```svelte
|
|
36
|
+
<script lang="ts">
|
|
37
|
+
import { CommandMenu } from 'stuic';
|
|
38
|
+
|
|
39
|
+
let menu: CommandMenu;
|
|
40
|
+
let selected = $state(null);
|
|
41
|
+
|
|
42
|
+
async function getOptions(q: string) {
|
|
43
|
+
const items = [
|
|
44
|
+
{ id: 'dashboard', label: 'Dashboard' },
|
|
45
|
+
{ id: 'settings', label: 'Settings' },
|
|
46
|
+
{ id: 'profile', label: 'Profile' },
|
|
47
|
+
];
|
|
48
|
+
return items.filter(i =>
|
|
49
|
+
i.label.toLowerCase().includes(q.toLowerCase())
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<button onclick={() => menu.open()}>Open Menu (Cmd+K)</button>
|
|
55
|
+
|
|
56
|
+
<CommandMenu
|
|
57
|
+
bind:this={menu}
|
|
58
|
+
bind:value={selected}
|
|
59
|
+
{getOptions}
|
|
60
|
+
renderOptionLabel={(item) => item.label}
|
|
61
|
+
/>
|
|
62
|
+
|
|
63
|
+
{#if selected}
|
|
64
|
+
<p>Selected: {selected.label}</p>
|
|
65
|
+
{/if}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### With Option Groups
|
|
69
|
+
|
|
70
|
+
```svelte
|
|
71
|
+
<script lang="ts">
|
|
72
|
+
async function getOptions(q: string) {
|
|
73
|
+
return [
|
|
74
|
+
{ id: 'new-file', label: 'New File', optgroup: 'file' },
|
|
75
|
+
{ id: 'open-file', label: 'Open File', optgroup: 'file' },
|
|
76
|
+
{ id: 'copy', label: 'Copy', optgroup: 'edit' },
|
|
77
|
+
{ id: 'paste', label: 'Paste', optgroup: 'edit' },
|
|
78
|
+
];
|
|
79
|
+
}
|
|
80
|
+
</script>
|
|
81
|
+
|
|
82
|
+
<CommandMenu
|
|
83
|
+
bind:this={menu}
|
|
84
|
+
{getOptions}
|
|
85
|
+
renderOptionLabel={(item) => item.label}
|
|
86
|
+
renderOptionGroup={(group) => group.toUpperCase()}
|
|
87
|
+
/>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Keyboard Navigation
|
|
91
|
+
|
|
92
|
+
- **Arrow Up/Down**: Navigate options
|
|
93
|
+
- **Cmd/Ctrl + Arrow Up**: Jump to first option
|
|
94
|
+
- **Cmd/Ctrl + Arrow Down**: Jump to last option
|
|
95
|
+
- **Enter**: Select active option
|
|
96
|
+
- **Escape**: Close menu (or clear input first)
|
|
97
|
+
- **Tab**: Focus back to search input
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# DismissibleMessage
|
|
2
|
+
|
|
3
|
+
A dismissible alert/message component with color themes and slide transition.
|
|
4
|
+
|
|
5
|
+
## Props
|
|
6
|
+
|
|
7
|
+
| Prop | Type | Default | Description |
|
|
8
|
+
|------|------|---------|-------------|
|
|
9
|
+
| `message` | `THC \| Error` | - | Message content (string, HTML, or Error object) |
|
|
10
|
+
| `theme` | `TW_COLORS` | - | Tailwind color theme (e.g., `"red"`, `"green"`, `"blue"`) |
|
|
11
|
+
| `forceAsHtml` | `boolean` | `true` | Render message as HTML |
|
|
12
|
+
| `duration` | `number` | `150` | Slide transition duration (ms) |
|
|
13
|
+
| `onDismiss` | `(() => void) \| null \| false` | - | Dismiss callback (set to `false` to hide X button) |
|
|
14
|
+
| `class` | `string` | - | CSS for container |
|
|
15
|
+
| `classContent` | `string` | - | CSS for content area |
|
|
16
|
+
| `classDismiss` | `string` | - | CSS for dismiss button |
|
|
17
|
+
| `classX` | `string` | - | CSS for X icon |
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### Basic Message
|
|
22
|
+
|
|
23
|
+
```svelte
|
|
24
|
+
<script lang="ts">
|
|
25
|
+
import { DismissibleMessage } from 'stuic';
|
|
26
|
+
|
|
27
|
+
let message = $state('This is an important notice.');
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<DismissibleMessage
|
|
31
|
+
{message}
|
|
32
|
+
onDismiss={() => message = ''}
|
|
33
|
+
/>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### With Theme Colors
|
|
37
|
+
|
|
38
|
+
```svelte
|
|
39
|
+
<script lang="ts">
|
|
40
|
+
import { DismissibleMessage } from 'stuic';
|
|
41
|
+
|
|
42
|
+
let error = $state('Something went wrong!');
|
|
43
|
+
let success = $state('Operation completed successfully.');
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<DismissibleMessage
|
|
47
|
+
message={error}
|
|
48
|
+
theme="red"
|
|
49
|
+
onDismiss={() => error = ''}
|
|
50
|
+
/>
|
|
51
|
+
|
|
52
|
+
<DismissibleMessage
|
|
53
|
+
message={success}
|
|
54
|
+
theme="green"
|
|
55
|
+
onDismiss={() => success = ''}
|
|
56
|
+
/>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Non-Dismissible
|
|
60
|
+
|
|
61
|
+
```svelte
|
|
62
|
+
<DismissibleMessage
|
|
63
|
+
message="This message cannot be dismissed."
|
|
64
|
+
theme="blue"
|
|
65
|
+
onDismiss={false}
|
|
66
|
+
/>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### With Error Object
|
|
70
|
+
|
|
71
|
+
```svelte
|
|
72
|
+
<script lang="ts">
|
|
73
|
+
let error = $state<Error | null>(null);
|
|
74
|
+
|
|
75
|
+
function doSomething() {
|
|
76
|
+
try {
|
|
77
|
+
throw new Error('Network request failed');
|
|
78
|
+
} catch (e) {
|
|
79
|
+
error = e as Error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
</script>
|
|
83
|
+
|
|
84
|
+
{#if error}
|
|
85
|
+
<DismissibleMessage
|
|
86
|
+
message={error}
|
|
87
|
+
theme="red"
|
|
88
|
+
onDismiss={() => error = null}
|
|
89
|
+
/>
|
|
90
|
+
{/if}
|
|
91
|
+
```
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Drawer
|
|
2
|
+
|
|
3
|
+
A slide-out panel component with configurable position, fly animation, and backdrop support.
|
|
4
|
+
|
|
5
|
+
## Props
|
|
6
|
+
|
|
7
|
+
| Prop | Type | Default | Description |
|
|
8
|
+
|------|------|---------|-------------|
|
|
9
|
+
| `visible` | `boolean` | `false` | Controls visibility (bindable) |
|
|
10
|
+
| `position` | `"left" \| "top" \| "right" \| "bottom"` | `"left"` | Slide direction |
|
|
11
|
+
| `transitionDuration` | `number` | `200` | Animation duration (ms) |
|
|
12
|
+
| `animOffset` | `string \| number` | `"75vw"` | Fly animation distance |
|
|
13
|
+
| `focusTrap` | `boolean \| FocusTrapOptions` | - | Enable focus trapping |
|
|
14
|
+
| `onEscape` | `() => void` | - | Callback on Escape key |
|
|
15
|
+
| `onOutside` | `() => void` | - | Callback on backdrop click |
|
|
16
|
+
| `noBackdropScrollLock` | `boolean` | `false` | Disable body scroll lock |
|
|
17
|
+
| `class` | `string` | - | CSS for drawer panel |
|
|
18
|
+
| `classBackdrop` | `string` | - | CSS for backdrop |
|
|
19
|
+
| `labelledby` | `string` | - | ARIA labelledby ID |
|
|
20
|
+
| `describedby` | `string` | - | ARIA describedby ID |
|
|
21
|
+
| `el` | `HTMLDivElement` | - | Panel element reference (bindable) |
|
|
22
|
+
| `elBackdrop` | `HTMLDivElement` | - | Backdrop element reference (bindable) |
|
|
23
|
+
|
|
24
|
+
## Methods
|
|
25
|
+
|
|
26
|
+
| Method | Description |
|
|
27
|
+
|--------|-------------|
|
|
28
|
+
| `open(opener?)` | Open drawer, optionally track opener for focus return |
|
|
29
|
+
| `close()` | Close drawer |
|
|
30
|
+
| `setOpener(el)` | Set element to refocus when closed |
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
### Basic Side Drawer
|
|
35
|
+
|
|
36
|
+
```svelte
|
|
37
|
+
<script lang="ts">
|
|
38
|
+
import { Drawer } from 'stuic';
|
|
39
|
+
|
|
40
|
+
let drawer: Drawer;
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<button onclick={(e) => drawer.open(e)}>Open Drawer</button>
|
|
44
|
+
|
|
45
|
+
<Drawer
|
|
46
|
+
bind:this={drawer}
|
|
47
|
+
onEscape={() => drawer.close()}
|
|
48
|
+
onOutside={() => drawer.close()}
|
|
49
|
+
class="bg-white dark:bg-neutral-900 max-w-md"
|
|
50
|
+
>
|
|
51
|
+
<div class="p-6">
|
|
52
|
+
<h2>Drawer Content</h2>
|
|
53
|
+
<p>This is a side drawer.</p>
|
|
54
|
+
<button onclick={() => drawer.close()}>Close</button>
|
|
55
|
+
</div>
|
|
56
|
+
</Drawer>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Right-Side Drawer
|
|
60
|
+
|
|
61
|
+
```svelte
|
|
62
|
+
<Drawer
|
|
63
|
+
bind:this={drawer}
|
|
64
|
+
position="right"
|
|
65
|
+
onEscape={() => drawer.close()}
|
|
66
|
+
onOutside={() => drawer.close()}
|
|
67
|
+
class="bg-white dark:bg-neutral-900 max-w-sm"
|
|
68
|
+
>
|
|
69
|
+
<nav class="p-4">
|
|
70
|
+
<ul>
|
|
71
|
+
<li>Menu Item 1</li>
|
|
72
|
+
<li>Menu Item 2</li>
|
|
73
|
+
</ul>
|
|
74
|
+
</nav>
|
|
75
|
+
</Drawer>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Bottom Sheet
|
|
79
|
+
|
|
80
|
+
```svelte
|
|
81
|
+
<Drawer
|
|
82
|
+
bind:this={drawer}
|
|
83
|
+
position="bottom"
|
|
84
|
+
animOffset="50vh"
|
|
85
|
+
onEscape={() => drawer.close()}
|
|
86
|
+
class="bg-white dark:bg-neutral-900 max-h-[50vh]"
|
|
87
|
+
>
|
|
88
|
+
<div class="p-6">
|
|
89
|
+
Bottom sheet content
|
|
90
|
+
</div>
|
|
91
|
+
</Drawer>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### With Visible Binding
|
|
95
|
+
|
|
96
|
+
```svelte
|
|
97
|
+
<script lang="ts">
|
|
98
|
+
let visible = $state(false);
|
|
99
|
+
</script>
|
|
100
|
+
|
|
101
|
+
<button onclick={() => visible = true}>Open</button>
|
|
102
|
+
|
|
103
|
+
<Drawer
|
|
104
|
+
bind:visible
|
|
105
|
+
onEscape={() => visible = false}
|
|
106
|
+
onOutside={() => visible = false}
|
|
107
|
+
>
|
|
108
|
+
<div class="p-4">Content</div>
|
|
109
|
+
</Drawer>
|
|
110
|
+
```
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# HoverExpandableWidth
|
|
2
|
+
|
|
3
|
+
A container that expands its width on hover, transitioning from a fixed width to a target width. Useful for sidebars or navigation that expand to show more content.
|
|
4
|
+
|
|
5
|
+
## Props
|
|
6
|
+
|
|
7
|
+
| Prop | Type | Default | Description |
|
|
8
|
+
|------|------|---------|-------------|
|
|
9
|
+
| `enabled` | `boolean` | `true` (if fine pointer) | Enable hover expansion |
|
|
10
|
+
| `targetWidth` | `number` | `256` | Expanded width in pixels |
|
|
11
|
+
| `duration` | `number` | `150` | Transition duration (ms) |
|
|
12
|
+
| `delayIn` | `number` | `500` | Delay before expanding (ms) |
|
|
13
|
+
| `delayOut` | `number` | `300` | Delay before shrinking (ms) |
|
|
14
|
+
| `shadowOpacity` | `number` | `0.5` | Box shadow opacity when expanded |
|
|
15
|
+
| `class` | `string` | - | CSS classes for container |
|
|
16
|
+
|
|
17
|
+
## Snippet Props
|
|
18
|
+
|
|
19
|
+
The `children` snippet receives state information:
|
|
20
|
+
|
|
21
|
+
| Prop | Type | Description |
|
|
22
|
+
|------|------|-------------|
|
|
23
|
+
| `isExpanded` | `boolean` | Currently expanded |
|
|
24
|
+
| `isExpanding` | `boolean` | Transition to expanded |
|
|
25
|
+
| `isShrinking` | `boolean` | Transition to collapsed |
|
|
26
|
+
| `inTransition` | `boolean` | Any transition active |
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
### Basic Expandable Sidebar
|
|
31
|
+
|
|
32
|
+
```svelte
|
|
33
|
+
<script lang="ts">
|
|
34
|
+
import { HoverExpandableWidth } from 'stuic';
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<div class="w-16 h-screen">
|
|
38
|
+
<HoverExpandableWidth targetWidth={256}>
|
|
39
|
+
{#snippet children({ isExpanded })}
|
|
40
|
+
<nav class="h-full bg-gray-100 p-2">
|
|
41
|
+
{#if isExpanded}
|
|
42
|
+
<ul>
|
|
43
|
+
<li>Dashboard</li>
|
|
44
|
+
<li>Settings</li>
|
|
45
|
+
<li>Profile</li>
|
|
46
|
+
</ul>
|
|
47
|
+
{:else}
|
|
48
|
+
<ul>
|
|
49
|
+
<li>D</li>
|
|
50
|
+
<li>S</li>
|
|
51
|
+
<li>P</li>
|
|
52
|
+
</ul>
|
|
53
|
+
{/if}
|
|
54
|
+
</nav>
|
|
55
|
+
{/snippet}
|
|
56
|
+
</HoverExpandableWidth>
|
|
57
|
+
</div>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### With Custom Delays
|
|
61
|
+
|
|
62
|
+
```svelte
|
|
63
|
+
<HoverExpandableWidth
|
|
64
|
+
targetWidth={320}
|
|
65
|
+
delayIn={300}
|
|
66
|
+
delayOut={200}
|
|
67
|
+
duration={200}
|
|
68
|
+
>
|
|
69
|
+
{#snippet children({ isExpanded, inTransition })}
|
|
70
|
+
<div class={inTransition ? 'opacity-50' : ''}>
|
|
71
|
+
{isExpanded ? 'Expanded content...' : 'Icon'}
|
|
72
|
+
</div>
|
|
73
|
+
{/snippet}
|
|
74
|
+
</HoverExpandableWidth>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Notes
|
|
78
|
+
|
|
79
|
+
- Respects `prefers-reduced-motion` - animations disabled when user prefers reduced motion
|
|
80
|
+
- Automatically disabled on touch devices (coarse pointer)
|
|
81
|
+
- Uses `position: fixed` during expansion for overlay effect
|