@titan-design/react-ui 0.5.0 → 0.7.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 (307) hide show
  1. package/dist/bodymap.js +241 -189
  2. package/dist/bodymap.js.map +1 -1
  3. package/dist/bodymap.mjs +241 -189
  4. package/dist/bodymap.mjs.map +1 -1
  5. package/dist/index.d.mts +1017 -130
  6. package/dist/index.d.ts +1017 -130
  7. package/dist/index.js +3058 -1090
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +2992 -1091
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/{pages-MO0JCySL.d.mts → pages-8u_4WbL-.d.mts} +280 -57
  12. package/dist/{pages-D7Jlssvp.d.ts → pages-DaWiBOGa.d.ts} +280 -57
  13. package/dist/pages.d.mts +1 -1
  14. package/dist/pages.d.ts +1 -1
  15. package/dist/pages.js +1797 -1292
  16. package/dist/pages.js.map +1 -1
  17. package/dist/pages.mjs +1797 -1292
  18. package/dist/pages.mjs.map +1 -1
  19. package/dist/theme/index.d.mts +464 -268
  20. package/dist/theme/index.d.ts +464 -268
  21. package/dist/theme/index.js +303 -191
  22. package/dist/theme/index.js.map +1 -1
  23. package/dist/theme/index.mjs +295 -192
  24. package/dist/theme/index.mjs.map +1 -1
  25. package/dist/theme/tokens-css.js +160 -143
  26. package/dist/theme/tokens-css.js.map +1 -1
  27. package/dist/theme/tokens-css.mjs +160 -143
  28. package/dist/theme/tokens-css.mjs.map +1 -1
  29. package/package.json +3 -1
  30. package/src/arch/Architecture.stories.tsx +870 -0
  31. package/src/arch/annotations.json +48 -0
  32. package/src/arch/arch-graph.json +4301 -0
  33. package/src/components/custom/DateTime/DateTime.stories.tsx +83 -7
  34. package/src/components/custom/DateTime/DateTime.test.tsx +29 -3
  35. package/src/components/custom/DateTime/DateTime.tsx +55 -15
  36. package/src/components/custom/EmptyState/EmptyState.stories.tsx +1 -1
  37. package/src/components/custom/Gauge/Gauge.stories.tsx +1 -1
  38. package/src/components/custom/Gauge/Gauge.test.tsx +2 -2
  39. package/src/components/custom/Gauge/Gauge.tsx +6 -3
  40. package/src/components/custom/Metric/Metric.stories.tsx +1 -1
  41. package/src/components/custom/Scatter/Scatter.stories.tsx +1 -1
  42. package/src/components/custom/Scatter/Scatter.tsx +3 -5
  43. package/src/components/custom/Sidebar/Sidebar.stories.tsx +1 -1
  44. package/src/components/custom/Table/Table.stories.tsx +1 -1
  45. package/src/components/custom/TimerReadout/TimerReadout.stories.tsx +62 -0
  46. package/src/components/custom/TimerReadout/TimerReadout.test.tsx +72 -0
  47. package/src/components/custom/TimerReadout/TimerReadout.tsx +75 -0
  48. package/src/components/custom/TimerReadout/index.ts +1 -0
  49. package/src/components/custom/Treemap/Treemap.stories.tsx +1 -1
  50. package/src/components/custom/Treemap/Treemap.tsx +3 -5
  51. package/src/components/custom/Typography/Typography.stories.tsx +46 -28
  52. package/src/components/custom/Typography/Typography.test.tsx +13 -3
  53. package/src/components/custom/Typography/Typography.tsx +5 -0
  54. package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +21 -13
  55. package/src/components/custom/Workout/ActiveWorkoutPage.test.tsx +16 -13
  56. package/src/components/custom/Workout/ActiveWorkoutPage.tsx +11 -16
  57. package/src/components/custom/Workout/BaseBadge.stories.tsx +1 -1
  58. package/src/components/custom/Workout/BodyMap.stories.tsx +1 -1
  59. package/src/components/custom/Workout/BodyMap.tsx +6 -3
  60. package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +1 -1
  61. package/src/components/custom/Workout/BodyMapDetailPanel.tsx +6 -3
  62. package/src/components/custom/Workout/CapacityBandChart.stories.tsx +1 -1
  63. package/src/components/custom/Workout/CapacityBandChart.test.tsx +2 -2
  64. package/src/components/custom/Workout/CapacityBandChart.tsx +9 -6
  65. package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
  66. package/src/components/custom/Workout/DeviationBar.test.tsx +1 -1
  67. package/src/components/custom/Workout/DeviationBar.tsx +7 -4
  68. package/src/components/custom/Workout/ExerciseCard.stories.tsx +51 -66
  69. package/src/components/custom/Workout/ExerciseCard.test.tsx +146 -143
  70. package/src/components/custom/Workout/ExerciseCard.tsx +176 -198
  71. package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +99 -0
  72. package/src/components/custom/Workout/ExerciseCardHeading.test.tsx +72 -0
  73. package/src/components/custom/Workout/ExerciseCardHeading.tsx +43 -0
  74. package/src/components/custom/Workout/ExerciseCardUnified.stories.tsx +151 -0
  75. package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +5 -3
  76. package/src/components/custom/Workout/ExerciseDetailPage.test.tsx +4 -4
  77. package/src/components/custom/Workout/ExerciseDetailPage.tsx +6 -6
  78. package/src/components/custom/Workout/ExerciseHeading.stories.tsx +81 -0
  79. package/src/components/custom/Workout/ExerciseHeading.test.tsx +77 -0
  80. package/src/components/custom/Workout/ExerciseHeading.tsx +85 -0
  81. package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +110 -0
  82. package/src/components/custom/Workout/ExerciseIndicator.test.tsx +93 -0
  83. package/src/components/custom/Workout/ExerciseIndicator.tsx +120 -0
  84. package/src/components/custom/Workout/FatigueMeter.stories.tsx +51 -0
  85. package/src/components/custom/Workout/FatigueMeter.test.tsx +73 -0
  86. package/src/components/custom/Workout/FatigueMeter.tsx +82 -0
  87. package/src/components/custom/Workout/InputBar.stories.tsx +1 -1
  88. package/src/components/custom/Workout/InputBar.tsx +2 -1
  89. package/src/components/custom/Workout/IntensityBar.stories.tsx +1 -1
  90. package/src/components/custom/Workout/IntensityBar.test.tsx +8 -8
  91. package/src/components/custom/Workout/IntensityBar.tsx +10 -6
  92. package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +153 -0
  93. package/src/components/custom/Workout/LiveAuraFrame.test.tsx +74 -0
  94. package/src/components/custom/Workout/LiveAuraFrame.tsx +105 -0
  95. package/src/components/custom/Workout/MesoCard.stories.tsx +1 -1
  96. package/src/components/custom/Workout/MesoCard.tsx +8 -3
  97. package/src/components/custom/Workout/MesoProgressBar.stories.tsx +1 -1
  98. package/src/components/custom/Workout/MesoProgressBar.test.tsx +1 -1
  99. package/src/components/custom/Workout/MesoProgressBar.tsx +3 -2
  100. package/src/components/custom/Workout/MesoStatusCard.stories.tsx +1 -1
  101. package/src/components/custom/Workout/MesoStatusCard.tsx +20 -13
  102. package/src/components/custom/Workout/MetricTiles.stories.tsx +60 -0
  103. package/src/components/custom/Workout/MetricTiles.test.tsx +58 -0
  104. package/src/components/custom/Workout/MetricTiles.tsx +48 -0
  105. package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +1 -1
  106. package/src/components/custom/Workout/MuscleGroupChip.tsx +7 -4
  107. package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +1 -1
  108. package/src/components/custom/Workout/PrBadge.stories.tsx +1 -1
  109. package/src/components/custom/Workout/PrBadge.tsx +5 -2
  110. package/src/components/custom/Workout/PrHistoryModal.stories.tsx +1 -1
  111. package/src/components/custom/Workout/PrHistoryModal.tsx +4 -3
  112. package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +4 -16
  113. package/src/components/custom/Workout/ProgramPlanningPage.tsx +10 -7
  114. package/src/components/custom/Workout/README.md +146 -4
  115. package/src/components/custom/Workout/ReadinessCheck.stories.tsx +1 -1
  116. package/src/components/custom/Workout/ReadinessCheck.tsx +7 -4
  117. package/src/components/custom/Workout/RestTimer.stories.tsx +1 -1
  118. package/src/components/custom/Workout/RestTimer.tsx +17 -11
  119. package/src/components/custom/Workout/RowInventory.stories.tsx +405 -0
  120. package/src/components/custom/Workout/S3FullRail.stories.tsx +34 -0
  121. package/src/components/custom/Workout/S3SessionPace.stories.tsx +95 -0
  122. package/src/components/custom/Workout/S3SessionRailHeading.stories.tsx +61 -0
  123. package/src/components/custom/Workout/S3SetTypes.stories.tsx +176 -0
  124. package/src/components/custom/Workout/S3WorkoutExpansion.stories.tsx +240 -0
  125. package/src/components/custom/Workout/ScheduleTiles.stories.tsx +40 -0
  126. package/src/components/custom/Workout/ScheduleTiles.test.tsx +34 -0
  127. package/src/components/custom/Workout/ScheduleTiles.tsx +70 -0
  128. package/src/components/custom/Workout/SegmentedBar.stories.tsx +99 -0
  129. package/src/components/custom/Workout/SegmentedBar.test.tsx +77 -0
  130. package/src/components/custom/Workout/SegmentedBar.tsx +172 -0
  131. package/src/components/custom/Workout/SegmentedProgressBar.stories.tsx +50 -0
  132. package/src/components/custom/Workout/SegmentedProgressBar.test.tsx +80 -0
  133. package/src/components/custom/Workout/SegmentedProgressBar.tsx +67 -0
  134. package/src/components/custom/Workout/SessionHeader.stories.tsx +121 -0
  135. package/src/components/custom/Workout/SessionHeader.test.tsx +122 -0
  136. package/src/components/custom/Workout/SessionHeader.tsx +152 -0
  137. package/src/components/custom/Workout/SessionRail.stories.tsx +153 -0
  138. package/src/components/custom/Workout/SessionRail.test.tsx +86 -0
  139. package/src/components/custom/Workout/SessionRail.tsx +126 -0
  140. package/src/components/custom/Workout/SetBar.stories.tsx +124 -0
  141. package/src/components/custom/Workout/SetBar.test.tsx +123 -0
  142. package/src/components/custom/Workout/SetBar.tsx +171 -0
  143. package/src/components/custom/Workout/SetRow.stories.tsx +50 -100
  144. package/src/components/custom/Workout/SetRow.test.tsx +94 -166
  145. package/src/components/custom/Workout/SetRow.tsx +170 -232
  146. package/src/components/custom/Workout/SetStrip.stories.tsx +154 -0
  147. package/src/components/custom/Workout/SetStrip.test.tsx +109 -0
  148. package/src/components/custom/Workout/SetStrip.tsx +71 -0
  149. package/src/components/custom/Workout/SetTableHeader.stories.tsx +66 -0
  150. package/src/components/custom/Workout/SetTableHeader.test.tsx +47 -0
  151. package/src/components/custom/Workout/SetTableHeader.tsx +88 -0
  152. package/src/components/custom/Workout/SetsRepsLoad.stories.tsx +47 -0
  153. package/src/components/custom/Workout/SetsRepsLoad.test.tsx +44 -0
  154. package/src/components/custom/Workout/SetsRepsLoad.tsx +52 -0
  155. package/src/components/custom/Workout/Sparkline.stories.tsx +1 -1
  156. package/src/components/custom/Workout/Sparkline.tsx +2 -1
  157. package/src/components/custom/Workout/StatusDot.stories.tsx +1 -1
  158. package/src/components/custom/Workout/StatusDot.test.tsx +2 -2
  159. package/src/components/custom/Workout/StatusDot.tsx +12 -9
  160. package/src/components/custom/Workout/StatusPill.stories.tsx +59 -0
  161. package/src/components/custom/Workout/StatusPill.test.tsx +63 -0
  162. package/src/components/custom/Workout/StatusPill.tsx +88 -0
  163. package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +1 -1
  164. package/src/components/custom/Workout/StrengthTrendChart.tsx +9 -6
  165. package/src/components/custom/Workout/SupersetWrapper.stories.tsx +1 -1
  166. package/src/components/custom/Workout/SupersetWrapper.tsx +2 -1
  167. package/src/components/custom/Workout/TempoBar.stories.tsx +1 -1
  168. package/src/components/custom/Workout/TempoDisplay.stories.tsx +12 -1
  169. package/src/components/custom/Workout/TempoDisplay.tsx +29 -36
  170. package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +1 -1
  171. package/src/components/custom/Workout/VelocityStrip.modalities.stories.tsx +254 -0
  172. package/src/components/custom/Workout/VelocityStrip.responsive.stories.tsx +145 -0
  173. package/src/components/custom/Workout/VelocityStrip.stories.tsx +129 -66
  174. package/src/components/custom/Workout/VelocityStrip.test.tsx +379 -144
  175. package/src/components/custom/Workout/VelocityStrip.tsx +508 -120
  176. package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +73 -0
  177. package/src/components/custom/Workout/VolumeLandmarkBar.test.tsx +119 -0
  178. package/src/components/custom/Workout/VolumeLandmarkBar.tsx +154 -0
  179. package/src/components/custom/Workout/WeekRow.stories.tsx +1 -1
  180. package/src/components/custom/Workout/WeekRow.tsx +3 -2
  181. package/src/components/custom/Workout/WeightBadge.stories.tsx +1 -1
  182. package/src/components/custom/Workout/WeightBadge.tsx +6 -2
  183. package/src/components/custom/Workout/WorkoutCard.stories.tsx +2 -6
  184. package/src/components/custom/Workout/WorkoutCard.test.tsx +13 -46
  185. package/src/components/custom/Workout/WorkoutCard.tsx +5 -2
  186. package/src/components/custom/Workout/WorkoutPill.stories.tsx +1 -1
  187. package/src/components/custom/Workout/WorkoutPill.tsx +12 -8
  188. package/src/components/custom/Workout/ZoneTrack.stories.tsx +140 -0
  189. package/src/components/custom/Workout/ZoneTrack.test.tsx +200 -0
  190. package/src/components/custom/Workout/ZoneTrack.tsx +315 -0
  191. package/src/components/custom/Workout/icons.tsx +6 -67
  192. package/src/components/custom/Workout/index.ts +55 -2
  193. package/src/components/custom/Workout/metricText.stories.tsx +69 -0
  194. package/src/components/custom/Workout/metricText.tsx +34 -0
  195. package/src/components/custom/Workout/paceTone.test.ts +29 -0
  196. package/src/components/custom/Workout/paceTone.ts +27 -0
  197. package/src/components/custom/Workout/setHeadingKit.tsx +177 -0
  198. package/src/components/custom/index.ts +1 -0
  199. package/src/components/custom/stepper/Stepper.stories.tsx +1 -1
  200. package/src/components/icons/SvgIcon.tsx +50 -0
  201. package/src/components/icons/icons.stories.tsx +122 -0
  202. package/src/components/icons/icons.test.tsx +73 -0
  203. package/src/components/icons/icons.tsx +153 -0
  204. package/src/components/icons/index.ts +4 -0
  205. package/src/components/index.ts +6 -0
  206. package/src/components/shell/BrandLockup.stories.tsx +27 -0
  207. package/src/components/shell/BrandLockup.test.tsx +23 -0
  208. package/src/components/shell/BrandLockup.tsx +47 -0
  209. package/src/components/shell/DashboardShell.stories.tsx +65 -0
  210. package/src/components/shell/DashboardShell.test.tsx +42 -0
  211. package/src/components/shell/DashboardShell.tsx +74 -0
  212. package/src/components/shell/DeviceIndicator.stories.tsx +28 -0
  213. package/src/components/shell/DeviceIndicator.test.tsx +26 -0
  214. package/src/components/shell/DeviceIndicator.tsx +52 -0
  215. package/src/components/shell/DeviceMenu.stories.tsx +47 -0
  216. package/src/components/shell/DeviceMenu.test.tsx +40 -0
  217. package/src/components/shell/DeviceMenu.tsx +65 -0
  218. package/src/components/shell/DeviceRow.stories.tsx +38 -0
  219. package/src/components/shell/DeviceRow.test.tsx +37 -0
  220. package/src/components/shell/DeviceRow.tsx +58 -0
  221. package/src/components/shell/NavItem.stories.tsx +52 -0
  222. package/src/components/shell/NavItem.test.tsx +35 -0
  223. package/src/components/shell/NavItem.tsx +75 -0
  224. package/src/components/shell/README.md +116 -0
  225. package/src/components/shell/SessionRailSpecimen.stories.tsx +753 -0
  226. package/src/components/shell/SessionStatePill.stories.tsx +27 -0
  227. package/src/components/shell/SessionStatePill.test.tsx +25 -0
  228. package/src/components/shell/SessionStatePill.tsx +46 -0
  229. package/src/components/shell/SideNav.stories.tsx +81 -0
  230. package/src/components/shell/SideNav.test.tsx +51 -0
  231. package/src/components/shell/SideNav.tsx +74 -0
  232. package/src/components/shell/TopBar.stories.tsx +49 -0
  233. package/src/components/shell/TopBar.test.tsx +36 -0
  234. package/src/components/shell/TopBar.tsx +89 -0
  235. package/src/components/shell/index.ts +13 -0
  236. package/src/components/ui/alert/Alert.stories.tsx +1 -1
  237. package/src/components/ui/autocomplete/Autocomplete.stories.tsx +1 -1
  238. package/src/components/ui/avatar/Avatar.stories.tsx +1 -1
  239. package/src/components/ui/badge/Badge.stories.tsx +1 -1
  240. package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +1 -1
  241. package/src/components/ui/button/Button.stories.tsx +1 -1
  242. package/src/components/ui/card/Card.stories.tsx +1 -1
  243. package/src/components/ui/checkbox/Checkbox.stories.tsx +1 -1
  244. package/src/components/ui/chip/Chip.stories.tsx +1 -1
  245. package/src/components/ui/collapse/Collapse.stories.tsx +1 -1
  246. package/src/components/ui/data-row/DataRow.stories.tsx +1 -1
  247. package/src/components/ui/divider/Divider.stories.tsx +1 -1
  248. package/src/components/ui/drawer/Drawer.stories.tsx +1 -1
  249. package/src/components/ui/form-field/FormField.stories.tsx +1 -1
  250. package/src/components/ui/help-tip/HelpTip.stories.tsx +1 -1
  251. package/src/components/ui/icon-box/IconBox.stories.tsx +1 -1
  252. package/src/components/ui/index.ts +1 -0
  253. package/src/components/ui/indicator/Indicator.stories.tsx +38 -2
  254. package/src/components/ui/indicator/Indicator.test.tsx +20 -1
  255. package/src/components/ui/indicator/Indicator.tsx +50 -6
  256. package/src/components/ui/indicator/index.ts +1 -1
  257. package/src/components/ui/input/Input.stories.tsx +1 -1
  258. package/src/components/ui/link/Link.stories.tsx +1 -1
  259. package/src/components/ui/list-item/ListItem.stories.tsx +1 -1
  260. package/src/components/ui/menu/Menu.stories.tsx +1 -1
  261. package/src/components/ui/modal/Modal.stories.tsx +1 -1
  262. package/src/components/ui/pill/Pill.stories.tsx +1 -1
  263. package/src/components/ui/popover/Popover.stories.tsx +1 -1
  264. package/src/components/ui/progress/Progress.stories.tsx +1 -1
  265. package/src/components/ui/radio/Radio.stories.tsx +1 -1
  266. package/src/components/ui/section/Section.stories.tsx +1 -1
  267. package/src/components/ui/select/Select.stories.tsx +1 -1
  268. package/src/components/ui/skeleton/Skeleton.stories.tsx +1 -1
  269. package/src/components/ui/spinner/Spinner.stories.tsx +1 -1
  270. package/src/components/ui/spinner/Spinner.tsx +3 -2
  271. package/src/components/ui/stack/Stack.stories.tsx +1 -1
  272. package/src/components/ui/surface/Surface.stories.tsx +1 -1
  273. package/src/components/ui/switch/Switch.stories.tsx +1 -1
  274. package/src/components/ui/tabs/Tabs.stories.tsx +1 -1
  275. package/src/components/ui/tile/Tile.stories.tsx +79 -0
  276. package/src/components/ui/tile/Tile.test.tsx +48 -0
  277. package/src/components/ui/tile/Tile.tsx +64 -0
  278. package/src/components/ui/tile/index.ts +2 -0
  279. package/src/components/ui/toast/Toast.stories.tsx +1 -1
  280. package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +1 -1
  281. package/src/components/ui/toolbar-button/ToolbarButton.tsx +2 -1
  282. package/src/components/ui/tooltip/Tooltip.stories.tsx +1 -1
  283. package/src/examples/react-hook-form/ReactHookForm.stories.tsx +1 -1
  284. package/src/hooks/index.ts +2 -0
  285. package/src/hooks/useTimer.test.ts +150 -0
  286. package/src/hooks/useTimer.ts +77 -0
  287. package/src/index.ts +3 -0
  288. package/src/pages.ts +1 -1
  289. package/src/stories/PresetShowcase.stories.tsx +1 -1
  290. package/src/stories/ThemePresets.stories.tsx +1 -1
  291. package/src/theme/ColorSystem.stories.tsx +323 -0
  292. package/src/theme/Colors.stories.tsx +74 -9
  293. package/src/theme/barrel.ts +1 -0
  294. package/src/theme/config.ts +20 -3
  295. package/src/theme/elevation.stories.tsx +1 -1
  296. package/src/theme/extracted-colors-dataviz.ts +21 -0
  297. package/src/theme/extracted-colors-ui.ts +20 -0
  298. package/src/theme/global.css +69 -53
  299. package/src/theme/gradients.test.ts +25 -0
  300. package/src/theme/gradients.ts +36 -0
  301. package/src/theme/shadows.stories.tsx +1 -1
  302. package/src/theme/tokens/primitives.test.ts +232 -0
  303. package/src/theme/tokens/primitives.ts +238 -87
  304. package/src/theme/tokens/semantic.ts +101 -81
  305. package/src/theme/workout-tokens.ts +26 -21
  306. package/src/utils/avatar-color.ts +3 -3
  307. package/tailwind.config.js +15 -3
