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