@noxlovette/material 0.4.8 → 0.5.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/dist/animation/Shapes.stories.svelte +56 -0
- package/dist/animation/Shapes.stories.svelte.d.ts +3 -0
- package/dist/animation/TransitionPatterns.stories.svelte +359 -0
- package/dist/animation/TransitionPatterns.stories.svelte.d.ts +3 -0
- package/dist/animation/containerTransform.d.ts +35 -12
- package/dist/animation/containerTransform.js +31 -138
- package/dist/animation/enterExit.d.ts +30 -3
- package/dist/animation/enterExit.js +69 -22
- package/dist/animation/index.d.ts +2 -1
- package/dist/animation/index.js +2 -1
- package/dist/animation/presence.svelte.d.ts +47 -0
- package/dist/animation/presence.svelte.js +84 -0
- package/dist/animation/shapeMorph.svelte.d.ts +52 -0
- package/dist/animation/shapeMorph.svelte.js +112 -0
- package/dist/animation/shapesAnimatableSmall.d.ts +28 -22
- package/dist/animation/shapesAnimatableSmall.js +32 -23
- package/dist/animation/sharedAxisTransition.d.ts +57 -37
- package/dist/animation/sharedAxisTransition.js +74 -22
- package/dist/animation/skeleton.d.ts +16 -0
- package/dist/animation/skeleton.js +28 -0
- package/dist/animation/spring.d.ts +61 -0
- package/dist/animation/spring.js +53 -0
- package/dist/animation/springValue.svelte.d.ts +22 -0
- package/dist/animation/springValue.svelte.js +42 -0
- package/dist/animation/transition.d.ts +3 -1
- package/dist/animation/transition.js +3 -1
- package/dist/attachments/clickOutside.d.ts +13 -0
- package/dist/attachments/clickOutside.js +18 -0
- package/dist/attachments/index.d.ts +1 -0
- package/dist/attachments/index.js +1 -0
- package/dist/components/buttons/FAB.svelte +23 -16
- package/dist/components/buttons/FABMenu.svelte +24 -29
- package/dist/components/buttons/FABMenu.svelte.d.ts +2 -1
- package/dist/components/buttons/FABMenuItem.svelte +23 -20
- package/dist/components/buttons/FABMenuItem.svelte.d.ts +5 -1
- package/dist/components/buttons/split-button/SplitButton.svelte +10 -17
- package/dist/components/buttons/split-button/theme.js +1 -1
- package/dist/components/buttons/types.d.ts +3 -3
- package/dist/components/cards/theme.js +1 -1
- package/dist/components/containers/bottom-sheet/BottomSheet.stories.svelte +8 -12
- package/dist/components/containers/bottom-sheet/BottomSheet.svelte +67 -55
- package/dist/components/containers/bottom-sheet/BottomSheet.svelte.d.ts +1 -1
- package/dist/components/containers/bottom-sheet/types.d.ts +8 -2
- package/dist/components/containers/context-menu/ContextMenu.svelte +23 -36
- package/dist/components/containers/context-menu/theme.js +2 -2
- package/dist/components/containers/dialogue/Dialogue.svelte +49 -65
- package/dist/components/containers/link-preview/LinkPreview.svelte +12 -23
- package/dist/components/containers/link-preview/theme.d.ts +3 -3
- package/dist/components/containers/menu/Menu.svelte +14 -21
- package/dist/components/containers/menu/MenuSub.svelte +10 -17
- package/dist/components/containers/pane/DraggablePane.svelte +90 -5
- package/dist/components/containers/pane/DraggablePane.svelte.d.ts +1 -1
- package/dist/components/containers/pane/theme.js +2 -2
- package/dist/components/containers/popover/Popover.svelte +31 -42
- package/dist/components/containers/popover/theme.d.ts +3 -3
- package/dist/components/containers/scroll-area/theme.js +2 -2
- package/dist/components/containers/side-sheet/SideSheet.stories.svelte +10 -15
- package/dist/components/containers/side-sheet/SideSheet.svelte +44 -44
- package/dist/components/containers/side-sheet/SideSheet.svelte.d.ts +4 -3
- package/dist/components/containers/side-sheet/types.d.ts +8 -2
- package/dist/components/date/DateField.svelte +99 -92
- package/dist/components/date/DateField.svelte.d.ts +4 -5
- package/dist/components/date/DateRangeField.svelte +127 -155
- package/dist/components/date/DateRangeField.svelte.d.ts +4 -5
- package/dist/components/date/calendarMotion.d.ts +9 -0
- package/dist/components/date/calendarMotion.js +23 -0
- package/dist/components/date/theme.d.ts +95 -0
- package/dist/components/date/theme.js +103 -14
- package/dist/components/date/types.d.ts +13 -0
- package/dist/components/forms/checkbox/theme.js +2 -2
- package/dist/components/forms/command/theme.d.ts +3 -3
- package/dist/components/forms/command/theme.js +1 -1
- package/dist/components/forms/pin/theme.js +2 -2
- package/dist/components/forms/radio-group/theme.js +2 -2
- package/dist/components/forms/select/Select.svelte +42 -54
- package/dist/components/forms/select/theme.js +4 -4
- package/dist/components/forms/slider/Slider.svelte +144 -126
- package/dist/components/forms/slider/Slider.svelte.d.ts +3 -0
- package/dist/components/forms/slider/theme.d.ts +154 -48
- package/dist/components/forms/slider/theme.js +60 -20
- package/dist/components/forms/slider/types.d.ts +1 -1
- package/dist/components/forms/switch/theme.js +4 -4
- package/dist/components/forms/textfield/Textfield.svelte +1 -1
- package/dist/components/forms/textfield/theme.d.ts +12 -0
- package/dist/components/forms/textfield/theme.js +30 -10
- package/dist/components/forms/toggle-group/theme.js +1 -1
- package/dist/components/forms/tooltip/Tooltip.svelte +24 -35
- package/dist/components/nav/breadcrumb/theme.js +1 -1
- package/dist/components/nav/navbar/theme.js +2 -2
- package/dist/components/nav/rail/Rail.svelte +4 -5
- package/dist/components/nav/rail/theme.js +7 -7
- package/dist/components/nav/tabs/TabHolder.svelte +29 -3
- package/dist/components/nav/tabs/TabHolder.svelte.d.ts +3 -1
- package/dist/components/nav/tabs/theme.js +1 -1
- package/dist/components/progress/CircularProgress.svelte +78 -4
- package/dist/components/progress/CircularProgress.svelte.d.ts +5 -0
- package/dist/components/progress/LinearProgress.svelte +70 -7
- package/dist/components/progress/LinearProgress.svelte.d.ts +5 -0
- package/dist/components/progress/WavyLinearProgress.svelte +27 -3
- package/dist/components/progress/WavyLinearProgress.svelte.d.ts +3 -0
- package/dist/components/progress/_wavy.d.ts +2 -0
- package/dist/components/progress/_wavy.js +3 -1
- package/dist/components/snackbar/Snackbar.svelte +13 -17
- package/dist/components/table/TableBody.svelte +3 -16
- package/dist/components/table/TableBody.svelte.d.ts +1 -5
- package/dist/components/table/TableHeader.svelte +1 -1
- package/dist/components/table/theme.js +1 -1
- package/dist/components/table/types.d.ts +0 -7
- package/dist/components/time/TimeField.stories.svelte +41 -0
- package/dist/components/time/TimeField.stories.svelte.d.ts +19 -0
- package/dist/components/time/TimeField.svelte +69 -62
- package/dist/components/time/TimeField.svelte.d.ts +6 -0
- package/dist/components/time/TimepickerInput.svelte +8 -4
- package/dist/components/time/types.d.ts +11 -1
- package/dist/components/toolbar/theme.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/styles/component.css +21 -22
- package/dist/styles/motion.css +317 -38
- package/dist/utils/Layer.svelte +16 -29
- package/dist/utils/icon/LoadingIndicator.svelte +61 -22
- package/dist/utils/icon/loadingShapes.d.ts +1 -0
- package/dist/utils/icon/loadingShapes.js +16 -0
- package/package.json +5 -2
- package/dist/actions/clickOutside.d.ts +0 -3
- package/dist/actions/clickOutside.js +0 -13
- package/dist/actions/floating.d.ts +0 -20
- package/dist/actions/floating.js +0 -87
- package/dist/actions/index.d.ts +0 -6
- package/dist/actions/index.js +0 -6
- package/dist/actions/keyboard.d.ts +0 -3
- package/dist/actions/keyboard.js +0 -14
- package/dist/actions/portal.d.ts +0 -3
- package/dist/actions/portal.js +0 -8
- package/dist/actions/positionFloating.d.ts +0 -10
- package/dist/actions/positionFloating.js +0 -59
- package/dist/actions/rovingFocusGroup.d.ts +0 -20
- package/dist/actions/rovingFocusGroup.js +0 -82
- package/dist/animation/easing.d.ts +0 -6
- package/dist/animation/easing.js +0 -78
- package/dist/animation/outroClass.d.ts +0 -3
- package/dist/animation/outroClass.js +0 -20
- package/dist/animation/transitionTypes.d.ts +0 -8
- package/dist/animation/transitionTypes.js +0 -1
|
@@ -4,7 +4,7 @@ import type { SliderVariants } from './theme.js';
|
|
|
4
4
|
/**
|
|
5
5
|
* Props for the Slider component.
|
|
6
6
|
*/
|
|
7
|
-
export type SliderProps = SliderVariants & Omit<HTMLAttributes<HTMLSpanElement>, 'size' | 'value'> & {
|
|
7
|
+
export type SliderProps = Omit<SliderVariants, 'active'> & Omit<HTMLAttributes<HTMLSpanElement>, 'size' | 'value'> & {
|
|
8
8
|
/**
|
|
9
9
|
* The current value of the slider.
|
|
10
10
|
*/
|
|
@@ -14,14 +14,14 @@ export const toggle = tv({
|
|
|
14
14
|
'group-data-[disabled]:cursor-not-allowed',
|
|
15
15
|
'group-data-[disabled]:border-md-sys-color-on-surface/12 group-data-[disabled]:bg-md-sys-color-surface-container-highest',
|
|
16
16
|
'group-data-[state=checked]:group-data-[disabled]:border-transparent group-data-[state=checked]:group-data-[disabled]:bg-md-sys-color-on-surface/12',
|
|
17
|
-
'transition-colors
|
|
17
|
+
'transition-colors md-sys-motion-effects'
|
|
18
18
|
].join(' '),
|
|
19
19
|
// Thumb — absolute over track
|
|
20
20
|
handle: [
|
|
21
21
|
'pointer-events-none absolute left-2 top-1/2 flex h-4 w-4 -translate-y-1/2',
|
|
22
22
|
'items-center justify-center rounded-full relative',
|
|
23
23
|
'bg-md-sys-color-outline',
|
|
24
|
-
'transition-[left,transform,background-color]
|
|
24
|
+
'transition-[left,transform,background-color] md-sys-motion-fast-spatial',
|
|
25
25
|
// checked: move right + scale up + on-primary color
|
|
26
26
|
'group-data-[state=checked]:left-[1.75rem] group-data-[state=checked]:scale-[1.5] group-data-[state=checked]:bg-md-sys-color-on-primary',
|
|
27
27
|
// disabled
|
|
@@ -38,7 +38,7 @@ export const toggle = tv({
|
|
|
38
38
|
hover: [
|
|
39
39
|
'pointer-events-none absolute left-4 top-1/2 h-12 w-12',
|
|
40
40
|
'-translate-x-1/2 -translate-y-1/2 rounded-full',
|
|
41
|
-
'transition-[left,background-color]
|
|
41
|
+
'transition-[left,background-color] md-sys-motion-fast-spatial',
|
|
42
42
|
'group-data-[state=checked]:left-9',
|
|
43
43
|
'group-data-[disabled]:hidden',
|
|
44
44
|
'group-hover:bg-md-sys-color-on-surface/8',
|
|
@@ -47,7 +47,7 @@ export const toggle = tv({
|
|
|
47
47
|
// Icon inside thumb - shared base (sized via size="xs" prop on Icon, not here)
|
|
48
48
|
icon: [
|
|
49
49
|
'absolute inset-0 m-auto pointer-events-none flex items-center justify-center',
|
|
50
|
-
'opacity-0 transition-opacity
|
|
50
|
+
'opacity-0 transition-opacity md-sys-motion-fast-effects shrink-0'
|
|
51
51
|
].join(' '),
|
|
52
52
|
// Specific icon states - colour matches M3 spec for each thumb background
|
|
53
53
|
iconChecked: [
|
|
@@ -34,7 +34,7 @@ Text fields allow users to enter and edit text.
|
|
|
34
34
|
...restProps
|
|
35
35
|
}: TextfieldProps = $props();
|
|
36
36
|
|
|
37
|
-
const cls = $derived(textfield({ disabled, error, variant }));
|
|
37
|
+
const cls = $derived(textfield({ disabled, error, variant, leadingIcon: !!leadingIconProps }));
|
|
38
38
|
|
|
39
39
|
const inputProps = $derived({
|
|
40
40
|
id,
|
|
@@ -4,6 +4,7 @@ export declare const textfield: import("tailwind-variants").TVReturnType<{
|
|
|
4
4
|
variant: {
|
|
5
5
|
filled: {
|
|
6
6
|
base: string;
|
|
7
|
+
input: string;
|
|
7
8
|
label: string;
|
|
8
9
|
};
|
|
9
10
|
outlined: {
|
|
@@ -20,6 +21,9 @@ export declare const textfield: import("tailwind-variants").TVReturnType<{
|
|
|
20
21
|
fieldset: string;
|
|
21
22
|
};
|
|
22
23
|
};
|
|
24
|
+
leadingIcon: {
|
|
25
|
+
true: {};
|
|
26
|
+
};
|
|
23
27
|
disabled: {
|
|
24
28
|
true: {
|
|
25
29
|
base: string;
|
|
@@ -42,6 +46,7 @@ export declare const textfield: import("tailwind-variants").TVReturnType<{
|
|
|
42
46
|
variant: {
|
|
43
47
|
filled: {
|
|
44
48
|
base: string;
|
|
49
|
+
input: string;
|
|
45
50
|
label: string;
|
|
46
51
|
};
|
|
47
52
|
outlined: {
|
|
@@ -58,6 +63,9 @@ export declare const textfield: import("tailwind-variants").TVReturnType<{
|
|
|
58
63
|
fieldset: string;
|
|
59
64
|
};
|
|
60
65
|
};
|
|
66
|
+
leadingIcon: {
|
|
67
|
+
true: {};
|
|
68
|
+
};
|
|
61
69
|
disabled: {
|
|
62
70
|
true: {
|
|
63
71
|
base: string;
|
|
@@ -80,6 +88,7 @@ export declare const textfield: import("tailwind-variants").TVReturnType<{
|
|
|
80
88
|
variant: {
|
|
81
89
|
filled: {
|
|
82
90
|
base: string;
|
|
91
|
+
input: string;
|
|
83
92
|
label: string;
|
|
84
93
|
};
|
|
85
94
|
outlined: {
|
|
@@ -96,6 +105,9 @@ export declare const textfield: import("tailwind-variants").TVReturnType<{
|
|
|
96
105
|
fieldset: string;
|
|
97
106
|
};
|
|
98
107
|
};
|
|
108
|
+
leadingIcon: {
|
|
109
|
+
true: {};
|
|
110
|
+
};
|
|
99
111
|
disabled: {
|
|
100
112
|
true: {
|
|
101
113
|
base: string;
|
|
@@ -4,11 +4,13 @@ export const textfield = tv({
|
|
|
4
4
|
base: `
|
|
5
5
|
group relative w-full h-14 flex items-center
|
|
6
6
|
`,
|
|
7
|
+
/* Stretches to the full field height so the label floats relative to the field's top edge,
|
|
8
|
+
not the input's. */
|
|
7
9
|
inputWrapper: `
|
|
8
|
-
relative flex-1 px-4
|
|
10
|
+
relative flex-1 self-stretch flex items-center px-4
|
|
9
11
|
`,
|
|
10
12
|
input: `
|
|
11
|
-
peer w-full
|
|
13
|
+
peer w-full bg-transparent outline-none
|
|
12
14
|
md-sys-typescale-body-large text-md-sys-color-on-surface
|
|
13
15
|
disabled:text-md-sys-color-on-surface/38
|
|
14
16
|
`,
|
|
@@ -17,7 +19,7 @@ export const textfield = tv({
|
|
|
17
19
|
md-sys-typescale-body-large
|
|
18
20
|
text-md-sys-color-on-surface-variant
|
|
19
21
|
pointer-events-none
|
|
20
|
-
transition-[top,font-size,line-height,color,transform]
|
|
22
|
+
transition-[top,left,font-size,line-height,color,transform] md-sys-motion-fast-spatial
|
|
21
23
|
|
|
22
24
|
peer-focus:text-md-sys-color-primary
|
|
23
25
|
`,
|
|
@@ -25,7 +27,7 @@ export const textfield = tv({
|
|
|
25
27
|
ml-0.5
|
|
26
28
|
text-md-sys-color-error
|
|
27
29
|
group-focus-within:text-md-sys-color-primary
|
|
28
|
-
transition-colors
|
|
30
|
+
transition-colors md-sys-motion-effects
|
|
29
31
|
`,
|
|
30
32
|
leadingIcon: `
|
|
31
33
|
text-md-sys-color-on-surface-variant size-6
|
|
@@ -43,9 +45,9 @@ export const textfield = tv({
|
|
|
43
45
|
text-md-sys-color-on-surface-variant
|
|
44
46
|
`,
|
|
45
47
|
fieldset: `
|
|
46
|
-
absolute -top-
|
|
48
|
+
absolute -top-2 left-0 right-0 bottom-0 m-0 px-3
|
|
47
49
|
pointer-events-none rounded-xs border border-md-sys-color-outline
|
|
48
|
-
transition-colors
|
|
50
|
+
transition-colors md-sys-motion-fast-effects
|
|
49
51
|
|
|
50
52
|
group-hover:border-md-sys-color-on-surface
|
|
51
53
|
group-focus-within:border-2 group-focus-within:border-md-sys-color-primary
|
|
@@ -53,7 +55,7 @@ export const textfield = tv({
|
|
|
53
55
|
legend: `
|
|
54
56
|
invisible float-none block h-auto max-w-[0.01px] overflow-hidden
|
|
55
57
|
whitespace-nowrap md-sys-typescale-body-small
|
|
56
|
-
transition-[max-width]
|
|
58
|
+
transition-[max-width] md-sys-motion-fast-spatial
|
|
57
59
|
|
|
58
60
|
group-focus-within:max-w-full
|
|
59
61
|
group-has-[input:not(:placeholder-shown)]:max-w-full
|
|
@@ -69,16 +71,19 @@ export const textfield = tv({
|
|
|
69
71
|
px-0 bg-md-sys-color-surface-container-highest
|
|
70
72
|
rounded-t-xs state-layer before:rounded-xs hover:before:bg-md-sys-color-on-surface/8
|
|
71
73
|
after:absolute after:bottom-0 after:left-0 after:right-0 after:h-px after:bg-md-sys-color-on-surface-variant
|
|
72
|
-
after:transition-[height,background-color] after:
|
|
74
|
+
after:transition-[height,background-color] after:md-sys-motion-fast-spatial
|
|
73
75
|
hover:after:bg-md-sys-color-on-surface
|
|
74
76
|
focus-within:after:bg-md-sys-color-primary
|
|
75
77
|
focus-within:after:h-[2px]
|
|
76
78
|
disabled:bg-md-sys-color-on-surface/4
|
|
77
79
|
disabled:after:bg-md-sys-color-on-surface/12
|
|
78
80
|
`,
|
|
81
|
+
/* Room for the label floated inside the container. Outlined labels float into the border
|
|
82
|
+
notch instead, so their input stays vertically centered. */
|
|
83
|
+
input: 'pt-4',
|
|
79
84
|
label: `
|
|
80
|
-
peer-focus:top-
|
|
81
|
-
peer-not-placeholder-shown:top-
|
|
85
|
+
peer-focus:top-2 peer-focus:md-sys-typescale-body-small peer-focus:translate-y-0
|
|
86
|
+
peer-not-placeholder-shown:top-2 peer-not-placeholder-shown:md-sys-typescale-body-small peer-not-placeholder-shown:translate-y-0
|
|
82
87
|
`
|
|
83
88
|
},
|
|
84
89
|
outlined: {
|
|
@@ -102,6 +107,9 @@ export const textfield = tv({
|
|
|
102
107
|
`
|
|
103
108
|
}
|
|
104
109
|
},
|
|
110
|
+
leadingIcon: {
|
|
111
|
+
true: {}
|
|
112
|
+
},
|
|
105
113
|
disabled: {
|
|
106
114
|
true: {
|
|
107
115
|
base: 'cursor-not-allowed opacity-60',
|
|
@@ -109,6 +117,18 @@ export const textfield = tv({
|
|
|
109
117
|
}
|
|
110
118
|
}
|
|
111
119
|
},
|
|
120
|
+
compoundVariants: [
|
|
121
|
+
{
|
|
122
|
+
/* M3: an outlined label floats back to the field's start edge, over the leading icon, into
|
|
123
|
+
the notch. The wrapper starts after the icon (ml-3 + size-6 = 36px), so -20px puts the
|
|
124
|
+
label at 16px from the field's edge. */
|
|
125
|
+
variant: 'outlined',
|
|
126
|
+
leadingIcon: true,
|
|
127
|
+
class: {
|
|
128
|
+
label: 'peer-focus:-left-5 peer-not-placeholder-shown:-left-5'
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
],
|
|
112
132
|
defaultVariants: {
|
|
113
133
|
variant: 'outlined'
|
|
114
134
|
}
|
|
@@ -13,7 +13,7 @@ export const toggleGroup = tv({
|
|
|
13
13
|
});
|
|
14
14
|
export const toggleGroupItem = tv({
|
|
15
15
|
slots: {
|
|
16
|
-
base: 'layer-container state-layer relative inline-flex min-w-12 flex-1 items-center justify-center gap-2 px-4 md-sys-typescale-label-large text-md-sys-color-on-surface outline-none transition-colors disabled:cursor-not-allowed disabled:opacity-38 [&:not(:last-child)]:border-r [&:not(:last-child)]:border-md-sys-color-outline data-[state=on]:bg-md-sys-color-secondary-container data-[state=on]:text-md-sys-color-on-secondary-container',
|
|
16
|
+
base: 'layer-container state-layer relative inline-flex min-w-12 flex-1 items-center justify-center gap-2 px-4 md-sys-typescale-label-large text-md-sys-color-on-surface outline-none transition-colors md-sys-motion-fast-effects disabled:cursor-not-allowed disabled:opacity-38 [&:not(:last-child)]:border-r [&:not(:last-child)]:border-md-sys-color-outline data-[state=on]:bg-md-sys-color-secondary-container data-[state=on]:text-md-sys-color-on-secondary-container',
|
|
17
17
|
checkIcon: 'size-[18px] text-[18px]',
|
|
18
18
|
icon: 'size-[18px] text-[18px]'
|
|
19
19
|
}
|
|
@@ -13,7 +13,7 @@ Plain tooltips are used for simple labels, while rich tooltips can contain forma
|
|
|
13
13
|
import { tooltip } from './theme.js';
|
|
14
14
|
import type { TooltipProps } from './types.js';
|
|
15
15
|
import { Tooltip } from 'bits-ui';
|
|
16
|
-
import { enterExit,
|
|
16
|
+
import { enterExit, presence } from '../../../animation/index.js';
|
|
17
17
|
|
|
18
18
|
let {
|
|
19
19
|
subhead,
|
|
@@ -66,43 +66,32 @@ Plain tooltips are used for simple labels, while rich tooltips can contain forma
|
|
|
66
66
|
{/if}
|
|
67
67
|
|
|
68
68
|
{#if hasTooltipContent}
|
|
69
|
-
<Tooltip.Content {...contentProps}
|
|
69
|
+
<Tooltip.Content {...contentProps}>
|
|
70
70
|
{#snippet child({ wrapperProps, props, open })}
|
|
71
|
-
{
|
|
72
|
-
<div
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
easing: easeEmphasizedAccel,
|
|
84
|
-
mode: 'scale'
|
|
85
|
-
}}
|
|
86
|
-
>
|
|
87
|
-
{#if variant === 'rich'}
|
|
88
|
-
<Layer />
|
|
89
|
-
{#if subhead}
|
|
90
|
-
<Title size="small" class={subheadCls()}>
|
|
91
|
-
{subhead}
|
|
92
|
-
</Title>
|
|
93
|
-
{/if}
|
|
94
|
-
<Body class={supportingTextCls()}>
|
|
95
|
-
{supportingText}
|
|
96
|
-
</Body>
|
|
97
|
-
{@render children?.()}
|
|
98
|
-
{:else if supportingText}
|
|
99
|
-
<Body class={supportingTextCls()}>
|
|
100
|
-
{supportingText}
|
|
101
|
-
</Body>
|
|
71
|
+
<div {...wrapperProps} class={wrapperProps.class as any}>
|
|
72
|
+
<div
|
|
73
|
+
{...props}
|
|
74
|
+
class={clsx(baseCls, props.class as any)}
|
|
75
|
+
{@attach presence(() => open, enterExit.scale)}
|
|
76
|
+
>
|
|
77
|
+
{#if variant === 'rich'}
|
|
78
|
+
<Layer />
|
|
79
|
+
{#if subhead}
|
|
80
|
+
<Title size="small" class={subheadCls()}>
|
|
81
|
+
{subhead}
|
|
82
|
+
</Title>
|
|
102
83
|
{/if}
|
|
103
|
-
|
|
84
|
+
<Body class={supportingTextCls()}>
|
|
85
|
+
{supportingText}
|
|
86
|
+
</Body>
|
|
87
|
+
{@render children?.()}
|
|
88
|
+
{:else if supportingText}
|
|
89
|
+
<Body class={supportingTextCls()}>
|
|
90
|
+
{supportingText}
|
|
91
|
+
</Body>
|
|
92
|
+
{/if}
|
|
104
93
|
</div>
|
|
105
|
-
|
|
94
|
+
</div>
|
|
106
95
|
{/snippet}
|
|
107
96
|
</Tooltip.Content>
|
|
108
97
|
{/if}
|
|
@@ -4,7 +4,7 @@ export const breadcrumb = tv({
|
|
|
4
4
|
base: 'flex',
|
|
5
5
|
list: 'flex flex-wrap items-center gap-1',
|
|
6
6
|
item: 'flex items-center gap-1',
|
|
7
|
-
link: 'md-sys-typescale-label-large text-md-sys-color-on-surface-variant md-sys-state-focus-indicator relative inline-flex min-h-12 items-center rounded-full px-3 transition-colors',
|
|
7
|
+
link: 'md-sys-typescale-label-large text-md-sys-color-on-surface-variant md-sys-state-focus-indicator relative inline-flex min-h-12 items-center rounded-full px-3 transition-colors md-sys-motion-fast-effects',
|
|
8
8
|
text: 'md-sys-typescale-label-large text-md-sys-color-on-surface-variant px-1 py-0.5',
|
|
9
9
|
current: 'md-sys-typescale-label-large font-bold px-1 py-0.5',
|
|
10
10
|
separator: 'text-md-sys-color-on-surface-variant inline-flex shrink-0'
|
|
@@ -9,9 +9,9 @@ export const navbar = tv({
|
|
|
9
9
|
});
|
|
10
10
|
export const navitem = tv({
|
|
11
11
|
slots: {
|
|
12
|
-
base: 'group relative flex flex-1 flex-col items-center justify-center min-w-0 max-w-[168px] h-full transition-colors',
|
|
12
|
+
base: 'group relative flex flex-1 flex-col items-center justify-center min-w-0 max-w-[168px] h-full transition-colors md-sys-motion-fast-effects',
|
|
13
13
|
content: 'flex flex-col items-center justify-center gap-1 w-full',
|
|
14
|
-
iconContainer: 'relative flex items-center justify-center h-8 w-16 rounded-full transition-colors
|
|
14
|
+
iconContainer: 'relative flex items-center justify-center h-8 w-16 rounded-full transition-colors md-sys-motion-effects',
|
|
15
15
|
icon: 'text-[24px] size-6',
|
|
16
16
|
label: 'md-sys-typescale-label-medium truncate w-full text-center'
|
|
17
17
|
},
|
|
@@ -8,7 +8,7 @@ On mobile (< md) it automatically renders as a bottom navigation bar.
|
|
|
8
8
|
@see https://m3.material.io/components/navigation-rail/overview
|
|
9
9
|
-->
|
|
10
10
|
<script lang="ts">
|
|
11
|
-
import { clickOutside } from '../../../
|
|
11
|
+
import { clickOutside } from '../../../attachments/index.js';
|
|
12
12
|
import clsx from 'clsx';
|
|
13
13
|
import { setContext, untrack } from 'svelte';
|
|
14
14
|
import type { RailProps } from './types';
|
|
@@ -62,9 +62,7 @@ On mobile (< md) it automatically renders as a bottom navigation bar.
|
|
|
62
62
|
class={railBaseClass}
|
|
63
63
|
style={cssVars}
|
|
64
64
|
data-expanded={expanded}
|
|
65
|
-
|
|
66
|
-
if (expanded) collapsed = true;
|
|
67
|
-
}}
|
|
65
|
+
{@attach expanded && clickOutside(() => (collapsed = true))}
|
|
68
66
|
>
|
|
69
67
|
{#if expandable}
|
|
70
68
|
<ButtonIcon
|
|
@@ -91,7 +89,8 @@ On mobile (< md) it automatically renders as a bottom navigation bar.
|
|
|
91
89
|
pointer-events: none;
|
|
92
90
|
background-color: transparent;
|
|
93
91
|
z-index: -1;
|
|
94
|
-
transition: opacity
|
|
92
|
+
transition: opacity var(--md-sys-motion-duration-effects-spring)
|
|
93
|
+
var(--md-sys-motion-timing-function-effects-spring);
|
|
95
94
|
}
|
|
96
95
|
|
|
97
96
|
.rail-scrim[data-expanded='true'] {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { tv } from 'tailwind-variants';
|
|
2
2
|
export const rail = tv({
|
|
3
3
|
slots: {
|
|
4
|
-
base: 'py-12 hidden bg-md-sys-color-surface-container z-40 flex-col md:flex gap-6 transition-[width,padding,background-color]
|
|
4
|
+
base: 'py-12 hidden bg-md-sys-color-surface-container z-40 flex-col md:flex gap-6 transition-[width,padding,background-color] md-sys-motion-spatial',
|
|
5
5
|
items: 'flex flex-col gap-3 pt-6 scrollbar-none w-full overflow-x-hidden',
|
|
6
|
-
ghost: 'hidden md:block shrink-0 transition-[width]
|
|
6
|
+
ghost: 'hidden md:block shrink-0 transition-[width] md-sys-motion-spatial',
|
|
7
7
|
scrim: 'hidden md:block lg:hidden inset-0'
|
|
8
8
|
},
|
|
9
9
|
variants: {
|
|
@@ -41,11 +41,11 @@ export const rail = tv({
|
|
|
41
41
|
});
|
|
42
42
|
export const railElement = tv({
|
|
43
43
|
slots: {
|
|
44
|
-
base: 'group relative z-30 flex w-full transition-all
|
|
45
|
-
content: 'flex items-center min-w-0 rounded-full gap-0.5 py-1.5 transition-all
|
|
46
|
-
iconContainer: 'relative rounded-full items-center justify-center inline-flex transition-all
|
|
47
|
-
icon: 'text-[24px] transition-all
|
|
48
|
-
label: 'transition-all
|
|
44
|
+
base: 'group relative z-30 flex w-full transition-all md-sys-motion-spatial',
|
|
45
|
+
content: 'flex items-center min-w-0 rounded-full gap-0.5 py-1.5 transition-all md-sys-motion-spatial',
|
|
46
|
+
iconContainer: 'relative rounded-full items-center justify-center inline-flex transition-all md-sys-motion-spatial',
|
|
47
|
+
icon: 'text-[24px] transition-all md-sys-motion-spatial',
|
|
48
|
+
label: 'transition-all md-sys-motion-spatial whitespace-nowrap'
|
|
49
49
|
},
|
|
50
50
|
variants: {
|
|
51
51
|
active: {
|
|
@@ -4,15 +4,18 @@ Material 3 Tabs container.
|
|
|
4
4
|
|
|
5
5
|
Wraps bits-ui Tabs.Root + Tabs.List and renders a sliding active indicator.
|
|
6
6
|
Use TabContent for content-panel tabs; omit it for navigation (href) tabs.
|
|
7
|
+
Switching panels runs the M3 lateral transition: the new panel pushes the old one out in the
|
|
8
|
+
direction of the selected tab.
|
|
7
9
|
|
|
8
10
|
@see https://m3.material.io/components/tabs/overview
|
|
9
11
|
-->
|
|
10
12
|
<script lang="ts">
|
|
13
|
+
import { lateral } from '../../../animation/index.js';
|
|
11
14
|
import { tabHolder } from './theme.js';
|
|
12
15
|
import type { TabHolderProps } from './types.js';
|
|
13
16
|
import Tab from './Tab.svelte';
|
|
14
17
|
import { Tabs } from 'bits-ui';
|
|
15
|
-
import type
|
|
18
|
+
import { tick, type Snippet } from 'svelte';
|
|
16
19
|
|
|
17
20
|
let {
|
|
18
21
|
items,
|
|
@@ -27,9 +30,28 @@ Use TabContent for content-panel tabs; omit it for navigation (href) tabs.
|
|
|
27
30
|
const idx = $derived(items.findIndex((i) => i.value === value));
|
|
28
31
|
const count = $derived(items.length);
|
|
29
32
|
const { base, bar } = $derived(tabHolder({ rounded }));
|
|
33
|
+
|
|
34
|
+
let panels = $state<HTMLElement>();
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Tabs are peers, so switching panels is the M3 lateral transition: the new panel pushes the
|
|
38
|
+
* old one out toward the side of the newly selected tab. Navigation (href) tabs have no panels
|
|
39
|
+
* here — the route change is the app's to animate.
|
|
40
|
+
* https://m3.material.io/styles/motion/transitions/transition-patterns#lateral
|
|
41
|
+
*/
|
|
42
|
+
const select = (next: string) => {
|
|
43
|
+
const from = idx;
|
|
44
|
+
const to = items.findIndex((i) => i.value === next);
|
|
45
|
+
const update = async () => {
|
|
46
|
+
value = next;
|
|
47
|
+
await tick();
|
|
48
|
+
};
|
|
49
|
+
if (!children || !panels || from === -1 || to === -1 || from === to) return void update();
|
|
50
|
+
lateral(update, { target: panels, direction: to > from ? 'forward' : 'backward' });
|
|
51
|
+
};
|
|
30
52
|
</script>
|
|
31
53
|
|
|
32
|
-
<Tabs.Root bind:value {onValueChange} {activationMode}>
|
|
54
|
+
<Tabs.Root bind:value={() => value, select} {onValueChange} {activationMode}>
|
|
33
55
|
<Tabs.List class={base()}>
|
|
34
56
|
{#each items as item (item)}
|
|
35
57
|
<Tab {...item} {variant} />
|
|
@@ -41,5 +63,9 @@ Use TabContent for content-panel tabs; omit it for navigation (href) tabs.
|
|
|
41
63
|
></div>
|
|
42
64
|
</Tabs.List>
|
|
43
65
|
|
|
44
|
-
{
|
|
66
|
+
{#if children}
|
|
67
|
+
<div bind:this={panels}>
|
|
68
|
+
{@render children()}
|
|
69
|
+
</div>
|
|
70
|
+
{/if}
|
|
45
71
|
</Tabs.Root>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { TabHolderProps } from './types.js';
|
|
2
|
-
import type
|
|
2
|
+
import { type Snippet } from 'svelte';
|
|
3
3
|
type $$ComponentProps = TabHolderProps & {
|
|
4
4
|
children?: Snippet;
|
|
5
5
|
};
|
|
@@ -8,6 +8,8 @@ type $$ComponentProps = TabHolderProps & {
|
|
|
8
8
|
*
|
|
9
9
|
* Wraps bits-ui Tabs.Root + Tabs.List and renders a sliding active indicator.
|
|
10
10
|
* Use TabContent for content-panel tabs; omit it for navigation (href) tabs.
|
|
11
|
+
* Switching panels runs the M3 lateral transition: the new panel pushes the old one out in the
|
|
12
|
+
* direction of the selected tab.
|
|
11
13
|
*
|
|
12
14
|
* @see https://m3.material.io/components/tabs/overview
|
|
13
15
|
*/
|
|
@@ -2,7 +2,7 @@ import { tv } from 'tailwind-variants';
|
|
|
2
2
|
export const tabHolder = tv({
|
|
3
3
|
slots: {
|
|
4
4
|
base: 'relative flex w-full bg-md-sys-color-surface',
|
|
5
|
-
bar: 'absolute bottom-0 h-[3px] rounded-t-full bg-md-sys-color-primary transition-transform
|
|
5
|
+
bar: 'absolute bottom-0 h-[3px] rounded-t-full bg-md-sys-color-primary transition-transform md-sys-motion-fast-spatial'
|
|
6
6
|
},
|
|
7
7
|
variants: {
|
|
8
8
|
rounded: {
|
|
@@ -2,10 +2,18 @@
|
|
|
2
2
|
@component
|
|
3
3
|
Material 3 Circular Progress Indicator.
|
|
4
4
|
|
|
5
|
+
Determinate: the arc springs to each new `percent` on the slow effects spring (critically damped,
|
|
6
|
+
so progress never visibly runs backwards), with a gap separating it from the track.
|
|
7
|
+
Indeterminate: the arc's head and tail take turns springing ahead, so it grows, shrinks and
|
|
8
|
+
travels around the circle.
|
|
9
|
+
|
|
5
10
|
@see https://m3.material.io/components/progress-indicators/overview#circular-progress-indicators
|
|
6
11
|
-->
|
|
7
12
|
<script lang="ts">
|
|
8
13
|
import { Progress } from 'bits-ui';
|
|
14
|
+
import { animate, type AnimationPlaybackControls } from 'motion';
|
|
15
|
+
import { springTokens, springTransition } from '../../animation/spring.js';
|
|
16
|
+
import { SpringValue } from '../../animation/springValue.svelte.js';
|
|
9
17
|
|
|
10
18
|
let {
|
|
11
19
|
percent,
|
|
@@ -20,8 +28,69 @@ Material 3 Circular Progress Indicator.
|
|
|
20
28
|
thickness?: number;
|
|
21
29
|
} = $props();
|
|
22
30
|
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
/* M3: 4dp between the indicator and the track, measured between the rounded caps. */
|
|
32
|
+
const GAP_PX = 4;
|
|
33
|
+
/* Indeterminate arc lengths it grows to and shrinks back to. Not M3 values: tune by eye. */
|
|
34
|
+
const MIN_SWEEP_DEG = 20;
|
|
35
|
+
const MAX_SWEEP_DEG = 270;
|
|
36
|
+
|
|
37
|
+
const r = $derived(size / 2 - thickness / 2);
|
|
38
|
+
const circumference = $derived(Math.PI * r * 2);
|
|
39
|
+
/* Round caps overhang each end by half the stroke; the gap sits between those caps. */
|
|
40
|
+
const gapDeg = $derived((((GAP_PX + thickness) / r) * 180) / Math.PI);
|
|
41
|
+
|
|
42
|
+
const shown = new SpringValue(() => percent ?? 0, springTokens.slowEffects);
|
|
43
|
+
|
|
44
|
+
let head = $state(0);
|
|
45
|
+
let tail = $state(0);
|
|
46
|
+
|
|
47
|
+
$effect(() => {
|
|
48
|
+
if (percent != null) return;
|
|
49
|
+
let running = true;
|
|
50
|
+
let controls: AnimationPlaybackControls | undefined;
|
|
51
|
+
const spring = springTransition(springTokens.spatial);
|
|
52
|
+
|
|
53
|
+
(async () => {
|
|
54
|
+
let h = 0;
|
|
55
|
+
let t = 0;
|
|
56
|
+
while (running) {
|
|
57
|
+
controls = animate(h, t + MAX_SWEEP_DEG, { ...spring, onUpdate: (v) => (head = v) });
|
|
58
|
+
await controls;
|
|
59
|
+
h = t + MAX_SWEEP_DEG;
|
|
60
|
+
if (!running) break;
|
|
61
|
+
controls = animate(t, h - MIN_SWEEP_DEG, { ...spring, onUpdate: (v) => (tail = v) });
|
|
62
|
+
await controls;
|
|
63
|
+
t = h - MIN_SWEEP_DEG;
|
|
64
|
+
}
|
|
65
|
+
})();
|
|
66
|
+
|
|
67
|
+
return () => {
|
|
68
|
+
running = false;
|
|
69
|
+
controls?.stop();
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
/** An arc from `fromDeg`, `sweepDeg` long, as stroke-dash values (0° = 12 o'clock). */
|
|
74
|
+
const arc = (fromDeg: number, sweepDeg: number) => ({
|
|
75
|
+
dasharray: `${(Math.max(0, sweepDeg) / 360) * circumference} ${circumference}`,
|
|
76
|
+
dashoffset: (-(fromDeg % 360) / 360) * circumference,
|
|
77
|
+
// A zero-length dash with round caps still paints a dot.
|
|
78
|
+
opacity: sweepDeg > 0.5 ? 1 : 0
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const from = $derived(percent != null ? 0 : tail);
|
|
82
|
+
const sweep = $derived(percent != null ? (shown.current / 100) * 360 : head - tail);
|
|
83
|
+
|
|
84
|
+
const indicator = $derived(arc(from, sweep));
|
|
85
|
+
/* The track fills the rest of the circle, keeping a gap either side of the indicator. M3 draws
|
|
86
|
+
no track while indeterminate. */
|
|
87
|
+
const track = $derived(
|
|
88
|
+
percent == null
|
|
89
|
+
? arc(0, 0)
|
|
90
|
+
: indicator.opacity
|
|
91
|
+
? arc(from + sweep + gapDeg, 360 - sweep - gapDeg * 2)
|
|
92
|
+
: arc(0, 360)
|
|
93
|
+
);
|
|
25
94
|
</script>
|
|
26
95
|
|
|
27
96
|
<Progress.Root value={percent ?? null} max={100} data-cy="m3-circular-progress">
|
|
@@ -40,6 +109,10 @@ Material 3 Circular Progress Indicator.
|
|
|
40
109
|
{r}
|
|
41
110
|
stroke="var(--color-md-sys-color-secondary-container)"
|
|
42
111
|
stroke-width={thickness}
|
|
112
|
+
stroke-dasharray={track.dasharray}
|
|
113
|
+
stroke-dashoffset={track.dashoffset}
|
|
114
|
+
opacity={track.opacity}
|
|
115
|
+
stroke-linecap="round"
|
|
43
116
|
fill="none"
|
|
44
117
|
/>
|
|
45
118
|
<circle
|
|
@@ -48,8 +121,9 @@ Material 3 Circular Progress Indicator.
|
|
|
48
121
|
{r}
|
|
49
122
|
stroke="var(--color-md-sys-color-primary)"
|
|
50
123
|
stroke-width={thickness}
|
|
51
|
-
stroke-dasharray=
|
|
52
|
-
stroke-dashoffset={
|
|
124
|
+
stroke-dasharray={indicator.dasharray}
|
|
125
|
+
stroke-dashoffset={indicator.dashoffset}
|
|
126
|
+
opacity={indicator.opacity}
|
|
53
127
|
stroke-linecap="round"
|
|
54
128
|
fill="none"
|
|
55
129
|
/>
|
|
@@ -9,6 +9,11 @@ type $$ComponentProps = {
|
|
|
9
9
|
/**
|
|
10
10
|
* Material 3 Circular Progress Indicator.
|
|
11
11
|
*
|
|
12
|
+
* Determinate: the arc springs to each new `percent` on the slow effects spring (critically damped,
|
|
13
|
+
* so progress never visibly runs backwards), with a gap separating it from the track.
|
|
14
|
+
* Indeterminate: the arc's head and tail take turns springing ahead, so it grows, shrinks and
|
|
15
|
+
* travels around the circle.
|
|
16
|
+
*
|
|
12
17
|
* @see https://m3.material.io/components/progress-indicators/overview#circular-progress-indicators
|
|
13
18
|
*/
|
|
14
19
|
declare const CircularProgress: import("svelte").Component<$$ComponentProps, {}, "">;
|