@bug-on/md3-react 2.0.3 → 3.0.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/.turbo/turbo-build.log +33 -0
- package/CHANGELOG.md +55 -0
- package/dist/index.css.d.ts +2 -0
- package/dist/index.d.mts +6127 -0
- package/dist/index.d.ts +6127 -71
- package/dist/index.js +1653 -614
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1566 -547
- package/dist/index.mjs.map +1 -1
- package/dist/material-symbols-cdn.css.d.ts +2 -0
- package/dist/material-symbols-self-hosted.css.d.ts +2 -0
- package/dist/typography.css.d.ts +2 -0
- package/package.json +22 -19
- package/scripts/copy-assets.js +82 -0
- package/src/assets/fonts/GoogleSansFlex-VariableFont.woff2 +0 -0
- package/src/assets/fonts/MaterialSymbolsOutlined-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
- package/src/assets/fonts/MaterialSymbolsRounded-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
- package/src/assets/fonts/MaterialSymbolsSharp-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
- package/src/assets/loading-indicator.svg +19 -0
- package/src/assets/material-symbols-cdn.css +65 -0
- package/src/assets/material-symbols-self-hosted.css +90 -0
- package/src/css.d.ts +20 -0
- package/src/hooks/useClickOutside.ts +37 -0
- package/src/hooks/useMediaQuery.ts +28 -0
- package/src/hooks/useRipple.ts +88 -0
- package/src/index.css +23 -0
- package/src/index.ts +349 -0
- package/src/lib/material-symbols-preconnect.tsx +82 -0
- package/src/lib/theme-utils.ts +180 -0
- package/src/lib/utils.ts +6 -0
- package/src/test/button.test.tsx +59 -0
- package/src/test/icon.test.tsx +91 -0
- package/src/test/loading-indicator.test.tsx +128 -0
- package/src/test/progress-indicator.test.tsx +306 -0
- package/src/test/setup.ts +80 -0
- package/src/test/typography.test.tsx +206 -0
- package/src/types/index.ts +7 -0
- package/src/types/md3.ts +31 -0
- package/src/ui/Text.tsx +60 -0
- package/src/ui/__snapshots__/divider.test.tsx.snap +63 -0
- package/src/ui/app-bar/app-bar-column.tsx +99 -0
- package/src/ui/app-bar/app-bar-item-button.tsx +71 -0
- package/src/ui/app-bar/app-bar-items.test.tsx +89 -0
- package/src/ui/app-bar/app-bar-overflow-indicator.tsx +108 -0
- package/src/ui/app-bar/app-bar-row.tsx +104 -0
- package/src/ui/app-bar/app-bar.test.tsx +87 -0
- package/src/ui/app-bar/app-bar.tokens.ts +223 -0
- package/src/ui/app-bar/app-bar.types.ts +441 -0
- package/src/ui/app-bar/bottom-app-bar.test.tsx +42 -0
- package/src/ui/app-bar/bottom-app-bar.tsx +84 -0
- package/src/ui/app-bar/docked-toolbar.test.tsx +34 -0
- package/src/ui/app-bar/docked-toolbar.tsx +54 -0
- package/src/ui/app-bar/flexible-app-bar.test.tsx +75 -0
- package/src/ui/app-bar/hooks/use-app-bar-scroll.ts +110 -0
- package/src/ui/app-bar/hooks/use-flexible-app-bar.ts +123 -0
- package/{dist/ui/app-bar/index.d.ts → src/ui/app-bar/index.ts} +35 -2
- package/src/ui/app-bar/large-flexible-app-bar.tsx +165 -0
- package/src/ui/app-bar/medium-flexible-app-bar.tsx +167 -0
- package/src/ui/app-bar/search-app-bar.test.tsx +49 -0
- package/src/ui/app-bar/search-app-bar.tsx +176 -0
- package/src/ui/app-bar/search-view.tsx +227 -0
- package/src/ui/app-bar/small-app-bar.test.tsx +48 -0
- package/src/ui/app-bar/small-app-bar.tsx +203 -0
- package/src/ui/badge.test.tsx +345 -0
- package/src/ui/badge.tsx +282 -0
- package/src/ui/button-group.test.tsx +71 -0
- package/src/ui/button-group.tsx +350 -0
- package/src/ui/button.test.tsx +297 -0
- package/src/ui/button.tsx +669 -0
- package/src/ui/card.test.tsx +187 -0
- package/src/ui/card.tsx +259 -0
- package/src/ui/checkbox.test.tsx +423 -0
- package/src/ui/checkbox.tsx +525 -0
- package/src/ui/chip.test.tsx +292 -0
- package/src/ui/chip.tsx +548 -0
- package/src/ui/code-block.tsx +219 -0
- package/src/ui/dialog.test.tsx +300 -0
- package/src/ui/dialog.tsx +384 -0
- package/src/ui/divider.test.tsx +314 -0
- package/src/ui/divider.tsx +412 -0
- package/src/ui/drawer.tsx +240 -0
- package/src/ui/fab-menu.test.tsx +494 -0
- package/src/ui/fab-menu.tsx +739 -0
- package/src/ui/fab.test.tsx +232 -0
- package/src/ui/fab.tsx +505 -0
- package/src/ui/icon-button.test.tsx +515 -0
- package/src/ui/icon-button.tsx +525 -0
- package/src/ui/icon.test.tsx +197 -0
- package/src/ui/icon.tsx +179 -0
- package/src/ui/loading-indicator.test.tsx +73 -0
- package/src/ui/loading-indicator.tsx +312 -0
- package/src/ui/menu/context-menu.tsx +275 -0
- package/src/ui/menu/index.ts +77 -0
- package/src/ui/menu/menu-animations.ts +102 -0
- package/src/ui/menu/menu-context.tsx +99 -0
- package/src/ui/menu/menu-divider.tsx +47 -0
- package/src/ui/menu/menu-group.tsx +200 -0
- package/src/ui/menu/menu-item.tsx +294 -0
- package/src/ui/menu/menu-tokens.ts +208 -0
- package/src/ui/menu/menu-types.ts +313 -0
- package/src/ui/menu/menu.test.tsx +624 -0
- package/src/ui/menu/menu.tsx +289 -0
- package/src/ui/menu/sub-menu.tsx +223 -0
- package/src/ui/menu/vertical-menu.tsx +382 -0
- package/src/ui/navigation-rail.test.tsx +404 -0
- package/src/ui/navigation-rail.tsx +604 -0
- package/src/ui/progress-indicator/circular.tsx +248 -0
- package/src/ui/progress-indicator/hooks.ts +51 -0
- package/{dist/ui/progress-indicator/index.d.ts → src/ui/progress-indicator/index.tsx} +20 -2
- package/src/ui/progress-indicator/linear-flat.tsx +83 -0
- package/src/ui/progress-indicator/linear-wavy.tsx +243 -0
- package/src/ui/progress-indicator/linear.tsx +143 -0
- package/src/ui/progress-indicator/types.ts +158 -0
- package/src/ui/progress-indicator/utils.ts +73 -0
- package/src/ui/radio-button.test.tsx +407 -0
- package/src/ui/radio-button.tsx +551 -0
- package/src/ui/ripple.test.tsx +72 -0
- package/src/ui/ripple.tsx +234 -0
- package/src/ui/scroll-area.test.tsx +58 -0
- package/src/ui/scroll-area.tsx +139 -0
- package/src/ui/search/animated-placeholder.tsx +145 -0
- package/src/ui/search/hooks/use-search-keyboard.test.ts +202 -0
- package/src/ui/search/hooks/use-search-keyboard.ts +104 -0
- package/src/ui/search/hooks/use-search-view-focus.test.ts +96 -0
- package/src/ui/search/hooks/use-search-view-focus.ts +24 -0
- package/src/ui/search/index.ts +44 -0
- package/src/ui/search/search-bar.tsx +220 -0
- package/src/ui/search/search-context.tsx +42 -0
- package/src/ui/search/search-view-docked.tsx +194 -0
- package/src/ui/search/search-view-fullscreen.tsx +247 -0
- package/src/ui/search/search.test.tsx +233 -0
- package/src/ui/search/search.tokens.ts +134 -0
- package/src/ui/search/search.tsx +131 -0
- package/src/ui/search/search.types.ts +154 -0
- package/src/ui/search/trailing-action.tsx +49 -0
- package/src/ui/shared/constants.ts +122 -0
- package/{dist/ui/shared/touch-target.d.ts → src/ui/shared/touch-target.tsx} +13 -1
- package/src/ui/slider/hooks/useSliderMath.ts +195 -0
- package/{dist/ui/slider/index.d.ts → src/ui/slider/index.ts} +12 -1
- package/src/ui/slider/range-slider.tsx +561 -0
- package/src/ui/slider/slider-thumb.tsx +379 -0
- package/src/ui/slider/slider-track.tsx +912 -0
- package/src/ui/slider/slider.tokens.ts +189 -0
- package/src/ui/slider/slider.tsx +259 -0
- package/src/ui/slider/slider.types.ts +288 -0
- package/src/ui/snackbar/index.ts +20 -0
- package/src/ui/snackbar/snackbar.test.tsx +338 -0
- package/src/ui/snackbar/snackbar.tsx +476 -0
- package/{dist/ui/switch/index.d.ts → src/ui/switch/index.ts} +1 -0
- package/src/ui/switch/switch.stories.tsx +309 -0
- package/src/ui/switch/switch.test.tsx +243 -0
- package/src/ui/switch/switch.tokens.ts +89 -0
- package/src/ui/switch/switch.tsx +504 -0
- package/src/ui/switch/switch.types.ts +62 -0
- package/{dist/ui/tabs/index.d.ts → src/ui/tabs/index.ts} +8 -1
- package/src/ui/tabs/tab.tsx +407 -0
- package/src/ui/tabs/tabs-content.tsx +89 -0
- package/src/ui/tabs/tabs-list.tsx +146 -0
- package/src/ui/tabs/tabs.test.tsx +290 -0
- package/src/ui/tabs/tabs.tokens.ts +121 -0
- package/src/ui/tabs/tabs.tsx +229 -0
- package/src/ui/tabs/tabs.types.ts +185 -0
- package/{dist/ui/text-field/index.d.ts → src/ui/text-field/index.ts} +8 -1
- package/src/ui/text-field/subcomponents/active-indicator.tsx +67 -0
- package/src/ui/text-field/subcomponents/floating-label.tsx +161 -0
- package/src/ui/text-field/subcomponents/leading-icon.tsx +46 -0
- package/src/ui/text-field/subcomponents/outline-container.tsx +170 -0
- package/src/ui/text-field/subcomponents/prefix-suffix.tsx +59 -0
- package/src/ui/text-field/subcomponents/supporting-text.tsx +145 -0
- package/src/ui/text-field/subcomponents/trailing-icon.tsx +199 -0
- package/src/ui/text-field/text-field.test.tsx +454 -0
- package/src/ui/text-field/text-field.tokens.ts +104 -0
- package/src/ui/text-field/text-field.tsx +548 -0
- package/src/ui/text-field/text-field.types.ts +180 -0
- package/src/ui/theme-provider/index.tsx +190 -0
- package/src/ui/toc.test.tsx +108 -0
- package/src/ui/toc.tsx +172 -0
- package/src/ui/tooltip/plain-tooltip.tsx +63 -0
- package/src/ui/tooltip/rich-tooltip.tsx +94 -0
- package/src/ui/tooltip/tooltip-box.tsx +266 -0
- package/src/ui/tooltip/tooltip-caret-shape.tsx +68 -0
- package/src/ui/tooltip/tooltip.tokens.ts +26 -0
- package/src/ui/tooltip/tooltip.types.ts +70 -0
- package/src/ui/tooltip/use-tooltip-position.ts +208 -0
- package/src/ui/tooltip/use-tooltip-state.ts +41 -0
- package/src/ui/typography/__tests__/typography.test.tsx +170 -0
- package/{dist/ui/typography/index.d.ts → src/ui/typography/index.ts} +21 -3
- package/src/ui/typography/type-scale-tokens.ts +205 -0
- package/src/ui/typography/typography-key-tokens.ts +43 -0
- package/src/ui/typography/typography-tokens.ts +360 -0
- package/src/ui/typography/typography.css +22 -0
- package/src/ui/typography/typography.tsx +559 -0
- package/test-render.tsx +4 -0
- package/test-shadow.html +26 -0
- package/test_output.txt +164 -0
- package/test_output_v2.txt +5 -0
- package/tsconfig.build.json +10 -0
- package/tsconfig.json +18 -0
- package/tsup.config.ts +20 -0
- package/vitest.config.ts +11 -0
- package/dist/hooks/useClickOutside.d.ts +0 -8
- package/dist/hooks/useMediaQuery.d.ts +0 -11
- package/dist/hooks/useRipple.d.ts +0 -26
- package/dist/lib/material-symbols-preconnect.d.ts +0 -42
- package/dist/lib/theme-utils.d.ts +0 -63
- package/dist/lib/utils.d.ts +0 -2
- package/dist/types/index.d.ts +0 -1
- package/dist/types/md3.d.ts +0 -14
- package/dist/ui/app-bar/app-bar-column.d.ts +0 -28
- package/dist/ui/app-bar/app-bar-item-button.d.ts +0 -16
- package/dist/ui/app-bar/app-bar-overflow-indicator.d.ts +0 -18
- package/dist/ui/app-bar/app-bar-row.d.ts +0 -36
- package/dist/ui/app-bar/app-bar.tokens.d.ts +0 -184
- package/dist/ui/app-bar/app-bar.types.d.ts +0 -392
- package/dist/ui/app-bar/bottom-app-bar.d.ts +0 -31
- package/dist/ui/app-bar/docked-toolbar.d.ts +0 -25
- package/dist/ui/app-bar/hooks/use-app-bar-scroll.d.ts +0 -42
- package/dist/ui/app-bar/hooks/use-flexible-app-bar.d.ts +0 -37
- package/dist/ui/app-bar/large-flexible-app-bar.d.ts +0 -26
- package/dist/ui/app-bar/medium-flexible-app-bar.d.ts +0 -28
- package/dist/ui/app-bar/search-app-bar.d.ts +0 -43
- package/dist/ui/app-bar/search-view.d.ts +0 -54
- package/dist/ui/app-bar/small-app-bar.d.ts +0 -37
- package/dist/ui/badge.d.ts +0 -125
- package/dist/ui/button-group.d.ts +0 -59
- package/dist/ui/button.d.ts +0 -148
- package/dist/ui/card.d.ts +0 -62
- package/dist/ui/checkbox.d.ts +0 -82
- package/dist/ui/chip.d.ts +0 -110
- package/dist/ui/code-block.d.ts +0 -14
- package/dist/ui/dialog.d.ts +0 -111
- package/dist/ui/divider.d.ts +0 -164
- package/dist/ui/drawer.d.ts +0 -39
- package/dist/ui/dropdown.d.ts +0 -29
- package/dist/ui/fab-menu.d.ts +0 -204
- package/dist/ui/fab.d.ts +0 -162
- package/dist/ui/icon-button.d.ts +0 -131
- package/dist/ui/icon.d.ts +0 -88
- package/dist/ui/loading-indicator.d.ts +0 -42
- package/dist/ui/navigation-rail.d.ts +0 -29
- package/dist/ui/progress-indicator/circular.d.ts +0 -3
- package/dist/ui/progress-indicator/hooks.d.ts +0 -3
- package/dist/ui/progress-indicator/linear-flat.d.ts +0 -10
- package/dist/ui/progress-indicator/linear-wavy.d.ts +0 -18
- package/dist/ui/progress-indicator/linear.d.ts +0 -3
- package/dist/ui/progress-indicator/types.d.ts +0 -151
- package/dist/ui/progress-indicator/utils.d.ts +0 -3
- package/dist/ui/radio-button.d.ts +0 -106
- package/dist/ui/ripple.d.ts +0 -126
- package/dist/ui/scroll-area.d.ts +0 -27
- package/dist/ui/search/animated-placeholder.d.ts +0 -54
- package/dist/ui/search/hooks/use-search-keyboard.d.ts +0 -32
- package/dist/ui/search/hooks/use-search-view-focus.d.ts +0 -6
- package/dist/ui/search/index.d.ts +0 -27
- package/dist/ui/search/search-bar.d.ts +0 -32
- package/dist/ui/search/search-context.d.ts +0 -24
- package/dist/ui/search/search-view-docked.d.ts +0 -25
- package/dist/ui/search/search-view-fullscreen.d.ts +0 -36
- package/dist/ui/search/search.d.ts +0 -50
- package/dist/ui/search/search.tokens.d.ts +0 -112
- package/dist/ui/search/search.types.d.ts +0 -131
- package/dist/ui/search/trailing-action.d.ts +0 -9
- package/dist/ui/shared/constants.d.ts +0 -86
- package/dist/ui/slider/hooks/useSliderMath.d.ts +0 -101
- package/dist/ui/slider/range-slider.d.ts +0 -47
- package/dist/ui/slider/slider-thumb.d.ts +0 -33
- package/dist/ui/slider/slider-track.d.ts +0 -25
- package/dist/ui/slider/slider.d.ts +0 -60
- package/dist/ui/slider/slider.tokens.d.ts +0 -151
- package/dist/ui/slider/slider.types.d.ts +0 -259
- package/dist/ui/snackbar/index.d.ts +0 -6
- package/dist/ui/snackbar/snackbar.d.ts +0 -197
- package/dist/ui/switch/switch.d.ts +0 -30
- package/dist/ui/switch/switch.stories.d.ts +0 -48
- package/dist/ui/switch/switch.tokens.d.ts +0 -67
- package/dist/ui/switch/switch.types.d.ts +0 -59
- package/dist/ui/tabs/tab.d.ts +0 -43
- package/dist/ui/tabs/tabs-content.d.ts +0 -36
- package/dist/ui/tabs/tabs-list.d.ts +0 -40
- package/dist/ui/tabs/tabs.d.ts +0 -60
- package/dist/ui/tabs/tabs.tokens.d.ts +0 -94
- package/dist/ui/tabs/tabs.types.d.ts +0 -172
- package/dist/ui/text-field/subcomponents/active-indicator.d.ts +0 -24
- package/dist/ui/text-field/subcomponents/floating-label.d.ts +0 -43
- package/dist/ui/text-field/subcomponents/leading-icon.d.ts +0 -23
- package/dist/ui/text-field/subcomponents/outline-container.d.ts +0 -42
- package/dist/ui/text-field/subcomponents/prefix-suffix.d.ts +0 -24
- package/dist/ui/text-field/subcomponents/supporting-text.d.ts +0 -37
- package/dist/ui/text-field/subcomponents/trailing-icon.d.ts +0 -41
- package/dist/ui/text-field/text-field.d.ts +0 -49
- package/dist/ui/text-field/text-field.tokens.d.ts +0 -76
- package/dist/ui/text-field/text-field.types.d.ts +0 -126
- package/dist/ui/theme-provider/index.d.ts +0 -48
- package/dist/ui/toc.d.ts +0 -80
- package/dist/ui/tooltip/plain-tooltip.d.ts +0 -2
- package/dist/ui/tooltip/rich-tooltip.d.ts +0 -2
- package/dist/ui/tooltip/tooltip-box.d.ts +0 -2
- package/dist/ui/tooltip/tooltip-caret-shape.d.ts +0 -9
- package/dist/ui/tooltip/tooltip.tokens.d.ts +0 -26
- package/dist/ui/tooltip/tooltip.types.d.ts +0 -56
- package/dist/ui/tooltip/use-tooltip-position.d.ts +0 -8
- package/dist/ui/tooltip/use-tooltip-state.d.ts +0 -2
- package/dist/ui/typography/type-scale-tokens.d.ts +0 -162
- package/dist/ui/typography/typography-key-tokens.d.ts +0 -40
- package/dist/ui/typography/typography-tokens.d.ts +0 -220
- package/dist/ui/typography/typography.d.ts +0 -265
- /package/{dist/hooks/index.d.ts → src/hooks/index.ts} +0 -0
- /package/{dist/ui/tooltip/index.d.ts → src/ui/tooltip/index.ts} +0 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file shared/constants.ts
|
|
3
|
+
*
|
|
4
|
+
* Shared animation constants for MD3 Expressive UI components.
|
|
5
|
+
* Centralises spring transition configs and motion variant objects to avoid
|
|
6
|
+
* duplication across button, icon-button, FAB, and other interactive components.
|
|
7
|
+
*
|
|
8
|
+
* @see https://m3.material.io/foundations/animation/overview
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { Target, TargetAndTransition, Transition } from "motion/react";
|
|
12
|
+
|
|
13
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
14
|
+
// Spring Transitions
|
|
15
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Fast critically-damped spring — used for border-radius morphing.
|
|
19
|
+
*
|
|
20
|
+
* - Duration: 200ms
|
|
21
|
+
* - Bounce: 0 (no overshoot → prevents negative radius jitter)
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* <m.button transition={{ borderRadius: SPRING_TRANSITION_FAST }}>...</m.button>
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export const SPRING_TRANSITION_FAST: Transition = {
|
|
29
|
+
type: "spring",
|
|
30
|
+
bounce: 0,
|
|
31
|
+
duration: 0.2,
|
|
32
|
+
} as const;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Standard critically-damped spring — used for icon/content scale animations.
|
|
36
|
+
*
|
|
37
|
+
* - Duration: 300ms
|
|
38
|
+
* - Bounce: 0 (no overshoot)
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```tsx
|
|
42
|
+
* <m.span transition={SPRING_TRANSITION}>...</m.span>
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export const SPRING_TRANSITION: Transition = {
|
|
46
|
+
type: "spring",
|
|
47
|
+
bounce: 0,
|
|
48
|
+
duration: 0.3,
|
|
49
|
+
} as const;
|
|
50
|
+
|
|
51
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
52
|
+
// Icon Span Motion Variants
|
|
53
|
+
// Used for icon/loading indicator swap animation inside FAB and IconButton.
|
|
54
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Framer Motion variants for animating icon spans in/out.
|
|
58
|
+
*
|
|
59
|
+
* Scale from near-zero → 1 on enter; back to near-zero on exit.
|
|
60
|
+
* The near-zero value (0.01) avoids the SMIL freeze bug on Chromium
|
|
61
|
+
* that occurs when an element starts at exactly `scale(0)`.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```tsx
|
|
65
|
+
* <AnimatePresence mode="wait">
|
|
66
|
+
* {loading ? (
|
|
67
|
+
* <m.span key="loading" {...ICON_SPAN_VARIANTS} transition={SPRING_TRANSITION}>
|
|
68
|
+
* <LoadingIndicator />
|
|
69
|
+
* </m.span>
|
|
70
|
+
* ) : (
|
|
71
|
+
* <m.span key="icon" {...ICON_SPAN_VARIANTS} transition={SPRING_TRANSITION}>
|
|
72
|
+
* {icon}
|
|
73
|
+
* </m.span>
|
|
74
|
+
* )}
|
|
75
|
+
* </AnimatePresence>
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export const ICON_SPAN_VARIANTS: {
|
|
79
|
+
initial: Target;
|
|
80
|
+
animate: TargetAndTransition;
|
|
81
|
+
exit: TargetAndTransition;
|
|
82
|
+
} = {
|
|
83
|
+
initial: { scale: 0.01 },
|
|
84
|
+
animate: { scale: 1 },
|
|
85
|
+
exit: { scale: 0.01 },
|
|
86
|
+
} as const;
|
|
87
|
+
|
|
88
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
89
|
+
// MD3 TextField Animation Constants
|
|
90
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* MD3 Standard easing curve — used for label float, active indicator expand.
|
|
94
|
+
* cubic-bezier(0.2, 0, 0, 1)
|
|
95
|
+
*
|
|
96
|
+
* @see https://m3.material.io/foundations/animation/easing-and-duration
|
|
97
|
+
*/
|
|
98
|
+
export const MD3_STANDARD_EASING: [number, number, number, number] = [
|
|
99
|
+
0.2, 0, 0, 1,
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Duration for floating label transition: 150ms.
|
|
104
|
+
* Used when label moves between inline position ↔ floated position.
|
|
105
|
+
*/
|
|
106
|
+
export const MD3_LABEL_FLOAT_DURATION = 0.15;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Duration for active indicator expand/collapse: 200ms.
|
|
110
|
+
* Used for the bottom border (filled) and outline (outlined) on focus.
|
|
111
|
+
*/
|
|
112
|
+
export const MD3_INDICATOR_DURATION = 0.2;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Duration for supporting text / error text appear/disappear: 120ms.
|
|
116
|
+
*/
|
|
117
|
+
export const MD3_SUPPORTING_DURATION = 0.12;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Duration for trailing icon appear/disappear (clear button, password toggle): 100ms.
|
|
121
|
+
*/
|
|
122
|
+
export const MD3_ICON_SWAP_DURATION = 0.1;
|
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
* @see https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html
|
|
13
13
|
* @see https://m3.material.io/foundations/accessible-design/accessibility-basics
|
|
14
14
|
*/
|
|
15
|
+
|
|
16
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
17
|
+
// TouchTarget Component
|
|
18
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
19
|
+
|
|
15
20
|
/**
|
|
16
21
|
* Invisible 48×48dp touch area expander — satisfies WCAG 2.5.5 + MD3 spec.
|
|
17
22
|
*
|
|
@@ -35,4 +40,11 @@
|
|
|
35
40
|
*
|
|
36
41
|
* @see https://m3.material.io/components/buttons/specs (Touch target section)
|
|
37
42
|
*/
|
|
38
|
-
export
|
|
43
|
+
export function TouchTarget() {
|
|
44
|
+
return (
|
|
45
|
+
<span
|
|
46
|
+
aria-hidden="true"
|
|
47
|
+
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 min-w-12 min-h-12 cursor-pointer pointer-events-none"
|
|
48
|
+
/>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file useSliderMath.ts
|
|
3
|
+
* MD3 Expressive Slider — Math utility hook.
|
|
4
|
+
*
|
|
5
|
+
* Handles all slider math in one place:
|
|
6
|
+
* - value coercion to [min, max]
|
|
7
|
+
* - step snapping for discrete mode
|
|
8
|
+
* - value ↔ percent conversion
|
|
9
|
+
* - keyboard delta calculation
|
|
10
|
+
* - tick position generation
|
|
11
|
+
*
|
|
12
|
+
* Exported as a pure hook for testability and reuse in both Slider and RangeSlider.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { useMemo } from "react";
|
|
16
|
+
|
|
17
|
+
// ─── Pure math helpers (exported for unit testing) ───────────────────────────
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Clamps `value` to the closed interval `[min, max]`.
|
|
21
|
+
*
|
|
22
|
+
* @example coerceValue(150, 0, 100) → 100
|
|
23
|
+
*/
|
|
24
|
+
export function coerceValue(value: number, min: number, max: number): number {
|
|
25
|
+
return Math.max(min, Math.min(max, value));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Rounds `value` to the nearest multiple of `step` relative to `min`.
|
|
30
|
+
* When `step === 0`, returns `value` unchanged (continuous mode).
|
|
31
|
+
*
|
|
32
|
+
* @example snapToStep(23, 0, 10) → 20
|
|
33
|
+
* @example snapToStep(27, 0, 10) → 30
|
|
34
|
+
*/
|
|
35
|
+
export function snapToStep(value: number, min: number, step: number): number {
|
|
36
|
+
if (step <= 0) return value;
|
|
37
|
+
const steps = Math.round((value - min) / step);
|
|
38
|
+
// Use toPrecision to avoid floating-point drift (e.g., 0.1 + 0.2 issues)
|
|
39
|
+
return Number((min + steps * step).toPrecision(10));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Converts a raw value to a 0–1 fraction of the [min, max] range.
|
|
44
|
+
* Returns 0 when max === min (degenerate range).
|
|
45
|
+
*
|
|
46
|
+
* @example valueToPercent(50, 0, 100) → 0.5
|
|
47
|
+
*/
|
|
48
|
+
export function valueToPercent(
|
|
49
|
+
value: number,
|
|
50
|
+
min: number,
|
|
51
|
+
max: number,
|
|
52
|
+
): number {
|
|
53
|
+
if (max === min) return 0;
|
|
54
|
+
return (value - min) / (max - min);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Converts a 0–1 fraction to a value within [min, max], then snaps to step.
|
|
59
|
+
* The result is also coerced to [min, max].
|
|
60
|
+
*
|
|
61
|
+
* @example percentToValue(0.5, 0, 100, 10) → 50
|
|
62
|
+
* @example percentToValue(0.23, 0, 100, 10) → 20
|
|
63
|
+
*/
|
|
64
|
+
export function percentToValue(
|
|
65
|
+
percent: number,
|
|
66
|
+
min: number,
|
|
67
|
+
max: number,
|
|
68
|
+
step: number,
|
|
69
|
+
): number {
|
|
70
|
+
const raw = min + percent * (max - min);
|
|
71
|
+
const snapped = snapToStep(raw, min, step);
|
|
72
|
+
return coerceValue(snapped, min, max);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Computes the keyboard increment for a given key.
|
|
77
|
+
*
|
|
78
|
+
* Key mappings per WAI-ARIA Slider pattern:
|
|
79
|
+
* - ArrowRight/Up → +step (or +1% of range if continuous)
|
|
80
|
+
* - ArrowLeft/Down → -step (or -1% of range)
|
|
81
|
+
* - PageUp → +10% of range (snapped to step)
|
|
82
|
+
* - PageDown → -10% of range (snapped to step)
|
|
83
|
+
* - Home / End → handled by the caller (jump to min/max)
|
|
84
|
+
*
|
|
85
|
+
* @returns the signed delta to add to current value, or `null` for Home/End.
|
|
86
|
+
*/
|
|
87
|
+
export function getKeyboardDelta(
|
|
88
|
+
key: string,
|
|
89
|
+
step: number,
|
|
90
|
+
min: number,
|
|
91
|
+
max: number,
|
|
92
|
+
): number | null {
|
|
93
|
+
const range = max - min;
|
|
94
|
+
const discreteStep = step > 0 ? step : range / 100; // 1% of range for continuous
|
|
95
|
+
|
|
96
|
+
switch (key) {
|
|
97
|
+
case "ArrowRight":
|
|
98
|
+
case "ArrowUp":
|
|
99
|
+
return discreteStep;
|
|
100
|
+
case "ArrowLeft":
|
|
101
|
+
case "ArrowDown":
|
|
102
|
+
return -discreteStep;
|
|
103
|
+
case "PageUp":
|
|
104
|
+
return step > 0 ? snapToStep(range * 0.1, 0, step) : range * 0.1;
|
|
105
|
+
case "PageDown":
|
|
106
|
+
return step > 0 ? -snapToStep(range * 0.1, 0, step) : -(range * 0.1);
|
|
107
|
+
default:
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Generates the list of value positions for tick marks.
|
|
114
|
+
* Returns an empty array when `step === 0` (continuous mode).
|
|
115
|
+
*
|
|
116
|
+
* @example generateTicks(0, 100, 10) → [0, 10, 20, ..., 100]
|
|
117
|
+
*/
|
|
118
|
+
export function generateTicks(
|
|
119
|
+
min: number,
|
|
120
|
+
max: number,
|
|
121
|
+
step: number,
|
|
122
|
+
): number[] {
|
|
123
|
+
if (step <= 0) return [];
|
|
124
|
+
const ticks: number[] = [];
|
|
125
|
+
let current = min;
|
|
126
|
+
while (current <= max) {
|
|
127
|
+
ticks.push(Number(current.toPrecision(10)));
|
|
128
|
+
current += step;
|
|
129
|
+
}
|
|
130
|
+
// Ensure max is included even if floating-point drift skips it
|
|
131
|
+
if (ticks[ticks.length - 1] !== max) {
|
|
132
|
+
ticks.push(max);
|
|
133
|
+
}
|
|
134
|
+
return ticks;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ─── Hook ────────────────────────────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
export interface UseSliderMathOptions {
|
|
140
|
+
min: number;
|
|
141
|
+
max: number;
|
|
142
|
+
step: number;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface UseSliderMathReturn {
|
|
146
|
+
/** Clamp value to [min, max]. */
|
|
147
|
+
coerce: (v: number) => number;
|
|
148
|
+
/** Snap value to nearest step (no-op if step=0). */
|
|
149
|
+
snap: (v: number) => number;
|
|
150
|
+
/** Value → percent [0, 1]. */
|
|
151
|
+
toPercent: (v: number) => number;
|
|
152
|
+
/** Percent [0, 1] → snapped value. */
|
|
153
|
+
fromPercent: (pct: number) => number;
|
|
154
|
+
/**
|
|
155
|
+
* Signed delta for a keyboard key.
|
|
156
|
+
* Returns `null` for Home/End (jump to min/max, handled by caller).
|
|
157
|
+
*/
|
|
158
|
+
getKeyDelta: (key: string) => number | null;
|
|
159
|
+
/** Tick positions. Empty array in continuous mode. */
|
|
160
|
+
ticks: number[];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Math utility hook for MD3 Slider components.
|
|
165
|
+
*
|
|
166
|
+
* Memoizes all math functions against `min`, `max`, `step` changes.
|
|
167
|
+
* Use this in both `<Slider>` and `<RangeSlider>` to share logic.
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```ts
|
|
171
|
+
* const { coerce, snap, toPercent, fromPercent, getKeyDelta, ticks } =
|
|
172
|
+
* useSliderMath({ min: 0, max: 100, step: 10 });
|
|
173
|
+
*
|
|
174
|
+
* const percent = toPercent(value); // → 0.5 for value=50
|
|
175
|
+
* const newValue = fromPercent(dragPercent); // → 50
|
|
176
|
+
* const delta = getKeyDelta("ArrowRight"); // → 10
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
export function useSliderMath({
|
|
180
|
+
min,
|
|
181
|
+
max,
|
|
182
|
+
step,
|
|
183
|
+
}: UseSliderMathOptions): UseSliderMathReturn {
|
|
184
|
+
return useMemo(
|
|
185
|
+
() => ({
|
|
186
|
+
coerce: (v: number) => coerceValue(v, min, max),
|
|
187
|
+
snap: (v: number) => snapToStep(v, min, step),
|
|
188
|
+
toPercent: (v: number) => valueToPercent(v, min, max),
|
|
189
|
+
fromPercent: (pct: number) => percentToValue(pct, min, max, step),
|
|
190
|
+
getKeyDelta: (key: string) => getKeyboardDelta(key, step, min, max),
|
|
191
|
+
ticks: generateTicks(min, max, step),
|
|
192
|
+
}),
|
|
193
|
+
[min, max, step],
|
|
194
|
+
);
|
|
195
|
+
}
|
|
@@ -2,8 +2,19 @@
|
|
|
2
2
|
* @file index.ts
|
|
3
3
|
* MD3 Expressive Slider — Public API exports.
|
|
4
4
|
*/
|
|
5
|
+
|
|
6
|
+
"use client";
|
|
7
|
+
|
|
5
8
|
export { RangeSlider } from "./range-slider";
|
|
6
9
|
export { Slider } from "./slider";
|
|
10
|
+
// Tokens (for customization)
|
|
7
11
|
export { SliderColors, SliderTokens } from "./slider.tokens";
|
|
8
|
-
|
|
12
|
+
// Types
|
|
13
|
+
export type {
|
|
14
|
+
RangeSliderProps,
|
|
15
|
+
SliderOrientation,
|
|
16
|
+
SliderProps,
|
|
17
|
+
SliderTrackSize,
|
|
18
|
+
SliderVariant,
|
|
19
|
+
} from "./slider.types";
|
|
9
20
|
export { SliderThumb } from "./slider-thumb";
|