@noxlovette/material 0.2.1 → 0.3.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/claude-skill/material-design/SKILL.md +3 -1
- package/claude-skill/material-design/references/component-patterns.md +15 -0
- package/dist/components/badge/Badge.stories.svelte +58 -0
- package/dist/components/badge/Badge.stories.svelte.d.ts +19 -0
- package/dist/components/buttons/Button.stories.svelte +50 -0
- package/dist/components/buttons/Button.stories.svelte.d.ts +19 -0
- package/dist/components/buttons/ButtonIcon.stories.svelte +74 -0
- package/dist/components/buttons/ButtonIcon.stories.svelte.d.ts +19 -0
- package/dist/components/buttons/FAB.stories.svelte +59 -0
- package/dist/components/buttons/FAB.stories.svelte.d.ts +19 -0
- package/dist/components/buttons/Toggle.stories.svelte +46 -0
- package/dist/components/buttons/Toggle.stories.svelte.d.ts +19 -0
- package/dist/components/cards/Card.stories.svelte +72 -0
- package/dist/components/cards/Card.stories.svelte.d.ts +19 -0
- package/dist/components/containers/bottom-sheet/BottomSheet.stories.svelte +35 -0
- package/dist/components/containers/bottom-sheet/BottomSheet.stories.svelte.d.ts +4 -0
- package/dist/components/containers/dialogue/Dialogue.stories.svelte +86 -0
- package/dist/components/containers/dialogue/Dialogue.stories.svelte.d.ts +4 -0
- package/dist/components/containers/menu/Menu.stories.svelte +62 -0
- package/dist/components/containers/menu/Menu.stories.svelte.d.ts +19 -0
- package/dist/components/containers/panes/SinglePane.stories.svelte +67 -0
- package/dist/components/containers/panes/SinglePane.stories.svelte.d.ts +19 -0
- package/dist/components/containers/panes/SplitPane.stories.svelte +124 -0
- package/dist/components/containers/panes/SplitPane.stories.svelte.d.ts +19 -0
- package/dist/components/containers/panes/SplitPane.svelte +25 -21
- package/dist/components/containers/panes/SplitPane.svelte.d.ts +4 -1
- package/dist/components/containers/panes/SupportingPane.stories.svelte +113 -0
- package/dist/components/containers/panes/SupportingPane.stories.svelte.d.ts +19 -0
- package/dist/components/containers/panes/theme.d.ts +18 -0
- package/dist/components/containers/panes/theme.js +11 -0
- package/dist/components/containers/panes/types.d.ts +17 -1
- package/dist/components/containers/popover/Popover.stories.svelte +65 -0
- package/dist/components/containers/popover/Popover.stories.svelte.d.ts +19 -0
- package/dist/components/containers/side-sheet/SideSheet.stories.svelte +54 -0
- package/dist/components/containers/side-sheet/SideSheet.stories.svelte.d.ts +4 -0
- package/dist/components/forms/checkbox/Checkbox.stories.svelte +42 -0
- package/dist/components/forms/checkbox/Checkbox.stories.svelte.d.ts +19 -0
- package/dist/components/forms/search/Search.stories.svelte +41 -0
- package/dist/components/forms/search/Search.stories.svelte.d.ts +19 -0
- package/dist/components/forms/select/Select.stories.svelte +89 -0
- package/dist/components/forms/select/Select.stories.svelte.d.ts +19 -0
- package/dist/components/forms/select/Select.svelte +1 -1
- package/dist/components/forms/slider/Slider.stories.svelte +76 -0
- package/dist/components/forms/slider/Slider.stories.svelte.d.ts +19 -0
- package/dist/components/forms/switch/Switch.stories.svelte +47 -0
- package/dist/components/forms/switch/Switch.stories.svelte.d.ts +19 -0
- package/dist/components/forms/textfield/Textfield.stories.svelte +74 -0
- package/dist/components/forms/textfield/Textfield.stories.svelte.d.ts +19 -0
- package/dist/components/misc/Avatar.svelte +2 -2
- package/dist/components/misc/types.d.ts +4 -0
- package/dist/components/nav/appbar/AppBar.stories.svelte +49 -0
- package/dist/components/nav/appbar/AppBar.stories.svelte.d.ts +19 -0
- package/dist/components/nav/navbar/Navbar.stories.svelte +33 -0
- package/dist/components/nav/navbar/Navbar.stories.svelte.d.ts +19 -0
- package/dist/components/nav/rail/Rail.stories.svelte +73 -0
- package/dist/components/nav/rail/Rail.stories.svelte.d.ts +19 -0
- package/dist/components/nav/rail/types.d.ts +1 -1
- package/dist/components/nav/tabs/Tabs.stories.svelte +55 -0
- package/dist/components/nav/tabs/Tabs.stories.svelte.d.ts +18 -0
- package/dist/components/pill/Pill.stories.svelte +48 -0
- package/dist/components/pill/Pill.stories.svelte.d.ts +19 -0
- package/dist/components/progress/Progress.stories.svelte +64 -0
- package/dist/components/progress/Progress.stories.svelte.d.ts +18 -0
- package/dist/components/snackbar/Snackbar.stories.svelte +76 -0
- package/dist/components/snackbar/Snackbar.stories.svelte.d.ts +4 -0
- package/dist/components/typography/body/Body.stories.svelte +30 -0
- package/dist/components/typography/body/Body.stories.svelte.d.ts +19 -0
- package/dist/components/typography/display/Display.stories.svelte +30 -0
- package/dist/components/typography/display/Display.stories.svelte.d.ts +19 -0
- package/dist/components/typography/headline/Headline.stories.svelte +30 -0
- package/dist/components/typography/headline/Headline.stories.svelte.d.ts +19 -0
- package/dist/components/typography/label/Label.stories.svelte +30 -0
- package/dist/components/typography/label/Label.stories.svelte.d.ts +19 -0
- package/dist/components/typography/title/Title.stories.svelte +30 -0
- package/dist/components/typography/title/Title.stories.svelte.d.ts +19 -0
- package/package.json +12 -4
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Menu owns its own open/close state internally (bits-ui DropdownMenu.Root bind:open, defaulting
|
|
3
|
+
to closed) and renders a default filled Button trigger when no `trigger` snippet is supplied —
|
|
4
|
+
so unlike Dialogue/Popover, no external open state is needed to demo it: clicking the default
|
|
5
|
+
trigger is enough.
|
|
6
|
+
-->
|
|
7
|
+
<script module lang="ts">
|
|
8
|
+
import { defineMeta } from '@storybook/addon-svelte-csf';
|
|
9
|
+
import Menu from './Menu.svelte';
|
|
10
|
+
import MenuItem from '../menu-item/MenuItem.svelte';
|
|
11
|
+
import ButtonIcon from '../../buttons/ButtonIcon.svelte';
|
|
12
|
+
import { Divider } from '../../../index.js';
|
|
13
|
+
|
|
14
|
+
const { Story } = defineMeta({
|
|
15
|
+
title: 'Containers/Menu',
|
|
16
|
+
component: Menu,
|
|
17
|
+
argTypes: {
|
|
18
|
+
label: { control: 'text' },
|
|
19
|
+
align: { control: 'select', options: ['start', 'center', 'end'] }
|
|
20
|
+
},
|
|
21
|
+
args: {
|
|
22
|
+
label: 'Open menu',
|
|
23
|
+
align: 'start'
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<Story name="Playground">
|
|
29
|
+
{#snippet template(args)}
|
|
30
|
+
<div class="p-6">
|
|
31
|
+
<Menu label={args.label} align={args.align}>
|
|
32
|
+
<MenuItem iconProps={{ name: 'content_cut' }}>Cut</MenuItem>
|
|
33
|
+
<MenuItem iconProps={{ name: 'content_copy' }}>Copy</MenuItem>
|
|
34
|
+
<MenuItem iconProps={{ name: 'content_paste' }}>Paste</MenuItem>
|
|
35
|
+
</Menu>
|
|
36
|
+
</div>
|
|
37
|
+
{/snippet}
|
|
38
|
+
</Story>
|
|
39
|
+
|
|
40
|
+
<Story name="With Custom Trigger" asChild>
|
|
41
|
+
<div class="p-6">
|
|
42
|
+
<Menu align="end">
|
|
43
|
+
{#snippet trigger()}
|
|
44
|
+
<ButtonIcon iconProps={{ name: 'more_vert' }} variant="text" />
|
|
45
|
+
{/snippet}
|
|
46
|
+
<MenuItem iconProps={{ name: 'edit' }}>Edit</MenuItem>
|
|
47
|
+
<MenuItem iconProps={{ name: 'archive' }} helper="Move to archive folder">Archive</MenuItem>
|
|
48
|
+
<Divider class="my-2" />
|
|
49
|
+
<MenuItem iconProps={{ name: 'delete' }} disabled>Delete</MenuItem>
|
|
50
|
+
</Menu>
|
|
51
|
+
</div>
|
|
52
|
+
</Story>
|
|
53
|
+
|
|
54
|
+
<Story name="With Selected Item" asChild>
|
|
55
|
+
<div class="p-6">
|
|
56
|
+
<Menu label="Sort by">
|
|
57
|
+
<MenuItem selected>Name</MenuItem>
|
|
58
|
+
<MenuItem>Date modified</MenuItem>
|
|
59
|
+
<MenuItem>Size</MenuItem>
|
|
60
|
+
</Menu>
|
|
61
|
+
</div>
|
|
62
|
+
</Story>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Menu from './Menu.svelte';
|
|
2
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
3
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
4
|
+
$$bindings?: Bindings;
|
|
5
|
+
} & Exports;
|
|
6
|
+
(internal: unknown, props: {
|
|
7
|
+
$$events?: Events;
|
|
8
|
+
$$slots?: Slots;
|
|
9
|
+
}): Exports & {
|
|
10
|
+
$set?: any;
|
|
11
|
+
$on?: any;
|
|
12
|
+
};
|
|
13
|
+
z_$$bindings?: Bindings;
|
|
14
|
+
}
|
|
15
|
+
declare const Menu: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
16
|
+
[evt: string]: CustomEvent<any>;
|
|
17
|
+
}, {}, {}, string>;
|
|
18
|
+
type Menu = InstanceType<typeof Menu>;
|
|
19
|
+
export default Menu;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import { defineMeta } from '@storybook/addon-svelte-csf';
|
|
3
|
+
import SinglePane from './SinglePane.svelte';
|
|
4
|
+
|
|
5
|
+
const { Story } = defineMeta({
|
|
6
|
+
title: 'Containers/Single Pane',
|
|
7
|
+
component: SinglePane,
|
|
8
|
+
argTypes: {
|
|
9
|
+
centered: {
|
|
10
|
+
control: 'select',
|
|
11
|
+
options: ['none', 'narrow', 'medium']
|
|
12
|
+
},
|
|
13
|
+
padding: {
|
|
14
|
+
control: 'select',
|
|
15
|
+
options: ['none', 'sm', 'md', 'lg']
|
|
16
|
+
},
|
|
17
|
+
gap: {
|
|
18
|
+
control: 'select',
|
|
19
|
+
options: ['none', 'sm', 'md', 'lg', 'xl']
|
|
20
|
+
},
|
|
21
|
+
background: { control: 'boolean' },
|
|
22
|
+
full: { control: 'boolean' },
|
|
23
|
+
rounded: { control: 'boolean' }
|
|
24
|
+
},
|
|
25
|
+
args: {
|
|
26
|
+
centered: 'medium',
|
|
27
|
+
padding: 'md',
|
|
28
|
+
gap: 'md',
|
|
29
|
+
background: true,
|
|
30
|
+
full: false,
|
|
31
|
+
rounded: true
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<Story name="Playground">
|
|
37
|
+
{#snippet template(args)}
|
|
38
|
+
<SinglePane
|
|
39
|
+
centered={args.centered}
|
|
40
|
+
padding={args.padding}
|
|
41
|
+
gap={args.gap}
|
|
42
|
+
background={args.background}
|
|
43
|
+
full={args.full}
|
|
44
|
+
rounded={args.rounded}
|
|
45
|
+
>
|
|
46
|
+
<div class="bg-md-sys-color-surface-container-high rounded-lg p-6">Block one</div>
|
|
47
|
+
<div class="bg-md-sys-color-surface-container-high rounded-lg p-6">Block two</div>
|
|
48
|
+
<div class="bg-md-sys-color-surface-container-high rounded-lg p-6">Block three</div>
|
|
49
|
+
</SinglePane>
|
|
50
|
+
{/snippet}
|
|
51
|
+
</Story>
|
|
52
|
+
|
|
53
|
+
<Story name="Centered Widths" asChild>
|
|
54
|
+
<div class="flex flex-col gap-6">
|
|
55
|
+
<SinglePane centered="narrow" background={false}>
|
|
56
|
+
<div class="bg-md-sys-color-secondary-container rounded-lg p-4 text-sm">narrow</div>
|
|
57
|
+
</SinglePane>
|
|
58
|
+
<SinglePane centered="medium" background={false}>
|
|
59
|
+
<div class="bg-md-sys-color-secondary-container rounded-lg p-4 text-sm">medium</div>
|
|
60
|
+
</SinglePane>
|
|
61
|
+
<SinglePane centered="none" background={false}>
|
|
62
|
+
<div class="bg-md-sys-color-secondary-container rounded-lg p-4 text-sm">
|
|
63
|
+
none (full width)
|
|
64
|
+
</div>
|
|
65
|
+
</SinglePane>
|
|
66
|
+
</div>
|
|
67
|
+
</Story>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import SinglePane from './SinglePane.svelte';
|
|
2
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
3
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
4
|
+
$$bindings?: Bindings;
|
|
5
|
+
} & Exports;
|
|
6
|
+
(internal: unknown, props: {
|
|
7
|
+
$$events?: Events;
|
|
8
|
+
$$slots?: Slots;
|
|
9
|
+
}): Exports & {
|
|
10
|
+
$set?: any;
|
|
11
|
+
$on?: any;
|
|
12
|
+
};
|
|
13
|
+
z_$$bindings?: Bindings;
|
|
14
|
+
}
|
|
15
|
+
declare const SinglePane: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
16
|
+
[evt: string]: CustomEvent<any>;
|
|
17
|
+
}, {}, {}, string>;
|
|
18
|
+
type SinglePane = InstanceType<typeof SinglePane>;
|
|
19
|
+
export default SinglePane;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import { defineMeta } from '@storybook/addon-svelte-csf';
|
|
3
|
+
import SplitPane from './SplitPane.svelte';
|
|
4
|
+
|
|
5
|
+
const { Story } = defineMeta({
|
|
6
|
+
title: 'Containers/Split Pane',
|
|
7
|
+
component: SplitPane,
|
|
8
|
+
argTypes: {
|
|
9
|
+
anchor: {
|
|
10
|
+
control: 'select',
|
|
11
|
+
options: ['viewport', 'parent', 'sticky']
|
|
12
|
+
},
|
|
13
|
+
resizable: { control: 'boolean' },
|
|
14
|
+
leftWidth: { control: 'number' },
|
|
15
|
+
minLeft: { control: 'number' },
|
|
16
|
+
maxLeft: { control: 'number' },
|
|
17
|
+
full: { control: 'boolean' },
|
|
18
|
+
rounded: { control: 'boolean' },
|
|
19
|
+
mobilePane: {
|
|
20
|
+
control: 'select',
|
|
21
|
+
options: ['left', 'right']
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
args: {
|
|
25
|
+
anchor: 'parent',
|
|
26
|
+
resizable: true,
|
|
27
|
+
leftWidth: 320,
|
|
28
|
+
minLeft: 240,
|
|
29
|
+
maxLeft: 560,
|
|
30
|
+
full: false,
|
|
31
|
+
rounded: true,
|
|
32
|
+
mobilePane: 'right'
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<!--
|
|
38
|
+
Playground uses anchor="parent" (bounded/relative container) since it's the
|
|
39
|
+
only mode that demos meaningfully inside Storybook's iframe canvas — "viewport"
|
|
40
|
+
fixes to the true browser edge and "sticky" needs a real scrolling page.
|
|
41
|
+
-->
|
|
42
|
+
<Story name="Playground">
|
|
43
|
+
{#snippet template(args)}
|
|
44
|
+
<div
|
|
45
|
+
class="relative h-[420px] overflow-hidden rounded-lg border border-md-sys-color-outline-variant"
|
|
46
|
+
>
|
|
47
|
+
<SplitPane {...args}>
|
|
48
|
+
{#snippet left()}
|
|
49
|
+
<div class="p-4">
|
|
50
|
+
<p class="md-sys-typescale-title-small mb-2">Left pane</p>
|
|
51
|
+
<p class="text-md-sys-color-on-surface-variant text-sm">
|
|
52
|
+
Drag the handle to resize. Persisted to localStorage when resizable.
|
|
53
|
+
</p>
|
|
54
|
+
</div>
|
|
55
|
+
{/snippet}
|
|
56
|
+
{#snippet right()}
|
|
57
|
+
<div class="p-4">
|
|
58
|
+
<p class="md-sys-typescale-title-small mb-2">Right pane</p>
|
|
59
|
+
<p class="text-md-sys-color-on-surface-variant text-sm">Main content area.</p>
|
|
60
|
+
</div>
|
|
61
|
+
{/snippet}
|
|
62
|
+
</SplitPane>
|
|
63
|
+
</div>
|
|
64
|
+
{/snippet}
|
|
65
|
+
</Story>
|
|
66
|
+
|
|
67
|
+
<!--
|
|
68
|
+
anchor="viewport" — left pane fixes to the true browser edge. Meant for a
|
|
69
|
+
full-page app layout where SplitPane is the outermost positioned element
|
|
70
|
+
(e.g. the root Rail). Shown here at a reduced height so the fixed left pane
|
|
71
|
+
is still visible inside the story canvas.
|
|
72
|
+
-->
|
|
73
|
+
<Story name="Anchor Viewport" asChild>
|
|
74
|
+
<div
|
|
75
|
+
class="relative h-[420px] overflow-hidden rounded-lg border border-md-sys-color-outline-variant"
|
|
76
|
+
>
|
|
77
|
+
<SplitPane anchor="viewport" resizable={false} leftWidth={280}>
|
|
78
|
+
{#snippet left()}
|
|
79
|
+
<div class="p-4">
|
|
80
|
+
<p class="md-sys-typescale-title-small mb-2">Fixed to viewport edge</p>
|
|
81
|
+
<p class="text-md-sys-color-on-surface-variant text-sm">
|
|
82
|
+
In a real page this pane is position: fixed to the true left edge of the browser.
|
|
83
|
+
</p>
|
|
84
|
+
</div>
|
|
85
|
+
{/snippet}
|
|
86
|
+
{#snippet right()}
|
|
87
|
+
<div class="p-4">
|
|
88
|
+
<p class="text-md-sys-color-on-surface-variant text-sm">Right / main content.</p>
|
|
89
|
+
</div>
|
|
90
|
+
{/snippet}
|
|
91
|
+
</SplitPane>
|
|
92
|
+
</div>
|
|
93
|
+
</Story>
|
|
94
|
+
|
|
95
|
+
<!--
|
|
96
|
+
anchor="sticky" — left/right sit in normal flex flow and the left pane
|
|
97
|
+
sticks (position: sticky) as the page scrolls. Used for a sidebar nested
|
|
98
|
+
inside other already-offset content, e.g. the docs secondary nav. Paired
|
|
99
|
+
with resizable={false} for a static, non-draggable sidebar. Needs a tall
|
|
100
|
+
scrollable right pane to demonstrate the sticky behaviour.
|
|
101
|
+
-->
|
|
102
|
+
<Story name="Anchor Sticky" asChild>
|
|
103
|
+
<div class="h-[420px] overflow-y-auto rounded-lg border border-md-sys-color-outline-variant">
|
|
104
|
+
<SplitPane anchor="sticky" resizable={false} leftWidth={240} rounded={false}>
|
|
105
|
+
{#snippet left()}
|
|
106
|
+
<div class="p-4">
|
|
107
|
+
<p class="md-sys-typescale-title-small mb-2">Sticky nav</p>
|
|
108
|
+
<p class="text-md-sys-color-on-surface-variant text-sm">
|
|
109
|
+
Stays pinned to the top of the viewport while the right pane scrolls past it.
|
|
110
|
+
</p>
|
|
111
|
+
</div>
|
|
112
|
+
{/snippet}
|
|
113
|
+
{#snippet right()}
|
|
114
|
+
<div class="flex flex-col gap-4 p-4">
|
|
115
|
+
{#each Array(12) as _, i}
|
|
116
|
+
<div class="bg-md-sys-color-surface-container-high rounded-lg p-6">
|
|
117
|
+
Scrollable content block {i + 1}
|
|
118
|
+
</div>
|
|
119
|
+
{/each}
|
|
120
|
+
</div>
|
|
121
|
+
{/snippet}
|
|
122
|
+
</SplitPane>
|
|
123
|
+
</div>
|
|
124
|
+
</Story>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import SplitPane from './SplitPane.svelte';
|
|
2
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
3
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
4
|
+
$$bindings?: Bindings;
|
|
5
|
+
} & Exports;
|
|
6
|
+
(internal: unknown, props: {
|
|
7
|
+
$$events?: Events;
|
|
8
|
+
$$slots?: Slots;
|
|
9
|
+
}): Exports & {
|
|
10
|
+
$set?: any;
|
|
11
|
+
$on?: any;
|
|
12
|
+
};
|
|
13
|
+
z_$$bindings?: Bindings;
|
|
14
|
+
}
|
|
15
|
+
declare const SplitPane: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
16
|
+
[evt: string]: CustomEvent<any>;
|
|
17
|
+
}, {}, {}, string>;
|
|
18
|
+
type SplitPane = InstanceType<typeof SplitPane>;
|
|
19
|
+
export default SplitPane;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
@component
|
|
3
|
-
SplitPane provides a
|
|
3
|
+
SplitPane provides a two-column layout. Set `resizable={false}` for a static,
|
|
4
|
+
non-draggable sidebar (e.g. a docs nav) instead of a user-resizable split.
|
|
4
5
|
-->
|
|
5
6
|
<script lang="ts">
|
|
6
7
|
import type { SplitPaneProps } from './types.js';
|
|
@@ -16,6 +17,7 @@ SplitPane provides a resizable two-column layout.
|
|
|
16
17
|
leftWidth = $bindable(396),
|
|
17
18
|
full = true,
|
|
18
19
|
rounded = true,
|
|
20
|
+
resizable = true,
|
|
19
21
|
minLeft = 280,
|
|
20
22
|
maxLeft = 720,
|
|
21
23
|
storageKey = 'splitpane:leftWidth',
|
|
@@ -76,7 +78,7 @@ SplitPane provides a resizable two-column layout.
|
|
|
76
78
|
onMount(() => {
|
|
77
79
|
mounted = true;
|
|
78
80
|
|
|
79
|
-
if (persist && typeof localStorage !== 'undefined') {
|
|
81
|
+
if (resizable && persist && typeof localStorage !== 'undefined') {
|
|
80
82
|
const stored = Number(localStorage.getItem(storageKey));
|
|
81
83
|
if (!Number.isNaN(stored)) {
|
|
82
84
|
leftWidth = clampWidth(stored);
|
|
@@ -96,7 +98,7 @@ SplitPane provides a resizable two-column layout.
|
|
|
96
98
|
});
|
|
97
99
|
|
|
98
100
|
$effect(() => {
|
|
99
|
-
if (!persist || typeof localStorage === 'undefined') return;
|
|
101
|
+
if (!resizable || !persist || typeof localStorage === 'undefined') return;
|
|
100
102
|
localStorage.setItem(storageKey, String(leftWidth));
|
|
101
103
|
});
|
|
102
104
|
</script>
|
|
@@ -113,25 +115,27 @@ SplitPane provides a resizable two-column layout.
|
|
|
113
115
|
</div>
|
|
114
116
|
|
|
115
117
|
<!-- DRAG HANDLE -->
|
|
116
|
-
|
|
117
|
-
class={hCls()}
|
|
118
|
-
role="separator"
|
|
119
|
-
aria-orientation="vertical"
|
|
120
|
-
aria-valuenow={leftWidth}
|
|
121
|
-
aria-valuemin={minLeft}
|
|
122
|
-
aria-valuemax={maxLeft}
|
|
123
|
-
style={`left: ${handleOffset};`}
|
|
124
|
-
onpointerdown={startDrag}
|
|
125
|
-
onpointermove={moveDrag}
|
|
126
|
-
onpointerup={endDrag}
|
|
127
|
-
onpointercancel={endDrag}
|
|
128
|
-
>
|
|
118
|
+
{#if resizable}
|
|
129
119
|
<div
|
|
130
|
-
class={
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
120
|
+
class={hCls()}
|
|
121
|
+
role="separator"
|
|
122
|
+
aria-orientation="vertical"
|
|
123
|
+
aria-valuenow={leftWidth}
|
|
124
|
+
aria-valuemin={minLeft}
|
|
125
|
+
aria-valuemax={maxLeft}
|
|
126
|
+
style={anchor === 'sticky' ? undefined : `left: ${handleOffset};`}
|
|
127
|
+
onpointerdown={startDrag}
|
|
128
|
+
onpointermove={moveDrag}
|
|
129
|
+
onpointerup={endDrag}
|
|
130
|
+
onpointercancel={endDrag}
|
|
131
|
+
>
|
|
132
|
+
<div
|
|
133
|
+
class={handleGrip({
|
|
134
|
+
class: clsx(dragging && 'w-0.5')
|
|
135
|
+
})}
|
|
136
|
+
></div>
|
|
137
|
+
</div>
|
|
138
|
+
{/if}
|
|
135
139
|
|
|
136
140
|
<!-- RIGHT PANE -->
|
|
137
141
|
<div class={rCls({ class: clsx(rightClass, rightMobileClass) })}>
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { SplitPaneProps } from './types.js';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* SplitPane provides a two-column layout. Set `resizable={false}` for a static,
|
|
4
|
+
* non-draggable sidebar (e.g. a docs nav) instead of a user-resizable split.
|
|
5
|
+
*/
|
|
3
6
|
declare const SplitPane: import("svelte").Component<SplitPaneProps, {}, "leftWidth">;
|
|
4
7
|
type SplitPane = ReturnType<typeof SplitPane>;
|
|
5
8
|
export default SplitPane;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import { defineMeta } from '@storybook/addon-svelte-csf';
|
|
3
|
+
import SupportingPane from './SupportingPane.svelte';
|
|
4
|
+
|
|
5
|
+
const { Story } = defineMeta({
|
|
6
|
+
title: 'Containers/Supporting Pane',
|
|
7
|
+
component: SupportingPane,
|
|
8
|
+
argTypes: {
|
|
9
|
+
anchor: {
|
|
10
|
+
control: 'select',
|
|
11
|
+
options: ['viewport', 'parent']
|
|
12
|
+
},
|
|
13
|
+
position: {
|
|
14
|
+
control: 'select',
|
|
15
|
+
options: ['left', 'right']
|
|
16
|
+
},
|
|
17
|
+
centered: { control: 'boolean' },
|
|
18
|
+
rounded: { control: 'boolean' },
|
|
19
|
+
full: { control: 'boolean' },
|
|
20
|
+
gap: {
|
|
21
|
+
control: 'select',
|
|
22
|
+
options: ['none', 'sm', 'md', 'lg']
|
|
23
|
+
},
|
|
24
|
+
collapsible: { control: 'boolean' },
|
|
25
|
+
supportingWidth: { control: 'number' },
|
|
26
|
+
mobileSheet: { control: 'boolean' }
|
|
27
|
+
},
|
|
28
|
+
args: {
|
|
29
|
+
anchor: 'parent',
|
|
30
|
+
position: 'right',
|
|
31
|
+
centered: false,
|
|
32
|
+
rounded: false,
|
|
33
|
+
full: false,
|
|
34
|
+
gap: 'md',
|
|
35
|
+
collapsible: false,
|
|
36
|
+
supportingWidth: 320,
|
|
37
|
+
mobileSheet: false
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<!--
|
|
43
|
+
anchor="parent" demos meaningfully in a bounded box (classic sticky-while-
|
|
44
|
+
scrolling-the-page behaviour). anchor="viewport" — used by the docs pages'
|
|
45
|
+
own "On this page" TOC — fixes the supporting pane to the true browser edge,
|
|
46
|
+
which only reads correctly on a real scrolling page, not inside Storybook's
|
|
47
|
+
iframe canvas.
|
|
48
|
+
-->
|
|
49
|
+
<Story name="Playground">
|
|
50
|
+
{#snippet template(args)}
|
|
51
|
+
<SupportingPane {...args}>
|
|
52
|
+
{#snippet main()}
|
|
53
|
+
<div class="flex flex-col gap-4 p-4">
|
|
54
|
+
{#each Array(6) as _, i}
|
|
55
|
+
<div class="bg-md-sys-color-surface-container-high rounded-lg p-6">
|
|
56
|
+
Main content block {i + 1}
|
|
57
|
+
</div>
|
|
58
|
+
{/each}
|
|
59
|
+
</div>
|
|
60
|
+
{/snippet}
|
|
61
|
+
{#snippet supporting()}
|
|
62
|
+
<div class="bg-md-sys-color-surface-container rounded-lg p-4">
|
|
63
|
+
<p class="md-sys-typescale-title-small mb-2">Supporting content</p>
|
|
64
|
+
<p class="text-md-sys-color-on-surface-variant text-sm">
|
|
65
|
+
Fixed-width side panel — e.g. a table of contents or contextual actions.
|
|
66
|
+
</p>
|
|
67
|
+
</div>
|
|
68
|
+
{/snippet}
|
|
69
|
+
</SupportingPane>
|
|
70
|
+
{/snippet}
|
|
71
|
+
</Story>
|
|
72
|
+
|
|
73
|
+
<Story name="Collapsible" asChild>
|
|
74
|
+
<SupportingPane anchor="parent" position="right" collapsible>
|
|
75
|
+
{#snippet main()}
|
|
76
|
+
<div class="flex flex-col gap-4 p-4">
|
|
77
|
+
{#each Array(4) as _, i}
|
|
78
|
+
<div class="bg-md-sys-color-surface-container-high rounded-lg p-6">
|
|
79
|
+
Main content block {i + 1}
|
|
80
|
+
</div>
|
|
81
|
+
{/each}
|
|
82
|
+
</div>
|
|
83
|
+
{/snippet}
|
|
84
|
+
{#snippet supporting()}
|
|
85
|
+
<div class="bg-md-sys-color-surface-container rounded-lg p-4">
|
|
86
|
+
<p class="md-sys-typescale-title-small mb-2">Collapsible panel</p>
|
|
87
|
+
<p class="text-md-sys-color-on-surface-variant text-sm">
|
|
88
|
+
A toggle button appears over the main pane to hide/show this panel (desktop only).
|
|
89
|
+
</p>
|
|
90
|
+
</div>
|
|
91
|
+
{/snippet}
|
|
92
|
+
</SupportingPane>
|
|
93
|
+
</Story>
|
|
94
|
+
|
|
95
|
+
<Story name="Position Left" asChild>
|
|
96
|
+
<SupportingPane anchor="parent" position="left">
|
|
97
|
+
{#snippet main()}
|
|
98
|
+
<div class="flex flex-col gap-4 p-4">
|
|
99
|
+
{#each Array(4) as _, i}
|
|
100
|
+
<div class="bg-md-sys-color-surface-container-high rounded-lg p-6">
|
|
101
|
+
Main content block {i + 1}
|
|
102
|
+
</div>
|
|
103
|
+
{/each}
|
|
104
|
+
</div>
|
|
105
|
+
{/snippet}
|
|
106
|
+
{#snippet supporting()}
|
|
107
|
+
<div class="bg-md-sys-color-surface-container rounded-lg p-4">
|
|
108
|
+
<p class="md-sys-typescale-title-small mb-2">Supporting content</p>
|
|
109
|
+
<p class="text-md-sys-color-on-surface-variant text-sm">Panel rendered on the left.</p>
|
|
110
|
+
</div>
|
|
111
|
+
{/snippet}
|
|
112
|
+
</SupportingPane>
|
|
113
|
+
</Story>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import SupportingPane from './SupportingPane.svelte';
|
|
2
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
3
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
4
|
+
$$bindings?: Bindings;
|
|
5
|
+
} & Exports;
|
|
6
|
+
(internal: unknown, props: {
|
|
7
|
+
$$events?: Events;
|
|
8
|
+
$$slots?: Slots;
|
|
9
|
+
}): Exports & {
|
|
10
|
+
$set?: any;
|
|
11
|
+
$on?: any;
|
|
12
|
+
};
|
|
13
|
+
z_$$bindings?: Bindings;
|
|
14
|
+
}
|
|
15
|
+
declare const SupportingPane: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
16
|
+
[evt: string]: CustomEvent<any>;
|
|
17
|
+
}, {}, {}, string>;
|
|
18
|
+
type SupportingPane = InstanceType<typeof SupportingPane>;
|
|
19
|
+
export default SupportingPane;
|
|
@@ -13,6 +13,12 @@ export declare const splitPane: import("tailwind-variants").TVReturnType<{
|
|
|
13
13
|
left: string;
|
|
14
14
|
handle: string;
|
|
15
15
|
};
|
|
16
|
+
sticky: {
|
|
17
|
+
base: string;
|
|
18
|
+
left: string;
|
|
19
|
+
handle: string;
|
|
20
|
+
right: string;
|
|
21
|
+
};
|
|
16
22
|
};
|
|
17
23
|
full: {
|
|
18
24
|
true: {
|
|
@@ -49,6 +55,12 @@ export declare const splitPane: import("tailwind-variants").TVReturnType<{
|
|
|
49
55
|
left: string;
|
|
50
56
|
handle: string;
|
|
51
57
|
};
|
|
58
|
+
sticky: {
|
|
59
|
+
base: string;
|
|
60
|
+
left: string;
|
|
61
|
+
handle: string;
|
|
62
|
+
right: string;
|
|
63
|
+
};
|
|
52
64
|
};
|
|
53
65
|
full: {
|
|
54
66
|
true: {
|
|
@@ -85,6 +97,12 @@ export declare const splitPane: import("tailwind-variants").TVReturnType<{
|
|
|
85
97
|
left: string;
|
|
86
98
|
handle: string;
|
|
87
99
|
};
|
|
100
|
+
sticky: {
|
|
101
|
+
base: string;
|
|
102
|
+
left: string;
|
|
103
|
+
handle: string;
|
|
104
|
+
right: string;
|
|
105
|
+
};
|
|
88
106
|
};
|
|
89
107
|
full: {
|
|
90
108
|
true: {
|
|
@@ -18,6 +18,17 @@ export const splitPane = tv({
|
|
|
18
18
|
base: 'relative',
|
|
19
19
|
left: 'md:absolute',
|
|
20
20
|
handle: 'absolute'
|
|
21
|
+
},
|
|
22
|
+
// In-flow flex row: left/right sit side by side and the left pane sticks
|
|
23
|
+
// as the page scrolls, instead of being fixed/absolute. Use for nested
|
|
24
|
+
// sidebars (e.g. a secondary docs nav) that live inside an already
|
|
25
|
+
// width-offset content column, where "viewport" would fix to the true
|
|
26
|
+
// left edge and "parent" would lose sticky-while-scrolling behaviour.
|
|
27
|
+
sticky: {
|
|
28
|
+
base: 'md:flex md:flex-row',
|
|
29
|
+
left: 'shrink-0 md:sticky md:top-0 md:self-start md:max-h-dvh',
|
|
30
|
+
handle: 'md:sticky md:top-0 md:ml-0 md:h-dvh md:translate-x-0 md:self-start',
|
|
31
|
+
right: 'md:ml-0 md:grow md:min-w-0'
|
|
21
32
|
}
|
|
22
33
|
},
|
|
23
34
|
full: {
|
|
@@ -36,8 +36,24 @@ export type SplitPaneProps = SplitPaneVariants & DivAttrs & {
|
|
|
36
36
|
persist?: boolean;
|
|
37
37
|
/** Called when the component wants to update external footer padding. */
|
|
38
38
|
onPaddingChange?: (padding: number) => void;
|
|
39
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* The anchor mode for the panes.
|
|
41
|
+
* - `viewport` — left pane fixes to the true browser edge (full-page app layouts).
|
|
42
|
+
* - `parent` — left pane is absolutely positioned within a `relative` ancestor
|
|
43
|
+
* (embedded/contained demos, does not stick while scrolling).
|
|
44
|
+
* - `sticky` — left/right sit in normal flex flow and the left pane sticks as
|
|
45
|
+
* the page scrolls. Use for a sidebar nested inside other offset content
|
|
46
|
+
* (e.g. a secondary docs nav inside an already Rail-offset column), where
|
|
47
|
+
* `viewport` would fix to the wrong edge and `parent` would lose sticky
|
|
48
|
+
* behaviour. Typically paired with `resizable={false}`.
|
|
49
|
+
*/
|
|
40
50
|
anchor?: SplitPaneVariants['anchor'];
|
|
51
|
+
/**
|
|
52
|
+
* Whether the split is user-draggable. Set to `false` for a static sidebar —
|
|
53
|
+
* hides the drag handle and skips width persistence.
|
|
54
|
+
* @default true
|
|
55
|
+
*/
|
|
56
|
+
resizable?: boolean;
|
|
41
57
|
/**
|
|
42
58
|
* Which pane to show on mobile. On desktop both panes are always visible.
|
|
43
59
|
* @default 'right'
|