@@ -36,3 +36,4 @@ export * from './surface'
36
36
  export * from './list-item'
37
37
  export * from './indicator'
38
38
  export * from './pill'
39
+ export * from './tile'
@@ -3,12 +3,15 @@ import { View } from 'react-native'
3
3
  import { Indicator } from './Indicator'
4
4
 
5
5
  const meta: Meta<typeof Indicator> = {
6
- title: 'Components/Indicator',
6
+ title: 'Components/Atoms/Indicator',
7
7
  component: Indicator,
8
8
  tags: ['autodocs'],
9
9
  argTypes: {
10
10
  size: { control: 'select', options: ['xs', 'sm', 'md', 'lg'] },
11
- color: { control: 'select', options: ['default', 'primary', 'success', 'error', 'warning', 'info'] },
11
+ color: {
12
+ control: 'select',
13
+ options: ['default', 'primary', 'success', 'live', 'error', 'warning', 'info'],
14
+ },
12
15
  glow: { control: 'boolean' },
13
16
  ring: { control: 'boolean' },
14
17
  },
@@ -72,3 +75,36 @@ export const CustomColor: Story = {
72
75
  </View>
73
76
  ),
74
77
  }
78
+
79
+ export const Pulse: Story = {
80
+ render: () => (
81
+ <View className="flex-row gap-6 items-center">
82
+ <Indicator size="md" color="success" pulse />
83
+ <Indicator size="md" color="success" pulse="ping" />
84
+ <Indicator size="md" color="warning" />
85
+ <Indicator size="md" color="default" />
86
+ </View>
87
+ ),
88
+ parameters: {
89
+ docs: {
90
+ description: {
91
+ story:
92
+ 'Opacity pulse (default), expanding-ring pulse (`pulse="ping"`, green for live), solid, dim.',
93
+ },
94
+ },
95
+ },
96
+ }
97
+
98
+ export const Vivid: Story = {
99
+ render: () => (
100
+ <View className="flex-row gap-4 items-center">
101
+ <Indicator size="md" color="success" />
102
+ <Indicator size="md" color="success" />
103
+ <Indicator size="md" color="error" />
104
+ <Indicator size="md" color="error-vivid" />
105
+ </View>
106
+ ),
107
+ parameters: {
108
+ docs: { description: { story: 'Standard status vs the vivid palette (success/error).' } },
109
+ },
110
+ }
@@ -18,7 +18,16 @@ describe('Indicator', () => {
18
18
  })
