@noxlovette/material 0.4.8 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/animation/TransitionPatterns.stories.svelte +357 -0
- package/dist/animation/TransitionPatterns.stories.svelte.d.ts +3 -0
- package/dist/animation/containerTransform.d.ts +26 -12
- package/dist/animation/containerTransform.js +22 -138
- package/dist/animation/enterExit.d.ts +21 -3
- package/dist/animation/enterExit.js +69 -22
- package/dist/animation/index.d.ts +1 -1
- package/dist/animation/index.js +1 -1
- package/dist/animation/presence.svelte.d.ts +47 -0
- package/dist/animation/presence.svelte.js +84 -0
- package/dist/animation/sharedAxisTransition.d.ts +35 -37
- package/dist/animation/sharedAxisTransition.js +50 -22
- package/dist/animation/skeleton.d.ts +11 -0
- package/dist/animation/skeleton.js +23 -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 +85 -92
- package/dist/components/date/DateField.svelte.d.ts +2 -5
- package/dist/components/date/DateRangeField.svelte +113 -155
- package/dist/components/date/DateRangeField.svelte.d.ts +2 -5
- package/dist/components/date/theme.d.ts +95 -0
- package/dist/components/date/theme.js +99 -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 +9 -0
- package/dist/components/forms/textfield/theme.js +26 -9
- 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/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 +62 -61
- 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 +15 -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
|
@@ -3,12 +3,12 @@ export type CommandVariants = VariantProps<typeof command>;
|
|
|
3
3
|
export declare const command: import("tailwind-variants").TVReturnType<{
|
|
4
4
|
[key: string]: {
|
|
5
5
|
[key: string]: import("tailwind-variants").ClassValue | {
|
|
6
|
+
item?: import("tailwind-variants").ClassValue;
|
|
6
7
|
input?: import("tailwind-variants").ClassValue;
|
|
7
8
|
base?: import("tailwind-variants").ClassValue;
|
|
8
9
|
group?: import("tailwind-variants").ClassValue;
|
|
9
10
|
list?: import("tailwind-variants").ClassValue;
|
|
10
11
|
separator?: import("tailwind-variants").ClassValue;
|
|
11
|
-
item?: import("tailwind-variants").ClassValue;
|
|
12
12
|
inputWrapper?: import("tailwind-variants").ClassValue;
|
|
13
13
|
inputIcon?: import("tailwind-variants").ClassValue;
|
|
14
14
|
empty?: import("tailwind-variants").ClassValue;
|
|
@@ -18,12 +18,12 @@ export declare const command: import("tailwind-variants").TVReturnType<{
|
|
|
18
18
|
} | {
|
|
19
19
|
[x: string]: {
|
|
20
20
|
[x: string]: import("tailwind-variants").ClassValue | {
|
|
21
|
+
item?: import("tailwind-variants").ClassValue;
|
|
21
22
|
input?: import("tailwind-variants").ClassValue;
|
|
22
23
|
base?: import("tailwind-variants").ClassValue;
|
|
23
24
|
group?: import("tailwind-variants").ClassValue;
|
|
24
25
|
list?: import("tailwind-variants").ClassValue;
|
|
25
26
|
separator?: import("tailwind-variants").ClassValue;
|
|
26
|
-
item?: import("tailwind-variants").ClassValue;
|
|
27
27
|
inputWrapper?: import("tailwind-variants").ClassValue;
|
|
28
28
|
inputIcon?: import("tailwind-variants").ClassValue;
|
|
29
29
|
empty?: import("tailwind-variants").ClassValue;
|
|
@@ -44,12 +44,12 @@ export declare const command: import("tailwind-variants").TVReturnType<{
|
|
|
44
44
|
}, undefined, {
|
|
45
45
|
[key: string]: {
|
|
46
46
|
[key: string]: import("tailwind-variants").ClassValue | {
|
|
47
|
+
item?: import("tailwind-variants").ClassValue;
|
|
47
48
|
input?: import("tailwind-variants").ClassValue;
|
|
48
49
|
base?: import("tailwind-variants").ClassValue;
|
|
49
50
|
group?: import("tailwind-variants").ClassValue;
|
|
50
51
|
list?: import("tailwind-variants").ClassValue;
|
|
51
52
|
separator?: import("tailwind-variants").ClassValue;
|
|
52
|
-
item?: import("tailwind-variants").ClassValue;
|
|
53
53
|
inputWrapper?: import("tailwind-variants").ClassValue;
|
|
54
54
|
inputIcon?: import("tailwind-variants").ClassValue;
|
|
55
55
|
empty?: import("tailwind-variants").ClassValue;
|
|
@@ -9,7 +9,7 @@ export const command = tv({
|
|
|
9
9
|
empty: 'py-6 text-center md-sys-typescale-body-medium text-md-sys-color-on-surface-variant',
|
|
10
10
|
group: 'overflow-hidden p-1 text-md-sys-color-on-surface-variant [&_[data-command-group-heading]]:px-3 [&_[data-command-group-heading]]:py-2 [&_[data-command-group-heading]]:md-sys-typescale-label-medium [&_[data-command-group-heading]]:text-md-sys-color-on-surface-variant',
|
|
11
11
|
separator: '-mx-1 h-px bg-md-sys-color-outline-variant',
|
|
12
|
-
item: 'relative flex cursor-default select-none items-center rounded-lg px-3 py-3 md-sys-typescale-body-medium outline-none data-[selected=true]:bg-md-sys-color-secondary-container data-[selected=true]:text-md-sys-color-on-secondary-container data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50 transition-colors',
|
|
12
|
+
item: 'relative flex cursor-default select-none items-center rounded-lg px-3 py-3 md-sys-typescale-body-medium outline-none data-[selected=true]:bg-md-sys-color-secondary-container data-[selected=true]:text-md-sys-color-on-secondary-container data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50 transition-colors md-sys-motion-fast-effects',
|
|
13
13
|
itemIcon: 'mr-3 h-5 w-5 shrink-0'
|
|
14
14
|
}
|
|
15
15
|
});
|
|
@@ -8,9 +8,9 @@ export const pinInput = tv({
|
|
|
8
8
|
relative flex h-14 w-12 items-center justify-center
|
|
9
9
|
bg-md-sys-color-surface-container-highest rounded-t-xs
|
|
10
10
|
md-sys-typescale-headline-small text-md-sys-color-on-surface
|
|
11
|
-
transition-
|
|
11
|
+
transition-colors md-sys-motion-fast-effects
|
|
12
12
|
after:absolute after:bottom-0 after:left-0 after:right-0 after:h-px after:bg-md-sys-color-on-surface-variant
|
|
13
|
-
after:transition-[height,background-color] after:
|
|
13
|
+
after:transition-[height,background-color] after:md-sys-motion-fast-spatial
|
|
14
14
|
data-[focus]:after:bg-md-sys-color-primary data-[focus]:after:h-[2px]
|
|
15
15
|
data-[placeholder]:text-md-sys-color-on-surface-variant/38
|
|
16
16
|
`,
|
|
@@ -5,8 +5,8 @@ export const radioGroup = tv({
|
|
|
5
5
|
item: 'inline-flex min-h-10 cursor-pointer select-none items-center gap-3 text-md-sys-color-on-surface',
|
|
6
6
|
indicator: 'relative inline-flex size-[20px] shrink-0',
|
|
7
7
|
control: 'relative layer-container absolute -inset-[10px] rounded-full text-md-sys-color-on-surface-variant state-layer before:rounded-full',
|
|
8
|
-
outerRing: 'absolute inset-0 rounded-full border-2 border-md-sys-color-outline transition-colors',
|
|
9
|
-
innerDot: 'absolute inset-0 m-auto size-2.5 scale-0 rounded-full bg-md-sys-color-on-primary-container transition-transform
|
|
8
|
+
outerRing: 'absolute inset-0 rounded-full border-2 border-md-sys-color-outline transition-colors md-sys-motion-fast-effects',
|
|
9
|
+
innerDot: 'absolute inset-0 m-auto size-2.5 scale-0 rounded-full bg-md-sys-color-on-primary-container transition-transform md-sys-motion-fast-spatial',
|
|
10
10
|
label: 'md-sys-typescale-body-large text-md-sys-color-on-surface',
|
|
11
11
|
supporting: 'md-sys-typescale-body-medium text-md-sys-color-on-surface-variant'
|
|
12
12
|
},
|
|
@@ -8,13 +8,12 @@ Powered by bits-ui for accessibility and behavior.
|
|
|
8
8
|
@see https://m3.material.io/components/menus/overview
|
|
9
9
|
-->
|
|
10
10
|
<script lang="ts">
|
|
11
|
+
import { enterExit, presence } from '../../../animation/index.js';
|
|
11
12
|
import { Select } from 'bits-ui';
|
|
12
13
|
import { select as selectCls } from './theme.js';
|
|
13
14
|
import { Icon } from '../../../utils/index.js';
|
|
14
15
|
import type { SelectProps } from './types.js';
|
|
15
16
|
import clsx from 'clsx';
|
|
16
|
-
import { enterExit } from '../../../animation/enterExit.js';
|
|
17
|
-
import { easeEmphasized } from '../../../animation/easing.js';
|
|
18
17
|
|
|
19
18
|
let {
|
|
20
19
|
value = $bindable(),
|
|
@@ -66,7 +65,6 @@ Powered by bits-ui for accessibility and behavior.
|
|
|
66
65
|
|
|
67
66
|
<Select.Portal disabled={portalDisabled}>
|
|
68
67
|
<Select.Content
|
|
69
|
-
forceMount
|
|
70
68
|
class={cls.content({ class: clsx('z-[100]', contentProps?.class) })}
|
|
71
69
|
sideOffset={4}
|
|
72
70
|
align="start"
|
|
@@ -74,58 +72,48 @@ Powered by bits-ui for accessibility and behavior.
|
|
|
74
72
|
>
|
|
75
73
|
{#snippet child({ wrapperProps, props, open })}
|
|
76
74
|
<div {...wrapperProps} class={clsx('z-[100]', wrapperProps.class as any)}>
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
75
|
+
<div
|
|
76
|
+
{...props}
|
|
77
|
+
class={clsx('relative flex flex-col outline-none', props.class as any)}
|
|
78
|
+
{@attach presence(() => open, enterExit.scale)}
|
|
79
|
+
style:width="var(--bits-select-anchor-width)"
|
|
80
|
+
style:max-width="min(calc(100vw - 32px), 560px)"
|
|
81
|
+
>
|
|
82
|
+
<Select.Viewport class="w-full">
|
|
83
|
+
{#each options as item, i (i)}
|
|
84
|
+
{#if item.type === 'group'}
|
|
85
|
+
<Select.Group>
|
|
86
|
+
{#if item.heading}
|
|
87
|
+
<Select.GroupHeading class={cls.groupLabel()}>
|
|
88
|
+
{item.heading}
|
|
89
|
+
</Select.GroupHeading>
|
|
90
|
+
{/if}
|
|
91
|
+
{#each item.items as innerItem, j (j)}
|
|
92
|
+
{#if innerItem.type !== 'group'}
|
|
93
|
+
<Select.Item {...innerItem} class={cls.item()}>
|
|
94
|
+
{#snippet children({ selected })}
|
|
95
|
+
<span class="flex-1 truncate">{innerItem.label}</span>
|
|
96
|
+
{#if selected}
|
|
97
|
+
<Icon aria-hidden="true" name="check" class="size-5 shrink-0" />
|
|
98
|
+
{/if}
|
|
99
|
+
{/snippet}
|
|
100
|
+
</Select.Item>
|
|
97
101
|
{/if}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
{/each}
|
|
114
|
-
</Select.Group>
|
|
115
|
-
{:else}
|
|
116
|
-
<Select.Item {...item} class={cls.item()}>
|
|
117
|
-
{#snippet children({ selected })}
|
|
118
|
-
<span class="flex-1 truncate">{item.label}</span>
|
|
119
|
-
{#if selected}
|
|
120
|
-
<Icon aria-hidden="true" name="check" class="size-5 shrink-0" />
|
|
121
|
-
{/if}
|
|
122
|
-
{/snippet}
|
|
123
|
-
</Select.Item>
|
|
124
|
-
{/if}
|
|
125
|
-
{/each}
|
|
126
|
-
</Select.Viewport>
|
|
127
|
-
</div>
|
|
128
|
-
{/if}
|
|
102
|
+
{/each}
|
|
103
|
+
</Select.Group>
|
|
104
|
+
{:else}
|
|
105
|
+
<Select.Item {...item} class={cls.item()}>
|
|
106
|
+
{#snippet children({ selected })}
|
|
107
|
+
<span class="flex-1 truncate">{item.label}</span>
|
|
108
|
+
{#if selected}
|
|
109
|
+
<Icon aria-hidden="true" name="check" class="size-5 shrink-0" />
|
|
110
|
+
{/if}
|
|
111
|
+
{/snippet}
|
|
112
|
+
</Select.Item>
|
|
113
|
+
{/if}
|
|
114
|
+
{/each}
|
|
115
|
+
</Select.Viewport>
|
|
116
|
+
</div>
|
|
129
117
|
</div>
|
|
130
118
|
{/snippet}
|
|
131
119
|
</Select.Content>
|
|
@@ -28,7 +28,7 @@ export const select = tv({
|
|
|
28
28
|
mr-3 text-md-sys-color-on-surface-variant size-6
|
|
29
29
|
group-data-[state=open]:text-md-sys-color-primary
|
|
30
30
|
group-data-[invalid]:text-md-sys-color-error z-20 text-[24px]
|
|
31
|
-
transition-transform
|
|
31
|
+
transition-transform md-sys-motion-fast-spatial
|
|
32
32
|
group-data-[state=open]:rotate-180
|
|
33
33
|
`,
|
|
34
34
|
content: `
|
|
@@ -45,7 +45,7 @@ export const select = tv({
|
|
|
45
45
|
data-[selected]:bg-md-sys-color-secondary-container
|
|
46
46
|
data-[selected]:text-md-sys-color-on-secondary-container
|
|
47
47
|
data-[disabled]:cursor-not-allowed data-[disabled]:opacity-38
|
|
48
|
-
transition-colors
|
|
48
|
+
transition-colors md-sys-motion-fast-effects
|
|
49
49
|
`,
|
|
50
50
|
groupLabel: `
|
|
51
51
|
px-3 pt-2 pb-1 md-sys-typescale-label-medium
|
|
@@ -68,7 +68,7 @@ export const select = tv({
|
|
|
68
68
|
bg-md-sys-color-surface-container-highest
|
|
69
69
|
rounded-t-xs state-layer before:rounded-xs hover:before:bg-md-sys-color-on-surface/8
|
|
70
70
|
after:absolute after:bottom-0 after:left-0 after:right-0 after:h-px after:bg-md-sys-color-on-surface-variant
|
|
71
|
-
after:transition-[height,background-color] after:
|
|
71
|
+
after:transition-[height,background-color] after:md-sys-motion-fast-spatial
|
|
72
72
|
hover:after:bg-md-sys-color-on-surface
|
|
73
73
|
has-[[data-state=open]]:after:bg-md-sys-color-primary
|
|
74
74
|
has-[[data-state=open]]:after:h-[2px]
|
|
@@ -79,7 +79,7 @@ export const select = tv({
|
|
|
79
79
|
outlined: {
|
|
80
80
|
base: `
|
|
81
81
|
rounded-xs border border-md-sys-color-outline
|
|
82
|
-
transition-colors
|
|
82
|
+
transition-colors md-sys-motion-fast-effects
|
|
83
83
|
hover:border-md-sys-color-on-surface
|
|
84
84
|
has-[[data-state=open]]:border-2 has-[[data-state=open]]:border-md-sys-color-primary
|
|
85
85
|
`
|
|
@@ -4,11 +4,17 @@ Material 3 Slider.
|
|
|
4
4
|
|
|
5
5
|
Sliders allow users to make selections from a range of values.
|
|
6
6
|
|
|
7
|
+
The handle follows the pointer 1:1 while dragged; any other change (a keyboard step, a click on
|
|
8
|
+
the track, a new `value` from outside) springs to it on the fast spatial spring.
|
|
9
|
+
|
|
7
10
|
@see https://m3.material.io/components/sliders/overview
|
|
8
11
|
-->
|
|
9
12
|
<script lang="ts">
|
|
10
13
|
import { Slider } from 'bits-ui';
|
|
11
14
|
import clsx from 'clsx';
|
|
15
|
+
import type { Attachment } from 'svelte/attachments';
|
|
16
|
+
import { springTokens } from '../../../animation/spring.js';
|
|
17
|
+
import { SpringValue } from '../../../animation/springValue.svelte.js';
|
|
12
18
|
import { Icon } from '../../../utils/index.js';
|
|
13
19
|
import { slider } from './theme.js';
|
|
14
20
|
import type { SliderProps } from './types.js';
|
|
@@ -26,58 +32,120 @@ Sliders allow users to make selections from a range of values.
|
|
|
26
32
|
stops = false,
|
|
27
33
|
endStops = true,
|
|
28
34
|
vertical = false,
|
|
29
|
-
format = (n: number) =>
|
|
30
|
-
return n.toFixed(0);
|
|
31
|
-
},
|
|
35
|
+
format = (n: number) => n.toFixed(0),
|
|
32
36
|
class: className,
|
|
33
37
|
dir,
|
|
34
38
|
id,
|
|
35
39
|
...extra
|
|
36
40
|
}: SliderProps = $props();
|
|
37
41
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
trackFill,
|
|
45
|
-
trackRest,
|
|
46
|
-
stopBase,
|
|
47
|
-
stopFill,
|
|
48
|
-
stopRest,
|
|
49
|
-
handle: handleCls,
|
|
50
|
-
value: valueCls
|
|
51
|
-
} = $derived(slider({ size, vertical }));
|
|
52
|
-
|
|
53
|
-
const sliderDir = $derived(dir === null || dir === 'auto' ? undefined : dir);
|
|
54
|
-
const sliderId = $derived(id === null ? undefined : id);
|
|
42
|
+
/* M3 slider geometry, in px. */
|
|
43
|
+
const HANDLE_PX = 4;
|
|
44
|
+
const GAP_PX = 6;
|
|
45
|
+
const ICON_PADDING_PX = 8;
|
|
46
|
+
/* Stops closer than this to the handle's center are hidden under its gap. */
|
|
47
|
+
const STOP_CLEARANCE_PX = HANDLE_PX / 2 + GAP_PX;
|
|
55
48
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
let inlineSize = $derived(vertical ? offsetHeight : offsetWidth);
|
|
49
|
+
const iconSize = $derived(size === 'xl' ? 'lg' : 'md');
|
|
50
|
+
const iconPx = $derived(size === 'xl' ? 40 : 24);
|
|
59
51
|
|
|
52
|
+
const stepValue = $derived(typeof step === 'number' ? step : undefined);
|
|
60
53
|
const range = $derived(max - min);
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
54
|
+
const toFraction = (v: number) => (range > 0 ? (v - min) / range : 0);
|
|
55
|
+
|
|
56
|
+
/* ---- motion ---------------------------------------------------------------------------- */
|
|
57
|
+
|
|
58
|
+
/*
|
|
59
|
+
A press that jumps the value (a click on the track) springs; once the pointer moves while
|
|
60
|
+
pressed, the handle is being dragged and tracks it directly.
|
|
61
|
+
*/
|
|
62
|
+
let dragging = $state(false);
|
|
63
|
+
|
|
64
|
+
const trackDrag: Attachment<HTMLElement> = (node) => {
|
|
65
|
+
const move = () => (dragging = true);
|
|
66
|
+
const release = () => {
|
|
67
|
+
dragging = false;
|
|
68
|
+
window.removeEventListener('pointermove', move);
|
|
69
|
+
window.removeEventListener('pointerup', release);
|
|
70
|
+
window.removeEventListener('pointercancel', release);
|
|
71
|
+
};
|
|
72
|
+
const press = () => {
|
|
73
|
+
if (disabled) return;
|
|
74
|
+
window.addEventListener('pointermove', move);
|
|
75
|
+
window.addEventListener('pointerup', release);
|
|
76
|
+
window.addEventListener('pointercancel', release);
|
|
77
|
+
};
|
|
78
|
+
node.addEventListener('pointerdown', press);
|
|
79
|
+
return () => {
|
|
80
|
+
node.removeEventListener('pointerdown', press);
|
|
81
|
+
release();
|
|
68
82
|
};
|
|
83
|
+
};
|
|
69
84
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
85
|
+
/** The rendered position as a 0–1 fraction; springs towards `value` unless dragging. */
|
|
86
|
+
const fraction = new SpringValue(
|
|
87
|
+
() => toFraction(value),
|
|
88
|
+
springTokens.fastSpatial,
|
|
89
|
+
() => dragging
|
|
90
|
+
);
|
|
76
91
|
|
|
77
|
-
|
|
92
|
+
/* The spring may overshoot the ends; the track can't. */
|
|
93
|
+
const pos = $derived(Math.min(1, Math.max(0, fraction.current)));
|
|
94
|
+
|
|
95
|
+
/* ---- geometry -------------------------------------------------------------------------- */
|
|
96
|
+
|
|
97
|
+
let trackWidth = $state(0);
|
|
98
|
+
let trackHeight = $state(0);
|
|
99
|
+
const length = $derived(vertical ? trackHeight : trackWidth);
|
|
100
|
+
|
|
101
|
+
const start = $derived(vertical ? 'bottom' : 'left');
|
|
102
|
+
const extent = $derived(vertical ? 'height' : 'width');
|
|
103
|
+
|
|
104
|
+
/** Handle center: travels the track inset by half a handle so it never overhangs the ends. */
|
|
105
|
+
const center = (f: number) => `calc(${HANDLE_PX / 2}px + ${f} * (100% - ${HANDLE_PX}px))`;
|
|
106
|
+
const centerPx = $derived(HANDLE_PX / 2 + pos * (length - HANDLE_PX));
|
|
107
|
+
|
|
108
|
+
const activePx = $derived(centerPx - HANDLE_PX / 2 - GAP_PX);
|
|
109
|
+
const inactivePx = $derived(length - centerPx - HANDLE_PX / 2 - GAP_PX);
|
|
110
|
+
|
|
111
|
+
const activeStyle = $derived(
|
|
112
|
+
`${extent}: max(0px, calc(${pos} * (100% - ${HANDLE_PX}px) - ${GAP_PX}px))`
|
|
113
|
+
);
|
|
114
|
+
const inactiveStyle = $derived(
|
|
115
|
+
`${extent}: max(0px, calc(${1 - pos} * (100% - ${HANDLE_PX}px) - ${GAP_PX}px))`
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
/* Stops sit inset by half the track thickness so the end stops land in the rounded caps. */
|
|
119
|
+
const stopList = $derived.by(() => {
|
|
120
|
+
const output: number[] = [];
|
|
121
|
+
if (stops && stepValue && range > 0) {
|
|
122
|
+
for (let v = min; v <= max; v += stepValue) output.push(toFraction(v));
|
|
123
|
+
} else if (endStops) {
|
|
124
|
+
output.push(1);
|
|
125
|
+
}
|
|
126
|
+
return output.filter((s) => {
|
|
127
|
+
if (leadingIconProps && s === 0) return false;
|
|
128
|
+
if (trailingIconProps && s === 1) return false;
|
|
129
|
+
return !length || Math.abs(s - pos) * length > STOP_CLEARANCE_PX;
|
|
130
|
+
});
|
|
78
131
|
});
|
|
79
132
|
|
|
80
|
-
|
|
133
|
+
/* Icons ride inside their track; when it gets too short they hop across the handle. */
|
|
134
|
+
const iconFits = (trackPx: number) => trackPx >= iconPx + ICON_PADDING_PX * 2;
|
|
135
|
+
const leadingOnActive = $derived(!length || iconFits(activePx));
|
|
136
|
+
const trailingOnInactive = $derived(!length || iconFits(inactivePx));
|
|
137
|
+
|
|
138
|
+
const leadingOffset = $derived(
|
|
139
|
+
leadingOnActive ? ICON_PADDING_PX : centerPx + HANDLE_PX / 2 + GAP_PX + ICON_PADDING_PX
|
|
140
|
+
);
|
|
141
|
+
const trailingOffset = $derived(
|
|
142
|
+
trailingOnInactive
|
|
143
|
+
? length - ICON_PADDING_PX - iconPx
|
|
144
|
+
: centerPx - HANDLE_PX / 2 - GAP_PX - ICON_PADDING_PX - iconPx
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const sliderDir = $derived(dir === null || dir === 'auto' ? undefined : dir);
|
|
148
|
+
const sliderId = $derived(id === null ? undefined : id);
|
|
81
149
|
</script>
|
|
82
150
|
|
|
83
151
|
<Slider.Root
|
|
@@ -93,109 +161,59 @@ Sliders allow users to make selections from a range of values.
|
|
|
93
161
|
{...extra}
|
|
94
162
|
>
|
|
95
163
|
{#snippet child({ props })}
|
|
164
|
+
{@const cls = slider({ size, vertical, active: dragging })}
|
|
96
165
|
<div
|
|
97
166
|
{...props}
|
|
98
|
-
class={root({ class: clsx(className) })}
|
|
99
|
-
|
|
167
|
+
class={cls.root({ class: clsx(className) })}
|
|
168
|
+
bind:clientWidth={trackWidth}
|
|
169
|
+
bind:clientHeight={trackHeight}
|
|
170
|
+
{@attach trackDrag}
|
|
100
171
|
>
|
|
101
|
-
<div
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
class: clsx(
|
|
111
|
-
vertical &&
|
|
112
|
-
'[clip-path:inset(calc(100%-var(--handle-left))_0_0_0_round_var(--m3-slider-track-in-shape)_var(--m3-slider-track-in-shape)_var(--track-radius)_var(--track-radius))]'
|
|
113
|
-
)
|
|
114
|
-
})}
|
|
115
|
-
>
|
|
116
|
-
{#each stopList as stop}
|
|
117
|
-
<div
|
|
118
|
-
class={clsx(
|
|
119
|
-
stopBase(),
|
|
120
|
-
stopFill(),
|
|
121
|
-
vertical ? '-translate-x-1/2 translate-y-1/2' : '-translate-x-1/2 -translate-y-1/2'
|
|
122
|
-
)}
|
|
123
|
-
style:--x={stop - 0.5}
|
|
124
|
-
></div>
|
|
125
|
-
{/each}
|
|
126
|
-
</div>
|
|
127
|
-
|
|
128
|
-
<div
|
|
129
|
-
class={trackRest({
|
|
130
|
-
class: clsx(
|
|
131
|
-
vertical &&
|
|
132
|
-
'[clip-path:inset(0_0_var(--handle-right)_0_round_var(--track-radius)_var(--track-radius)_var(--m3-slider-track-in-shape)_var(--m3-slider-track-in-shape))]'
|
|
133
|
-
)
|
|
134
|
-
})}
|
|
135
|
-
>
|
|
136
|
-
{#each stopList as stop}
|
|
137
|
-
<div
|
|
138
|
-
class={clsx(
|
|
139
|
-
stopBase(),
|
|
140
|
-
stopRest(),
|
|
141
|
-
vertical ? '-translate-x-1/2 translate-y-1/2' : '-translate-x-1/2 -translate-y-1/2'
|
|
142
|
-
)}
|
|
143
|
-
style:--x={stop - 0.5}
|
|
144
|
-
></div>
|
|
145
|
-
{/each}
|
|
146
|
-
</div>
|
|
172
|
+
<div class={cls.track({ class: cls.activeTrack() })} style={activeStyle}></div>
|
|
173
|
+
<div class={cls.track({ class: cls.inactiveTrack() })} style={inactiveStyle}></div>
|
|
174
|
+
|
|
175
|
+
{#each stopList as s (s)}
|
|
176
|
+
<div
|
|
177
|
+
class={cls.stop({ class: s < pos ? cls.stopOnActive() : cls.stopOnInactive() })}
|
|
178
|
+
style="{start}: calc(var(--track) / 2 + {s} * (100% - var(--track)))"
|
|
179
|
+
></div>
|
|
180
|
+
{/each}
|
|
147
181
|
|
|
148
182
|
{#if leadingIconProps}
|
|
149
|
-
<
|
|
150
|
-
class={
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
)
|
|
156
|
-
})}
|
|
157
|
-
{...leadingIconProps}
|
|
158
|
-
/>
|
|
183
|
+
<span
|
|
184
|
+
class={cls.icon({ class: leadingOnActive ? cls.iconOnActive() : cls.iconOnInactive() })}
|
|
185
|
+
style="{start}: {leadingOffset}px"
|
|
186
|
+
>
|
|
187
|
+
<Icon size={iconSize} {...leadingIconProps} />
|
|
188
|
+
</span>
|
|
159
189
|
{/if}
|
|
160
190
|
{#if trailingIconProps}
|
|
161
|
-
<
|
|
162
|
-
class={
|
|
163
|
-
class:
|
|
164
|
-
trailingIconProps.class,
|
|
165
|
-
inlineSize * (1 - handlePosition) < (size == 'xl' ? 48 : 40) && trailingIconPop(),
|
|
166
|
-
vertical && 'translate-x-[-50%] translate-y-0'
|
|
167
|
-
)
|
|
191
|
+
<span
|
|
192
|
+
class={cls.icon({
|
|
193
|
+
class: trailingOnInactive ? cls.iconOnInactive() : cls.iconOnActive()
|
|
168
194
|
})}
|
|
169
|
-
{
|
|
170
|
-
|
|
195
|
+
style="{start}: {trailingOffset}px"
|
|
196
|
+
>
|
|
197
|
+
<Icon size={iconSize} {...trailingIconProps} />
|
|
198
|
+
</span>
|
|
171
199
|
{/if}
|
|
172
200
|
|
|
173
|
-
<Slider.Thumb
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
201
|
+
<Slider.Thumb index={0}>
|
|
202
|
+
{#snippet child({ props: thumbProps, active })}
|
|
203
|
+
<div
|
|
204
|
+
{...thumbProps}
|
|
205
|
+
class={cls.handle()}
|
|
206
|
+
data-active={active || undefined}
|
|
207
|
+
style="{start}: {center(pos)}"
|
|
208
|
+
></div>
|
|
209
|
+
{/snippet}
|
|
210
|
+
</Slider.Thumb>
|
|
179
211
|
|
|
180
212
|
{#if showValue}
|
|
181
|
-
<div
|
|
182
|
-
class={valueCls({
|
|
183
|
-
class: clsx(vertical ? 'translate-x-0 translate-y-1/2 rotate-90' : '-translate-x-1/2')
|
|
184
|
-
})}
|
|
185
|
-
>
|
|
213
|
+
<div class={cls.value()} style="{start}: {center(pos)}" aria-hidden="true">
|
|
186
214
|
{format(value)}
|
|
187
215
|
</div>
|
|
188
216
|
{/if}
|
|
189
217
|
</div>
|
|
190
218
|
{/snippet}
|
|
191
219
|
</Slider.Root>
|
|
192
|
-
|
|
193
|
-
<style>
|
|
194
|
-
@layer tokens {
|
|
195
|
-
:root {
|
|
196
|
-
--m3-slider-track-out-shape: 0.5rem;
|
|
197
|
-
--m3-slider-track-in-shape: 0.125rem;
|
|
198
|
-
--m3-slider-handle-shape: calc(infinity * 1px);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
</style>
|
|
@@ -5,6 +5,9 @@ import type { SliderProps } from './types.js';
|
|
|
5
5
|
*
|
|
6
6
|
* Sliders allow users to make selections from a range of values.
|
|
7
7
|
*
|
|
8
|
+
* The handle follows the pointer 1:1 while dragged; any other change (a keyboard step, a click on
|
|
9
|
+
* the track, a new `value` from outside) springs to it on the fast spatial spring.
|
|
10
|
+
*
|
|
8
11
|
* @see https://m3.material.io/components/sliders/overview
|
|
9
12
|
*/
|
|
10
13
|
declare const Slider: import("svelte").Component<SliderProps, {}, "value">;
|