@teamofsilicons/silicon-ui 0.3.0 → 0.4.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/components/animated-counter/animated-counter.js +1 -1
- package/components/bar-chart/bar-chart.js +1 -1
- package/components/billing-toggle/billing-toggle.js +2 -2
- package/components/bottom-sheet/bottom-sheet.js +1 -1
- package/components/button-group/button-group.module.css +1 -1
- package/components/calendar/calendar.js +15 -5
- package/components/calendar/calendar.module.css +6 -3
- package/components/card/card.js +4 -4
- package/components/card-stack/card-stack.js +2 -2
- package/components/carousel/carousel.module.css +1 -1
- package/components/chip-group/chip-group.js +1 -1
- package/components/combobox/combobox.js +5 -1
- package/components/command-palette/command-palette.js +18 -11
- package/components/confirm-morph/confirm-morph.js +5 -5
- package/components/context-menu/context-menu.js +10 -28
- package/components/copy-button/copy-button.js +4 -4
- package/components/date-picker/date-picker.js +3 -3
- package/components/date-picker/date-picker.module.css +1 -1
- package/components/date-range-picker/date-range-picker.js +13 -10
- package/components/date-range-picker/date-range-picker.module.css +2 -1
- package/components/donut-chart/donut-chart.module.css +1 -1
- package/components/drawer/drawer.module.css +2 -2
- package/components/elastic-slider/elastic-slider.js +2 -2
- package/components/expandable-card/expandable-card.js +1 -1
- package/components/expanding-button-group/expanding-button-group.module.css +2 -2
- package/components/expanding-search/expanding-search.js +3 -3
- package/components/file-dropzone/file-dropzone.js +3 -3
- package/components/filter-toolbar/filter-toolbar.js +7 -3
- package/components/floating-button-group/floating-button-group.module.css +1 -1
- package/components/gauge/gauge.js +1 -1
- package/components/hover-card/hover-card.js +2 -2
- package/components/line-chart/line-chart.js +1 -1
- package/components/mention-input/mention-input.js +4 -2
- package/components/money-input/money-input.js +6 -3
- package/components/money-input/money-input.module.css +1 -1
- package/components/morph-select/morph-select.js +7 -5
- package/components/multi-select/multi-select.js +4 -2
- package/components/nav-list/nav-list.d.ts +2 -2
- package/components/nav-list/nav-list.js +6 -13
- package/components/number-field/number-field.js +3 -3
- package/components/number-field/number-field.module.css +1 -1
- package/components/pagination/pagination.module.css +1 -1
- package/components/password-field/password-field.module.css +1 -1
- package/components/password-strength/password-strength.module.css +1 -1
- package/components/phone-input/phone-input.js +9 -7
- package/components/radio-group/radio-group.js +4 -1
- package/components/radio-group/radio-group.module.css +3 -2
- package/components/rich-text-editor/rich-text-editor.js +3 -3
- package/components/segmented-control/segmented-control.js +4 -1
- package/components/segmented-control/segmented-control.module.css +3 -1
- package/components/shortcut-recorder/shortcut-recorder.js +5 -5
- package/components/shortcut-recorder/shortcut-recorder.module.css +1 -1
- package/components/slot-text/slot-text.js +2 -1
- package/components/sparkline/sparkline.js +1 -1
- package/components/split-button/split-button.module.css +1 -1
- package/components/swipe-actions/swipe-actions.js +2 -2
- package/components/switch/switch.js +2 -2
- package/components/tabs/tabs.js +7 -3
- package/components/tabs/tabs.module.css +3 -1
- package/components/text-reveal/text-reveal.js +1 -1
- package/components/time-picker/time-picker.js +4 -2
- package/components/timeline/timeline.js +4 -4
- package/components/toast/toast.js +1 -1
- package/components/toast-stack/toast-stack.js +1 -1
- package/components/tree-view/tree-view.js +16 -12
- package/components/tree-view/tree-view.module.css +4 -2
- package/components/usage-meter/usage-meter.js +1 -1
- package/components/user-menu/user-menu.js +16 -14
- package/lib/fluid-hover.d.ts +83 -0
- package/lib/fluid-hover.js +184 -0
- package/lib/menu-highlight.d.ts +24 -11
- package/lib/menu-highlight.js +29 -28
- package/lib/motion-tokens.d.ts +76 -18
- package/lib/motion-tokens.js +45 -10
- package/package.json +1 -1
- package/styles.css +6 -3
package/lib/menu-highlight.js
CHANGED
|
@@ -1,43 +1,44 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
3
|
+
import { useRef } from "react";
|
|
4
4
|
import { motion, useReducedMotion } from "motion/react";
|
|
5
|
+
import { FluidHoverHighlight, useFluidHover } from "./fluid-hover.js";
|
|
5
6
|
import { motionTokens } from "./motion-tokens.js";
|
|
7
|
+
const ITEMS = '[role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"], [role="option"]';
|
|
6
8
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* call `reset` when it opens, and render `<MenuHighlight state={highlight}
|
|
10
|
-
* Items mark a destructive tone with `data-tone="danger"`.
|
|
9
|
+
* Fluid hover for a Radix menu or listbox (lib/fluid-hover): the item nearest the pointer is lit, gaps and padding
|
|
10
|
+
* included, a click in a gap picks the lit item, and the keyboard moves the highlight with Radix's focus, without travel.
|
|
11
|
+
* Spread `contentProps` on the content, call `reset` when it opens, and render `<MenuHighlight state={highlight} … />`
|
|
12
|
+
* as its first child. Items mark a destructive tone with `data-tone="danger"`.
|
|
11
13
|
*/
|
|
12
|
-
export function useMenuHighlight(itemSelector =
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
window.clearTimeout(clearTimer.current);
|
|
20
|
-
if (!item) {
|
|
21
|
-
// A short grace period keeps the highlight gliding across separators and item gaps.
|
|
22
|
-
clearTimer.current = window.setTimeout(() => setHighlight(null), pointer.current ? 70 : 0);
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
const next = { top: item.offsetTop, height: item.offsetHeight, danger: item.dataset.tone === "danger" };
|
|
26
|
-
const glide = pointer.current;
|
|
27
|
-
setHighlight(current => ({ ...next, glide: glide && current !== null }));
|
|
28
|
-
}
|
|
14
|
+
export function useMenuHighlight(itemSelector = ITEMS) {
|
|
15
|
+
// The content element is taken from its own events, so the hook never competes with the content's ref.
|
|
16
|
+
const content = useRef(null);
|
|
17
|
+
const hover = useFluidHover(content, { items: itemSelector });
|
|
18
|
+
const own = (event) => { content.current = event.currentTarget; };
|
|
19
|
+
const { handlers } = hover;
|
|
20
|
+
const highlight = { rect: hover.rect, glide: hover.glide, session: hover.session, danger: hover.item?.dataset.tone === "danger" };
|
|
29
21
|
return {
|
|
30
22
|
highlight,
|
|
31
|
-
reset:
|
|
23
|
+
reset: hover.reset,
|
|
32
24
|
contentProps: {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
25
|
+
onMouseEnter: (event) => { own(event); handlers.onMouseEnter(); },
|
|
26
|
+
onMouseMove: (event) => { own(event); handlers.onMouseMove(event); },
|
|
27
|
+
onMouseLeave: handlers.onMouseLeave,
|
|
28
|
+
onKeyDownCapture: handlers.onKeyDownCapture,
|
|
29
|
+
onFocus: (event) => { own(event); handlers.onFocus(event); },
|
|
30
|
+
onBlur: handlers.onBlur,
|
|
31
|
+
onClick: (event) => { own(event); handlers.onClick(event); },
|
|
36
32
|
},
|
|
37
33
|
};
|
|
38
34
|
}
|
|
39
|
-
/** The
|
|
35
|
+
/** The highlight itself: the fluid hover highlight, or a marker that moves by top and height (a current-page marker). */
|
|
40
36
|
export function MenuHighlight({ state, className }) {
|
|
37
|
+
if (state && "session" in state)
|
|
38
|
+
return _jsx(FluidHoverHighlight, { hover: state, className: className, tone: state.danger ? "danger" : undefined });
|
|
39
|
+
return _jsx(BarMarker, { state: state, className: className });
|
|
40
|
+
}
|
|
41
|
+
function BarMarker({ state, className }) {
|
|
41
42
|
const reduced = useReducedMotion();
|
|
42
|
-
return _jsx(motion.span, { className: className, "data-tone": state?.danger ? "danger" : undefined, "aria-hidden": "true", initial: false, animate: state ? { y: state.top, height: state.height, opacity: 1 } : { opacity: 0 }, transition: { default: state?.glide && !reduced ? motionTokens.spring.
|
|
43
|
+
return _jsx(motion.span, { className: className, "data-tone": state?.danger ? "danger" : undefined, "aria-hidden": "true", initial: false, animate: state ? { y: state.top, height: state.height, opacity: 1 } : { opacity: 0 }, transition: { default: state?.glide && !reduced ? motionTokens.spring.fast : { duration: 0 }, opacity: { duration: reduced ? 0 : motionTokens.duration.fast } } });
|
|
43
44
|
}
|
package/lib/motion-tokens.d.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
/** Motion presets are shared by the gallery and future registry consumers. */
|
|
2
1
|
export declare const motionTokens: {
|
|
2
|
+
/** Tween durations in seconds, for opacity, colour and blur, and for CSS (the same steps as `--duration-*`). */
|
|
3
3
|
readonly duration: {
|
|
4
|
-
readonly instant: 0.
|
|
5
|
-
readonly fast: 0.
|
|
6
|
-
readonly exit: 0.
|
|
7
|
-
readonly standard: 0.
|
|
8
|
-
readonly considered: 0.
|
|
4
|
+
readonly instant: 0.06;
|
|
5
|
+
readonly fast: 0.08;
|
|
6
|
+
readonly exit: 0.12;
|
|
7
|
+
readonly standard: 0.16;
|
|
8
|
+
readonly considered: 0.24;
|
|
9
|
+
/** Motion that tells a story over time rather than answering a click: a chart drawing in, a counter rolling, text
|
|
10
|
+
* revealing, a success ring drawing, a slow image zoom. Never for a hover, press, menu or panel. */
|
|
11
|
+
readonly expressive: 0.48;
|
|
9
12
|
};
|
|
10
13
|
readonly ease: {
|
|
11
14
|
readonly enter: readonly [0.16, 1, 0.3, 1];
|
|
@@ -15,30 +18,76 @@ export declare const motionTokens: {
|
|
|
15
18
|
readonly inOut: readonly [0.65, 0, 0.35, 1];
|
|
16
19
|
};
|
|
17
20
|
readonly spring: {
|
|
18
|
-
|
|
21
|
+
/** Hover highlights, presses, toggles, thumbs and small indicators. */
|
|
22
|
+
readonly fast: {
|
|
19
23
|
readonly type: "spring";
|
|
20
|
-
readonly
|
|
21
|
-
readonly
|
|
24
|
+
readonly visualDuration: 0.05;
|
|
25
|
+
readonly bounce: 0;
|
|
26
|
+
readonly exit: {
|
|
27
|
+
readonly duration: 0.06;
|
|
28
|
+
};
|
|
22
29
|
};
|
|
23
|
-
|
|
30
|
+
/** Menus, popovers, tabs, drawers, height changes: short travel that must land exactly. */
|
|
31
|
+
readonly moderate: {
|
|
24
32
|
readonly type: "spring";
|
|
25
|
-
readonly
|
|
26
|
-
readonly
|
|
33
|
+
readonly visualDuration: 0.09;
|
|
34
|
+
readonly bounce: 0;
|
|
35
|
+
readonly exit: {
|
|
36
|
+
readonly duration: 0.12;
|
|
37
|
+
};
|
|
27
38
|
};
|
|
28
|
-
/**
|
|
29
|
-
readonly
|
|
39
|
+
/** Dialogs, sheets, shape morphs and large shared-layout moves. A little bounce. */
|
|
40
|
+
readonly slow: {
|
|
30
41
|
readonly type: "spring";
|
|
31
|
-
readonly visualDuration: 0.
|
|
42
|
+
readonly visualDuration: 0.15;
|
|
32
43
|
readonly bounce: 0.12;
|
|
44
|
+
readonly exit: {
|
|
45
|
+
readonly duration: 0.16;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
/** Earlier names, kept so existing code keeps working. Prefer the tier names. */
|
|
49
|
+
readonly snappy: {
|
|
50
|
+
readonly type: "spring";
|
|
51
|
+
readonly visualDuration: 0.05;
|
|
52
|
+
readonly bounce: 0;
|
|
53
|
+
readonly exit: {
|
|
54
|
+
readonly duration: 0.06;
|
|
55
|
+
};
|
|
33
56
|
};
|
|
34
|
-
/** Panels, height changes, and layout shifts. Critically damped, never overshoots. */
|
|
35
57
|
readonly smooth: {
|
|
36
58
|
readonly type: "spring";
|
|
37
|
-
readonly visualDuration: 0.
|
|
59
|
+
readonly visualDuration: 0.09;
|
|
38
60
|
readonly bounce: 0;
|
|
61
|
+
readonly exit: {
|
|
62
|
+
readonly duration: 0.12;
|
|
63
|
+
};
|
|
39
64
|
};
|
|
40
|
-
/** Shape morphs, shared layout highlights, and width changes that follow new content. */
|
|
41
65
|
readonly morph: {
|
|
66
|
+
readonly type: "spring";
|
|
67
|
+
readonly visualDuration: 0.15;
|
|
68
|
+
readonly bounce: 0.12;
|
|
69
|
+
readonly exit: {
|
|
70
|
+
readonly duration: 0.16;
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
readonly responsive: {
|
|
74
|
+
readonly type: "spring";
|
|
75
|
+
readonly visualDuration: 0.09;
|
|
76
|
+
readonly bounce: 0;
|
|
77
|
+
readonly exit: {
|
|
78
|
+
readonly duration: 0.12;
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
readonly gentle: {
|
|
82
|
+
readonly type: "spring";
|
|
83
|
+
readonly visualDuration: 0.09;
|
|
84
|
+
readonly bounce: 0;
|
|
85
|
+
readonly exit: {
|
|
86
|
+
readonly duration: 0.12;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
/** The spring for expressive motion (see duration.expressive): a bar growing, a value settling into place. */
|
|
90
|
+
readonly expressive: {
|
|
42
91
|
readonly type: "spring";
|
|
43
92
|
readonly visualDuration: 0.42;
|
|
44
93
|
readonly bounce: 0.16;
|
|
@@ -58,3 +107,12 @@ export declare const motionTokens: {
|
|
|
58
107
|
readonly text: 8;
|
|
59
108
|
};
|
|
60
109
|
};
|
|
110
|
+
/**
|
|
111
|
+
* How long (ms) a deferred-unmount timer waits for a tier's exit before forcing it: the exit plus a safety margin, for
|
|
112
|
+
* a throttled background tab that never finishes the animation.
|
|
113
|
+
*/
|
|
114
|
+
export declare const exitFallbackMs: (tier: {
|
|
115
|
+
exit: {
|
|
116
|
+
duration: number;
|
|
117
|
+
};
|
|
118
|
+
}) => number;
|
package/lib/motion-tokens.js
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Motion tokens, shared by every component and by apps (`@teamofsilicons/silicon-ui/motion-tokens`).
|
|
3
|
+
*
|
|
4
|
+
* Interface motion runs on three spring tiers. Each tier is the ENTER transition (critically damped, except slow, which
|
|
5
|
+
* keeps a little bounce), and its `.exit` is the matching EXIT: a plain tween one tier quicker, so a dismissal reads as
|
|
6
|
+
* crisp and final instead of replaying the entrance in reverse. The bigger the thing that moves, the slower the tier.
|
|
7
|
+
* Never hand-write a duration: reach for a tier.
|
|
8
|
+
*
|
|
9
|
+
* transition={motionTokens.spring.fast} // enter
|
|
10
|
+
* exit={{ opacity: 0, transition: motionTokens.spring.fast.exit }} // leave
|
|
11
|
+
*
|
|
12
|
+
* The tier values and the exit rule are adapted from Fluid Functionalism by Micka Touillaud (MIT),
|
|
13
|
+
* https://github.com/mickadesign/fluid-functionalism.
|
|
14
|
+
*/
|
|
15
|
+
// Written as visualDuration (the form the components read) with values that arrive (95% of the way) at the same moment as
|
|
16
|
+
// FF's settling durations of 0.08, 0.16 and 0.24s: 42, 83 and 109ms.
|
|
17
|
+
const fast = { type: "spring", visualDuration: 0.05, bounce: 0, exit: { duration: 0.06 } };
|
|
18
|
+
const moderate = { type: "spring", visualDuration: 0.09, bounce: 0, exit: { duration: 0.12 } };
|
|
19
|
+
const slow = { type: "spring", visualDuration: 0.15, bounce: 0.12, exit: { duration: 0.16 } };
|
|
2
20
|
export const motionTokens = {
|
|
3
|
-
|
|
21
|
+
/** Tween durations in seconds, for opacity, colour and blur, and for CSS (the same steps as `--duration-*`). */
|
|
22
|
+
duration: {
|
|
23
|
+
instant: 0.06, fast: 0.08, exit: 0.12, standard: 0.16, considered: 0.24,
|
|
24
|
+
/** Motion that tells a story over time rather than answering a click: a chart drawing in, a counter rolling, text
|
|
25
|
+
* revealing, a success ring drawing, a slow image zoom. Never for a hover, press, menu or panel. */
|
|
26
|
+
expressive: 0.48,
|
|
27
|
+
},
|
|
4
28
|
ease: {
|
|
5
29
|
enter: [0.16, 1, 0.3, 1],
|
|
6
30
|
exit: [0.7, 0, 0.84, 0],
|
|
@@ -9,17 +33,28 @@ export const motionTokens = {
|
|
|
9
33
|
inOut: [0.65, 0, 0.35, 1],
|
|
10
34
|
},
|
|
11
35
|
spring: {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
|
|
36
|
+
/** Hover highlights, presses, toggles, thumbs and small indicators. */
|
|
37
|
+
fast,
|
|
38
|
+
/** Menus, popovers, tabs, drawers, height changes: short travel that must land exactly. */
|
|
39
|
+
moderate,
|
|
40
|
+
/** Dialogs, sheets, shape morphs and large shared-layout moves. A little bounce. */
|
|
41
|
+
slow,
|
|
42
|
+
/** Earlier names, kept so existing code keeps working. Prefer the tier names. */
|
|
43
|
+
snappy: fast,
|
|
44
|
+
smooth: moderate,
|
|
45
|
+
morph: slow,
|
|
46
|
+
responsive: moderate,
|
|
47
|
+
gentle: moderate,
|
|
48
|
+
/** The spring for expressive motion (see duration.expressive): a bar growing, a value settling into place. */
|
|
49
|
+
expressive: { type: "spring", visualDuration: 0.42, bounce: 0.16 },
|
|
20
50
|
},
|
|
21
51
|
/** Stagger steps in seconds. Keep total stagger under roughly 0.4s. */
|
|
22
52
|
stagger: { char: 0.016, word: 0.04, line: 0.08, item: 0.035 },
|
|
23
53
|
/** Blur radii in px for text and content crossfades. Keep blur small and brief. */
|
|
24
54
|
blur: { subtle: 2, soft: 4, text: 8 },
|
|
25
55
|
};
|
|
56
|
+
/**
|
|
57
|
+
* How long (ms) a deferred-unmount timer waits for a tier's exit before forcing it: the exit plus a safety margin, for
|
|
58
|
+
* a throttled background tab that never finishes the animation.
|
|
59
|
+
*/
|
|
60
|
+
export const exitFallbackMs = (tier) => Math.round(tier.exit.duration * 1000) + 100;
|
package/package.json
CHANGED
package/styles.css
CHANGED
|
@@ -110,9 +110,12 @@
|
|
|
110
110
|
--ease-standard: cubic-bezier(.22,1,.36,1);
|
|
111
111
|
--ease-exit: cubic-bezier(.7,0,.84,0);
|
|
112
112
|
--ease-in-out: cubic-bezier(.65,0,.35,1);
|
|
113
|
-
/*
|
|
114
|
-
--ease-spring: linear(0, 0.
|
|
115
|
-
|
|
113
|
+
/* The slow spring tier (bounce .12, settles in 240ms), sampled for CSS. Pair with --duration-spring for transforms. */
|
|
114
|
+
--ease-spring: linear(0, 0.0515, 0.1675, 0.3072, 0.4464, 0.5725, 0.6797, 0.7668, 0.8352, 0.887, 0.9253, 0.9527, 0.9717, 0.9846, 0.9928, 0.9979, 1.0008, 1.0023, 1.0029, 1.0029, 1.0027, 1.0023, 1.0019, 1.0015, 1);
|
|
115
|
+
/* The same steps as motionTokens.duration: fast is a hover or press, standard a menu or panel, considered a dialog. */
|
|
116
|
+
--duration-instant: 60ms; --duration-fast: 80ms; --duration-standard: 160ms; --duration-considered: 240ms; --duration-spring: 240ms;
|
|
117
|
+
/* Motion that tells a story (a chart drawing in, a slow image zoom), never a hover, press or panel. */
|
|
118
|
+
--duration-expressive: 480ms;
|
|
116
119
|
/* Stagger steps (the same values as motionTokens.stagger). Keep total stagger under roughly 400ms. */
|
|
117
120
|
--stagger-item: 35ms;
|
|
118
121
|
/* Stacking order, by role. Menus and listboxes sit above popovers and dialogs because they open from inside them.
|