@sveltekitchen/sveltekitchen 0.0.0-stage → 1.0.1
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 +137 -2
- package/dist/components/Accordion.svelte +36 -0
- package/dist/components/Accordion.svelte.d.ts +15 -0
- package/dist/components/AccordionItem.svelte +73 -0
- package/dist/components/AccordionItem.svelte.d.ts +21 -0
- package/dist/components/Alert.svelte +23 -0
- package/dist/components/Alert.svelte.d.ts +15 -0
- package/dist/components/Badge.svelte +16 -0
- package/dist/components/Badge.svelte.d.ts +15 -0
- package/dist/components/Button.svelte +43 -0
- package/dist/components/Button.svelte.d.ts +25 -0
- package/dist/components/Calendar.svelte +88 -0
- package/dist/components/Calendar.svelte.d.ts +13 -0
- package/dist/components/Card.svelte +68 -0
- package/dist/components/Card.svelte.d.ts +25 -0
- package/dist/components/Chevron.svelte +36 -0
- package/dist/components/Chevron.svelte.d.ts +19 -0
- package/dist/components/Collapsible.svelte +46 -0
- package/dist/components/Collapsible.svelte.d.ts +19 -0
- package/dist/components/ConfirmDialog.svelte +70 -0
- package/dist/components/ConfirmDialog.svelte.d.ts +27 -0
- package/dist/components/DatePicker.svelte +60 -0
- package/dist/components/DatePicker.svelte.d.ts +17 -0
- package/dist/components/Divider.svelte +25 -0
- package/dist/components/Divider.svelte.d.ts +17 -0
- package/dist/components/Dropdown.svelte +100 -0
- package/dist/components/Dropdown.svelte.d.ts +21 -0
- package/dist/components/Form/Checkbox.svelte +37 -0
- package/dist/components/Form/Checkbox.svelte.d.ts +19 -0
- package/dist/components/Form/Field.svelte +28 -0
- package/dist/components/Form/Field.svelte.d.ts +21 -0
- package/dist/components/Form/Input.svelte +56 -0
- package/dist/components/Form/Input.svelte.d.ts +29 -0
- package/dist/components/Form/MaskedInput.svelte +152 -0
- package/dist/components/Form/MaskedInput.svelte.d.ts +25 -0
- package/dist/components/Form/Radio.svelte +28 -0
- package/dist/components/Form/Radio.svelte.d.ts +19 -0
- package/dist/components/Form/RadioGroup.svelte +28 -0
- package/dist/components/Form/RadioGroup.svelte.d.ts +19 -0
- package/dist/components/Form/Select.svelte +20 -0
- package/dist/components/Form/Select.svelte.d.ts +19 -0
- package/dist/components/Form/Textarea.svelte +30 -0
- package/dist/components/Form/Textarea.svelte.d.ts +25 -0
- package/dist/components/Form/Toggle.svelte +21 -0
- package/dist/components/Form/Toggle.svelte.d.ts +17 -0
- package/dist/components/Modal.svelte +77 -0
- package/dist/components/Modal.svelte.d.ts +27 -0
- package/dist/components/Pagination.svelte +77 -0
- package/dist/components/Pagination.svelte.d.ts +17 -0
- package/dist/components/Progress.svelte +34 -0
- package/dist/components/Progress.svelte.d.ts +21 -0
- package/dist/components/Spinner.svelte +24 -0
- package/dist/components/Spinner.svelte.d.ts +13 -0
- package/dist/components/Table.svelte +100 -0
- package/dist/components/Table.svelte.d.ts +21 -0
- package/dist/components/ThemeToggle.svelte +22 -0
- package/dist/components/ThemeToggle.svelte.d.ts +11 -0
- package/dist/components/Tooltip.svelte +45 -0
- package/dist/components/Tooltip.svelte.d.ts +15 -0
- package/dist/css/base/root.css +103 -0
- package/dist/css/base.css +47 -0
- package/dist/css/components/accordion.css +151 -0
- package/dist/css/components/alert.css +56 -0
- package/dist/css/components/badge.css +56 -0
- package/dist/css/components/button.css +171 -0
- package/dist/css/components/calendar.css +105 -0
- package/dist/css/components/card.css +128 -0
- package/dist/css/components/checkbox-radio.css +105 -0
- package/dist/css/components/collapsible.css +61 -0
- package/dist/css/components/divider.css +42 -0
- package/dist/css/components/dock-layout.css +111 -0
- package/dist/css/components/dropdown.css +68 -0
- package/dist/css/components/floating-shell.css +93 -0
- package/dist/css/components/form.css +286 -0
- package/dist/css/components/login.css +145 -0
- package/dist/css/components/modal.css +179 -0
- package/dist/css/components/nav.css +190 -0
- package/dist/css/components/nestable.css +187 -0
- package/dist/css/components/pagination.css +50 -0
- package/dist/css/components/progress.css +50 -0
- package/dist/css/components/sidebar.css +78 -0
- package/dist/css/components/spinner.css +36 -0
- package/dist/css/components/table.css +98 -0
- package/dist/css/components/tooltip.css +77 -0
- package/dist/css/components/topbar-layout.css +108 -0
- package/dist/css/icons/icons.css +52 -0
- package/dist/css/index.css +20 -0
- package/dist/css/layout/container.css +12 -0
- package/dist/css/layout/grid.css +40 -0
- package/dist/css/layout/shell.css +185 -0
- package/dist/css/overlay/default.css +27 -0
- package/dist/css/tokens/colors.css +81 -0
- package/dist/css/tokens/radius.css +8 -0
- package/dist/css/tokens/shadows.css +23 -0
- package/dist/css/tokens/spacing.css +30 -0
- package/dist/css/tokens/transitions.css +10 -0
- package/dist/css/tokens/typography.css +44 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +52 -0
- package/dist/layout/NavItem.svelte +84 -0
- package/dist/layout/NavItem.svelte.d.ts +25 -0
- package/dist/layout/Shell.svelte +7 -0
- package/dist/layout/Shell.svelte.d.ts +13 -0
- package/dist/layout/SidebarProfile.svelte +31 -0
- package/dist/layout/SidebarProfile.svelte.d.ts +19 -0
- package/dist/layouts/AppShell.svelte +60 -0
- package/dist/layouts/AppShell.svelte.d.ts +21 -0
- package/dist/layouts/BarLayout.svelte +60 -0
- package/dist/layouts/BarLayout.svelte.d.ts +21 -0
- package/dist/layouts/BottomBarLayout.svelte +23 -0
- package/dist/layouts/BottomBarLayout.svelte.d.ts +15 -0
- package/dist/layouts/DockLayout.svelte +81 -0
- package/dist/layouts/DockLayout.svelte.d.ts +15 -0
- package/dist/layouts/FloatingDockLayout.svelte +56 -0
- package/dist/layouts/FloatingDockLayout.svelte.d.ts +13 -0
- package/dist/layouts/FloatingLayout.svelte +80 -0
- package/dist/layouts/FloatingLayout.svelte.d.ts +19 -0
- package/dist/layouts/FloatingLeftLayout.svelte +59 -0
- package/dist/layouts/FloatingLeftLayout.svelte.d.ts +15 -0
- package/dist/layouts/FloatingShell.svelte +80 -0
- package/dist/layouts/FloatingShell.svelte.d.ts +19 -0
- package/dist/layouts/FloatingTopLayout.svelte +68 -0
- package/dist/layouts/FloatingTopLayout.svelte.d.ts +15 -0
- package/dist/layouts/HybridLayout.svelte +20 -0
- package/dist/layouts/HybridLayout.svelte.d.ts +17 -0
- package/dist/layouts/IconRailLayout.svelte +75 -0
- package/dist/layouts/IconRailLayout.svelte.d.ts +15 -0
- package/dist/layouts/LeftBarLayout.svelte +19 -0
- package/dist/layouts/LeftBarLayout.svelte.d.ts +15 -0
- package/dist/layouts/RightBarLayout.svelte +21 -0
- package/dist/layouts/RightBarLayout.svelte.d.ts +15 -0
- package/dist/layouts/SideBar.svelte +86 -0
- package/dist/layouts/SideBar.svelte.d.ts +17 -0
- package/dist/layouts/SidebarLayout.svelte +11 -0
- package/dist/layouts/SidebarLayout.svelte.d.ts +15 -0
- package/dist/layouts/TopBar.svelte +90 -0
- package/dist/layouts/TopBar.svelte.d.ts +21 -0
- package/dist/layouts/TopBarLayout.svelte +26 -0
- package/dist/layouts/TopBarLayout.svelte.d.ts +15 -0
- package/dist/theme/theme.svelte.d.ts +11 -0
- package/dist/theme/theme.svelte.js +48 -0
- package/package.json +56 -4
package/README.md
CHANGED
|
@@ -1,3 +1,138 @@
|
|
|
1
|
-
#
|
|
1
|
+
# SvelteKitchen
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A themeable **Svelte 5** component library with a bundled CSS design system, a
|
|
4
|
+
light/dark theme engine, and ready-made application layouts.
|
|
5
|
+
|
|
6
|
+
- **Docs & live examples:** https://sveltekitchen.net
|
|
7
|
+
- **Package:** [`@sveltekitchen/sveltekitchen`](https://www.npmjs.com/package/@sveltekitchen/sveltekitchen)
|
|
8
|
+
|
|
9
|
+
Everything is built on runes (`$props`, `$state`, `$derived`, `$bindable`) and
|
|
10
|
+
plain CSS custom properties — no Tailwind, no Sass, no CSS-in-JS.
|
|
11
|
+
|
|
12
|
+
> **Svelte 5 only.** This is a native Svelte component library — it is not a
|
|
13
|
+
> React, Vue, or web-components package, and there are no plans to port it.
|
|
14
|
+
> The one exception is the stylesheet: the bundled CSS design system is
|
|
15
|
+
> framework-agnostic and can be used on its own in any project.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @sveltekitchen/sveltekitchen
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`svelte@^5` is a peer dependency.
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
Load the CSS **once**, globally (in your root layout or app entry):
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
import '@sveltekitchen/sveltekitchen/css';
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Then import components anywhere:
|
|
34
|
+
|
|
35
|
+
```svelte
|
|
36
|
+
<script>
|
|
37
|
+
import { Button, Card, Field, Input } from '@sveltekitchen/sveltekitchen';
|
|
38
|
+
|
|
39
|
+
let email = $state('');
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<Card title="Welcome" subtitle="Sign in to continue">
|
|
43
|
+
<Field label="Email" required>
|
|
44
|
+
<Input bind:value={email} type="email" iconLeft="mail" />
|
|
45
|
+
</Field>
|
|
46
|
+
|
|
47
|
+
{#snippet footer()}
|
|
48
|
+
<Button variant="primary" full>Continue</Button>
|
|
49
|
+
{/snippet}
|
|
50
|
+
</Card>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Theming (light / dark)
|
|
54
|
+
|
|
55
|
+
The package ships a tiny reactive theme store. It respects the OS preference on
|
|
56
|
+
first visit and persists the choice to `localStorage`:
|
|
57
|
+
|
|
58
|
+
```svelte
|
|
59
|
+
<script>
|
|
60
|
+
import { theme, ThemeToggle } from '@sveltekitchen/sveltekitchen';
|
|
61
|
+
import { onMount } from 'svelte';
|
|
62
|
+
|
|
63
|
+
onMount(() => theme.init());
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<ThemeToggle />
|
|
67
|
+
|
|
68
|
+
<!-- or drive it yourself -->
|
|
69
|
+
<button onclick={() => theme.toggle()}>
|
|
70
|
+
{theme.isDark ? 'Light' : 'Dark'} mode
|
|
71
|
+
</button>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Icons use [Material Symbols Outlined](https://fonts.google.com/icons); the docs
|
|
75
|
+
site loads them (and the Inter font) via a `<link>` in `app.html`.
|
|
76
|
+
|
|
77
|
+
## What's inside
|
|
78
|
+
|
|
79
|
+
- **~30 UI components** — Button, Card, Modal, ConfirmDialog, Alert, Badge,
|
|
80
|
+
Accordion, Dropdown, Table, Pagination, Progress, Spinner, Tooltip, Divider,
|
|
81
|
+
Collapsible, Calendar, DatePicker, and more.
|
|
82
|
+
- **9 form controls** — Field, Input, Select, Toggle, Checkbox, Radio,
|
|
83
|
+
RadioGroup, Textarea, MaskedInput.
|
|
84
|
+
- **Layout primitives** — `Shell`, `NavItem`, `SidebarProfile` for building an
|
|
85
|
+
app chrome, plus **10 importable app-shell layouts** (top bar, sidebar, icon
|
|
86
|
+
rail, floating dock, and variants).
|
|
87
|
+
- **A bundled CSS design system** — resets, layout utilities, component styles,
|
|
88
|
+
and a full set of design tokens.
|
|
89
|
+
|
|
90
|
+
## CSS architecture — base + overlay
|
|
91
|
+
|
|
92
|
+
The stylesheet is split into two layers:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
css/
|
|
96
|
+
overlay/default.css → CUSTOMIZABLE: every token *value* (colors, type,
|
|
97
|
+
spacing, radius, shadow) for light + dark mode.
|
|
98
|
+
base.css → STRUCTURAL: resets, layout, component styles.
|
|
99
|
+
Consumes tokens via var(); defines none.
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`css/index.css` imports the overlay first (so tokens exist), then the base. To
|
|
103
|
+
ship a custom theme you either replace `overlay/default.css`, or load
|
|
104
|
+
`index.css` and then your own `theme.css` after it:
|
|
105
|
+
|
|
106
|
+
```js
|
|
107
|
+
import '@sveltekitchen/sveltekitchen/css'; // structure + default tokens
|
|
108
|
+
import './my-theme.css'; // overrides token values only
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Because structural CSS only ever reads tokens through `var()`, re-skinning the
|
|
112
|
+
whole system is a matter of overriding custom properties — no component changes.
|
|
113
|
+
|
|
114
|
+
### Package exports
|
|
115
|
+
|
|
116
|
+
| Import | What it is |
|
|
117
|
+
|--------|-----------|
|
|
118
|
+
| `@sveltekitchen/sveltekitchen` | All components + the `theme` store (Svelte). |
|
|
119
|
+
| `@sveltekitchen/sveltekitchen/css` | The full stylesheet (base + default overlay). |
|
|
120
|
+
| `@sveltekitchen/sveltekitchen/css/*` | Individual CSS files, if you want to cherry-pick. |
|
|
121
|
+
| `@sveltekitchen/sveltekitchen/theme` | The theme store on its own. |
|
|
122
|
+
|
|
123
|
+
## Develop
|
|
124
|
+
|
|
125
|
+
The library is consumed **from source** by the [docs site](https://github.com/sveltekitchen/designsystem),
|
|
126
|
+
which gives you instant hot-reload while iterating. Clone both repos side by
|
|
127
|
+
side and run the docs site — see its README for details.
|
|
128
|
+
|
|
129
|
+
Build the distributable package locally:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
npm install
|
|
133
|
+
npm run package # svelte-package: src/lib → dist/
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## License
|
|
137
|
+
|
|
138
|
+
MIT
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { setContext } from 'svelte';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
variant = 'default',
|
|
6
|
+
multiple = false,
|
|
7
|
+
children,
|
|
8
|
+
} = $props();
|
|
9
|
+
|
|
10
|
+
const variantClass = {
|
|
11
|
+
default: '',
|
|
12
|
+
separated: 'accordion-separated',
|
|
13
|
+
minimal: 'accordion-minimal',
|
|
14
|
+
flush: 'accordion-flush',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
let openItems = $state(new Set());
|
|
18
|
+
|
|
19
|
+
setContext('accordion', {
|
|
20
|
+
get openItems() { return openItems; },
|
|
21
|
+
toggle(id) {
|
|
22
|
+
const next = multiple ? new Set(openItems) : new Set();
|
|
23
|
+
if (openItems.has(id)) next.delete(id);
|
|
24
|
+
else next.add(id);
|
|
25
|
+
openItems = next;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
let classes = $derived(
|
|
30
|
+
['accordion', variantClass[variant]].filter(Boolean).join(' ')
|
|
31
|
+
);
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<div class={classes}>
|
|
35
|
+
{@render children?.()}
|
|
36
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default Accordion;
|
|
2
|
+
type Accordion = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const Accordion: import("svelte").Component<{
|
|
7
|
+
variant?: string;
|
|
8
|
+
multiple?: boolean;
|
|
9
|
+
children: any;
|
|
10
|
+
}, {}, "">;
|
|
11
|
+
type $$ComponentProps = {
|
|
12
|
+
variant?: string;
|
|
13
|
+
multiple?: boolean;
|
|
14
|
+
children: any;
|
|
15
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { getContext, onMount } from 'svelte';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
title = '',
|
|
6
|
+
subtitle = undefined,
|
|
7
|
+
icon = undefined,
|
|
8
|
+
badge = undefined,
|
|
9
|
+
initialOpen = false,
|
|
10
|
+
children,
|
|
11
|
+
} = $props();
|
|
12
|
+
|
|
13
|
+
const id = Math.random().toString(36).slice(2);
|
|
14
|
+
const ctx = getContext('accordion');
|
|
15
|
+
|
|
16
|
+
// standalone (no accordion context) falls back to local state
|
|
17
|
+
let localOpen = $state(initialOpen);
|
|
18
|
+
|
|
19
|
+
let isOpen = $derived(ctx ? ctx.openItems.has(id) : localOpen);
|
|
20
|
+
|
|
21
|
+
function toggle() {
|
|
22
|
+
if (ctx) ctx.toggle(id);
|
|
23
|
+
else localOpen = !localOpen;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// height animation
|
|
27
|
+
let bodyEl = $state(null);
|
|
28
|
+
let bodyInnerEl = $state(null);
|
|
29
|
+
|
|
30
|
+
$effect(() => {
|
|
31
|
+
if (!bodyEl || !bodyInnerEl) return;
|
|
32
|
+
if (isOpen) {
|
|
33
|
+
bodyEl.style.height = bodyInnerEl.scrollHeight + 'px';
|
|
34
|
+
const onend = () => { if (isOpen) bodyEl.style.height = 'auto'; };
|
|
35
|
+
bodyEl.addEventListener('transitionend', onend, { once: true });
|
|
36
|
+
} else {
|
|
37
|
+
if (bodyEl.style.height === 'auto') {
|
|
38
|
+
bodyEl.style.height = bodyInnerEl.scrollHeight + 'px';
|
|
39
|
+
}
|
|
40
|
+
requestAnimationFrame(() => { bodyEl.style.height = '0'; });
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
onMount(() => {
|
|
45
|
+
if (initialOpen && ctx) ctx.toggle(id);
|
|
46
|
+
});
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<div class="accordion-item" class:open={isOpen}>
|
|
50
|
+
|
|
51
|
+
<button class="accordion-trigger" onclick={toggle} aria-expanded={isOpen}>
|
|
52
|
+
<div class="accordion-trigger-content">
|
|
53
|
+
{#if icon}
|
|
54
|
+
<span class="material-symbols-outlined accordion-icon">{icon}</span>
|
|
55
|
+
{/if}
|
|
56
|
+
<div>
|
|
57
|
+
<div class="accordion-title">{title}</div>
|
|
58
|
+
{#if subtitle}<div class="accordion-subtitle">{subtitle}</div>{/if}
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
{#if badge !== undefined}
|
|
62
|
+
<span class="accordion-badge">{badge}</span>
|
|
63
|
+
{/if}
|
|
64
|
+
<span class="material-symbols-outlined accordion-chevron">expand_more</span>
|
|
65
|
+
</button>
|
|
66
|
+
|
|
67
|
+
<div class="accordion-body" style="height:{initialOpen ? 'auto' : '0'}" bind:this={bodyEl}>
|
|
68
|
+
<div class="accordion-body-inner" bind:this={bodyInnerEl}>
|
|
69
|
+
{@render children?.()}
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
</div>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export default AccordionItem;
|
|
2
|
+
type AccordionItem = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const AccordionItem: import("svelte").Component<{
|
|
7
|
+
title?: string;
|
|
8
|
+
subtitle?: any;
|
|
9
|
+
icon?: any;
|
|
10
|
+
badge?: any;
|
|
11
|
+
initialOpen?: boolean;
|
|
12
|
+
children: any;
|
|
13
|
+
}, {}, "">;
|
|
14
|
+
type $$ComponentProps = {
|
|
15
|
+
title?: string;
|
|
16
|
+
subtitle?: any;
|
|
17
|
+
icon?: any;
|
|
18
|
+
badge?: any;
|
|
19
|
+
initialOpen?: boolean;
|
|
20
|
+
children: any;
|
|
21
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
let {
|
|
3
|
+
variant = 'info',
|
|
4
|
+
title = undefined,
|
|
5
|
+
children,
|
|
6
|
+
...restProps
|
|
7
|
+
} = $props();
|
|
8
|
+
|
|
9
|
+
const icons = {
|
|
10
|
+
info: 'info',
|
|
11
|
+
success: 'check_circle',
|
|
12
|
+
warning: 'warning',
|
|
13
|
+
error: 'error',
|
|
14
|
+
};
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<div class="alert alert-{variant}" {...restProps}>
|
|
18
|
+
<span class="material-symbols-outlined alert-icon">{icons[variant]}</span>
|
|
19
|
+
<div class="alert-content">
|
|
20
|
+
{#if title}<div class="alert-title">{title}</div>{/if}
|
|
21
|
+
{@render children?.()}
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default Alert;
|
|
2
|
+
type Alert = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const Alert: import("svelte").Component<{
|
|
7
|
+
variant?: string;
|
|
8
|
+
title?: any;
|
|
9
|
+
children: any;
|
|
10
|
+
} & Record<string, any>, {}, "">;
|
|
11
|
+
type $$ComponentProps = {
|
|
12
|
+
variant?: string;
|
|
13
|
+
title?: any;
|
|
14
|
+
children: any;
|
|
15
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
let {
|
|
3
|
+
variant = 'default',
|
|
4
|
+
dot = false,
|
|
5
|
+
children,
|
|
6
|
+
...restProps
|
|
7
|
+
} = $props();
|
|
8
|
+
|
|
9
|
+
let classes = $derived(
|
|
10
|
+
['badge', `badge-${variant}`, dot ? 'badge-dot' : ''].filter(Boolean).join(' ')
|
|
11
|
+
);
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<span class={classes} {...restProps}>
|
|
15
|
+
{@render children?.()}
|
|
16
|
+
</span>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default Badge;
|
|
2
|
+
type Badge = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const Badge: import("svelte").Component<{
|
|
7
|
+
variant?: string;
|
|
8
|
+
dot?: boolean;
|
|
9
|
+
children: any;
|
|
10
|
+
} & Record<string, any>, {}, "">;
|
|
11
|
+
type $$ComponentProps = {
|
|
12
|
+
variant?: string;
|
|
13
|
+
dot?: boolean;
|
|
14
|
+
children: any;
|
|
15
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Spinner from './Spinner.svelte';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {'primary'|'secondary'|'ghost'|'danger'|'danger-outline'} Variant
|
|
6
|
+
* @typedef {'sm'|'md'|'lg'} Size
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
let {
|
|
10
|
+
variant = 'primary',
|
|
11
|
+
size = 'md',
|
|
12
|
+
full = false,
|
|
13
|
+
icon = false,
|
|
14
|
+
disabled = false,
|
|
15
|
+
loading = false,
|
|
16
|
+
href = undefined,
|
|
17
|
+
children,
|
|
18
|
+
...restProps
|
|
19
|
+
} = $props();
|
|
20
|
+
|
|
21
|
+
const sizeClass = { sm: 'btn-sm', md: '', lg: 'btn-lg' };
|
|
22
|
+
|
|
23
|
+
let classes = $derived([
|
|
24
|
+
'btn',
|
|
25
|
+
`btn-${variant}`,
|
|
26
|
+
sizeClass[size],
|
|
27
|
+
full ? 'btn-full' : '',
|
|
28
|
+
icon ? 'btn-icon' : '',
|
|
29
|
+
loading ? 'btn-loading' : '',
|
|
30
|
+
].filter(Boolean).join(' '));
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
{#if href}
|
|
34
|
+
<a {href} class={classes} aria-busy={loading} {...restProps}>
|
|
35
|
+
{#if loading}<Spinner />{/if}
|
|
36
|
+
{@render children?.()}
|
|
37
|
+
</a>
|
|
38
|
+
{:else}
|
|
39
|
+
<button class={classes} disabled={disabled || loading} aria-busy={loading} {...restProps}>
|
|
40
|
+
{#if loading}<Spinner />{/if}
|
|
41
|
+
{@render children?.()}
|
|
42
|
+
</button>
|
|
43
|
+
{/if}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export default Button;
|
|
2
|
+
type Button = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const Button: import("svelte").Component<{
|
|
7
|
+
variant?: string;
|
|
8
|
+
size?: string;
|
|
9
|
+
full?: boolean;
|
|
10
|
+
icon?: boolean;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
loading?: boolean;
|
|
13
|
+
href?: any;
|
|
14
|
+
children: any;
|
|
15
|
+
} & Record<string, any>, {}, "">;
|
|
16
|
+
type $$ComponentProps = {
|
|
17
|
+
variant?: string;
|
|
18
|
+
size?: string;
|
|
19
|
+
full?: boolean;
|
|
20
|
+
icon?: boolean;
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
loading?: boolean;
|
|
23
|
+
href?: any;
|
|
24
|
+
children: any;
|
|
25
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
// Calendar — an inline month grid. Click a day to select it.
|
|
3
|
+
// Simple by design: no keyboard nav, no min/max, no time — just a date.
|
|
4
|
+
//
|
|
5
|
+
// `value` is a JS Date (or null), bindable.
|
|
6
|
+
let {
|
|
7
|
+
value = $bindable(null),
|
|
8
|
+
onselect,
|
|
9
|
+
...restProps
|
|
10
|
+
} = $props();
|
|
11
|
+
|
|
12
|
+
const MONTHS = ['January','February','March','April','May','June',
|
|
13
|
+
'July','August','September','October','November','December'];
|
|
14
|
+
const DOW = ['Su','Mo','Tu','We','Th','Fr','Sa'];
|
|
15
|
+
|
|
16
|
+
// The month currently being viewed (defaults to value's month, or today).
|
|
17
|
+
let view = $state(startOfMonth(value ?? new Date()));
|
|
18
|
+
|
|
19
|
+
function startOfMonth(d) { return new Date(d.getFullYear(), d.getMonth(), 1); }
|
|
20
|
+
function sameDay(a, b) {
|
|
21
|
+
return a && b &&
|
|
22
|
+
a.getFullYear() === b.getFullYear() &&
|
|
23
|
+
a.getMonth() === b.getMonth() &&
|
|
24
|
+
a.getDate() === b.getDate();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const today = new Date();
|
|
28
|
+
|
|
29
|
+
let cells = $derived.by(() => {
|
|
30
|
+
const year = view.getFullYear();
|
|
31
|
+
const month = view.getMonth();
|
|
32
|
+
const startDay = new Date(year, month, 1).getDay();
|
|
33
|
+
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
34
|
+
const prevDays = new Date(year, month, 0).getDate();
|
|
35
|
+
const out = [];
|
|
36
|
+
for (let i = startDay - 1; i >= 0; i--) {
|
|
37
|
+
out.push({ date: new Date(year, month - 1, prevDays - i), outside: true });
|
|
38
|
+
}
|
|
39
|
+
for (let d = 1; d <= daysInMonth; d++) {
|
|
40
|
+
out.push({ date: new Date(year, month, d), outside: false });
|
|
41
|
+
}
|
|
42
|
+
let next = 1;
|
|
43
|
+
while (out.length < 42) {
|
|
44
|
+
out.push({ date: new Date(year, month + 1, next++), outside: true });
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
function prevMonth() { view = new Date(view.getFullYear(), view.getMonth() - 1, 1); }
|
|
50
|
+
function nextMonth() { view = new Date(view.getFullYear(), view.getMonth() + 1, 1); }
|
|
51
|
+
|
|
52
|
+
function pick(date) {
|
|
53
|
+
value = date;
|
|
54
|
+
// if they clicked an outside day, follow to that month
|
|
55
|
+
if (date.getMonth() !== view.getMonth()) view = startOfMonth(date);
|
|
56
|
+
onselect?.(date);
|
|
57
|
+
}
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<div class="calendar" {...restProps}>
|
|
61
|
+
<div class="calendar-header">
|
|
62
|
+
<button type="button" class="calendar-nav" onclick={prevMonth} aria-label="Previous month">
|
|
63
|
+
<span class="material-symbols-outlined">chevron_left</span>
|
|
64
|
+
</button>
|
|
65
|
+
<div class="calendar-title">{MONTHS[view.getMonth()]} {view.getFullYear()}</div>
|
|
66
|
+
<button type="button" class="calendar-nav" onclick={nextMonth} aria-label="Next month">
|
|
67
|
+
<span class="material-symbols-outlined">chevron_right</span>
|
|
68
|
+
</button>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<div class="calendar-grid">
|
|
72
|
+
{#each DOW as d}
|
|
73
|
+
<div class="calendar-dow">{d}</div>
|
|
74
|
+
{/each}
|
|
75
|
+
{#each cells as cell}
|
|
76
|
+
<button
|
|
77
|
+
type="button"
|
|
78
|
+
class="calendar-day"
|
|
79
|
+
class:outside={cell.outside}
|
|
80
|
+
class:selected={sameDay(cell.date, value)}
|
|
81
|
+
class:today={sameDay(cell.date, today)}
|
|
82
|
+
onclick={() => pick(cell.date)}
|
|
83
|
+
>
|
|
84
|
+
{cell.date.getDate()}
|
|
85
|
+
</button>
|
|
86
|
+
{/each}
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export default Calendar;
|
|
2
|
+
type Calendar = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const Calendar: import("svelte").Component<{
|
|
7
|
+
value?: any;
|
|
8
|
+
onselect: any;
|
|
9
|
+
} & Record<string, any>, {}, "value">;
|
|
10
|
+
type $$ComponentProps = {
|
|
11
|
+
value?: any;
|
|
12
|
+
onselect: any;
|
|
13
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
let {
|
|
3
|
+
variant = 'default',
|
|
4
|
+
padding = 'md',
|
|
5
|
+
title = undefined,
|
|
6
|
+
subtitle = undefined,
|
|
7
|
+
header,
|
|
8
|
+
actions,
|
|
9
|
+
footer,
|
|
10
|
+
children,
|
|
11
|
+
...restProps
|
|
12
|
+
} = $props();
|
|
13
|
+
|
|
14
|
+
const variantClass = {
|
|
15
|
+
default: '',
|
|
16
|
+
flat: 'card-flat',
|
|
17
|
+
raised: 'card-raised',
|
|
18
|
+
interactive: 'card-interactive',
|
|
19
|
+
selected: 'card-selected',
|
|
20
|
+
subtle: 'card-subtle',
|
|
21
|
+
info: 'card-info',
|
|
22
|
+
success: 'card-success',
|
|
23
|
+
warning: 'card-warning',
|
|
24
|
+
error: 'card-error',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const paddingClass = {
|
|
28
|
+
sm: 'card-body-sm',
|
|
29
|
+
md: '',
|
|
30
|
+
lg: 'card-body-lg',
|
|
31
|
+
none: 'card-body-none',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
let cardClass = $derived(['card', variantClass[variant]].filter(Boolean).join(' '));
|
|
35
|
+
let bodyClass = $derived(['card-body', paddingClass[padding]].filter(Boolean).join(' '));
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<div class={cardClass} {...restProps}>
|
|
39
|
+
|
|
40
|
+
{#if header || title}
|
|
41
|
+
<div class="card-header">
|
|
42
|
+
{#if header}
|
|
43
|
+
{@render header()}
|
|
44
|
+
{:else}
|
|
45
|
+
<div>
|
|
46
|
+
<h3 class="card-title">{title}</h3>
|
|
47
|
+
{#if subtitle}<p class="card-subtitle">{subtitle}</p>{/if}
|
|
48
|
+
</div>
|
|
49
|
+
{/if}
|
|
50
|
+
{#if actions}
|
|
51
|
+
<div class="card-header-actions">
|
|
52
|
+
{@render actions()}
|
|
53
|
+
</div>
|
|
54
|
+
{/if}
|
|
55
|
+
</div>
|
|
56
|
+
{/if}
|
|
57
|
+
|
|
58
|
+
<div class={bodyClass}>
|
|
59
|
+
{@render children?.()}
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
{#if footer}
|
|
63
|
+
<div class="card-footer">
|
|
64
|
+
{@render footer()}
|
|
65
|
+
</div>
|
|
66
|
+
{/if}
|
|
67
|
+
|
|
68
|
+
</div>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export default Card;
|
|
2
|
+
type Card = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const Card: import("svelte").Component<{
|
|
7
|
+
variant?: string;
|
|
8
|
+
padding?: string;
|
|
9
|
+
title?: any;
|
|
10
|
+
subtitle?: any;
|
|
11
|
+
header: any;
|
|
12
|
+
actions: any;
|
|
13
|
+
footer: any;
|
|
14
|
+
children: any;
|
|
15
|
+
} & Record<string, any>, {}, "">;
|
|
16
|
+
type $$ComponentProps = {
|
|
17
|
+
variant?: string;
|
|
18
|
+
padding?: string;
|
|
19
|
+
title?: any;
|
|
20
|
+
subtitle?: any;
|
|
21
|
+
header: any;
|
|
22
|
+
actions: any;
|
|
23
|
+
footer: any;
|
|
24
|
+
children: any;
|
|
25
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
// Chevron — a small clickable chevron that toggles a bindable `expanded`
|
|
3
|
+
// state. Use it to drive show/hide of anything yourself:
|
|
4
|
+
//
|
|
5
|
+
// <script> let open = $state(false); <\/script>
|
|
6
|
+
// <Chevron bind:expanded={open} /> {#if open}…{/if}
|
|
7
|
+
//
|
|
8
|
+
// Purely presentational + the boolean; it doesn't hide anything itself.
|
|
9
|
+
let {
|
|
10
|
+
expanded = $bindable(false),
|
|
11
|
+
size = undefined, // e.g. '1.25rem'; defaults to 1.25em
|
|
12
|
+
direction = 'right', // collapsed-state direction: 'right' | 'down'
|
|
13
|
+
disabled = false,
|
|
14
|
+
onToggle,
|
|
15
|
+
...restProps
|
|
16
|
+
} = $props();
|
|
17
|
+
|
|
18
|
+
function toggle() {
|
|
19
|
+
if (disabled) return;
|
|
20
|
+
expanded = !expanded;
|
|
21
|
+
onToggle?.(expanded);
|
|
22
|
+
}
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<button
|
|
26
|
+
type="button"
|
|
27
|
+
class="chevron chevron-from-{direction}"
|
|
28
|
+
class:expanded
|
|
29
|
+
{disabled}
|
|
30
|
+
aria-expanded={expanded}
|
|
31
|
+
onclick={toggle}
|
|
32
|
+
style:font-size={size}
|
|
33
|
+
{...restProps}
|
|
34
|
+
>
|
|
35
|
+
<span class="material-symbols-outlined">chevron_right</span>
|
|
36
|
+
</button>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export default Chevron;
|
|
2
|
+
type Chevron = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const Chevron: import("svelte").Component<{
|
|
7
|
+
expanded?: boolean;
|
|
8
|
+
size?: any;
|
|
9
|
+
direction?: string;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
onToggle: any;
|
|
12
|
+
} & Record<string, any>, {}, "expanded">;
|
|
13
|
+
type $$ComponentProps = {
|
|
14
|
+
expanded?: boolean;
|
|
15
|
+
size?: any;
|
|
16
|
+
direction?: string;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
onToggle: any;
|
|
19
|
+
} & Record<string, any>;
|