19
19
 
20
20
  it('renders with all color options', () => {
21
- const colors = ['default', 'primary', 'success', 'error', 'warning', 'info'] as const
21
+ const colors = [
22
+ 'default',
23
+ 'primary',
24
+ 'success',
25
+ 'live',
26
+ 'error',
27
+ 'warning',
28
+ 'info',
29
+ 'error-vivid',
30
+ ] as const
22
31
  colors.forEach((color) => {
23
32
  const { unmount } = render(<Indicator color={color} />)
24
33
  unmount()
@@ -45,6 +54,16 @@ describe('Indicator', () => {
45
54
  expect(container.firstChild).toBeInTheDocument()
46
55
  })
47
56
 
57
+ it('supports pulse prop (opacity)', () => {
58
+ const { container } = render(<Indicator pulse color="success" />)
59
+ expect(container.firstChild).toBeInTheDocument()
60
+ })
61
+
62
+ it('supports the ping pulse variant', () => {
63
+ const { container } = render(<Indicator pulse="ping" color="success" />)
64
+ expect(container.firstChild).toBeInTheDocument()
65
+ })
66
+
48
67
  describe('accessibility', () => {
49
68
  it('has no accessibility violations', async () => {
50
69
  const { container } = render(<Indicator color="success" />)
@@ -1,9 +1,19 @@
1
- import React from 'react'
2
1
  import { View, type ViewProps } from 'react-native'
3
2
  import { cn } from '../../../utils/cn'
4
3
 
5
4
  export type IndicatorSize = 'xs' | 'sm' | 'md' | 'lg'
6
- export type IndicatorColor = 'default' | 'primary' | 'success' | 'error' | 'warning' | 'info'
5
+ export type IndicatorColor =
6
+ | 'default'
7
+ | 'primary'
8
+ | 'success'
9
+ | 'live'
10
+ | 'error'
11
+ | 'warning'
12
+ | 'info'
13
+ | 'error-vivid'
14
+
15
+ /** How a pulsing indicator animates: a subtle opacity fade, or an expanding ring. */
16
+ export type IndicatorPulse = 'opacity' | 'ping'
7
17
 
8
18
  export interface IndicatorProps extends ViewProps {
9
19
  /** Dot size */
@@ -16,6 +26,8 @@ export interface IndicatorProps extends ViewProps {
16
26
  glow?: boolean
17
27
  /** Add a ring border */
18
28
  ring?: boolean
29
+ /** Animate for live/active status. `true` = opacity fade; `'ping'` = expanding ring. */
30
+ pulse?: boolean | IndicatorPulse
19
31
  /** Additional className */
20
32
  className?: string
21
33
  }
@@ -31,9 +43,11 @@ const colorStyles: Record<IndicatorColor, string> = {
31
43
  default: 'bg-text-tertiary',
32
44
  primary: 'bg-brand-primary',
33
45
  success: 'bg-status-success',
46
+ live: 'bg-status-live',
34
47
  error: 'bg-status-error',
35
48
  warning: 'bg-status-warning',
36
49
  info: 'bg-status-info',
50
+ 'error-vivid': 'bg-status-error-vivid',
37
51
  }
38
52
 
39
53
  export function Indicator({
@@ -42,14 +56,39 @@ export function Indicator({
42
56
  customColor,
43
57
  glow = false,
44
58
  ring = false,
59
+ pulse = false,
45
60
  className,
46
61
  style,
47
62
  ...props
48
63
  }: IndicatorProps) {
64
+ const pulseMode: IndicatorPulse | false = pulse === true ? 'opacity' : pulse || false
65
+ const colorClass = !customColor ? colorStyles[color] : undefined
66
+ const colorStyle = customColor ? { backgroundColor: customColor } : undefined
67
+
68
+ // Expanding-ring pulse: a ping layer behind the solid dot (for live/active status).
69
+ if (pulseMode === 'ping') {
70
+ return (
71
+ <View className={cn('relative', sizeStyles[size], className)} style={style} {...props}>
72
+ <View
73
+ className={cn('absolute inset-0 rounded-full animate-ping', colorClass)}
74
+ style={colorStyle}
75
+ />
76
+ <View
77
+ className={cn(
78
+ 'relative rounded-full',
79
+ sizeStyles[size],
80
+ colorClass,
81
+ ring && 'border-2 border-background-base'
82
+ )}
83
+ style={colorStyle}
84
+ />
85
+ </View>
86
+ )
87
+ }
88
+
49
89
  const dynamicStyle = {
50
90
  ...(typeof style === 'object' ? style : {}),
51
- ...(customColor ? { backgroundColor: customColor } : {}),
52
- ...(glow && !customColor ? {} : {}),
91
+ ...(colorStyle ?? {}),
53
92
  ...(glow && customColor ? { boxShadow: `0 0 6px ${customColor}` } : {}),
54
93
  }
55
94
 
@@ -58,12 +97,17 @@ export function Indicator({
58
97
  className={cn(
59
98
  'rounded-full shrink-0',
60
99
  sizeStyles[size],
61
- !customColor && colorStyles[color],
100
+ colorClass,
101
+ pulseMode === 'opacity' && 'animate-pulse',
62
102
  ring && 'border-2 border-background-base',
63
- glow && !customColor && color === 'success' && 'shadow-[0_0_6px_rgba(20,184,166,0.6)]',
103
+ glow &&
104
+ !customColor &&
105
+ (color === 'success' || color === 'live') &&
106
+ 'shadow-[0_0_6px_rgba(46,213,115,0.6)]',
64
107
  glow && !customColor && color === 'error' && 'shadow-[0_0_6px_rgba(239,68,68,0.6)]',
65
108
  glow && !customColor && color === 'warning' && 'shadow-[0_0_6px_rgba(245,158,11,0.6)]',
66
109
  glow && !customColor && color === 'primary' && 'shadow-[0_0_6px_rgba(255,121,0,0.6)]',
110
+ glow && !customColor && color === 'error-vivid' && 'shadow-[0_0_6px_rgba(255,71,87,0.6)]',
67
111
  className
68
112
  )}
69
113
  style={Object.keys(dynamicStyle).length > 0 ? dynamicStyle : style}
@@ -1,2 +1,2 @@
1
1
  export { Indicator } from './Indicator'
2
- export type { IndicatorProps, IndicatorSize, IndicatorColor } from './Indicator'
2
+ export type { IndicatorProps, IndicatorSize, IndicatorColor, IndicatorPulse } from './Indicator'
@@ -3,7 +3,7 @@ import { View, Text } from 'react-native'
3
3
  import { Input, InputGroup, PasswordInput } from './Input'
4
4
 
5
5
  const meta: Meta<typeof Input> = {
6
- title: 'Components/Input',
6
+ title: 'Components/Molecules/Input',
7
7
  component: Input,
8
8
  tags: ['autodocs'],
9
9
  argTypes: {
@@ -3,7 +3,7 @@ import { View } from 'react-native'
3
3
  import { Link } from './Link'
4
4
 
5
5
  const meta: Meta<typeof Link> = {
6
- title: 'Components/Link',
6
+ title: 'Components/Atoms/Link',
7
7
  component: Link,
8
8
  tags: ['autodocs'],
9
9
  argTypes: {
@@ -45,7 +45,7 @@ function ChevronRight({ size = 16 }: { size?: number }) {
45
45
  }
46
46
 
47
47
  const meta: Meta<typeof ListItem> = {
48
- title: 'Components/ListItem',
48
+ title: 'Components/Molecules/ListItem',
49
49
  component: ListItem,
50
50
  tags: ['autodocs'],
51
51
  }
@@ -4,7 +4,7 @@ import { Menu, MenuTrigger, MenuList, MenuItem, MenuDivider, MenuGroup } from '.
4
4
  import { Button, ButtonText } from '../button/Button'
5
5
 
6
6
  const meta: Meta<typeof Menu> = {
7
- title: 'Components/Menu',
7
+ title: 'Components/Molecules/Menu',
8
8
  component: Menu,
9
9
  tags: ['autodocs'],
10
10
  argTypes: {
@@ -13,7 +13,7 @@ import {
13
13
  import { Button, ButtonText } from '../button/Button'
14
14
 
15
15
  const meta: Meta<typeof Modal> = {
16
- title: 'Components/Modal',
16
+ title: 'Components/Organisms/Modal',
17
17
  component: Modal,
18
18
  tags: ['autodocs'],
19
19
  argTypes: {
@@ -4,7 +4,7 @@ import { Pill } from './Pill'
4
4
  import { Indicator } from '../indicator'
5
5
 
6
6
  const meta: Meta<typeof Pill> = {
7
- title: 'Components/Pill',
7
+ title: 'Components/Atoms/Pill',
8
8
  component: Pill,
9
9
  tags: ['autodocs'],
10
10
  argTypes: {
@@ -4,7 +4,7 @@ import { Popover, PopoverTrigger, PopoverContent, PopoverCloseButton } from './P
4
4
  import { Button, ButtonText } from '../button/Button'
5
5
 
6
6
  const meta: Meta<typeof Popover> = {
7
- title: 'Components/Popover',
7
+ title: 'Components/Molecules/Popover',
8
8
  component: Popover,
9
9
  tags: ['autodocs'],
10
10
  argTypes: {
@@ -3,7 +3,7 @@ import { View, Text } from 'react-native'
3
3
  import { Progress, CircularProgress, ProgressSteps } from './Progress'
4
4
 
5
5
  const meta: Meta<typeof Progress> = {
6
- title: 'Components/Progress',
6
+ title: 'Components/Molecules/Progress',
7
7
  component: Progress,
8
8
  tags: ['autodocs'],
9
9
  argTypes: {
@@ -4,7 +4,7 @@ import { View, Text } from 'react-native'
4
4
  import { Radio, RadioGroup } from './Radio'
5
5
 
6
6
  const meta: Meta<typeof RadioGroup> = {
7
- title: 'Components/Radio',
7
+ title: 'Components/Molecules/Radio',
8
8
  component: RadioGroup,
9
9
  tags: ['autodocs'],
10
10
  argTypes: {
@@ -3,7 +3,7 @@ import { View, Text } from 'react-native'
3
3
  import { Section, SectionHeader, SectionContent } from './Section'
4
4
 
5
5
  const meta: Meta<typeof Section> = {
6
- title: 'Components/Section',
6
+ title: 'Components/Atoms/Section',
7
7
  component: Section,
8
8
  tags: ['autodocs'],
9
9
  }
@@ -12,7 +12,7 @@ const basicOptions: SelectOption[] = [
12
12
  ]
13
13
 
14
14
  const meta: Meta<typeof Select> = {
15
- title: 'Components/Select',
15
+ title: 'Components/Molecules/Select',
16
16
  component: Select,
17
17
  tags: ['autodocs'],
18
18
  argTypes: {
@@ -9,7 +9,7 @@ import {
9
9
  } from './Skeleton'
10
10
 
11
11
  const meta: Meta<typeof Skeleton> = {
12
- title: 'Components/Skeleton',
12
+ title: 'Components/Atoms/Skeleton',
13
13
  component: Skeleton,
14
14
  tags: ['autodocs'],
15
15
  argTypes: {
@@ -3,7 +3,7 @@ import { View } from 'react-native'
3
3
  import { Spinner } from './Spinner'
4
4
 
5
5
  const meta: Meta<typeof Spinner> = {
6
- title: 'Components/Spinner',
6
+ title: 'Components/Atoms/Spinner',
7
7
  component: Spinner,
8
8
  tags: ['autodocs'],
9
9
  argTypes: {
@@ -1,6 +1,7 @@
1
1
  import React from 'react'
2
2
  import { ActivityIndicator, View, type ViewProps } from 'react-native'
3
3
  import { cn } from '../../../utils/cn'
4
+ import { SPINNER_PRIMARY, SPINNER_SECONDARY } from '../../../theme/extracted-colors-ui'
4
5
 
5
6
  export type SpinnerSize = 'sm' | 'md' | 'lg' | 'xl'
6
7
  export type SpinnerColor = 'primary' | 'secondary' | 'white' | 'default'
@@ -24,8 +25,8 @@ const sizeMap: Record<SpinnerSize, 'small' | 'large'> = {
24
25
  }
25
26
 
26
27
  const colorMap: Record<SpinnerColor, string> = {
27
- primary: '#5048E5',
28
- secondary: '#10B981',
28
+ primary: SPINNER_PRIMARY,
29
+ secondary: SPINNER_SECONDARY,
29
30
  white: '#FFFFFF',
30
31
  default: '#6B7280',
31
32
  }
@@ -3,7 +3,7 @@ import { View, Text } from 'react-native'
3
3
  import { Stack, HStack, VStack } from './Stack'
4
4
 
5
5
  const meta: Meta<typeof Stack> = {
6
- title: 'Components/Stack',
6
+ title: 'Components/Atoms/Stack',
7
7
  component: Stack,
8
8
  tags: ['autodocs'],
9
9
  argTypes: {
@@ -4,7 +4,7 @@ import { Surface } from './Surface'
4
4
  import type { ElevationLevel } from '../../../theme/elevation'
5
5
 
6
6
  const meta: Meta<typeof Surface> = {
7
- title: 'Components/Surface',
7
+ title: 'Components/Atoms/Surface',
8
8
  component: Surface,
9
9
  tags: ['autodocs'],
10
10
  argTypes: {
@@ -3,7 +3,7 @@ import { View } from 'react-native'
3
3
  import { Switch } from './Switch'
4
4
 
5
5
  const meta: Meta<typeof Switch> = {
6
- title: 'Components/Switch',
6
+ title: 'Components/Molecules/Switch',
7
7
  component: Switch,
8
8
  tags: ['autodocs'],
9
9
  argTypes: {
@@ -3,7 +3,7 @@ import { View, Text } from 'react-native'
3
3
  import { Tabs, TabList, Tab, TabPanels, TabPanel } from './Tabs'
4
4
 
5
5
  const meta: Meta<typeof Tabs> = {
6
- title: 'Components/Tabs',
6
+ title: 'Components/Molecules/Tabs',
7
7
  component: Tabs,
8
8
  tags: ['autodocs'],
9
9
  argTypes: {
@@ -0,0 +1,79 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import { View } from 'react-native'
3
+ import { Tile } from './Tile'
4
+ import { getSemanticColors } from '../../../theme'
5
+
6
+ const ACCENT = getSemanticColors('dark')['status-warning']
7
+ const SUCCESS = getSemanticColors('dark')['status-success']
8
+
9
+ const meta: Meta<typeof Tile> = {
10
+ title: 'Components/Atoms/Tile',
11
+ component: Tile,
12
+ tags: ['autodocs'],
13
+ parameters: {
14
+ docs: {
15
+ description: {
16
+ component: '**Atom.** A compact card preset; grouped by SessionMetrics / ScheduleTiles.',
17
+ },
18
+ },
19
+ },
20
+ argTypes: {
21
+ label: { control: 'text', description: 'Uppercase micro-label' },
22
+ value: { control: 'text', description: 'Primary mono value' },
23
+ valueColor: { control: 'color', description: 'Tints the value' },
24
+ align: {
25
+ control: 'radio',
26
+ options: ['center', 'start'],
27
+ description: 'Content alignment',
28
+ },
29
+ },
30
+ }
31
+
32
+ export default meta
33
+ type Story = StoryObj<typeof Tile>
34
+
35
+ export const Default: Story = {
36
+ args: {
37
+ label: 'Volume',
38
+ value: '76%',
39
+ align: 'center',
40
+ },
41
+ }
42
+
43
+ export const Row: Story = {
44
+ render: () => (
45
+ <View style={{ flexDirection: 'row', gap: 4, width: 246 }}>
46
+ <Tile label="Volume" value="76%" />
47
+ <Tile label="Load" value="7.3k" />
48
+ <Tile label="Fatigue" value="MOD" valueColor={ACCENT} />
49
+ </View>
50
+ ),
51
+ }
52
+
53
+ export const ScheduleTiles: Story = {
54
+ render: () => (
55
+ <View style={{ flexDirection: 'row', gap: 4, width: 246 }}>
56
+ <Tile label="Date" value="Jul 10" />
57
+ <Tile label="Time" value="6:30 PM" />
58
+ <Tile label="Until" value="5h" valueColor={ACCENT} />
59
+ </View>
60
+ ),
61
+ }
62
+
63
+ export const ValueColors: Story = {
64
+ render: () => (
65
+ <View style={{ flexDirection: 'row', gap: 4, width: 246 }}>
66
+ <Tile label="Default" value="12" />
67
+ <Tile label="Success" value="OK" valueColor={SUCCESS} />
68
+ <Tile label="Warning" value="MOD" valueColor={ACCENT} />
69
+ </View>
70
+ ),
71
+ }
72
+
73
+ export const StartAligned: Story = {
74
+ args: {
75
+ label: 'Program',
76
+ value: 'Pull A',
77
+ align: 'start',
78
+ },
79
+ }
@@ -0,0 +1,48 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { render, screen } from '@testing-library/react'
3
+ import { axe } from 'jest-axe'
4
+ import { Tile } from './Tile'
5
+
6
+ describe('Tile', () => {
7
+ it('renders label and value', () => {
8
+ render(<Tile label="Volume" value="76%" />)
9
+ expect(screen.getByText('Volume')).toBeInTheDocument()
10
+ expect(screen.getByText('76%')).toBeInTheDocument()
11
+ })
12
+
13
+ it('applies valueColor to the value text', () => {
14
+ render(<Tile label="Fatigue" value="MOD" valueColor="#F5A623" />)
15
+ const value = screen.getByText('MOD')
16
+ expect(value).toHaveStyle({ color: '#F5A623' })
17
+ })
18
+
19
+ it('does not set an inline color when valueColor is omitted', () => {
20
+ render(<Tile label="Volume" value="76%" />)
21
+ const value = screen.getByText('76%')
22
+ expect(value.style.color).toBe('')
23
+ })
24
+
25
+ it('renders with start alignment', () => {
26
+ render(<Tile label="Program" value="Pull A" align="start" />)
27
+ expect(screen.getByText('Program')).toBeInTheDocument()
28
+ expect(screen.getByText('Pull A')).toBeInTheDocument()
29
+ })
30
+
31
+ it('applies custom className', () => {
32
+ const { container } = render(<Tile label="Load" value="7.3k" className="custom-class" />)
33
+ expect(container.firstChild).toBeInTheDocument()
34
+ })
35
+
36
+ it('passes additional props through', () => {
37
+ render(<Tile label="Load" value="7.3k" testID="tile-test" />)
38
+ expect(screen.getByText('7.3k')).toBeInTheDocument()
39
+ })
40
+
41
+ describe('accessibility', () => {
42
+ it('has no accessibility violations', async () => {
43
+ const { container } = render(<Tile label="Volume" value="76%" />)
44
+ const results = await axe(container)
45
+ expect(results).toHaveNoViolations()
46
+ })
47
+ })
48
+ })
@@ -0,0 +1,64 @@
1
+ import { View, Text, type ViewProps } from 'react-native'
2
+ import { cn } from '../../../utils/cn'
3
+
4
+ export interface TileProps extends ViewProps {
5
+ /** Uppercase micro-label shown above the value */
6
+ label: string
7
+ /** Primary value, rendered in a bold mono face */
8
+ value: string
9
+ /** Tints the value (status/accent hex or CSS color); defaults to primary text */
10
+ valueColor?: string
11
+ /** Content alignment within the tile (default: 'center') */
12
+ align?: 'center' | 'start'
13
+ /** Additional className */
14
+ className?: string
15
+ }
16
+
17
+ /**
18
+ * Tile — a compact label-over-value stat card.
19
+ *
20
+ * A rounded raised box with an uppercase micro-label stacked over a bold mono
21
+ * value. Fills its flex slot so a row of Tiles reads as an even HStack.
22
+ *
23
+ * @example
24
+ * <Tile label="Volume" value="76%" />
25
+ * <Tile label="Fatigue" value="MOD" valueColor="#F5A623" />
26
+ */
27
+ export function Tile({
28
+ label,
29
+ value,
30
+ valueColor,
31
+ align = 'center',
32
+ className,
33
+ ...props
34
+ }: TileProps) {
35
+ const isCenter = align === 'center'
36
+ return (
37
+ <View
38
+ className={cn(
39
+ 'flex-1 rounded-md bg-surface-raised px-1.5 py-2',
40
+ isCenter ? 'items-center' : 'items-start',
41
+ className
42
+ )}
43
+ {...props}
44
+ >
45
+ <Text
46
+ className={cn(
47
+ 'text-[10px] font-bold uppercase tracking-wider text-text-tertiary',
48
+ isCenter ? 'text-center' : 'text-left'
49
+ )}
50
+ >
51
+ {label}
52
+ </Text>
53
+ <Text
54
+ className={cn(
55
+ 'mt-0.5 font-mono text-sm font-bold text-text-primary',
56
+ isCenter ? 'text-center' : 'text-left'
57
+ )}
58
+ style={valueColor ? { color: valueColor } : undefined}
59
+ >
60
+ {value}
61
+ </Text>
62
+ </View>
63
+ )
64
+ }
@@ -0,0 +1,2 @@
1
+ export { Tile } from './Tile'
2
+ export type { TileProps } from './Tile'
@@ -4,7 +4,7 @@ import { Toast, ToastProvider, useToast } from './Toast'
4
4
  import { Button, ButtonText } from '../button'
5
5
 
6
6
  const meta: Meta<typeof Toast> = {
7
- title: 'Components/Toast',
7
+ title: 'Components/Molecules/Toast',
8
8
  component: Toast,
9
9
  tags: ['autodocs'],
10
10
  argTypes: {
@@ -4,7 +4,7 @@ import { View, Text } from 'react-native'
4
4
  import { ToolbarButton, ToolbarButtonGroup } from './ToolbarButton'
5
5
 
6
6
  const meta: Meta<typeof ToolbarButton> = {
7
- title: 'Components/ToolbarButton',
7
+ title: 'Components/Molecules/ToolbarButton',
8
8
  component: ToolbarButton,
9
9
  tags: ['autodocs'],
10
10
  argTypes: {
@@ -2,6 +2,7 @@ import React, { useState, useCallback, createContext, useContext } from 'react'
2
2
  import { View, Text, Pressable, type ViewProps, StyleSheet, Platform } from 'react-native'
3
3
  import { cn } from '../../../utils/cn'
4
4
  import { neumorphicShadows, getHoverColors } from '../../../theme'
5
+ import { resolveColor } from '../../../theme/resolve-color'
5
6
 
6
7
  export type ToolbarButtonVariant = 'default' | 'neumorphic'
7
8
  export type ToolbarButtonSize = 'sm' | 'md' | 'lg'
@@ -129,7 +130,7 @@ export function ToolbarButton({
129
130
 
130
131
  // Determine icon color based on state
131
132
  const getIconColor = () => {
132
- if (isActive === true) return '#FF7900' // brand-primary
133
+ if (isActive === true) return resolveColor('brand-primary')
133
134
  return showActive ? '#FFFFFF' : '#D1D1D1'
134
135
  }
135
136
 
@@ -4,7 +4,7 @@ import { Tooltip } from './Tooltip'
4
4
  import { Button, ButtonText } from '../button'
5
5
 
6
6
  const meta: Meta<typeof Tooltip> = {
7
- title: 'Components/Tooltip',
7
+ title: 'Components/Molecules/Tooltip',
8
8
  component: Tooltip,
9
9
  tags: ['autodocs'],
10
10
  argTypes: {
@@ -10,7 +10,7 @@ import { RhfContactForm, type ContactFormValues } from './RhfContactForm'
10
10
  * `Controller` render-prop idiom. See `ControlledFields.tsx` for the adapters.
11
11
  */
12
12
  const meta: Meta<typeof RhfContactForm> = {
13
- title: 'Examples/React Hook Form',
13
+ title: 'Lab/Recipes/React Hook Form',
14
14
  component: RhfContactForm,
15
15
  tags: ['autodocs'],
16
16
  parameters: {
@@ -0,0 +1,2 @@
1
+ export { useTimer, formatDuration } from './useTimer'
2
+ export type { UseTimerOptions, TimerState, TimerMode } from './useTimer'