@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,143 @@
1
+ import { domMax, LazyMotion } from "motion/react";
2
+ import * as React from "react";
3
+ import { cn } from "../../lib/utils";
4
+ import { useMergedRef } from "./hooks";
5
+ import { FlatLinearTrack } from "./linear-flat";
6
+ import { WavyLinearTrack } from "./linear-wavy";
7
+ import type { LinearProgressProps } from "./types";
8
+
9
+ export const LinearProgress = React.forwardRef<
10
+ HTMLDivElement,
11
+ LinearProgressProps
12
+ >(
13
+ (
14
+ {
15
+ value,
16
+ shape = "flat",
17
+ trackShape,
18
+ trackHeight = 4,
19
+ amplitude,
20
+ wavelength = 40,
21
+ indeterminateWavelength = 20,
22
+ waveSpeed = 1,
23
+ crawlerSpeed = 1,
24
+ determinateAnimation = "md3",
25
+ indeterminateAnimation = "continuous",
26
+ gapSize = 4,
27
+ showStopIndicator = "auto",
28
+ color,
29
+ trackColor,
30
+ className,
31
+ "aria-label": ariaLabel,
32
+ ...restProps
33
+ },
34
+ ref,
35
+ ) => {
36
+ const isDeterminate = value !== undefined;
37
+ const clampedValue = isDeterminate ? Math.min(100, Math.max(0, value)) : 0;
38
+
39
+ const containerRef = React.useRef<HTMLDivElement>(null);
40
+ const mergedRef = useMergedRef(ref, containerRef);
41
+ const [isRtl, setIsRtl] = React.useState(false);
42
+
43
+ React.useEffect(() => {
44
+ if (containerRef.current) {
45
+ const dir = getComputedStyle(containerRef.current).direction;
46
+ setIsRtl(dir === "rtl");
47
+ }
48
+ }, []);
49
+
50
+ const isWavy = shape === "wavy";
51
+ const resolvedTrackShape = trackShape ?? shape;
52
+
53
+ const effectiveAmplitude = React.useMemo(() => amplitude ?? 3, [amplitude]);
54
+ const svgHeight = React.useMemo(
55
+ () => (isWavy ? trackHeight + effectiveAmplitude * 2 : trackHeight),
56
+ [isWavy, trackHeight, effectiveAmplitude],
57
+ );
58
+
59
+ const shouldShowStop = React.useMemo(
60
+ () =>
61
+ isDeterminate &&
62
+ resolvedTrackShape === "flat" &&
63
+ (showStopIndicator === true ||
64
+ (showStopIndicator === "auto" && isDeterminate)),
65
+ [isDeterminate, resolvedTrackShape, showStopIndicator],
66
+ );
67
+
68
+ const stopSize = React.useMemo(
69
+ () => Math.max(2, trackHeight > 4 ? 4 : trackHeight / 2),
70
+ [trackHeight],
71
+ );
72
+
73
+ const stopOffset = (trackHeight - stopSize) / 2;
74
+
75
+ const activeColor = color || "var(--md-sys-color-indicator-active)";
76
+ const bgTrackColor = trackColor || "var(--md-sys-color-indicator-track)";
77
+
78
+ return (
79
+ <LazyMotion features={domMax} strict>
80
+ <div
81
+ ref={mergedRef}
82
+ role="progressbar"
83
+ aria-label={ariaLabel}
84
+ aria-valuenow={isDeterminate ? clampedValue : undefined}
85
+ aria-valuemin={0}
86
+ aria-valuemax={100}
87
+ className={cn(
88
+ "relative flex w-full flex-col justify-center",
89
+ className,
90
+ )}
91
+ style={{ height: svgHeight }}
92
+ {...restProps}
93
+ >
94
+ {isWavy ? (
95
+ <WavyLinearTrack
96
+ trackHeight={trackHeight}
97
+ svgHeight={svgHeight}
98
+ amplitude={effectiveAmplitude}
99
+ wavelength={wavelength}
100
+ indeterminateWavelength={indeterminateWavelength}
101
+ activeColor={activeColor}
102
+ trackColor={bgTrackColor}
103
+ value={isDeterminate ? clampedValue : undefined}
104
+ isRtl={isRtl}
105
+ gapSize={gapSize}
106
+ waveSpeed={waveSpeed}
107
+ crawlerSpeed={crawlerSpeed}
108
+ determinateAnimation={determinateAnimation}
109
+ indeterminateAnimation={indeterminateAnimation}
110
+ trackShape={resolvedTrackShape}
111
+ />
112
+ ) : (
113
+ <FlatLinearTrack
114
+ trackHeight={trackHeight}
115
+ activeColor={activeColor}
116
+ trackColor={bgTrackColor}
117
+ value={isDeterminate ? clampedValue : undefined}
118
+ isRtl={isRtl}
119
+ gapSize={gapSize}
120
+ crawlerSpeed={crawlerSpeed}
121
+ />
122
+ )}
123
+
124
+ {shouldShowStop && (
125
+ <div
126
+ aria-hidden="true"
127
+ className="absolute rounded-full"
128
+ style={{
129
+ width: stopSize,
130
+ height: stopSize,
131
+ backgroundColor: "var(--md-sys-color-indicator-stop)",
132
+ top: svgHeight / 2 - stopSize / 2,
133
+ ...(isRtl ? { left: stopOffset } : { right: stopOffset }),
134
+ }}
135
+ />
136
+ )}
137
+ </div>
138
+ </LazyMotion>
139
+ );
140
+ },
141
+ );
142
+
143
+ LinearProgress.displayName = "LinearProgress";
@@ -0,0 +1,158 @@
1
+ import type * as React from "react";
2
+
3
+ export interface ProgressBaseProps
4
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
5
+ /**
6
+ * Giá trị phần trăm tiến trình hiện tại (từ 0 đến 100).
7
+ * Nếu truyền giá trị này, tiến trình sẽ hiển thị ở trạng thái Determinate.
8
+ * Nếu bỏ trống (undefined), tiến trình sẽ tự động ở trạng thái Indeterminate.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * <ProgressIndicator value={45} aria-label="Loading profile..." />
13
+ * ```
14
+ */
15
+ value?: number;
16
+ /**
17
+ * Nhan đề mô tả mục đích của thanh tiến trình dành cho screen readers (bắt buộc).
18
+ */
19
+ "aria-label": string;
20
+ /**
21
+ * Độ dày của track (đường nền) của thanh tiến trình (đơn vị: px).
22
+ * - Với Linear: là chiều cao của thanh.
23
+ * - Với Circular: là độ dày của viền hình tròn.
24
+ *
25
+ * @example
26
+ * trackHeight={8} // Dày hơn bình thường
27
+ */
28
+ trackHeight?: number;
29
+ /**
30
+ * Màu sắc của phần tiến trình (phần đã hoàn thành).
31
+ * Mặc định mượn màu `currentColor` của thẻ wrap, để dễ dàng tuỳ biến qua utility class.
32
+ *
33
+ * @example
34
+ * color="var(--md-sys-color-primary)" // Sử dụng custom token
35
+ */
36
+ color?: string;
37
+ /**
38
+ * Màu sắc của thanh nền (phần chưa hoàn thành).
39
+ * Mặc định sử dụng màu được tính toán từ bề mặt hoặc transparency để tạo sự tinh tế.
40
+ */
41
+ trackColor?: string;
42
+ }
43
+
44
+ export interface LinearProgressProps extends ProgressBaseProps {
45
+ /** Phân loại component sang kiểu dáng Linear (đường thẳng ngang). */
46
+ variant: "linear";
47
+ /**
48
+ * Hình dáng của vạch tiến trình (phần đã hoàn thành).
49
+ * - `flat`: Đường nét liền phẳng (mặc định)
50
+ * - `wavy`: Đường lượn sóng động
51
+ */
52
+ shape?: "flat" | "wavy";
53
+ /**
54
+ * Hình dáng của thanh nền chờ (track).
55
+ * - `flat`: Đường nét liền phẳng (mặc định)
56
+ * - `wavy`: Đường lượn sóng cố định hoặc động
57
+ */
58
+ trackShape?: "flat" | "wavy";
59
+ /**
60
+ * Biên độ sóng (áp dụng khi `shape` hoặc `trackShape` là "wavy").
61
+ * Chỉ định độ lượn cao thấp của con sóng.
62
+ */
63
+ amplitude?: number;
64
+ /**
65
+ * Chiều dài một nhịp sóng (áp dụng khi `shape` là "wavy" theo determinate).
66
+ * Khoảng cách giữa 2 đỉnh sóng kề nhau.
67
+ */
68
+ wavelength?: number;
69
+ /**
70
+ * Nhịp sóng dành riêng cho trạng thái chạy liên tục (Indeterminate Wavy).
71
+ */
72
+ indeterminateWavelength?: number;
73
+ /**
74
+ * Khoảng hở kết dính giữa cụm vạch đang chạy và thanh nền.
75
+ * Cho phép truyền \`0\` để hai thanh chạm sát vào nhau liền mạch.
76
+ *
77
+ * @example
78
+ * ```tsx
79
+ * <ProgressIndicator variant="linear" shape="wavy" gapSize={0} /> // Sóng chạm track liền nét
80
+ * ```
81
+ */
82
+ gapSize?: number;
83
+ /**
84
+ * Tốc độ dao động vỗ của sóng (Multiplier). Mặc định là \`1\`.
85
+ * Tăng giá trị (VD: 1.5, 2) để sóng dao động nhanh hơn.
86
+ */
87
+ waveSpeed?: number;
88
+ /**
89
+ * Tốc độ lướt / cuộn dải màu dọc trên track (Crawler) đối với trạng thái Indeterminate.
90
+ * Mặc định là \`1\`.
91
+ */
92
+ crawlerSpeed?: number;
93
+ /**
94
+ * Cấu hình hiệu ứng gợn khi thanh determinate ở mức xấp xỉ mép viền (<= 10% hoặc >= 90%).
95
+ * - `md3`: Tự động ép phẳng biên độ sóng thành số 0 một cách mềm mại (Chuẩn Google MD3).
96
+ * - `continuous`: Bỏ qua các ràng buộc ép phẳng, con sóng vẫn gợn lấp lóa trên mọi giá trị phần trăm.
97
+ *
98
+ * @example
99
+ * ```tsx
100
+ * <ProgressIndicator variant="linear" shape="wavy" determinateAnimation="continuous" />
101
+ * ```
102
+ */
103
+ determinateAnimation?: "md3" | "continuous";
104
+ /**
105
+ * Kiểu kịch bản tịnh tiến cho thanh Indeterminate.
106
+ * - `md3`: Render 2 vạch song song vọt tới & co giãn mô phỏng vật lý (Chuẩn Google MD3).
107
+ * - `continuous`: Vạch không ngắt quãng mà tràn lướt vòng lặp mượt mà.
108
+ *
109
+ * @example
110
+ * ```tsx
111
+ * <ProgressIndicator variant="linear" indeterminateAnimation="continuous" />
112
+ * ```
113
+ */
114
+ indeterminateAnimation?: "md3" | "continuous";
115
+ /**
116
+ * Bật/tắt dấm chấm điểm báo kết thúc ở cuối track (Stop Indicator).
117
+ * - `true`: Luôn thấy một chấm tròn bé xíu ở cuối đường đi
118
+ * - `false`: Tắt hoàn toàn
119
+ * - `"auto"`: Chấm tròn chỉ hiển thị và hòa trộn khi progress đạt 100%
120
+ */
121
+ showStopIndicator?: boolean | "auto";
122
+ }
123
+
124
+ export interface CircularProgressProps extends ProgressBaseProps {
125
+ /** Phân loại component sang kiểu dáng Circular (hình tròn khép kín). */
126
+ variant: "circular";
127
+ /**
128
+ * Đường kính hiển thị của vòng biểu đồ, đơn vị px.
129
+ *
130
+ * @example
131
+ * ```tsx
132
+ * <ProgressIndicator variant="circular" size={48} aria-label="Loading..." />
133
+ * ```
134
+ */
135
+ size?: number;
136
+ /**
137
+ * Phong cách nét vẽ của đường viền trong trạng thái trượt.
138
+ * - `flat`: Đường nét cứng, vuốt tròn hai đầu stroke.
139
+ * - `wavy`: Vệt màu chuyển động rung tạo hình răng cưa/gợn sóng.
140
+ */
141
+ shape?: "flat" | "wavy";
142
+ /**
143
+ * Biên độ gợn của trạng thái `wavy` cho vòng tròn.
144
+ */
145
+ amplitude?: number;
146
+ /** Bước sóng bao trọn chu vi hình tròn. */
147
+ wavelength?: number;
148
+ /** Khoảng hở khe đứt ngang nối đỉnh nét vẽ. */
149
+ gapSize?: number;
150
+ /**
151
+ * Xoay nhanh hay chậm theo số nhân cho Crawler Circular quay Indeterminate.
152
+ */
153
+ crawlerSpeed?: number;
154
+ }
155
+
156
+ export type ProgressIndicatorProps =
157
+ | LinearProgressProps
158
+ | CircularProgressProps;
@@ -0,0 +1,73 @@
1
+ export function easeInOutCubic(x: number): number {
2
+ return x < 0.5 ? 4 * x * x * x : 1 - (-2 * x + 2) ** 3 / 2;
3
+ }
4
+
5
+ export function generateWavyCircularPath(
6
+ center: number,
7
+ radius: number,
8
+ amplitude: number,
9
+ wavelength: number,
10
+ ): string {
11
+ const circumference = 2 * Math.PI * radius;
12
+ const numWaves = Math.max(
13
+ 3,
14
+ Math.round(circumference / Math.max(1, wavelength)),
15
+ );
16
+ const steps = numWaves * 4;
17
+ const dt = (2 * Math.PI) / steps;
18
+
19
+ const rAt = (t: number) => radius + amplitude * Math.sin(numWaves * t);
20
+ const drAt = (t: number) => amplitude * numWaves * Math.cos(numWaves * t);
21
+ const xAt = (t: number) => center + rAt(t) * Math.cos(t);
22
+ const yAt = (t: number) => center + rAt(t) * Math.sin(t);
23
+ const dxAt = (t: number) => drAt(t) * Math.cos(t) - rAt(t) * Math.sin(t);
24
+ const dyAt = (t: number) => drAt(t) * Math.sin(t) + rAt(t) * Math.cos(t);
25
+
26
+ let d = "";
27
+ const tStart = 0;
28
+
29
+ for (let i = 0; i < steps; i++) {
30
+ const t0 = tStart + i * dt;
31
+ const t1 = tStart + (i + 1) * dt;
32
+
33
+ const scale = dt / 3;
34
+ const cp1x = xAt(t0) + scale * dxAt(t0);
35
+ const cp1y = yAt(t0) + scale * dyAt(t0);
36
+ const cp2x = xAt(t1) - scale * dxAt(t1);
37
+ const cp2y = yAt(t1) - scale * dyAt(t1);
38
+
39
+ if (i === 0) d += `M ${xAt(t0).toFixed(2)} ${yAt(t0).toFixed(2)}`;
40
+ d += ` C ${cp1x.toFixed(2)} ${cp1y.toFixed(2)}, ${cp2x.toFixed(2)} ${cp2y.toFixed(2)}, ${xAt(t1).toFixed(2)} ${yAt(t1).toFixed(2)}`;
41
+ }
42
+ d += " Z";
43
+ return d;
44
+ }
45
+
46
+ export function getSinePath(
47
+ startX: number,
48
+ endX: number,
49
+ phase: number,
50
+ wl: number,
51
+ amp: number,
52
+ ) {
53
+ if (startX >= endX) return "";
54
+ let d = "";
55
+ const step = amp === 0 ? Math.max(10, endX - startX) : 1;
56
+
57
+ const yStart = Math.sin(((startX + phase) / wl) * 2 * Math.PI) * amp;
58
+ d += `M ${startX.toFixed(2)} ${yStart.toFixed(2)}`;
59
+
60
+ let nextX = Math.ceil(startX / step) * step;
61
+ if (nextX === startX) nextX += step;
62
+
63
+ while (nextX < endX) {
64
+ const y = Math.sin(((nextX + phase) / wl) * 2 * Math.PI) * amp;
65
+ d += ` L ${nextX.toFixed(2)} ${y.toFixed(2)}`;
66
+ nextX += step;
67
+ }
68
+
69
+ const yEnd = Math.sin(((endX + phase) / wl) * 2 * Math.PI) * amp;
70
+ d += ` L ${endX.toFixed(2)} ${yEnd.toFixed(2)}`;
71
+
72
+ return d;
73
+ }