@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
|
@@ -2,12 +2,20 @@
|
|
|
2
2
|
@component
|
|
3
3
|
Material 3 Linear Progress Indicator.
|
|
4
4
|
|
|
5
|
+
Determinate: the bar springs to each new `percent` on the slow effects spring (critically damped,
|
|
6
|
+
so progress never visibly runs backwards). Indeterminate: the bar's head and tail spring across
|
|
7
|
+
the track on springs of different stiffness, so it stretches out and catches up on every pass.
|
|
8
|
+
The track always keeps a gap either side of the bar.
|
|
9
|
+
|
|
5
10
|
@see https://m3.material.io/components/progress-indicators/overview#linear-progress-indicators
|
|
6
11
|
-->
|
|
7
12
|
<script lang="ts">
|
|
8
13
|
import clsx from 'clsx';
|
|
9
14
|
import { tv } from 'tailwind-variants';
|
|
10
15
|
import { Progress } from 'bits-ui';
|
|
16
|
+
import { animate, type AnimationPlaybackControls } from 'motion';
|
|
17
|
+
import { springTokens, springTransition } from '../../animation/spring.js';
|
|
18
|
+
import { SpringValue } from '../../animation/springValue.svelte.js';
|
|
11
19
|
|
|
12
20
|
let {
|
|
13
21
|
percent,
|
|
@@ -21,15 +29,58 @@ Material 3 Linear Progress Indicator.
|
|
|
21
29
|
class?: string;
|
|
22
30
|
} = $props();
|
|
23
31
|
|
|
32
|
+
/* M3: 4dp between the indicator and the track. */
|
|
33
|
+
const GAP_PX = 4;
|
|
34
|
+
|
|
24
35
|
const linearProgress = tv({
|
|
25
36
|
slots: {
|
|
26
|
-
container: '
|
|
27
|
-
|
|
28
|
-
|
|
37
|
+
container: 'relative overflow-hidden',
|
|
38
|
+
segment: 'absolute inset-y-0 rounded-full',
|
|
39
|
+
bar: 'bg-md-sys-color-primary',
|
|
40
|
+
track: 'bg-md-sys-color-secondary-container'
|
|
29
41
|
}
|
|
30
42
|
});
|
|
31
43
|
|
|
32
|
-
const { container,
|
|
44
|
+
const { container, segment, bar, track } = linearProgress();
|
|
45
|
+
|
|
46
|
+
const shown = new SpringValue(() => (percent ?? 0) / 100, springTokens.slowEffects);
|
|
47
|
+
|
|
48
|
+
/* Indeterminate pass: the head leads on the spatial spring, the tail trails on the slower one. */
|
|
49
|
+
let head = $state(0);
|
|
50
|
+
let tail = $state(0);
|
|
51
|
+
|
|
52
|
+
$effect(() => {
|
|
53
|
+
if (percent != null) return;
|
|
54
|
+
let running = true;
|
|
55
|
+
let controls: AnimationPlaybackControls[] = [];
|
|
56
|
+
|
|
57
|
+
(async () => {
|
|
58
|
+
while (running) {
|
|
59
|
+
head = 0;
|
|
60
|
+
tail = 0;
|
|
61
|
+
controls = [
|
|
62
|
+
animate(0, 1, {
|
|
63
|
+
...springTransition(springTokens.spatial),
|
|
64
|
+
onUpdate: (v) => (head = v)
|
|
65
|
+
}),
|
|
66
|
+
animate(0, 1, {
|
|
67
|
+
...springTransition(springTokens.slowSpatial),
|
|
68
|
+
onUpdate: (v) => (tail = v)
|
|
69
|
+
})
|
|
70
|
+
];
|
|
71
|
+
await Promise.all(controls);
|
|
72
|
+
}
|
|
73
|
+
})();
|
|
74
|
+
|
|
75
|
+
return () => {
|
|
76
|
+
running = false;
|
|
77
|
+
controls.forEach((c) => c.stop());
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const clamp01 = (v: number) => Math.min(1, Math.max(0, v));
|
|
82
|
+
const start = $derived(percent != null ? 0 : clamp01(tail));
|
|
83
|
+
const end = $derived(percent != null ? clamp01(shown.current) : clamp01(head));
|
|
33
84
|
</script>
|
|
34
85
|
|
|
35
86
|
<Progress.Root
|
|
@@ -39,8 +90,20 @@ Material 3 Linear Progress Indicator.
|
|
|
39
90
|
style="height: {height / 16}rem"
|
|
40
91
|
data-cy="m3-linear-progress"
|
|
41
92
|
>
|
|
42
|
-
{#if
|
|
43
|
-
<div
|
|
93
|
+
{#if start > 0}
|
|
94
|
+
<div
|
|
95
|
+
class={segment({ class: track() })}
|
|
96
|
+
style="left: 0; width: max(0px, calc({start} * 100% - {GAP_PX}px))"
|
|
97
|
+
></div>
|
|
98
|
+
{/if}
|
|
99
|
+
{#if end > start}
|
|
100
|
+
<div
|
|
101
|
+
class={segment({ class: bar() })}
|
|
102
|
+
style="left: calc({start} * 100%); width: calc({end - start} * 100%)"
|
|
103
|
+
></div>
|
|
44
104
|
{/if}
|
|
45
|
-
<div
|
|
105
|
+
<div
|
|
106
|
+
class={segment({ class: track() })}
|
|
107
|
+
style="right: 0; width: max(0px, calc({1 - end} * 100% - {end > start ? GAP_PX : 0}px))"
|
|
108
|
+
></div>
|
|
46
109
|
</Progress.Root>
|
|
@@ -8,6 +8,11 @@ type $$ComponentProps = {
|
|
|
8
8
|
/**
|
|
9
9
|
* Material 3 Linear Progress Indicator.
|
|
10
10
|
*
|
|
11
|
+
* Determinate: the bar springs to each new `percent` on the slow effects spring (critically damped,
|
|
12
|
+
* so progress never visibly runs backwards). Indeterminate: the bar's head and tail spring across
|
|
13
|
+
* the track on springs of different stiffness, so it stretches out and catches up on every pass.
|
|
14
|
+
* The track always keeps a gap either side of the bar.
|
|
15
|
+
*
|
|
11
16
|
* @see https://m3.material.io/components/progress-indicators/overview#linear-progress-indicators
|
|
12
17
|
*/
|
|
13
18
|
declare const LinearProgress: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
@@ -2,11 +2,17 @@
|
|
|
2
2
|
@component
|
|
3
3
|
Wavy linear progress indicator — a custom M3-inspired variant.
|
|
4
4
|
|
|
5
|
+
The wave travels continuously; the filled length springs to each new `percent` on the slow
|
|
6
|
+
effects spring (critically damped, so progress never visibly runs backwards).
|
|
7
|
+
|
|
5
8
|
@see https://m3.material.io/components/progress-indicators/overview
|
|
6
9
|
-->
|
|
7
10
|
<script lang="ts">
|
|
8
11
|
import { Progress } from 'bits-ui';
|
|
9
|
-
import {
|
|
12
|
+
import { animate } from 'motion';
|
|
13
|
+
import { springTokens } from '../../animation/spring.js';
|
|
14
|
+
import { SpringValue } from '../../animation/springValue.svelte.js';
|
|
15
|
+
import { linear, trackOpacity, WAVE_PERIOD_MS } from './_wavy.js';
|
|
10
16
|
|
|
11
17
|
let {
|
|
12
18
|
width = 600,
|
|
@@ -26,10 +32,28 @@ Wavy linear progress indicator — a custom M3-inspired variant.
|
|
|
26
32
|
class?: string;
|
|
27
33
|
} = $props();
|
|
28
34
|
|
|
35
|
+
const shown = new SpringValue(() => percent ?? 0, springTokens.slowEffects);
|
|
36
|
+
|
|
37
|
+
/* The wave's phase: a travelling wave moves at constant speed, one wavelength per period. */
|
|
38
|
+
let time = $state(0);
|
|
39
|
+
$effect(() => {
|
|
40
|
+
const controls = animate(0, WAVE_PERIOD_MS, {
|
|
41
|
+
duration: WAVE_PERIOD_MS / 1000,
|
|
42
|
+
ease: 'linear',
|
|
43
|
+
repeat: Infinity,
|
|
44
|
+
onUpdate: (v) => (time = v)
|
|
45
|
+
});
|
|
46
|
+
return () => controls.stop();
|
|
47
|
+
});
|
|
48
|
+
|
|
29
49
|
let left = $derived(thickness * 0.5);
|
|
30
50
|
let right = $derived(width - thickness * 0.5);
|
|
31
|
-
let percentX = $derived(
|
|
32
|
-
|
|
51
|
+
let percentX = $derived(
|
|
52
|
+
percent != null
|
|
53
|
+
? (Math.min(100, Math.max(0, shown.current)) / 100) * (right - left) + left
|
|
54
|
+
: left
|
|
55
|
+
);
|
|
56
|
+
let wavePath = $derived(linear(height / 2 - thickness / 2, height / 2, left, percentX, time));
|
|
33
57
|
</script>
|
|
34
58
|
|
|
35
59
|
<Progress.Root value={percent ?? null} max={100}>
|
|
@@ -12,6 +12,9 @@ type $$ComponentProps = {
|
|
|
12
12
|
/**
|
|
13
13
|
* Wavy linear progress indicator — a custom M3-inspired variant.
|
|
14
14
|
*
|
|
15
|
+
* The wave travels continuously; the filled length springs to each new `percent` on the slow
|
|
16
|
+
* effects spring (critically damped, so progress never visibly runs backwards).
|
|
17
|
+
*
|
|
15
18
|
* @see https://m3.material.io/components/progress-indicators/overview
|
|
16
19
|
*/
|
|
17
20
|
declare const WavyLinearProgress: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
@@ -1,2 +1,4 @@
|
|
|
1
|
+
/** One full wave cycle, in ms. */
|
|
2
|
+
export declare const WAVE_PERIOD_MS = 1000;
|
|
1
3
|
export declare const linear: (amp: number, center: number, from: number, to: number, time: number, cutoffTo?: number) => string;
|
|
2
4
|
export declare const trackOpacity: (right: number, x: number) => number;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
/** One full wave cycle, in ms. */
|
|
2
|
+
export const WAVE_PERIOD_MS = 1000;
|
|
3
|
+
const frequencyT = (Math.PI * 2) / WAVE_PERIOD_MS;
|
|
2
4
|
const frequencyX = (Math.PI * 2) / 40;
|
|
3
5
|
const round2 = (x) => Math.round(x * 100) / 100;
|
|
4
6
|
const round1 = (x) => Math.round(x * 10) / 10;
|
|
@@ -9,8 +9,7 @@ Snackbars provide brief messages about app processes at the bottom of the screen
|
|
|
9
9
|
import type { SnackBarProps } from './types.js';
|
|
10
10
|
import Icon from '../../utils/icon/Icon.svelte';
|
|
11
11
|
import { Layer } from '../../utils/index.js';
|
|
12
|
-
import { enterExit } from '../../animation/
|
|
13
|
-
import { easeEmphasizedDecel, easeEmphasizedAccel } from '../../animation/easing.js';
|
|
12
|
+
import { enterExit, Presence } from '../../animation/index.js';
|
|
14
13
|
|
|
15
14
|
let {
|
|
16
15
|
message = $bindable(''),
|
|
@@ -49,6 +48,12 @@ Snackbars provide brief messages about app processes at the bottom of the screen
|
|
|
49
48
|
};
|
|
50
49
|
});
|
|
51
50
|
|
|
51
|
+
const snack = new Presence(() => Boolean(message) && !dismissed);
|
|
52
|
+
|
|
53
|
+
// Dismissing clears `message`; keep rendering the last one while the exit animation plays.
|
|
54
|
+
let lastMessage: SnackBarProps['message'] = '';
|
|
55
|
+
const shownMessage = $derived.by(() => (message ? (lastMessage = message) : lastMessage));
|
|
56
|
+
|
|
52
57
|
const {
|
|
53
58
|
base,
|
|
54
59
|
icon,
|
|
@@ -58,27 +63,18 @@ Snackbars provide brief messages about app processes at the bottom of the screen
|
|
|
58
63
|
} = $derived(snackbar({ fixed }));
|
|
59
64
|
</script>
|
|
60
65
|
|
|
61
|
-
{#if
|
|
66
|
+
{#if snack.mounted}
|
|
62
67
|
<div
|
|
63
68
|
class={base()}
|
|
64
69
|
data-cy="m3-snackbar"
|
|
65
70
|
{...restProps}
|
|
66
|
-
|
|
67
|
-
duration: 400,
|
|
68
|
-
easing: easeEmphasizedDecel,
|
|
69
|
-
mode: 'slide-up'
|
|
70
|
-
}}
|
|
71
|
-
out:enterExit={{
|
|
72
|
-
duration: 200,
|
|
73
|
-
easing: easeEmphasizedAccel,
|
|
74
|
-
mode: 'slide-up'
|
|
75
|
-
}}
|
|
71
|
+
{@attach snack.attach(enterExit.slideUp)}
|
|
76
72
|
>
|
|
77
|
-
{#if typeof
|
|
78
|
-
<p class={supportingText()}>{
|
|
79
|
-
{:else}
|
|
73
|
+
{#if typeof shownMessage === 'string'}
|
|
74
|
+
<p class={supportingText()}>{shownMessage}</p>
|
|
75
|
+
{:else if shownMessage}
|
|
80
76
|
<p class={supportingText()}>
|
|
81
|
-
{@render
|
|
77
|
+
{@render shownMessage()}
|
|
82
78
|
</p>
|
|
83
79
|
{/if}
|
|
84
80
|
|
|
@@ -1,30 +1,17 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
@component
|
|
3
|
-
Wraps the data rows of a Table.
|
|
4
|
-
stop on the table once (roving tabindex), and ArrowUp/ArrowDown or k/j move focus
|
|
5
|
-
between rows. Set `navigable={false}` to opt a purely presentational table out.
|
|
3
|
+
Wraps the data rows of a Table.
|
|
6
4
|
-->
|
|
7
5
|
<script lang="ts">
|
|
8
6
|
import type { TableBodyProps } from './types.js';
|
|
9
7
|
import { table } from './theme.js';
|
|
10
|
-
import { rovingFocusGroup } from '../../actions/index.js';
|
|
11
8
|
import clsx from 'clsx';
|
|
12
9
|
|
|
13
|
-
let {
|
|
14
|
-
children,
|
|
15
|
-
navigable = true,
|
|
16
|
-
loop = false,
|
|
17
|
-
class: className,
|
|
18
|
-
...restProps
|
|
19
|
-
}: TableBodyProps = $props();
|
|
10
|
+
let { children, class: className, ...restProps }: TableBodyProps = $props();
|
|
20
11
|
|
|
21
12
|
const { body } = table();
|
|
22
13
|
</script>
|
|
23
14
|
|
|
24
|
-
<tbody
|
|
25
|
-
class={body({ class: clsx(className) })}
|
|
26
|
-
use:rovingFocusGroup={{ candidateSelector: '[data-table-row]', loop, enabled: navigable }}
|
|
27
|
-
{...restProps}
|
|
28
|
-
>
|
|
15
|
+
<tbody class={body({ class: clsx(className) })} {...restProps}>
|
|
29
16
|
{@render children()}
|
|
30
17
|
</tbody>
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import type { TableBodyProps } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Wraps the data rows of a Table. Rows are keyboard-navigable by default: Tab/Shift+Tab
|
|
4
|
-
* stop on the table once (roving tabindex), and ArrowUp/ArrowDown or k/j move focus
|
|
5
|
-
* between rows. Set `navigable={false}` to opt a purely presentational table out.
|
|
6
|
-
*/
|
|
2
|
+
/** Wraps the data rows of a Table. */
|
|
7
3
|
declare const TableBody: import("svelte").Component<TableBodyProps, {}, "">;
|
|
8
4
|
type TableBody = ReturnType<typeof TableBody>;
|
|
9
5
|
export default TableBody;
|
|
@@ -30,7 +30,7 @@ and `sortDirection` props. Defaults to `scope="col"`.
|
|
|
30
30
|
{#if sortable}
|
|
31
31
|
<span
|
|
32
32
|
class={[
|
|
33
|
-
'transition-[opacity,transform]
|
|
33
|
+
'md-sys-motion-fast-spatial transition-[opacity,transform]',
|
|
34
34
|
sorted ? 'opacity-100' : 'opacity-0 group-hover:opacity-40',
|
|
35
35
|
sortDirection === 'desc' ? 'rotate-180' : 'rotate-0'
|
|
36
36
|
].join(' ')}
|
|
@@ -5,7 +5,7 @@ export const table = tv({
|
|
|
5
5
|
head: '',
|
|
6
6
|
body: '[&_tr:last-child]:border-b-0',
|
|
7
7
|
foot: 'border-t border-md-sys-color-outline-variant',
|
|
8
|
-
row: 'border-b border-md-sys-color-outline-variant transition-colors focus-visible:outline focus-visible:-outline-offset-2 focus-visible:outline-md-sys-color-primary',
|
|
8
|
+
row: 'border-b border-md-sys-color-outline-variant transition-colors md-sys-motion-fast-effects focus-visible:outline focus-visible:-outline-offset-2 focus-visible:outline-md-sys-color-primary',
|
|
9
9
|
cell: 'px-4 py-3 align-middle',
|
|
10
10
|
header: 'px-4 py-4 align-middle md-sys-typescale-label-large text-md-sys-color-on-surface font-medium whitespace-nowrap'
|
|
11
11
|
},
|
|
@@ -13,13 +13,6 @@ export type TableHeadProps = HTMLAttributes<HTMLTableSectionElement> & {
|
|
|
13
13
|
};
|
|
14
14
|
export type TableBodyProps = HTMLAttributes<HTMLTableSectionElement> & {
|
|
15
15
|
children: Snippet;
|
|
16
|
-
/**
|
|
17
|
-
* Make rows keyboard-navigable: roving tabindex so Tab/Shift+Tab stop on the table
|
|
18
|
-
* once, and ArrowUp/ArrowDown (or k/j) move focus between rows. Default `true`.
|
|
19
|
-
*/
|
|
20
|
-
navigable?: boolean;
|
|
21
|
-
/** Loop focus from the last row back to the first (and vice versa). Default `false`. */
|
|
22
|
-
loop?: boolean;
|
|
23
16
|
};
|
|
24
17
|
export type TableFootProps = HTMLAttributes<HTMLTableSectionElement> & {
|
|
25
18
|
children: Snippet;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
TimeField anchors its TimepickerInput overlay to the whole field via a bits-ui Popover
|
|
3
|
+
(`customAnchor`), so the panel is portaled, flips above the field when there's no room below,
|
|
4
|
+
and dismisses on outside click / Escape.
|
|
5
|
+
-->
|
|
6
|
+
<script module lang="ts">
|
|
7
|
+
import { defineMeta } from '@storybook/addon-svelte-csf';
|
|
8
|
+
import TimeField from './TimeField.svelte';
|
|
9
|
+
|
|
10
|
+
const { Story } = defineMeta({
|
|
11
|
+
title: 'Time/TimeField',
|
|
12
|
+
component: TimeField,
|
|
13
|
+
argTypes: {
|
|
14
|
+
label: { control: 'text' },
|
|
15
|
+
disabled: { control: 'boolean' },
|
|
16
|
+
error: { control: 'boolean' },
|
|
17
|
+
required: { control: 'boolean' }
|
|
18
|
+
},
|
|
19
|
+
args: {
|
|
20
|
+
label: 'Time',
|
|
21
|
+
value: '09:30',
|
|
22
|
+
disabled: false,
|
|
23
|
+
error: false,
|
|
24
|
+
required: false
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<Story name="Playground">
|
|
30
|
+
{#snippet template(args)}
|
|
31
|
+
<div class="max-w-xs p-8">
|
|
32
|
+
<TimeField {...args} />
|
|
33
|
+
</div>
|
|
34
|
+
{/snippet}
|
|
35
|
+
</Story>
|
|
36
|
+
|
|
37
|
+
<Story name="Near Bottom Edge" asChild>
|
|
38
|
+
<div class="flex min-h-dvh max-w-xs flex-col justify-end p-8">
|
|
39
|
+
<TimeField label="Time" value="18:45" />
|
|
40
|
+
</div>
|
|
41
|
+
</Story>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import TimeField from './TimeField.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 TimeField: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
16
|
+
[evt: string]: CustomEvent<any>;
|
|
17
|
+
}, {}, {}, string>;
|
|
18
|
+
type TimeField = InstanceType<typeof TimeField>;
|
|
19
|
+
export default TimeField;
|
|
@@ -6,15 +6,16 @@ It combines a Material 3 Textfield with a TimepickerInput for intuitive time sel
|
|
|
6
6
|
@see https://m3.material.io/components/time-pickers/overview
|
|
7
7
|
-->
|
|
8
8
|
<script lang="ts">
|
|
9
|
+
import { enterExit, presence } from '../../animation/index.js';
|
|
9
10
|
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
10
11
|
import Textfield from '../forms/textfield/Textfield.svelte';
|
|
11
12
|
import TimepickerInput from './TimepickerInput.svelte';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
13
|
+
import { Popover } from 'bits-ui';
|
|
14
|
+
import type { Snippet } from 'svelte';
|
|
15
|
+
import type { TimepickerLabels } from './types.js';
|
|
15
16
|
|
|
16
17
|
let {
|
|
17
|
-
label = '
|
|
18
|
+
label = 'Time',
|
|
18
19
|
value = $bindable(),
|
|
19
20
|
required = false,
|
|
20
21
|
disabled = false,
|
|
@@ -22,6 +23,8 @@ It combines a Material 3 Textfield with a TimepickerInput for intuitive time sel
|
|
|
22
23
|
datePickerTitle = 'Pick date',
|
|
23
24
|
name,
|
|
24
25
|
leadingIconProps,
|
|
26
|
+
supportingText,
|
|
27
|
+
pickerLabels,
|
|
25
28
|
...restProps
|
|
26
29
|
}: {
|
|
27
30
|
/** The label for the text field. */
|
|
@@ -40,6 +43,10 @@ It combines a Material 3 Textfield with a TimepickerInput for intuitive time sel
|
|
|
40
43
|
datePickerTitle?: string;
|
|
41
44
|
/** Props for the leading icon. */
|
|
42
45
|
leadingIconProps?: any;
|
|
46
|
+
/** Supporting text under the field. */
|
|
47
|
+
supportingText?: Snippet;
|
|
48
|
+
/** Headline, input and button strings for the picker overlay (English by default). */
|
|
49
|
+
pickerLabels?: TimepickerLabels;
|
|
43
50
|
} & HTMLInputAttributes = $props();
|
|
44
51
|
|
|
45
52
|
const id = $props.id();
|
|
@@ -48,64 +55,64 @@ It combines a Material 3 Textfield with a TimepickerInput for intuitive time sel
|
|
|
48
55
|
let anchorEl = $state<HTMLDivElement>();
|
|
49
56
|
</script>
|
|
50
57
|
|
|
51
|
-
<
|
|
52
|
-
class="relative w-full"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
trailingIconProps={{ name: 'timer' }}
|
|
68
|
-
trailingOnClick={() => !disabled && (picker = !picker)}
|
|
69
|
-
{...restProps as any}
|
|
70
|
-
>
|
|
71
|
-
{#snippet supportingText()}
|
|
72
|
-
ЧЧ-ММ
|
|
73
|
-
{/snippet}
|
|
74
|
-
</Textfield>
|
|
58
|
+
<Popover.Root bind:open={picker}>
|
|
59
|
+
<div class="relative w-full" bind:this={anchorEl}>
|
|
60
|
+
<Textfield
|
|
61
|
+
id={id as string}
|
|
62
|
+
{label}
|
|
63
|
+
{value}
|
|
64
|
+
{name}
|
|
65
|
+
{disabled}
|
|
66
|
+
{error}
|
|
67
|
+
{leadingIconProps}
|
|
68
|
+
class="pointer-events-none"
|
|
69
|
+
trailingIconProps={{ name: 'schedule' }}
|
|
70
|
+
trailingOnClick={() => !disabled && (picker = !picker)}
|
|
71
|
+
{supportingText}
|
|
72
|
+
{...restProps as any}
|
|
73
|
+
/>
|
|
75
74
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
75
|
+
<Popover.Trigger {disabled}>
|
|
76
|
+
{#snippet child({ props })}
|
|
77
|
+
<button
|
|
78
|
+
{...props}
|
|
79
|
+
title="time-overlay"
|
|
80
|
+
class="absolute inset-0 cursor-pointer disabled:cursor-not-allowed"
|
|
81
|
+
type="button"
|
|
82
|
+
data-cy="calendar-time-toggle"
|
|
83
|
+
></button>
|
|
84
|
+
{/snippet}
|
|
85
|
+
</Popover.Trigger>
|
|
86
|
+
</div>
|
|
84
87
|
|
|
85
|
-
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}}
|
|
94
|
-
out:enterExit={{
|
|
95
|
-
duration: 150,
|
|
96
|
-
easing: easeEmphasizedAccel,
|
|
97
|
-
mode: 'scale'
|
|
98
|
-
}}
|
|
99
|
-
style="transform-origin: top center;"
|
|
88
|
+
<Popover.Portal>
|
|
89
|
+
<Popover.Content
|
|
90
|
+
customAnchor={anchorEl}
|
|
91
|
+
side="bottom"
|
|
92
|
+
align="start"
|
|
93
|
+
sideOffset={12}
|
|
94
|
+
collisionPadding={8}
|
|
95
|
+
class="z-100"
|
|
100
96
|
>
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
97
|
+
{#snippet child({ wrapperProps, props, open })}
|
|
98
|
+
<div {...wrapperProps}>
|
|
99
|
+
<div
|
|
100
|
+
{...props}
|
|
101
|
+
class={[
|
|
102
|
+
props.class,
|
|
103
|
+
'max-h-(--bits-floating-available-height) min-w-(--bits-floating-anchor-width) overflow-auto'
|
|
104
|
+
]}
|
|
105
|
+
{@attach presence(() => open, enterExit.scale)}
|
|
106
|
+
>
|
|
107
|
+
<TimepickerInput
|
|
108
|
+
{...pickerLabels}
|
|
109
|
+
time={value}
|
|
110
|
+
close={() => (picker = false)}
|
|
111
|
+
setTime={(t) => (value = t)}
|
|
112
|
+
/>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
{/snippet}
|
|
116
|
+
</Popover.Content>
|
|
117
|
+
</Popover.Portal>
|
|
118
|
+
</Popover.Root>
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { TimepickerLabels } from './types.js';
|
|
2
4
|
type $$ComponentProps = {
|
|
3
5
|
/** The label for the text field. */
|
|
4
6
|
label?: string;
|
|
@@ -16,6 +18,10 @@ type $$ComponentProps = {
|
|
|
16
18
|
datePickerTitle?: string;
|
|
17
19
|
/** Props for the leading icon. */
|
|
18
20
|
leadingIconProps?: any;
|
|
21
|
+
/** Supporting text under the field. */
|
|
22
|
+
supportingText?: Snippet;
|
|
23
|
+
/** Headline, input and button strings for the picker overlay (English by default). */
|
|
24
|
+
pickerLabels?: TimepickerLabels;
|
|
19
25
|
} & HTMLInputAttributes;
|
|
20
26
|
/**
|
|
21
27
|
* TimeField component provides a text input that opens a Timepicker overlay when interacted with.
|
|
@@ -15,7 +15,11 @@ It follows the Material 3 design for time input pickers.
|
|
|
15
15
|
import { ButtonIcon } from '../buttons/index.js';
|
|
16
16
|
|
|
17
17
|
const {
|
|
18
|
-
label = '
|
|
18
|
+
label = 'Enter time',
|
|
19
|
+
hourLabel = 'Hour',
|
|
20
|
+
minuteLabel = 'Minute',
|
|
21
|
+
cancelText = 'Cancel',
|
|
22
|
+
confirmText = 'OK',
|
|
19
23
|
time = '09:41',
|
|
20
24
|
close,
|
|
21
25
|
setTime,
|
|
@@ -48,7 +52,7 @@ It follows the Material 3 design for time input pickers.
|
|
|
48
52
|
{value}
|
|
49
53
|
</TimeField.Segment>
|
|
50
54
|
<p class={cls.timeInputSupportingText()}>
|
|
51
|
-
{part === 'hour' ?
|
|
55
|
+
{part === 'hour' ? hourLabel : minuteLabel}
|
|
52
56
|
</p>
|
|
53
57
|
</div>
|
|
54
58
|
{/if}
|
|
@@ -61,7 +65,7 @@ It follows the Material 3 design for time input pickers.
|
|
|
61
65
|
<div class={cls.buttonDiv()}>
|
|
62
66
|
<ButtonIcon type="button" iconProps={{ name: 'schedule' }} />
|
|
63
67
|
<div>
|
|
64
|
-
<Button variant="text" type="button" onclick={() => close()}
|
|
68
|
+
<Button variant="text" type="button" onclick={() => close()}>{cancelText}</Button>
|
|
65
69
|
<Button
|
|
66
70
|
variant="text"
|
|
67
71
|
type="button"
|
|
@@ -69,7 +73,7 @@ It follows the Material 3 design for time input pickers.
|
|
|
69
73
|
setTime(output);
|
|
70
74
|
close();
|
|
71
75
|
}}
|
|
72
|
-
data-cy="calendar-time-confirm"
|
|
76
|
+
data-cy="calendar-time-confirm">{confirmText}</Button
|
|
73
77
|
>
|
|
74
78
|
</div>
|
|
75
79
|
</div>
|
|
@@ -9,9 +9,17 @@ import type { TimepickerVariants } from './theme.js';
|
|
|
9
9
|
export type TimepickerProps = TimepickerVariants & HTMLAttributes<HTMLInputElement> & {
|
|
10
10
|
/**
|
|
11
11
|
* The label displayed at the top of the timepicker.
|
|
12
|
-
* @default '
|
|
12
|
+
* @default 'Enter time'
|
|
13
13
|
*/
|
|
14
14
|
label?: string;
|
|
15
|
+
/** Supporting text under the hour input. @default 'Hour' */
|
|
16
|
+
hourLabel?: string;
|
|
17
|
+
/** Supporting text under the minute input. @default 'Minute' */
|
|
18
|
+
minuteLabel?: string;
|
|
19
|
+
/** Label of the dismiss button. @default 'Cancel' */
|
|
20
|
+
cancelText?: string;
|
|
21
|
+
/** Label of the confirm button. @default 'OK' */
|
|
22
|
+
confirmText?: string;
|
|
15
23
|
/**
|
|
16
24
|
* The current time value in HH:MM format.
|
|
17
25
|
* @default '09:41'
|
|
@@ -32,3 +40,5 @@ export type TimepickerProps = TimepickerVariants & HTMLAttributes<HTMLInputEleme
|
|
|
32
40
|
*/
|
|
33
41
|
setTime: (time: string) => void;
|
|
34
42
|
};
|
|
43
|
+
/** The TimepickerInput strings a TimeField forwards to its picker overlay. */
|
|
44
|
+
export type TimepickerLabels = Partial<Pick<TimepickerProps, 'label' | 'hourLabel' | 'minuteLabel' | 'cancelText' | 'confirmText'>>;
|
|
@@ -48,7 +48,7 @@ export const toolbar = tv({
|
|
|
48
48
|
});
|
|
49
49
|
export const toolbarButton = tv({
|
|
50
50
|
slots: {
|
|
51
|
-
base: 'layer-container state-layer before:rounded-full relative inline-flex size-10 items-center justify-center rounded-full outline-none transition-colors disabled:cursor-not-allowed disabled:opacity-38',
|
|
51
|
+
base: 'layer-container state-layer before:rounded-full relative inline-flex size-10 items-center justify-center rounded-full outline-none transition-colors md-sys-motion-fast-effects disabled:cursor-not-allowed disabled:opacity-38',
|
|
52
52
|
icon: 'size-5 text-[20px]'
|
|
53
53
|
},
|
|
54
54
|
variants: {
|
|
@@ -61,7 +61,7 @@ export const toolbarButton = tv({
|
|
|
61
61
|
});
|
|
62
62
|
export const toolbarGroupItem = tv({
|
|
63
63
|
slots: {
|
|
64
|
-
base: 'layer-container state-layer before:rounded-full relative inline-flex size-10 items-center justify-center rounded-full outline-none transition-colors disabled:cursor-not-allowed disabled:opacity-38',
|
|
64
|
+
base: 'layer-container state-layer before:rounded-full relative inline-flex size-10 items-center justify-center rounded-full outline-none transition-colors md-sys-motion-fast-effects disabled:cursor-not-allowed disabled:opacity-38',
|
|
65
65
|
icon: 'size-5 text-[20px]'
|
|
66
66
|
},
|
|
67
67
|
variants: {
|