@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
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
// FloatingTopLayout — a detached, rounded top menu card floating over the
|
|
3
|
+
// content. Nestable single-region layout. Items with children open a flyout.
|
|
4
|
+
import ThemeToggle from '../components/ThemeToggle.svelte';
|
|
5
|
+
|
|
6
|
+
let { menu = $bindable(undefined), actions, children, ...rest } = $props();
|
|
7
|
+
let items = $derived(menu ? (menu.sections ?? []).flatMap((s) => s.items ?? []) : []);
|
|
8
|
+
let brand = $derived(menu?.brand ?? {});
|
|
9
|
+
|
|
10
|
+
let openId = $state(null);
|
|
11
|
+
const toggle = (id) => { openId = openId === id ? null : id; };
|
|
12
|
+
$effect(() => {
|
|
13
|
+
if (openId == null) return;
|
|
14
|
+
const onDoc = (e) => { if (!e.target.closest?.('.nlf-flywrap')) openId = null; };
|
|
15
|
+
document.addEventListener('pointerdown', onDoc);
|
|
16
|
+
return () => document.removeEventListener('pointerdown', onDoc);
|
|
17
|
+
});
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<div class="nl nl-col nlf-pad" {...rest}>
|
|
21
|
+
{#if menu}
|
|
22
|
+
<div class="nlf-topcard">
|
|
23
|
+
{#if brand.label}
|
|
24
|
+
<div class="nlf-brand">
|
|
25
|
+
{#if brand.icon}<span class="material-symbols-outlined nlf-brand-icon">{brand.icon}</span>{/if}
|
|
26
|
+
{brand.label}
|
|
27
|
+
</div>
|
|
28
|
+
{/if}
|
|
29
|
+
<nav class="nlf-topnav">
|
|
30
|
+
{#each items as item}
|
|
31
|
+
{#if item.children?.length}
|
|
32
|
+
<div class="nlf-flywrap">
|
|
33
|
+
<button
|
|
34
|
+
type="button"
|
|
35
|
+
class="nlf-toplink nlf-parent"
|
|
36
|
+
class:active={item.id === menu.active || item.children.some((c) => c.id === menu.active)}
|
|
37
|
+
aria-expanded={openId === item.id}
|
|
38
|
+
aria-haspopup="menu"
|
|
39
|
+
onclick={() => toggle(item.id)}
|
|
40
|
+
>
|
|
41
|
+
{#if item.icon}<span class="material-symbols-outlined">{item.icon}</span>{/if}
|
|
42
|
+
{item.label}
|
|
43
|
+
<span class="material-symbols-outlined nlf-caret" class:open={openId === item.id}>expand_more</span>
|
|
44
|
+
</button>
|
|
45
|
+
{#if openId === item.id}
|
|
46
|
+
<div class="nlf-flyout">
|
|
47
|
+
{#each item.children as child}
|
|
48
|
+
<a href={child.href} class="nlf-flyout-item" class:active={child.id === menu.active} onclick={() => (openId = null)}>
|
|
49
|
+
{#if child.icon}<span class="material-symbols-outlined">{child.icon}</span>{/if}
|
|
50
|
+
{child.label}
|
|
51
|
+
</a>
|
|
52
|
+
{/each}
|
|
53
|
+
</div>
|
|
54
|
+
{/if}
|
|
55
|
+
</div>
|
|
56
|
+
{:else}
|
|
57
|
+
<a href={item.href} class="nlf-toplink" class:active={item.id === menu.active}>
|
|
58
|
+
{#if item.icon}<span class="material-symbols-outlined">{item.icon}</span>{/if}
|
|
59
|
+
{item.label}
|
|
60
|
+
</a>
|
|
61
|
+
{/if}
|
|
62
|
+
{/each}
|
|
63
|
+
</nav>
|
|
64
|
+
<div class="nlf-actions">{#if actions}{@render actions()}{:else}<ThemeToggle />{/if}</div>
|
|
65
|
+
</div>
|
|
66
|
+
{/if}
|
|
67
|
+
<div class="nl-fill nl-scroll nlf-content">{@render children?.()}</div>
|
|
68
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default FloatingTopLayout;
|
|
2
|
+
type FloatingTopLayout = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const FloatingTopLayout: import("svelte").Component<{
|
|
7
|
+
menu?: any;
|
|
8
|
+
actions: any;
|
|
9
|
+
children: any;
|
|
10
|
+
} & Record<string, any>, {}, "menu">;
|
|
11
|
+
type $$ComponentProps = {
|
|
12
|
+
menu?: any;
|
|
13
|
+
actions: any;
|
|
14
|
+
children: any;
|
|
15
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
// HybridLayout — global top bar + contextual side rail + content.
|
|
3
|
+
// The clearest demonstration of composition: it's just TopBar + SideBar.
|
|
4
|
+
import TopBar from './TopBar.svelte';
|
|
5
|
+
import SideBar from './SideBar.svelte';
|
|
6
|
+
|
|
7
|
+
let { menu = { brand: {}, sections: [], active: undefined }, actions, footer, children, ...rest } = $props();
|
|
8
|
+
// top bar shows the section groups as top-level tabs; sidebar shows items
|
|
9
|
+
let topItems = $derived((menu.sections ?? []).map((s, i) => ({
|
|
10
|
+
id: `sec-${i}`, label: s.label ?? 'Menu', href: '#'
|
|
11
|
+
})));
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<div class="lyt lyt-col" {...rest}>
|
|
15
|
+
<TopBar brand={menu.brand} items={topItems} variant="tabs" {actions} />
|
|
16
|
+
<div class="lyt-row lyt-fill">
|
|
17
|
+
<SideBar sections={menu.sections} active={menu.active} {footer} />
|
|
18
|
+
<main class="lyt-main"><div class="lyt-inner">{@render children?.()}</div></main>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export default HybridLayout;
|
|
2
|
+
type HybridLayout = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const HybridLayout: import("svelte").Component<{
|
|
7
|
+
menu?: Record<string, any>;
|
|
8
|
+
actions: any;
|
|
9
|
+
footer: any;
|
|
10
|
+
children: any;
|
|
11
|
+
} & Record<string, any>, {}, "">;
|
|
12
|
+
type $$ComponentProps = {
|
|
13
|
+
menu?: Record<string, any>;
|
|
14
|
+
actions: any;
|
|
15
|
+
footer: any;
|
|
16
|
+
children: any;
|
|
17
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
// IconRailLayout — a narrow, icon-only vertical rail + fillable content.
|
|
3
|
+
// Icons show a tooltip label on hover. Nestable like the other bar layouts.
|
|
4
|
+
import ThemeToggle from '../components/ThemeToggle.svelte';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
menu = $bindable(undefined), // { brand?, sections, active? } — labels used as tooltips
|
|
8
|
+
footer,
|
|
9
|
+
children,
|
|
10
|
+
...rest
|
|
11
|
+
} = $props();
|
|
12
|
+
|
|
13
|
+
let items = $derived(menu ? (menu.sections ?? []).flatMap((s) => s.items ?? []) : []);
|
|
14
|
+
let brand = $derived(menu?.brand ?? {});
|
|
15
|
+
|
|
16
|
+
let openId = $state(null);
|
|
17
|
+
const toggle = (id) => { openId = openId === id ? null : id; };
|
|
18
|
+
$effect(() => {
|
|
19
|
+
if (openId == null) return;
|
|
20
|
+
const onDoc = (e) => { if (!e.target.closest?.('.ir-flywrap')) openId = null; };
|
|
21
|
+
document.addEventListener('pointerdown', onDoc);
|
|
22
|
+
return () => document.removeEventListener('pointerdown', onDoc);
|
|
23
|
+
});
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<div class="nl nl-row" {...rest}>
|
|
27
|
+
{#if menu}
|
|
28
|
+
<div class="ir">
|
|
29
|
+
{#if brand.icon}
|
|
30
|
+
<a href={brand.href ?? '#'} class="ir-brand" aria-label={brand.label}>
|
|
31
|
+
<span class="material-symbols-outlined">{brand.icon}</span>
|
|
32
|
+
</a>
|
|
33
|
+
{/if}
|
|
34
|
+
<nav class="ir-nav">
|
|
35
|
+
{#each items as item}
|
|
36
|
+
{#if item.children?.length}
|
|
37
|
+
<div class="ir-flywrap">
|
|
38
|
+
<button
|
|
39
|
+
class="ir-item"
|
|
40
|
+
class:active={item.id === menu.active || item.children.some((c) => c.id === menu.active)}
|
|
41
|
+
aria-label={item.label}
|
|
42
|
+
onclick={() => toggle(item.id)}
|
|
43
|
+
>
|
|
44
|
+
{#if item.icon}<span class="material-symbols-outlined">{item.icon}</span>{/if}
|
|
45
|
+
<span class="ir-tip">{item.label}</span>
|
|
46
|
+
{#if item.badge != null}<span class="ir-dot"></span>{/if}
|
|
47
|
+
</button>
|
|
48
|
+
{#if openId === item.id}
|
|
49
|
+
<div class="ir-flyout">
|
|
50
|
+
<div class="ir-flyout-head">{item.label}</div>
|
|
51
|
+
{#each item.children as child}
|
|
52
|
+
<a href={child.href} class="ir-flyout-item" class:active={child.id === menu.active} onclick={() => (openId = null)}>
|
|
53
|
+
{#if child.icon}<span class="material-symbols-outlined">{child.icon}</span>{/if}
|
|
54
|
+
{child.label}
|
|
55
|
+
</a>
|
|
56
|
+
{/each}
|
|
57
|
+
</div>
|
|
58
|
+
{/if}
|
|
59
|
+
</div>
|
|
60
|
+
{:else}
|
|
61
|
+
<a href={item.href} class="ir-item" class:active={item.id === menu.active} aria-label={item.label}>
|
|
62
|
+
{#if item.icon}<span class="material-symbols-outlined">{item.icon}</span>{/if}
|
|
63
|
+
<span class="ir-tip">{item.label}</span>
|
|
64
|
+
{#if item.badge != null}<span class="ir-dot"></span>{/if}
|
|
65
|
+
</a>
|
|
66
|
+
{/if}
|
|
67
|
+
{/each}
|
|
68
|
+
</nav>
|
|
69
|
+
<div class="ir-foot">
|
|
70
|
+
{#if footer}{@render footer()}{:else}<ThemeToggle />{/if}
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
{/if}
|
|
74
|
+
<div class="nl-fill nl-scroll">{@render children?.()}</div>
|
|
75
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default IconRailLayout;
|
|
2
|
+
type IconRailLayout = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const IconRailLayout: import("svelte").Component<{
|
|
7
|
+
menu?: any;
|
|
8
|
+
footer: any;
|
|
9
|
+
children: any;
|
|
10
|
+
} & Record<string, any>, {}, "menu">;
|
|
11
|
+
type $$ComponentProps = {
|
|
12
|
+
menu?: any;
|
|
13
|
+
footer: any;
|
|
14
|
+
children: any;
|
|
15
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
// LeftBarLayout — a single left sidebar region + a fillable content area.
|
|
3
|
+
// Nestable: children fill the content area completely.
|
|
4
|
+
import SideBar from './SideBar.svelte';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
menu = $bindable(undefined), // { brand?, sections, active? }
|
|
8
|
+
footer,
|
|
9
|
+
children,
|
|
10
|
+
...rest
|
|
11
|
+
} = $props();
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<div class="nl nl-row" {...rest}>
|
|
15
|
+
{#if menu}
|
|
16
|
+
<SideBar brand={menu.brand} sections={menu.sections} active={menu.active} {footer} />
|
|
17
|
+
{/if}
|
|
18
|
+
<div class="nl-fill nl-scroll">{@render children?.()}</div>
|
|
19
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default LeftBarLayout;
|
|
2
|
+
type LeftBarLayout = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const LeftBarLayout: import("svelte").Component<{
|
|
7
|
+
menu?: any;
|
|
8
|
+
footer: any;
|
|
9
|
+
children: any;
|
|
10
|
+
} & Record<string, any>, {}, "menu">;
|
|
11
|
+
type $$ComponentProps = {
|
|
12
|
+
menu?: any;
|
|
13
|
+
footer: any;
|
|
14
|
+
children: any;
|
|
15
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
// RightBarLayout — a single right-side sidebar region + fillable content.
|
|
3
|
+
// Content comes first, bar on the right. Nestable.
|
|
4
|
+
import SideBar from './SideBar.svelte';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
menu = $bindable(undefined),
|
|
8
|
+
footer,
|
|
9
|
+
children,
|
|
10
|
+
...rest
|
|
11
|
+
} = $props();
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<div class="nl nl-row" {...rest}>
|
|
15
|
+
<div class="nl-fill nl-scroll">{@render children?.()}</div>
|
|
16
|
+
{#if menu}
|
|
17
|
+
<div class="nl-right">
|
|
18
|
+
<SideBar brand={menu.brand} sections={menu.sections} active={menu.active} {footer} />
|
|
19
|
+
</div>
|
|
20
|
+
{/if}
|
|
21
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default RightBarLayout;
|
|
2
|
+
type RightBarLayout = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const RightBarLayout: import("svelte").Component<{
|
|
7
|
+
menu?: any;
|
|
8
|
+
footer: any;
|
|
9
|
+
children: any;
|
|
10
|
+
} & Record<string, any>, {}, "menu">;
|
|
11
|
+
type $$ComponentProps = {
|
|
12
|
+
menu?: any;
|
|
13
|
+
footer: any;
|
|
14
|
+
children: any;
|
|
15
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
// SideBar — a vertical navigation region. Shared building block used by
|
|
3
|
+
// the sidebar-style layouts. Renders an optional brand and the grouped
|
|
4
|
+
// `sections` as a vertical menu. Items may carry `children` for a second
|
|
5
|
+
// level, which expands/collapses (and auto-expands when a child is active).
|
|
6
|
+
let {
|
|
7
|
+
brand = undefined,
|
|
8
|
+
sections = [],
|
|
9
|
+
active = undefined,
|
|
10
|
+
footer,
|
|
11
|
+
...restProps
|
|
12
|
+
} = $props();
|
|
13
|
+
|
|
14
|
+
// Track which parent items are expanded. An item is open if the user toggled
|
|
15
|
+
// it, or (by default) if one of its children is the active item.
|
|
16
|
+
let toggled = $state({});
|
|
17
|
+
|
|
18
|
+
function isChildActive(item) {
|
|
19
|
+
return item.children?.some((c) => c.id === active);
|
|
20
|
+
}
|
|
21
|
+
function isOpen(item) {
|
|
22
|
+
return toggled[item.id] ?? isChildActive(item);
|
|
23
|
+
}
|
|
24
|
+
function toggle(item) {
|
|
25
|
+
toggled = { ...toggled, [item.id]: !isOpen(item) };
|
|
26
|
+
}
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<aside class="sb" {...restProps}>
|
|
30
|
+
{#if brand?.label}
|
|
31
|
+
{#if brand.href}
|
|
32
|
+
<a href={brand.href} class="sb-brand">
|
|
33
|
+
{#if brand.icon}<span class="material-symbols-outlined sb-brand-icon">{brand.icon}</span>{/if}
|
|
34
|
+
{brand.label}
|
|
35
|
+
</a>
|
|
36
|
+
{:else}
|
|
37
|
+
<div class="sb-brand">
|
|
38
|
+
{#if brand.icon}<span class="material-symbols-outlined sb-brand-icon">{brand.icon}</span>{/if}
|
|
39
|
+
{brand.label}
|
|
40
|
+
</div>
|
|
41
|
+
{/if}
|
|
42
|
+
{/if}
|
|
43
|
+
|
|
44
|
+
<nav class="sb-nav">
|
|
45
|
+
{#each sections as section}
|
|
46
|
+
{#if section.label}<div class="sb-section-label">{section.label}</div>{/if}
|
|
47
|
+
{#each section.items as item}
|
|
48
|
+
{#if item.children?.length}
|
|
49
|
+
<button
|
|
50
|
+
type="button"
|
|
51
|
+
class="sb-link sb-parent"
|
|
52
|
+
class:active={item.id === active || isChildActive(item)}
|
|
53
|
+
aria-expanded={isOpen(item)}
|
|
54
|
+
onclick={() => toggle(item)}
|
|
55
|
+
>
|
|
56
|
+
{#if item.icon}<span class="material-symbols-outlined">{item.icon}</span>{/if}
|
|
57
|
+
<span class="sb-link-label">{item.label}</span>
|
|
58
|
+
{#if item.badge != null}<span class="sb-badge">{item.badge}</span>{/if}
|
|
59
|
+
<span class="material-symbols-outlined sb-chevron" class:open={isOpen(item)}>chevron_right</span>
|
|
60
|
+
</button>
|
|
61
|
+
{#if isOpen(item)}
|
|
62
|
+
<div class="sb-subnav">
|
|
63
|
+
{#each item.children as child}
|
|
64
|
+
<a href={child.href} class="sb-sublink" class:active={child.id === active}>
|
|
65
|
+
{#if child.icon}<span class="material-symbols-outlined">{child.icon}</span>{/if}
|
|
66
|
+
<span class="sb-link-label">{child.label}</span>
|
|
67
|
+
{#if child.badge != null}<span class="sb-badge">{child.badge}</span>{/if}
|
|
68
|
+
</a>
|
|
69
|
+
{/each}
|
|
70
|
+
</div>
|
|
71
|
+
{/if}
|
|
72
|
+
{:else}
|
|
73
|
+
<a href={item.href} class="sb-link" class:active={item.id === active}>
|
|
74
|
+
{#if item.icon}<span class="material-symbols-outlined">{item.icon}</span>{/if}
|
|
75
|
+
<span class="sb-link-label">{item.label}</span>
|
|
76
|
+
{#if item.badge != null}<span class="sb-badge">{item.badge}</span>{/if}
|
|
77
|
+
</a>
|
|
78
|
+
{/if}
|
|
79
|
+
{/each}
|
|
80
|
+
{/each}
|
|
81
|
+
</nav>
|
|
82
|
+
|
|
83
|
+
{#if footer}
|
|
84
|
+
<div class="sb-footer">{@render footer()}</div>
|
|
85
|
+
{/if}
|
|
86
|
+
</aside>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export default SideBar;
|
|
2
|
+
type SideBar = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const SideBar: import("svelte").Component<{
|
|
7
|
+
brand?: any;
|
|
8
|
+
sections?: any[];
|
|
9
|
+
active?: any;
|
|
10
|
+
footer: any;
|
|
11
|
+
} & Record<string, any>, {}, "">;
|
|
12
|
+
type $$ComponentProps = {
|
|
13
|
+
brand?: any;
|
|
14
|
+
sections?: any[];
|
|
15
|
+
active?: any;
|
|
16
|
+
footer: any;
|
|
17
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
// SidebarLayout — classic left rail + content. Composed from SideBar.
|
|
3
|
+
import SideBar from './SideBar.svelte';
|
|
4
|
+
|
|
5
|
+
let { menu = { brand: {}, sections: [], active: undefined }, footer, children, ...rest } = $props();
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<div class="lyt lyt-row" {...rest}>
|
|
9
|
+
<SideBar brand={menu.brand} sections={menu.sections} active={menu.active} {footer} />
|
|
10
|
+
<main class="lyt-main"><div class="lyt-inner">{@render children?.()}</div></main>
|
|
11
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default SidebarLayout;
|
|
2
|
+
type SidebarLayout = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const SidebarLayout: import("svelte").Component<{
|
|
7
|
+
menu?: Record<string, any>;
|
|
8
|
+
footer: any;
|
|
9
|
+
children: any;
|
|
10
|
+
} & Record<string, any>, {}, "">;
|
|
11
|
+
type $$ComponentProps = {
|
|
12
|
+
menu?: Record<string, any>;
|
|
13
|
+
footer: any;
|
|
14
|
+
children: any;
|
|
15
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
// TopBar — a horizontal top navigation region. A shared building block used
|
|
3
|
+
// by several layouts (topbar, hybrid, drawer, right, bottom…). Renders brand +
|
|
4
|
+
// an optional horizontal menu + an actions area. When `items` is empty it's
|
|
5
|
+
// just a bar (brand + actions), which is how "bar with no menu" layouts work.
|
|
6
|
+
import ThemeToggle from '../components/ThemeToggle.svelte';
|
|
7
|
+
|
|
8
|
+
let {
|
|
9
|
+
brand = {},
|
|
10
|
+
items = [], // flat list of menu items (already flattened by the layout)
|
|
11
|
+
active = undefined, // active item id
|
|
12
|
+
variant = 'default', // 'default' | 'tabs' (visual style of the links)
|
|
13
|
+
actions, // optional snippet; defaults to a ThemeToggle
|
|
14
|
+
onmenu, // optional: show a hamburger button, called on click
|
|
15
|
+
...restProps
|
|
16
|
+
} = $props();
|
|
17
|
+
|
|
18
|
+
let openId = $state(null);
|
|
19
|
+
function toggle(id) { openId = openId === id ? null : id; }
|
|
20
|
+
$effect(() => {
|
|
21
|
+
if (openId == null) return;
|
|
22
|
+
function onDoc(e) { if (!e.target.closest?.('.tb-flywrap')) openId = null; }
|
|
23
|
+
document.addEventListener('pointerdown', onDoc);
|
|
24
|
+
return () => document.removeEventListener('pointerdown', onDoc);
|
|
25
|
+
});
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<header class="tb" {...restProps}>
|
|
29
|
+
{#if onmenu}
|
|
30
|
+
<button class="tb-hamburger" aria-label="Open menu" onclick={onmenu}>
|
|
31
|
+
<span class="material-symbols-outlined">menu</span>
|
|
32
|
+
</button>
|
|
33
|
+
{/if}
|
|
34
|
+
|
|
35
|
+
{#if brand?.href}
|
|
36
|
+
<a href={brand.href} class="tb-brand">
|
|
37
|
+
{#if brand.icon}<span class="material-symbols-outlined tb-brand-icon">{brand.icon}</span>{/if}
|
|
38
|
+
{brand.label}
|
|
39
|
+
</a>
|
|
40
|
+
{:else if brand?.label}
|
|
41
|
+
<div class="tb-brand">
|
|
42
|
+
{#if brand.icon}<span class="material-symbols-outlined tb-brand-icon">{brand.icon}</span>{/if}
|
|
43
|
+
{brand.label}
|
|
44
|
+
</div>
|
|
45
|
+
{/if}
|
|
46
|
+
|
|
47
|
+
{#if items.length}
|
|
48
|
+
<nav class="tb-nav" class:tb-nav-tabs={variant === 'tabs'}>
|
|
49
|
+
{#each items as item}
|
|
50
|
+
{#if item.children}
|
|
51
|
+
<div class="tb-flywrap">
|
|
52
|
+
<button
|
|
53
|
+
type="button"
|
|
54
|
+
class="tb-link tb-parent"
|
|
55
|
+
class:active={item.id === active || item.children.some((c) => c.id === active)}
|
|
56
|
+
aria-expanded={openId === item.id}
|
|
57
|
+
aria-haspopup="menu"
|
|
58
|
+
onclick={() => toggle(item.id)}
|
|
59
|
+
>
|
|
60
|
+
{#if item.icon}<span class="material-symbols-outlined">{item.icon}</span>{/if}
|
|
61
|
+
{item.label}
|
|
62
|
+
<span class="material-symbols-outlined tb-caret" class:open={openId === item.id}>expand_more</span>
|
|
63
|
+
</button>
|
|
64
|
+
{#if openId === item.id}
|
|
65
|
+
<div class="tb-flyout">
|
|
66
|
+
{#each item.children as child}
|
|
67
|
+
<a href={child.href} class="tb-flyout-item" class:active={child.id === active} onclick={() => (openId = null)}>
|
|
68
|
+
{#if child.icon}<span class="material-symbols-outlined">{child.icon}</span>{/if}
|
|
69
|
+
{child.label}
|
|
70
|
+
</a>
|
|
71
|
+
{/each}
|
|
72
|
+
</div>
|
|
73
|
+
{/if}
|
|
74
|
+
</div>
|
|
75
|
+
{:else}
|
|
76
|
+
<a href={item.href} class="tb-link" class:active={item.id === active}>
|
|
77
|
+
{#if item.icon}<span class="material-symbols-outlined">{item.icon}</span>{/if}
|
|
78
|
+
{item.label}
|
|
79
|
+
</a>
|
|
80
|
+
{/if}
|
|
81
|
+
{/each}
|
|
82
|
+
</nav>
|
|
83
|
+
{:else}
|
|
84
|
+
<div class="tb-spacer"></div>
|
|
85
|
+
{/if}
|
|
86
|
+
|
|
87
|
+
<div class="tb-actions">
|
|
88
|
+
{#if actions}{@render actions()}{:else}<ThemeToggle />{/if}
|
|
89
|
+
</div>
|
|
90
|
+
</header>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export default TopBar;
|
|
2
|
+
type TopBar = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const TopBar: import("svelte").Component<{
|
|
7
|
+
brand?: Record<string, any>;
|
|
8
|
+
items?: any[];
|
|
9
|
+
active?: any;
|
|
10
|
+
variant?: string;
|
|
11
|
+
actions: any;
|
|
12
|
+
onmenu: any;
|
|
13
|
+
} & Record<string, any>, {}, "">;
|
|
14
|
+
type $$ComponentProps = {
|
|
15
|
+
brand?: Record<string, any>;
|
|
16
|
+
items?: any[];
|
|
17
|
+
active?: any;
|
|
18
|
+
variant?: string;
|
|
19
|
+
actions: any;
|
|
20
|
+
onmenu: any;
|
|
21
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
// TopBarLayout — a single top bar region + a content area that a child fills.
|
|
3
|
+
// Designed to nest: put another *Layout inside as children and it fills the
|
|
4
|
+
// content area completely.
|
|
5
|
+
//
|
|
6
|
+
// <TopBarLayout menu={topMenu}>
|
|
7
|
+
// ...content, or another layout...
|
|
8
|
+
// </TopBarLayout>
|
|
9
|
+
import TopBar from './TopBar.svelte';
|
|
10
|
+
|
|
11
|
+
let {
|
|
12
|
+
menu = $bindable(undefined), // { brand?, sections, active? }
|
|
13
|
+
actions,
|
|
14
|
+
children,
|
|
15
|
+
...rest
|
|
16
|
+
} = $props();
|
|
17
|
+
|
|
18
|
+
let items = $derived(menu ? (menu.sections ?? []).flatMap((s) => s.items ?? []) : []);
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<div class="nl nl-col" {...rest}>
|
|
22
|
+
{#if menu}
|
|
23
|
+
<TopBar brand={menu.brand} {items} active={menu.active} {actions} />
|
|
24
|
+
{/if}
|
|
25
|
+
<div class="nl-fill">{@render children?.()}</div>
|
|
26
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default TopBarLayout;
|
|
2
|
+
type TopBarLayout = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const TopBarLayout: import("svelte").Component<{
|
|
7
|
+
menu?: any;
|
|
8
|
+
actions: any;
|
|
9
|
+
children: any;
|
|
10
|
+
} & Record<string, any>, {}, "menu">;
|
|
11
|
+
type $$ComponentProps = {
|
|
12
|
+
menu?: any;
|
|
13
|
+
actions: any;
|
|
14
|
+
children: any;
|
|
15
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export namespace theme {
|
|
2
|
+
export const current: string;
|
|
3
|
+
export const isDark: boolean;
|
|
4
|
+
export { init };
|
|
5
|
+
export { toggle };
|
|
6
|
+
export { set };
|
|
7
|
+
}
|
|
8
|
+
declare function init(): void;
|
|
9
|
+
declare function toggle(): void;
|
|
10
|
+
declare function set(value: any): void;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// theme.svelte.js
|
|
2
|
+
// Usage: import { theme } from '@sveltekitchen/sveltekitchen/theme'; theme.init(); theme.toggle();
|
|
3
|
+
|
|
4
|
+
const STORAGE_KEY = 'theme';
|
|
5
|
+
|
|
6
|
+
function createTheme() {
|
|
7
|
+
let current = $state('light');
|
|
8
|
+
|
|
9
|
+
function apply(value) {
|
|
10
|
+
current = value;
|
|
11
|
+
if (typeof document !== 'undefined') {
|
|
12
|
+
document.documentElement.setAttribute('data-theme', value);
|
|
13
|
+
}
|
|
14
|
+
if (typeof localStorage !== 'undefined') {
|
|
15
|
+
localStorage.setItem(STORAGE_KEY, value);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function init() {
|
|
20
|
+
if (typeof localStorage === 'undefined') return;
|
|
21
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
22
|
+
if (stored) {
|
|
23
|
+
apply(stored);
|
|
24
|
+
} else {
|
|
25
|
+
// Respect OS preference on first visit
|
|
26
|
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
27
|
+
apply(prefersDark ? 'dark' : 'light');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function toggle() {
|
|
32
|
+
apply(current === 'light' ? 'dark' : 'light');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function set(value) {
|
|
36
|
+
apply(value);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
get current() { return current; },
|
|
41
|
+
get isDark() { return current === 'dark'; },
|
|
42
|
+
init,
|
|
43
|
+
toggle,
|
|
44
|
+
set,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const theme = createTheme();
|