@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.
Files changed (308) hide show
  1. package/.turbo/turbo-build.log +33 -0
  2. package/CHANGELOG.md +55 -0
  3. package/dist/index.css.d.ts +2 -0
  4. package/dist/index.d.mts +6127 -0
  5. package/dist/index.d.ts +6127 -71
  6. package/dist/index.js +1653 -614
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +1566 -547
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/material-symbols-cdn.css.d.ts +2 -0
  11. package/dist/material-symbols-self-hosted.css.d.ts +2 -0
  12. package/dist/typography.css.d.ts +2 -0
  13. package/package.json +22 -19
  14. package/scripts/copy-assets.js +82 -0
  15. package/src/assets/fonts/GoogleSansFlex-VariableFont.woff2 +0 -0
  16. package/src/assets/fonts/MaterialSymbolsOutlined-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
  17. package/src/assets/fonts/MaterialSymbolsRounded-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
  18. package/src/assets/fonts/MaterialSymbolsSharp-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
  19. package/src/assets/loading-indicator.svg +19 -0
  20. package/src/assets/material-symbols-cdn.css +65 -0
  21. package/src/assets/material-symbols-self-hosted.css +90 -0
  22. package/src/css.d.ts +20 -0
  23. package/src/hooks/useClickOutside.ts +37 -0
  24. package/src/hooks/useMediaQuery.ts +28 -0
  25. package/src/hooks/useRipple.ts +88 -0
  26. package/src/index.css +23 -0
  27. package/src/index.ts +349 -0
  28. package/src/lib/material-symbols-preconnect.tsx +82 -0
  29. package/src/lib/theme-utils.ts +180 -0
  30. package/src/lib/utils.ts +6 -0
  31. package/src/test/button.test.tsx +59 -0
  32. package/src/test/icon.test.tsx +91 -0
  33. package/src/test/loading-indicator.test.tsx +128 -0
  34. package/src/test/progress-indicator.test.tsx +306 -0
  35. package/src/test/setup.ts +80 -0
  36. package/src/test/typography.test.tsx +206 -0
  37. package/src/types/index.ts +7 -0
  38. package/src/types/md3.ts +31 -0
  39. package/src/ui/Text.tsx +60 -0
  40. package/src/ui/__snapshots__/divider.test.tsx.snap +63 -0
  41. package/src/ui/app-bar/app-bar-column.tsx +99 -0
  42. package/src/ui/app-bar/app-bar-item-button.tsx +71 -0
  43. package/src/ui/app-bar/app-bar-items.test.tsx +89 -0
  44. package/src/ui/app-bar/app-bar-overflow-indicator.tsx +108 -0
  45. package/src/ui/app-bar/app-bar-row.tsx +104 -0
  46. package/src/ui/app-bar/app-bar.test.tsx +87 -0
  47. package/src/ui/app-bar/app-bar.tokens.ts +223 -0
  48. package/src/ui/app-bar/app-bar.types.ts +441 -0
  49. package/src/ui/app-bar/bottom-app-bar.test.tsx +42 -0
  50. package/src/ui/app-bar/bottom-app-bar.tsx +84 -0
  51. package/src/ui/app-bar/docked-toolbar.test.tsx +34 -0
  52. package/src/ui/app-bar/docked-toolbar.tsx +54 -0
  53. package/src/ui/app-bar/flexible-app-bar.test.tsx +75 -0
  54. package/src/ui/app-bar/hooks/use-app-bar-scroll.ts +110 -0
  55. package/src/ui/app-bar/hooks/use-flexible-app-bar.ts +123 -0
  56. package/{dist/ui/app-bar/index.d.ts → src/ui/app-bar/index.ts} +35 -2
  57. package/src/ui/app-bar/large-flexible-app-bar.tsx +165 -0
  58. package/src/ui/app-bar/medium-flexible-app-bar.tsx +167 -0
  59. package/src/ui/app-bar/search-app-bar.test.tsx +49 -0
  60. package/src/ui/app-bar/search-app-bar.tsx +176 -0
  61. package/src/ui/app-bar/search-view.tsx +227 -0
  62. package/src/ui/app-bar/small-app-bar.test.tsx +48 -0
  63. package/src/ui/app-bar/small-app-bar.tsx +203 -0
  64. package/src/ui/badge.test.tsx +345 -0
  65. package/src/ui/badge.tsx +282 -0
  66. package/src/ui/button-group.test.tsx +71 -0
  67. package/src/ui/button-group.tsx +350 -0
  68. package/src/ui/button.test.tsx +297 -0
  69. package/src/ui/button.tsx +669 -0
  70. package/src/ui/card.test.tsx +187 -0
  71. package/src/ui/card.tsx +259 -0
  72. package/src/ui/checkbox.test.tsx +423 -0
  73. package/src/ui/checkbox.tsx +525 -0
  74. package/src/ui/chip.test.tsx +292 -0
  75. package/src/ui/chip.tsx +548 -0
  76. package/src/ui/code-block.tsx +219 -0
  77. package/src/ui/dialog.test.tsx +300 -0
  78. package/src/ui/dialog.tsx +384 -0
  79. package/src/ui/divider.test.tsx +314 -0
  80. package/src/ui/divider.tsx +412 -0
  81. package/src/ui/drawer.tsx +240 -0
  82. package/src/ui/fab-menu.test.tsx +494 -0
  83. package/src/ui/fab-menu.tsx +739 -0
  84. package/src/ui/fab.test.tsx +232 -0
  85. package/src/ui/fab.tsx +505 -0
  86. package/src/ui/icon-button.test.tsx +515 -0
  87. package/src/ui/icon-button.tsx +525 -0
  88. package/src/ui/icon.test.tsx +197 -0
  89. package/src/ui/icon.tsx +179 -0
  90. package/src/ui/loading-indicator.test.tsx +73 -0
  91. package/src/ui/loading-indicator.tsx +312 -0
  92. package/src/ui/menu/context-menu.tsx +275 -0
  93. package/src/ui/menu/index.ts +77 -0
  94. package/src/ui/menu/menu-animations.ts +102 -0
  95. package/src/ui/menu/menu-context.tsx +99 -0
  96. package/src/ui/menu/menu-divider.tsx +47 -0
  97. package/src/ui/menu/menu-group.tsx +200 -0
  98. package/src/ui/menu/menu-item.tsx +294 -0
  99. package/src/ui/menu/menu-tokens.ts +208 -0
  100. package/src/ui/menu/menu-types.ts +313 -0
  101. package/src/ui/menu/menu.test.tsx +624 -0
  102. package/src/ui/menu/menu.tsx +289 -0
  103. package/src/ui/menu/sub-menu.tsx +223 -0
  104. package/src/ui/menu/vertical-menu.tsx +382 -0
  105. package/src/ui/navigation-rail.test.tsx +404 -0
  106. package/src/ui/navigation-rail.tsx +604 -0
  107. package/src/ui/progress-indicator/circular.tsx +248 -0
  108. package/src/ui/progress-indicator/hooks.ts +51 -0
  109. package/{dist/ui/progress-indicator/index.d.ts → src/ui/progress-indicator/index.tsx} +20 -2
  110. package/src/ui/progress-indicator/linear-flat.tsx +83 -0
  111. package/src/ui/progress-indicator/linear-wavy.tsx +243 -0
  112. package/src/ui/progress-indicator/linear.tsx +143 -0
  113. package/src/ui/progress-indicator/types.ts +158 -0
  114. package/src/ui/progress-indicator/utils.ts +73 -0
  115. package/src/ui/radio-button.test.tsx +407 -0
  116. package/src/ui/radio-button.tsx +551 -0
  117. package/src/ui/ripple.test.tsx +72 -0
  118. package/src/ui/ripple.tsx +234 -0
  119. package/src/ui/scroll-area.test.tsx +58 -0
  120. package/src/ui/scroll-area.tsx +139 -0
  121. package/src/ui/search/animated-placeholder.tsx +145 -0
  122. package/src/ui/search/hooks/use-search-keyboard.test.ts +202 -0
  123. package/src/ui/search/hooks/use-search-keyboard.ts +104 -0
  124. package/src/ui/search/hooks/use-search-view-focus.test.ts +96 -0
  125. package/src/ui/search/hooks/use-search-view-focus.ts +24 -0
  126. package/src/ui/search/index.ts +44 -0
  127. package/src/ui/search/search-bar.tsx +220 -0
  128. package/src/ui/search/search-context.tsx +42 -0
  129. package/src/ui/search/search-view-docked.tsx +194 -0
  130. package/src/ui/search/search-view-fullscreen.tsx +247 -0
  131. package/src/ui/search/search.test.tsx +233 -0
  132. package/src/ui/search/search.tokens.ts +134 -0
  133. package/src/ui/search/search.tsx +131 -0
  134. package/src/ui/search/search.types.ts +154 -0
  135. package/src/ui/search/trailing-action.tsx +49 -0
  136. package/src/ui/shared/constants.ts +122 -0
  137. package/{dist/ui/shared/touch-target.d.ts → src/ui/shared/touch-target.tsx} +13 -1
  138. package/src/ui/slider/hooks/useSliderMath.ts +195 -0
  139. package/{dist/ui/slider/index.d.ts → src/ui/slider/index.ts} +12 -1
  140. package/src/ui/slider/range-slider.tsx +561 -0
  141. package/src/ui/slider/slider-thumb.tsx +379 -0
  142. package/src/ui/slider/slider-track.tsx +912 -0
  143. package/src/ui/slider/slider.tokens.ts +189 -0
  144. package/src/ui/slider/slider.tsx +259 -0
  145. package/src/ui/slider/slider.types.ts +288 -0
  146. package/src/ui/snackbar/index.ts +20 -0
  147. package/src/ui/snackbar/snackbar.test.tsx +338 -0
  148. package/src/ui/snackbar/snackbar.tsx +476 -0
  149. package/{dist/ui/switch/index.d.ts → src/ui/switch/index.ts} +1 -0
  150. package/src/ui/switch/switch.stories.tsx +309 -0
  151. package/src/ui/switch/switch.test.tsx +243 -0
  152. package/src/ui/switch/switch.tokens.ts +89 -0
  153. package/src/ui/switch/switch.tsx +504 -0
  154. package/src/ui/switch/switch.types.ts +62 -0
  155. package/{dist/ui/tabs/index.d.ts → src/ui/tabs/index.ts} +8 -1
  156. package/src/ui/tabs/tab.tsx +407 -0
  157. package/src/ui/tabs/tabs-content.tsx +89 -0
  158. package/src/ui/tabs/tabs-list.tsx +146 -0
  159. package/src/ui/tabs/tabs.test.tsx +290 -0
  160. package/src/ui/tabs/tabs.tokens.ts +121 -0
  161. package/src/ui/tabs/tabs.tsx +229 -0
  162. package/src/ui/tabs/tabs.types.ts +185 -0
  163. package/{dist/ui/text-field/index.d.ts → src/ui/text-field/index.ts} +8 -1
  164. package/src/ui/text-field/subcomponents/active-indicator.tsx +67 -0
  165. package/src/ui/text-field/subcomponents/floating-label.tsx +161 -0
  166. package/src/ui/text-field/subcomponents/leading-icon.tsx +46 -0
  167. package/src/ui/text-field/subcomponents/outline-container.tsx +170 -0
  168. package/src/ui/text-field/subcomponents/prefix-suffix.tsx +59 -0
  169. package/src/ui/text-field/subcomponents/supporting-text.tsx +145 -0
  170. package/src/ui/text-field/subcomponents/trailing-icon.tsx +199 -0
  171. package/src/ui/text-field/text-field.test.tsx +454 -0
  172. package/src/ui/text-field/text-field.tokens.ts +104 -0
  173. package/src/ui/text-field/text-field.tsx +548 -0
  174. package/src/ui/text-field/text-field.types.ts +180 -0
  175. package/src/ui/theme-provider/index.tsx +190 -0
  176. package/src/ui/toc.test.tsx +108 -0
  177. package/src/ui/toc.tsx +172 -0
  178. package/src/ui/tooltip/plain-tooltip.tsx +63 -0
  179. package/src/ui/tooltip/rich-tooltip.tsx +94 -0
  180. package/src/ui/tooltip/tooltip-box.tsx +266 -0
  181. package/src/ui/tooltip/tooltip-caret-shape.tsx +68 -0
  182. package/src/ui/tooltip/tooltip.tokens.ts +26 -0
  183. package/src/ui/tooltip/tooltip.types.ts +70 -0
  184. package/src/ui/tooltip/use-tooltip-position.ts +208 -0
  185. package/src/ui/tooltip/use-tooltip-state.ts +41 -0
  186. package/src/ui/typography/__tests__/typography.test.tsx +170 -0
  187. package/{dist/ui/typography/index.d.ts → src/ui/typography/index.ts} +21 -3
  188. package/src/ui/typography/type-scale-tokens.ts +205 -0
  189. package/src/ui/typography/typography-key-tokens.ts +43 -0
  190. package/src/ui/typography/typography-tokens.ts +360 -0
  191. package/src/ui/typography/typography.css +22 -0
  192. package/src/ui/typography/typography.tsx +559 -0
  193. package/test-render.tsx +4 -0
  194. package/test-shadow.html +26 -0
  195. package/test_output.txt +164 -0
  196. package/test_output_v2.txt +5 -0
  197. package/tsconfig.build.json +10 -0
  198. package/tsconfig.json +18 -0
  199. package/tsup.config.ts +20 -0
  200. package/vitest.config.ts +11 -0
  201. package/dist/hooks/useClickOutside.d.ts +0 -8
  202. package/dist/hooks/useMediaQuery.d.ts +0 -11
  203. package/dist/hooks/useRipple.d.ts +0 -26
  204. package/dist/lib/material-symbols-preconnect.d.ts +0 -42
  205. package/dist/lib/theme-utils.d.ts +0 -63
  206. package/dist/lib/utils.d.ts +0 -2
  207. package/dist/types/index.d.ts +0 -1
  208. package/dist/types/md3.d.ts +0 -14
  209. package/dist/ui/app-bar/app-bar-column.d.ts +0 -28
  210. package/dist/ui/app-bar/app-bar-item-button.d.ts +0 -16
  211. package/dist/ui/app-bar/app-bar-overflow-indicator.d.ts +0 -18
  212. package/dist/ui/app-bar/app-bar-row.d.ts +0 -36
  213. package/dist/ui/app-bar/app-bar.tokens.d.ts +0 -184
  214. package/dist/ui/app-bar/app-bar.types.d.ts +0 -392
  215. package/dist/ui/app-bar/bottom-app-bar.d.ts +0 -31
  216. package/dist/ui/app-bar/docked-toolbar.d.ts +0 -25
  217. package/dist/ui/app-bar/hooks/use-app-bar-scroll.d.ts +0 -42
  218. package/dist/ui/app-bar/hooks/use-flexible-app-bar.d.ts +0 -37
  219. package/dist/ui/app-bar/large-flexible-app-bar.d.ts +0 -26
  220. package/dist/ui/app-bar/medium-flexible-app-bar.d.ts +0 -28
  221. package/dist/ui/app-bar/search-app-bar.d.ts +0 -43
  222. package/dist/ui/app-bar/search-view.d.ts +0 -54
  223. package/dist/ui/app-bar/small-app-bar.d.ts +0 -37
  224. package/dist/ui/badge.d.ts +0 -125
  225. package/dist/ui/button-group.d.ts +0 -59
  226. package/dist/ui/button.d.ts +0 -148
  227. package/dist/ui/card.d.ts +0 -62
  228. package/dist/ui/checkbox.d.ts +0 -82
  229. package/dist/ui/chip.d.ts +0 -110
  230. package/dist/ui/code-block.d.ts +0 -14
  231. package/dist/ui/dialog.d.ts +0 -111
  232. package/dist/ui/divider.d.ts +0 -164
  233. package/dist/ui/drawer.d.ts +0 -39
  234. package/dist/ui/dropdown.d.ts +0 -29
  235. package/dist/ui/fab-menu.d.ts +0 -204
  236. package/dist/ui/fab.d.ts +0 -162
  237. package/dist/ui/icon-button.d.ts +0 -131
  238. package/dist/ui/icon.d.ts +0 -88
  239. package/dist/ui/loading-indicator.d.ts +0 -42
  240. package/dist/ui/navigation-rail.d.ts +0 -29
  241. package/dist/ui/progress-indicator/circular.d.ts +0 -3
  242. package/dist/ui/progress-indicator/hooks.d.ts +0 -3
  243. package/dist/ui/progress-indicator/linear-flat.d.ts +0 -10
  244. package/dist/ui/progress-indicator/linear-wavy.d.ts +0 -18
  245. package/dist/ui/progress-indicator/linear.d.ts +0 -3
  246. package/dist/ui/progress-indicator/types.d.ts +0 -151
  247. package/dist/ui/progress-indicator/utils.d.ts +0 -3
  248. package/dist/ui/radio-button.d.ts +0 -106
  249. package/dist/ui/ripple.d.ts +0 -126
  250. package/dist/ui/scroll-area.d.ts +0 -27
  251. package/dist/ui/search/animated-placeholder.d.ts +0 -54
  252. package/dist/ui/search/hooks/use-search-keyboard.d.ts +0 -32
  253. package/dist/ui/search/hooks/use-search-view-focus.d.ts +0 -6
  254. package/dist/ui/search/index.d.ts +0 -27
  255. package/dist/ui/search/search-bar.d.ts +0 -32
  256. package/dist/ui/search/search-context.d.ts +0 -24
  257. package/dist/ui/search/search-view-docked.d.ts +0 -25
  258. package/dist/ui/search/search-view-fullscreen.d.ts +0 -36
  259. package/dist/ui/search/search.d.ts +0 -50
  260. package/dist/ui/search/search.tokens.d.ts +0 -112
  261. package/dist/ui/search/search.types.d.ts +0 -131
  262. package/dist/ui/search/trailing-action.d.ts +0 -9
  263. package/dist/ui/shared/constants.d.ts +0 -86
  264. package/dist/ui/slider/hooks/useSliderMath.d.ts +0 -101
  265. package/dist/ui/slider/range-slider.d.ts +0 -47
  266. package/dist/ui/slider/slider-thumb.d.ts +0 -33
  267. package/dist/ui/slider/slider-track.d.ts +0 -25
  268. package/dist/ui/slider/slider.d.ts +0 -60
  269. package/dist/ui/slider/slider.tokens.d.ts +0 -151
  270. package/dist/ui/slider/slider.types.d.ts +0 -259
  271. package/dist/ui/snackbar/index.d.ts +0 -6
  272. package/dist/ui/snackbar/snackbar.d.ts +0 -197
  273. package/dist/ui/switch/switch.d.ts +0 -30
  274. package/dist/ui/switch/switch.stories.d.ts +0 -48
  275. package/dist/ui/switch/switch.tokens.d.ts +0 -67
  276. package/dist/ui/switch/switch.types.d.ts +0 -59
  277. package/dist/ui/tabs/tab.d.ts +0 -43
  278. package/dist/ui/tabs/tabs-content.d.ts +0 -36
  279. package/dist/ui/tabs/tabs-list.d.ts +0 -40
  280. package/dist/ui/tabs/tabs.d.ts +0 -60
  281. package/dist/ui/tabs/tabs.tokens.d.ts +0 -94
  282. package/dist/ui/tabs/tabs.types.d.ts +0 -172
  283. package/dist/ui/text-field/subcomponents/active-indicator.d.ts +0 -24
  284. package/dist/ui/text-field/subcomponents/floating-label.d.ts +0 -43
  285. package/dist/ui/text-field/subcomponents/leading-icon.d.ts +0 -23
  286. package/dist/ui/text-field/subcomponents/outline-container.d.ts +0 -42
  287. package/dist/ui/text-field/subcomponents/prefix-suffix.d.ts +0 -24
  288. package/dist/ui/text-field/subcomponents/supporting-text.d.ts +0 -37
  289. package/dist/ui/text-field/subcomponents/trailing-icon.d.ts +0 -41
  290. package/dist/ui/text-field/text-field.d.ts +0 -49
  291. package/dist/ui/text-field/text-field.tokens.d.ts +0 -76
  292. package/dist/ui/text-field/text-field.types.d.ts +0 -126
  293. package/dist/ui/theme-provider/index.d.ts +0 -48
  294. package/dist/ui/toc.d.ts +0 -80
  295. package/dist/ui/tooltip/plain-tooltip.d.ts +0 -2
  296. package/dist/ui/tooltip/rich-tooltip.d.ts +0 -2
  297. package/dist/ui/tooltip/tooltip-box.d.ts +0 -2
  298. package/dist/ui/tooltip/tooltip-caret-shape.d.ts +0 -9
  299. package/dist/ui/tooltip/tooltip.tokens.d.ts +0 -26
  300. package/dist/ui/tooltip/tooltip.types.d.ts +0 -56
  301. package/dist/ui/tooltip/use-tooltip-position.d.ts +0 -8
  302. package/dist/ui/tooltip/use-tooltip-state.d.ts +0 -2
  303. package/dist/ui/typography/type-scale-tokens.d.ts +0 -162
  304. package/dist/ui/typography/typography-key-tokens.d.ts +0 -40
  305. package/dist/ui/typography/typography-tokens.d.ts +0 -220
  306. package/dist/ui/typography/typography.d.ts +0 -265
  307. /package/{dist/hooks/index.d.ts → src/hooks/index.ts} +0 -0
  308. /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 declare function TouchTarget(): import("react/jsx-runtime").JSX.Element;
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
- export type { RangeSliderProps, SliderOrientation, SliderProps, SliderTrackSize, SliderVariant, } from "./slider.types";
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";