@sincpro/mobile-ui 3.0.1 → 3.2.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 (314) hide show
  1. package/dist/Actions/Actions.Button.d.ts +6 -6
  2. package/dist/Actions/Actions.Button.js +650 -180
  3. package/dist/Actions/Actions.Dock.d.ts +1 -1
  4. package/dist/Actions/Actions.Dock.js +297 -18
  5. package/dist/Actions/Actions.FAB.js +100 -12
  6. package/dist/Actions/Actions.FilterButton.d.ts +17 -0
  7. package/dist/Actions/Actions.FilterButton.js +263 -33
  8. package/dist/Actions/Actions.IconButton.d.ts +1 -0
  9. package/dist/Actions/Actions.IconButton.js +196 -33
  10. package/dist/Actions/index.js +13 -13
  11. package/dist/Dialog/Confirmation.context.js +82 -16
  12. package/dist/Dialog/Dialog.ActionSheet.js +200 -41
  13. package/dist/Dialog/Dialog.Confirmation.js +168 -31
  14. package/dist/Dialog/Dialog.EditValue.js +437 -62
  15. package/dist/Dialog/Dialog.Sheet.d.ts +7 -3
  16. package/dist/Dialog/Dialog.Sheet.js +267 -15
  17. package/dist/Dialog/OverlayHost.d.ts +24 -0
  18. package/dist/Dialog/OverlayHost.js +230 -0
  19. package/dist/Dialog/SheetSurface.d.ts +43 -0
  20. package/dist/Dialog/SheetSurface.js +495 -0
  21. package/dist/Dialog/dialog-actions.d.ts +17 -0
  22. package/dist/Dialog/dialog-actions.js +59 -0
  23. package/dist/Dialog/index.d.ts +4 -1
  24. package/dist/Dialog/index.js +8 -7
  25. package/dist/Dialog/overlay.store.d.ts +24 -0
  26. package/dist/Dialog/overlay.store.js +59 -0
  27. package/dist/Dialog/requestConfirmation.js +20 -10
  28. package/dist/Display/Display.Accordion.js +185 -13
  29. package/dist/Display/Display.AmountSummary.js +206 -15
  30. package/dist/Display/Display.Avatar.js +93 -31
  31. package/dist/Display/Display.AvatarGroup.js +132 -17
  32. package/dist/Display/Display.Badge.d.ts +18 -2
  33. package/dist/Display/Display.Badge.js +117 -49
  34. package/dist/Display/Display.Card.js +163 -26
  35. package/dist/Display/Display.Carousel.js +459 -105
  36. package/dist/Display/Display.Chip.d.ts +4 -1
  37. package/dist/Display/Display.Chip.js +165 -31
  38. package/dist/Display/Display.CircleButton.d.ts +1 -0
  39. package/dist/Display/Display.CircleButton.js +86 -27
  40. package/dist/Display/Display.CopyableText.js +289 -66
  41. package/dist/Display/Display.CountRecords.js +60 -3
  42. package/dist/Display/Display.DataTable.js +123 -13
  43. package/dist/Display/Display.Date.js +243 -33
  44. package/dist/Display/Display.Divider.d.ts +10 -10
  45. package/dist/Display/Display.Divider.js +67 -20
  46. package/dist/Display/Display.ExpoImage.js +97 -8
  47. package/dist/Display/Display.GradientSurface.d.ts +9 -4
  48. package/dist/Display/Display.GradientSurface.js +196 -46
  49. package/dist/Display/Display.Icon.js +129 -15
  50. package/dist/Display/Display.IconBadge.js +95 -25
  51. package/dist/Display/Display.Image.d.ts +7 -1
  52. package/dist/Display/Display.Image.js +344 -39
  53. package/dist/Display/Display.KeyValue.js +90 -5
  54. package/dist/Display/Display.ListGroup.js +181 -9
  55. package/dist/Display/Display.ListItem.js +324 -28
  56. package/dist/Display/Display.Logo.js +84 -29
  57. package/dist/Display/Display.MediaCard.js +125 -4
  58. package/dist/Display/Display.MediaEmbed.d.ts +0 -5
  59. package/dist/Display/Display.MediaEmbed.js +170 -26
  60. package/dist/Display/Display.MenuButton.d.ts +2 -2
  61. package/dist/Display/Display.MenuButton.js +380 -94
  62. package/dist/Display/Display.MenuCard.js +122 -18
  63. package/dist/Display/Display.MenuGrid.js +43 -3
  64. package/dist/Display/Display.Monetary.js +90 -5
  65. package/dist/Display/Display.Pattern.js +306 -47
  66. package/dist/Display/Display.PriceTag.js +117 -14
  67. package/dist/Display/Display.ProductCard.js +154 -4
  68. package/dist/Display/Display.Rating.js +92 -11
  69. package/dist/Display/Display.SearchHistory.js +132 -6
  70. package/dist/Display/Display.SectionHeader.d.ts +5 -1
  71. package/dist/Display/Display.SectionHeader.js +125 -12
  72. package/dist/Display/Display.Stat.js +62 -3
  73. package/dist/Display/Display.StatCard.js +131 -7
  74. package/dist/Display/Display.StatusBadge.d.ts +29 -0
  75. package/dist/Display/Display.StatusBadge.js +68 -0
  76. package/dist/Display/Display.StatusDot.js +93 -18
  77. package/dist/Display/Display.Story.js +285 -35
  78. package/dist/Display/Display.StoryTray.js +354 -34
  79. package/dist/Display/Display.StoryViewer.js +571 -72
  80. package/dist/Display/Display.SwipeableRow.js +148 -20
  81. package/dist/Display/Display.Tag.d.ts +4 -1
  82. package/dist/Display/Display.Tag.js +66 -4
  83. package/dist/Display/Display.Timeline.js +102 -13
  84. package/dist/Display/Display.Tooltip.js +122 -26
  85. package/dist/Display/chevron.d.ts +6 -0
  86. package/dist/Display/chevron.js +32 -0
  87. package/dist/Display/count-badge.d.ts +21 -0
  88. package/dist/Display/count-badge.js +124 -0
  89. package/dist/Display/group-title.d.ts +10 -0
  90. package/dist/Display/group-title.js +53 -0
  91. package/dist/Display/icon-font.d.ts +3 -0
  92. package/dist/Display/icon-font.ios.d.ts +3 -0
  93. package/dist/Display/icon-font.ios.js +4 -0
  94. package/dist/Display/icon-font.js +7 -0
  95. package/dist/Display/icon-glyphs.d.ts +2 -0
  96. package/dist/Display/icon-glyphs.js +87 -0
  97. package/dist/Display/icon-map.d.ts +15 -11
  98. package/dist/Display/icon-map.js +344 -84
  99. package/dist/Display/icon-registry.d.ts +21 -0
  100. package/dist/Display/icon-registry.js +21 -0
  101. package/dist/Display/index.d.ts +6 -1
  102. package/dist/Display/index.js +53 -48
  103. package/dist/Display/pill.d.ts +16 -0
  104. package/dist/Display/pill.js +47 -0
  105. package/dist/Feedback/ErrorBoundary.js +35 -23
  106. package/dist/Feedback/Feedback.Banner.js +202 -24
  107. package/dist/Feedback/Feedback.DomainSplashScreen.js +246 -70
  108. package/dist/Feedback/Feedback.Empty.js +54 -3
  109. package/dist/Feedback/Feedback.EmptyState.js +114 -5
  110. package/dist/Feedback/Feedback.Error.d.ts +1 -1
  111. package/dist/Feedback/Feedback.Error.js +130 -5
  112. package/dist/Feedback/Feedback.Loading.d.ts +1 -1
  113. package/dist/Feedback/Feedback.Loading.js +70 -3
  114. package/dist/Feedback/Feedback.PermissionCard.js +161 -5
  115. package/dist/Feedback/Feedback.Progress.js +199 -41
  116. package/dist/Feedback/Feedback.ProgressCircle.js +184 -12
  117. package/dist/Feedback/Feedback.Skeleton.js +149 -36
  118. package/dist/Feedback/Feedback.Spinner.js +407 -79
  119. package/dist/Feedback/Feedback.Splash.js +240 -33
  120. package/dist/Feedback/Feedback.State.d.ts +17 -0
  121. package/dist/Feedback/Feedback.State.js +104 -0
  122. package/dist/Feedback/Toast.context.js +883 -167
  123. package/dist/Feedback/index.d.ts +3 -0
  124. package/dist/Feedback/index.js +15 -13
  125. package/dist/Form/Form.Checkbox.js +212 -22
  126. package/dist/Form/Form.ControlledCheckbox.d.ts +1 -1
  127. package/dist/Form/Form.ControlledCheckbox.js +46 -4
  128. package/dist/Form/Form.ControlledDropdown.d.ts +1 -1
  129. package/dist/Form/Form.ControlledDropdown.js +62 -4
  130. package/dist/Form/Form.ControlledInput.d.ts +4 -3
  131. package/dist/Form/Form.ControlledInput.js +72 -4
  132. package/dist/Form/Form.ControlledMonetaryInput.d.ts +6 -3
  133. package/dist/Form/Form.ControlledMonetaryInput.js +50 -4
  134. package/dist/Form/Form.ControlledRadioButton.d.ts +2 -2
  135. package/dist/Form/Form.ControlledRadioButton.js +45 -4
  136. package/dist/Form/Form.DatePicker.js +507 -107
  137. package/dist/Form/Form.Dropdown.js +303 -52
  138. package/dist/Form/Form.ErrorSummary.js +236 -44
  139. package/dist/Form/Form.Field.d.ts +24 -0
  140. package/dist/Form/Form.Field.js +55 -0
  141. package/dist/Form/Form.FractionalQuantityInput.js +508 -96
  142. package/dist/Form/Form.Input.d.ts +17 -3
  143. package/dist/Form/Form.Input.js +497 -29
  144. package/dist/Form/Form.KeyboardForm.d.ts +22 -8
  145. package/dist/Form/Form.KeyboardForm.js +83 -8
  146. package/dist/Form/Form.KeyboardToolbarHost.d.ts +14 -0
  147. package/dist/Form/Form.KeyboardToolbarHost.js +223 -0
  148. package/dist/Form/Form.MonetaryInput.js +164 -17
  149. package/dist/Form/Form.PickerField.d.ts +31 -0
  150. package/dist/Form/Form.PickerField.js +539 -0
  151. package/dist/Form/Form.PinInput.js +150 -15
  152. package/dist/Form/Form.QuantitySelector.js +229 -25
  153. package/dist/Form/Form.RadioButton.d.ts +1 -0
  154. package/dist/Form/Form.RadioButton.js +26 -29
  155. package/dist/Form/Form.Select.d.ts +2 -1
  156. package/dist/Form/Form.Select.js +388 -60
  157. package/dist/Form/Form.Slider.js +133 -10
  158. package/dist/Form/Form.Switch.js +99 -4
  159. package/dist/Form/choice-row.d.ts +15 -0
  160. package/dist/Form/choice-row.js +236 -0
  161. package/dist/Form/index.d.ts +11 -6
  162. package/dist/Form/index.js +26 -27
  163. package/dist/Form/input-text.d.ts +7 -0
  164. package/dist/Form/input-text.js +18 -0
  165. package/dist/Navigation/Navigation.AppBar.d.ts +3 -4
  166. package/dist/Navigation/Navigation.AppBar.js +535 -99
  167. package/dist/Navigation/Navigation.BottomNav.js +363 -98
  168. package/dist/Navigation/Navigation.CollapsingHeader.d.ts +28 -13
  169. package/dist/Navigation/Navigation.CollapsingHeader.js +369 -36
  170. package/dist/Navigation/Navigation.FloatingBar.js +71 -11
  171. package/dist/Navigation/Navigation.Menu.js +309 -95
  172. package/dist/Navigation/Navigation.MenuSection.js +80 -11
  173. package/dist/Navigation/Navigation.RowItem.d.ts +4 -2
  174. package/dist/Navigation/Navigation.RowItem.js +93 -8
  175. package/dist/Navigation/Navigation.Search.js +241 -27
  176. package/dist/Navigation/Navigation.SearchBar.d.ts +7 -1
  177. package/dist/Navigation/Navigation.SearchBar.js +218 -29
  178. package/dist/Navigation/Navigation.SearchSuggestions.js +77 -3
  179. package/dist/Navigation/Navigation.SegmentedControl.js +152 -14
  180. package/dist/Navigation/Navigation.Steps.js +105 -11
  181. package/dist/Navigation/Navigation.Tabs.js +449 -41
  182. package/dist/Navigation/index.d.ts +9 -4
  183. package/dist/Navigation/index.js +13 -13
  184. package/dist/Typography/Typography.Heading.js +33 -3
  185. package/dist/Typography/Typography.SelectableText.js +95 -7
  186. package/dist/Typography/Typography.Text.d.ts +4 -2
  187. package/dist/Typography/Typography.Text.js +217 -7
  188. package/dist/Typography/index.js +3 -3
  189. package/dist/branding.js +29 -13
  190. package/dist/icons/AddItemIcon.js +76 -2
  191. package/dist/icons/ArrangeCircle.js +82 -2
  192. package/dist/icons/ArrowDownIcon.js +63 -3
  193. package/dist/icons/ArrowUpIcon.js +63 -3
  194. package/dist/icons/BoxIcon.js +64 -2
  195. package/dist/icons/BoxTimeIcon.js +63 -2
  196. package/dist/icons/CalendarIcon.js +145 -2
  197. package/dist/icons/CardIcon.js +80 -2
  198. package/dist/icons/CashierIcon.js +39 -2
  199. package/dist/icons/CreditIcon.js +76 -3
  200. package/dist/icons/DuoIcon.js +75 -2
  201. package/dist/icons/HomeIcon.js +39 -2
  202. package/dist/icons/HomeIconOutlined.js +53 -2
  203. package/dist/icons/LogoIcon.js +87 -2
  204. package/dist/icons/MoneyReceiveIcon.js +86 -2
  205. package/dist/icons/PinIcon.js +39 -2
  206. package/dist/icons/ProfileIcon.js +47 -2
  207. package/dist/icons/SettingsIcon.js +114 -2
  208. package/dist/icons/SinpeIcon.js +60 -2
  209. package/dist/icons/ToggleOffIcon.js +53 -2
  210. package/dist/icons/WalletMoneyIcon.js +81 -2
  211. package/dist/index.d.ts +1 -0
  212. package/dist/index.js +8 -2
  213. package/dist/layouts/AuthContainer.d.ts +4 -5
  214. package/dist/layouts/AuthContainer.js +231 -7
  215. package/dist/layouts/BottomInset.js +4 -1
  216. package/dist/layouts/Container.d.ts +2 -2
  217. package/dist/layouts/Container.js +45 -3
  218. package/dist/layouts/GradientContainer.js +37 -5
  219. package/dist/layouts/PlainLayout.js +19 -3
  220. package/dist/layouts/ScrollContainer.d.ts +14 -9
  221. package/dist/layouts/ScrollContainer.js +125 -4
  222. package/dist/layouts/edges.d.ts +2 -0
  223. package/dist/layouts/edges.js +1 -0
  224. package/dist/primitives/AspectRatio.js +75 -3
  225. package/dist/primitives/Box.js +45 -3
  226. package/dist/primitives/Pressable.js +95 -5
  227. package/dist/primitives/Row.js +84 -13
  228. package/dist/primitives/Spacer.js +42 -5
  229. package/dist/primitives/Stack.js +66 -7
  230. package/dist/primitives/index.js +1 -1
  231. package/dist/theme/active.d.ts +2 -0
  232. package/dist/theme/active.js +33 -18
  233. package/dist/theme/build.js +118 -87
  234. package/dist/theme/color.js +87 -87
  235. package/dist/theme/components/ThemedActivityIndicator.d.ts +2 -15
  236. package/dist/theme/components/ThemedActivityIndicator.js +35 -3
  237. package/dist/theme/components/index.js +1 -0
  238. package/dist/theme/contrast.js +32 -33
  239. package/dist/theme/css-vars.js +71 -75
  240. package/dist/theme/dark.js +116 -97
  241. package/dist/theme/default.js +116 -97
  242. package/dist/theme/define.js +16 -12
  243. package/dist/theme/index.d.ts +1 -0
  244. package/dist/theme/index.js +7 -6
  245. package/dist/theme/on-dark-surface.d.ts +12 -0
  246. package/dist/theme/on-dark-surface.js +13 -0
  247. package/dist/theme/theme.js +1 -0
  248. package/dist/theme/tw.js +3 -1
  249. package/dist/theme/types.js +0 -18
  250. package/dist/theme/typography.d.ts +1 -1
  251. package/dist/theme/typography.js +337 -303
  252. package/dist/theme/useTheme.d.ts +4 -0
  253. package/dist/theme/useTheme.js +7 -3
  254. package/dist/theme/useThemeVars.d.ts +9 -0
  255. package/dist/theme/useThemeVars.js +23 -0
  256. package/dist/theme/validate.js +175 -166
  257. package/dist/tokens/index.js +1 -1
  258. package/dist/tokens/motion.js +30 -13
  259. package/dist/utils/async.d.ts +14 -0
  260. package/dist/utils/async.js +22 -0
  261. package/dist/utils/criteria.js +66 -85
  262. package/dist/utils/date.d.ts +8 -2
  263. package/dist/utils/date.js +63 -50
  264. package/dist/utils/image.js +8 -8
  265. package/dist/utils/intl.d.ts +1 -0
  266. package/dist/utils/intl.js +13 -0
  267. package/dist/utils/keys.js +3 -5
  268. package/dist/utils/monetary.d.ts +1 -0
  269. package/dist/utils/monetary.js +28 -24
  270. package/dist/utils/progress.js +2 -3
  271. package/dist/utils/quantity.js +25 -26
  272. package/dist/utils/reorder.js +11 -20
  273. package/dist/utils/search.d.ts +4 -0
  274. package/dist/utils/search.js +12 -0
  275. package/dist/utils/serializer.js +38 -38
  276. package/dist/utils/useReducedMotion.js +32 -30
  277. package/dist/views/AppSplashView.js +117 -3
  278. package/dist/views/AuthFormView.d.ts +4 -5
  279. package/dist/views/AuthFormView.js +119 -11
  280. package/dist/views/FormViewV2.context.js +75 -23
  281. package/dist/views/FormViewV2.d.ts +4 -5
  282. package/dist/views/FormViewV2.js +658 -92
  283. package/dist/views/ListViewV2.context.js +91 -47
  284. package/dist/views/ListViewV2.d.ts +84 -43
  285. package/dist/views/ListViewV2.js +1350 -200
  286. package/dist/views/Wizard/Wizard.context.js +248 -84
  287. package/dist/views/Wizard/Wizard.js +260 -67
  288. package/dist/views/types/IListView.js +34 -31
  289. package/dist/widgets/CollectionOptions.js +557 -138
  290. package/dist/widgets/FilterBar.js +159 -7
  291. package/dist/widgets/FilterBuilder.js +375 -102
  292. package/dist/widgets/JSONViewer.js +275 -129
  293. package/dist/widgets/PromoBanner.js +336 -42
  294. package/dist/widgets/PromoModal.d.ts +2 -2
  295. package/dist/widgets/PromoModal.js +389 -62
  296. package/dist/widgets/QueryChoice.js +150 -8
  297. package/dist/widgets/QuickActions.js +120 -31
  298. package/dist/widgets/ReorderableList.js +399 -128
  299. package/dist/widgets/TimeZoneSelectorModal.js +458 -51
  300. package/dist/widgets/index.js +3 -3
  301. package/package.json +6 -1
  302. package/tailwind.preset.js +7 -5
  303. package/dist/Form/Form.InputField.d.ts +0 -10
  304. package/dist/Form/Form.InputField.js +0 -38
  305. package/dist/Form/Form.KeyboardToolbar.d.ts +0 -22
  306. package/dist/Form/Form.KeyboardToolbar.js +0 -66
  307. package/dist/Form/Form.PasswordInput.d.ts +0 -9
  308. package/dist/Form/Form.PasswordInput.js +0 -17
  309. package/dist/Form/Form.TextInput.d.ts +0 -8
  310. package/dist/Form/Form.TextInput.js +0 -8
  311. package/dist/primitives/BaseButton.d.ts +0 -150
  312. package/dist/primitives/BaseButton.js +0 -229
  313. package/dist/utils/useSheetTranslation.d.ts +0 -5
  314. package/dist/utils/useSheetTranslation.js +0 -23
@@ -1,3 +1,7 @@
1
1
  import type { ThemeTokens } from "./types.js";
2
+ /**
3
+ * The active theme, as a new object after every `setActiveTheme`. Values memoized on it
4
+ * (by hand or by React Compiler) recompute on a light/dark switch.
5
+ */
2
6
  export declare function useTheme(): ThemeTokens;
3
7
  export declare const theme: ThemeTokens;
@@ -1,7 +1,11 @@
1
- import { getActiveTheme, getThemeVersion, subscribeTheme, } from "./active.js";
1
+ import { getActiveTheme, getThemeSnapshot, subscribeTheme } from "./active.js";
2
2
  import { useSyncExternalStore } from "react";
3
+
4
+ /**
5
+ * The active theme, as a new object after every `setActiveTheme`. Values memoized on it
6
+ * (by hand or by React Compiler) recompute on a light/dark switch.
7
+ */
3
8
  export function useTheme() {
4
- useSyncExternalStore(subscribeTheme, getThemeVersion, getThemeVersion);
5
- return getActiveTheme();
9
+ return useSyncExternalStore(subscribeTheme, getThemeSnapshot, getThemeSnapshot);
6
10
  }
7
11
  export const theme = getActiveTheme();
@@ -0,0 +1,9 @@
1
+ import { vars } from "nativewind";
2
+ type ThemeVars = ReturnType<typeof vars>;
3
+ /**
4
+ * NativeWind CSS variables for the active theme, as one stable object per
5
+ * theme version. A new object on every render would make every `className`
6
+ * consumer below re-resolve its styles.
7
+ */
8
+ export declare function useThemeVars(): ThemeVars;
9
+ export {};
@@ -0,0 +1,23 @@
1
+ import { getActiveTheme, getThemeVersion, subscribeTheme } from "./active.js";
2
+ import { themeToVars } from "./css-vars.js";
3
+ import { vars } from "nativewind";
4
+ import { useSyncExternalStore } from "react";
5
+ let cachedVersion = -1;
6
+ let cachedVars;
7
+ function currentVars() {
8
+ const version = getThemeVersion();
9
+ if (!cachedVars || cachedVersion !== version) {
10
+ cachedVersion = version;
11
+ cachedVars = vars(themeToVars(getActiveTheme()));
12
+ }
13
+ return cachedVars;
14
+ }
15
+
16
+ /**
17
+ * NativeWind CSS variables for the active theme, as one stable object per
18
+ * theme version. A new object on every render would make every `className`
19
+ * consumer below re-resolve its styles.
20
+ */
21
+ export function useThemeVars() {
22
+ return useSyncExternalStore(subscribeTheme, currentVars, currentVars);
23
+ }
@@ -9,61 +9,77 @@ import { contrastRatio, resolveOnColor } from "./contrast.js";
9
9
  * tests intentionally pass partial themes). Can also be called directly.
10
10
  */
11
11
  export function validateTheme(theme) {
12
- const issues = [];
13
- if (typeof theme.name !== "string" || theme.name.trim() === "") {
14
- issues.push({ path: "name", message: "Theme must have a non-empty `name`." });
12
+ const issues = [];
13
+ if (typeof theme.name !== "string" || theme.name.trim() === "") {
14
+ issues.push({
15
+ path: "name",
16
+ message: "Theme must have a non-empty `name`."
17
+ });
18
+ }
19
+ if (!isHex(theme.primary)) {
20
+ issues.push({
21
+ path: "primary",
22
+ message: `Invalid hex color: ${String(theme.primary)}`
23
+ });
24
+ }
25
+ if (!isHex(theme.accent)) {
26
+ issues.push({
27
+ path: "accent",
28
+ message: `Invalid hex color: ${String(theme.accent)}`
29
+ });
30
+ }
31
+ if (!isHex(theme.bg?.page)) {
32
+ issues.push({
33
+ path: "bg.page",
34
+ message: `Invalid hex color: ${String(theme.bg?.page)}`
35
+ });
36
+ }
37
+ if (!isHex(theme.text?.primary)) {
38
+ issues.push({
39
+ path: "text.primary",
40
+ message: `Invalid hex color: ${String(theme.text?.primary)}`
41
+ });
42
+ }
43
+ if (theme.pattern) {
44
+ if (!isFiniteSize(theme.pattern.size)) {
45
+ issues.push({
46
+ path: "pattern.size",
47
+ message: `Tile size must be > 0 (got ${String(theme.pattern.size)}).`
48
+ });
15
49
  }
16
- if (!isHex(theme.primary)) {
17
- issues.push({ path: "primary", message: `Invalid hex color: ${String(theme.primary)}` });
18
- }
19
- if (!isHex(theme.accent)) {
20
- issues.push({ path: "accent", message: `Invalid hex color: ${String(theme.accent)}` });
21
- }
22
- if (!isHex(theme.bg?.page)) {
23
- issues.push({ path: "bg.page", message: `Invalid hex color: ${String(theme.bg?.page)}` });
24
- }
25
- if (!isHex(theme.text?.primary)) {
26
- issues.push({
27
- path: "text.primary",
28
- message: `Invalid hex color: ${String(theme.text?.primary)}`,
29
- });
30
- }
31
- if (theme.pattern) {
32
- if (!isFiniteSize(theme.pattern.size)) {
33
- issues.push({
34
- path: "pattern.size",
35
- message: `Tile size must be > 0 (got ${String(theme.pattern.size)}).`,
36
- });
37
- }
38
- }
39
- if (theme.shadow) {
40
- issues.push(...validateShadow("shadow.sm", theme.shadow.sm));
41
- issues.push(...validateShadow("shadow.md", theme.shadow.md));
42
- issues.push(...validateShadow("shadow.lg", theme.shadow.lg));
43
- }
44
- issues.push(...validateContrast(theme));
45
- issues.push(...validateSemanticDistinct(theme));
46
- return issues;
50
+ }
51
+ if (theme.shadow) {
52
+ issues.push(...validateShadow("shadow.sm", theme.shadow.sm));
53
+ issues.push(...validateShadow("shadow.md", theme.shadow.md));
54
+ issues.push(...validateShadow("shadow.lg", theme.shadow.lg));
55
+ }
56
+ issues.push(...validateContrast(theme));
57
+ issues.push(...validateSemanticDistinct(theme));
58
+ return issues;
47
59
  }
48
60
  function isHex(value) {
49
- return typeof value === "string" && /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(value.trim());
61
+ return typeof value === "string" && /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(value.trim());
50
62
  }
51
63
  function isFiniteSize(n) {
52
- return typeof n === "number" && Number.isFinite(n) && n > 0;
64
+ return typeof n === "number" && Number.isFinite(n) && n > 0;
53
65
  }
54
66
  function validateShadow(path, s) {
55
- const out = [];
56
- if (typeof s.shadowColor !== "string" || s.shadowColor.trim() === "") {
57
- out.push({ path: `${path}.shadowColor`, message: "shadowColor must be a string." });
58
- }
59
- if (typeof s.shadowOpacity !== "number" || s.shadowOpacity < 0 || s.shadowOpacity > 1) {
60
- out.push({
61
- path: `${path}.shadowOpacity`,
62
- message: `shadowOpacity must be in [0, 1] (got ${s.shadowOpacity}).`,
63
- });
64
- }
65
- return out;
67
+ const out = [];
68
+ if (typeof s.shadowColor !== "string" || s.shadowColor.trim() === "") {
69
+ out.push({
70
+ path: `${path}.shadowColor`,
71
+ message: "shadowColor must be a string."
72
+ });
73
+ }
74
+ if (typeof s.shadowOpacity !== "number" || s.shadowOpacity < 0 || s.shadowOpacity > 1) {
75
+ out.push({
76
+ path: `${path}.shadowOpacity`,
77
+ message: `shadowOpacity must be in [0, 1] (got ${s.shadowOpacity}).`
78
+ });
79
+ }
80
+ return out;
66
81
  }
82
+
67
83
  /**
68
84
  * WCAG 2.1 contrast validation. Checks the P0 on-color pairs that every consumer
69
85
  * relies on (CTA text/bg, focus ring, body text on surfaces). Flags any pair below the
@@ -71,130 +87,123 @@ function validateShadow(path, s) {
71
87
  * entries — never throws — so the dev-mode warning stays non-fatal.
72
88
  */
73
89
  function validateContrast(theme) {
74
- const issues = [];
75
- const checks = [
76
- {
77
- fg: theme.text?.onPrimary,
78
- bg: theme.primary,
79
- min: 4.5,
80
- label: "text.onPrimary on primary (CTA)",
81
- },
82
- {
83
- fg: theme.text?.onSecondary,
84
- bg: theme.secondary,
85
- min: 4.5,
86
- label: "text.onSecondary on secondary",
87
- },
88
- {
89
- fg: theme.text?.onAccent,
90
- bg: theme.accent,
91
- min: 4.5,
92
- label: "text.onAccent on accent",
93
- },
94
- { fg: theme.border?.focus, bg: theme.bg?.page, min: 3, label: "border.focus on bg.page" },
95
- { fg: theme.border?.focus, bg: theme.bg?.card, min: 3, label: "border.focus on bg.card" },
96
- {
97
- fg: theme.text?.onDanger,
98
- bg: theme.danger,
99
- min: 4.5,
100
- label: "text.onDanger on danger",
101
- },
102
- {
103
- fg: theme.text?.onSuccess,
104
- bg: theme.success,
105
- min: 4.5,
106
- label: "text.onSuccess on success",
107
- },
108
- {
109
- fg: theme.text?.accent,
110
- bg: theme.bg?.card,
111
- min: 4.5,
112
- label: "text.accent on bg.card (brand text)",
113
- },
114
- {
115
- fg: theme.text?.accent,
116
- bg: theme.bg?.page,
117
- min: 4.5,
118
- label: "text.accent on bg.page (brand text)",
119
- },
120
- {
121
- fg: theme.container?.onPrimary,
122
- bg: theme.container?.primary,
123
- min: 4.5,
124
- label: "container.onPrimary on container.primary",
125
- },
126
- {
127
- fg: theme.container?.onSuccess,
128
- bg: theme.container?.success,
129
- min: 4.5,
130
- label: "container.onSuccess on container.success",
131
- },
132
- {
133
- fg: theme.container?.onDanger,
134
- bg: theme.container?.danger,
135
- min: 4.5,
136
- label: "container.onDanger on container.danger",
137
- },
138
- ];
139
- for (const c of checks) {
140
- if (!isHex(c.fg) || !isHex(c.bg))
141
- continue;
142
- const ratio = contrastRatio(c.fg, c.bg);
143
- if (ratio < c.min) {
144
- let message = `Contrast ratio ${ratio.toFixed(2)}:1 is below WCAG AA minimum ${c.min}:1 for body/UI text.`;
145
- if (c.label.includes("onPrimary") && isHex(theme.primary)) {
146
- const suggested = resolveOnColor(theme.primary);
147
- message += ` Use resolveOnColor(primary) → ${suggested}.`;
148
- }
149
- issues.push({ path: c.label, message });
150
- }
90
+ const issues = [];
91
+ const checks = [{
92
+ fg: theme.text?.onPrimary,
93
+ bg: theme.primary,
94
+ min: 4.5,
95
+ label: "text.onPrimary on primary (CTA)"
96
+ }, {
97
+ fg: theme.text?.onSecondary,
98
+ bg: theme.secondary,
99
+ min: 4.5,
100
+ label: "text.onSecondary on secondary"
101
+ }, {
102
+ fg: theme.text?.onAccent,
103
+ bg: theme.accent,
104
+ min: 4.5,
105
+ label: "text.onAccent on accent"
106
+ }, {
107
+ fg: theme.border?.focus,
108
+ bg: theme.bg?.page,
109
+ min: 3,
110
+ label: "border.focus on bg.page"
111
+ }, {
112
+ fg: theme.border?.focus,
113
+ bg: theme.bg?.card,
114
+ min: 3,
115
+ label: "border.focus on bg.card"
116
+ }, {
117
+ fg: theme.text?.onDanger,
118
+ bg: theme.danger,
119
+ min: 4.5,
120
+ label: "text.onDanger on danger"
121
+ }, {
122
+ fg: theme.text?.onSuccess,
123
+ bg: theme.success,
124
+ min: 4.5,
125
+ label: "text.onSuccess on success"
126
+ }, {
127
+ fg: theme.text?.accent,
128
+ bg: theme.bg?.card,
129
+ min: 4.5,
130
+ label: "text.accent on bg.card (brand text)"
131
+ }, {
132
+ fg: theme.text?.accent,
133
+ bg: theme.bg?.page,
134
+ min: 4.5,
135
+ label: "text.accent on bg.page (brand text)"
136
+ }, {
137
+ fg: theme.container?.onPrimary,
138
+ bg: theme.container?.primary,
139
+ min: 4.5,
140
+ label: "container.onPrimary on container.primary"
141
+ }, {
142
+ fg: theme.container?.onSuccess,
143
+ bg: theme.container?.success,
144
+ min: 4.5,
145
+ label: "container.onSuccess on container.success"
146
+ }, {
147
+ fg: theme.container?.onDanger,
148
+ bg: theme.container?.danger,
149
+ min: 4.5,
150
+ label: "container.onDanger on container.danger"
151
+ }];
152
+ for (const c of checks) {
153
+ if (!isHex(c.fg) || !isHex(c.bg)) continue;
154
+ const ratio = contrastRatio(c.fg, c.bg);
155
+ if (ratio < c.min) {
156
+ let message = `Contrast ratio ${ratio.toFixed(2)}:1 is below WCAG AA minimum ${c.min}:1 for body/UI text.`;
157
+ if (c.label.includes("onPrimary") && isHex(theme.primary)) {
158
+ const suggested = resolveOnColor(theme.primary);
159
+ message += ` Use resolveOnColor(primary) → ${suggested}.`;
160
+ }
161
+ issues.push({
162
+ path: c.label,
163
+ message
164
+ });
151
165
  }
152
- return issues;
166
+ }
167
+ return issues;
153
168
  }
169
+
154
170
  /** Roles that must stay visually distinct for maintainable state semantics. */
155
171
  function validateSemanticDistinct(theme) {
156
- const issues = [];
157
- const pairs = [
158
- ["bg.disabled vs bg.muted", theme.bg?.disabled, theme.bg?.muted],
159
- ["text.disabled vs text.muted", theme.text?.disabled, theme.text?.muted],
160
- ["icon.disabled vs icon.secondary", theme.icon?.disabled, theme.icon?.secondary],
161
- ];
162
- for (const [label, a, b] of pairs) {
163
- if (!isHex(a) || !isHex(b))
164
- continue;
165
- if (a.trim().toLowerCase() === b.trim().toLowerCase()) {
166
- issues.push({
167
- path: label,
168
- message: `Tokens share the same hex (${a}). Keep roles distinct so disabled/muted states stay maintainable.`,
169
- });
170
- }
172
+ const issues = [];
173
+ const pairs = [["bg.disabled vs bg.muted", theme.bg?.disabled, theme.bg?.muted], ["text.disabled vs text.muted", theme.text?.disabled, theme.text?.muted], ["icon.disabled vs icon.secondary", theme.icon?.disabled, theme.icon?.secondary]];
174
+ for (const [label, a, b] of pairs) {
175
+ if (!isHex(a) || !isHex(b)) continue;
176
+ if (a.trim().toLowerCase() === b.trim().toLowerCase()) {
177
+ issues.push({
178
+ path: label,
179
+ message: `Tokens share the same hex (${a}). Keep roles distinct so disabled/muted states stay maintainable.`
180
+ });
181
+ }
182
+ }
183
+ if (theme.state) {
184
+ for (const key of ["hover", "press", "focus", "drag"]) {
185
+ const v = theme.state[key];
186
+ if (typeof v !== "number" || v < 0 || v > 1) {
187
+ issues.push({
188
+ path: `state.${key}`,
189
+ message: `State layer opacity must be in [0, 1] (got ${String(v)}).`
190
+ });
191
+ }
171
192
  }
172
- if (theme.state) {
173
- for (const key of ["hover", "press", "focus", "drag"]) {
174
- const v = theme.state[key];
175
- if (typeof v !== "number" || v < 0 || v > 1) {
176
- issues.push({
177
- path: `state.${key}`,
178
- message: `State layer opacity must be in [0, 1] (got ${String(v)}).`,
179
- });
180
- }
181
- }
193
+ }
194
+ if (theme.material) {
195
+ if (typeof theme.material.opacity === "number" && (theme.material.opacity < 0 || theme.material.opacity > 1)) {
196
+ issues.push({
197
+ path: "material.opacity",
198
+ message: `material.opacity must be in [0, 1] (got ${theme.material.opacity}).`
199
+ });
182
200
  }
183
- if (theme.material) {
184
- if (typeof theme.material.opacity === "number" &&
185
- (theme.material.opacity < 0 || theme.material.opacity > 1)) {
186
- issues.push({
187
- path: "material.opacity",
188
- message: `material.opacity must be in [0, 1] (got ${theme.material.opacity}).`,
189
- });
190
- }
191
- if (typeof theme.material.blur === "number" &&
192
- (!Number.isFinite(theme.material.blur) || theme.material.blur < 0)) {
193
- issues.push({
194
- path: "material.blur",
195
- message: `material.blur must be ≥ 0 (got ${theme.material.blur}).`,
196
- });
197
- }
201
+ if (typeof theme.material.blur === "number" && (!Number.isFinite(theme.material.blur) || theme.material.blur < 0)) {
202
+ issues.push({
203
+ path: "material.blur",
204
+ message: `material.blur must be ≥ 0 (got ${theme.material.blur}).`
205
+ });
198
206
  }
199
- return issues;
207
+ }
208
+ return issues;
200
209
  }
@@ -1,2 +1,2 @@
1
1
  export { motion } from "./motion.js";
2
- export { duration, easing, spring, } from "./motion.js";
2
+ export { duration, easing, spring } from "./motion.js";
@@ -2,24 +2,41 @@
2
2
  * Motion tokens — durations and curves for consistent animations (reanimated, LayoutAnimation).
3
3
  * Single source of the DS "rhythm": enter/exit, press, theme transitions, sheets, skeletons.
4
4
  */
5
+
5
6
  export const duration = {
6
- instant: 0,
7
- fast: 120,
8
- normal: 200,
9
- slow: 320,
10
- slower: 480,
7
+ instant: 0,
8
+ fast: 120,
9
+ normal: 200,
10
+ slow: 320,
11
+ slower: 480
11
12
  };
12
13
  /** Bezier curves (x1,y1,x2,y2) for Easing.bezier / CSS-like. */
13
14
  export const easing = {
14
- standard: [0.2, 0, 0, 1],
15
- decelerate: [0, 0, 0, 1],
16
- accelerate: [0.3, 0, 1, 1],
17
- emphasized: [0.2, 0, 0, 1],
15
+ standard: [0.2, 0, 0, 1],
16
+ decelerate: [0, 0, 0, 1],
17
+ accelerate: [0.3, 0, 1, 1],
18
+ emphasized: [0.2, 0, 0, 1]
18
19
  };
19
20
  /** Config for reanimated withSpring (spring presets). */
20
21
  export const spring = {
21
- default: { damping: 20, stiffness: 200, mass: 1 },
22
- gentle: { damping: 24, stiffness: 120, mass: 1 },
23
- snappy: { damping: 18, stiffness: 300, mass: 1 },
22
+ default: {
23
+ damping: 20,
24
+ stiffness: 200,
25
+ mass: 1
26
+ },
27
+ gentle: {
28
+ damping: 24,
29
+ stiffness: 120,
30
+ mass: 1
31
+ },
32
+ snappy: {
33
+ damping: 18,
34
+ stiffness: 300,
35
+ mass: 1
36
+ }
37
+ };
38
+ export const motion = {
39
+ duration,
40
+ easing,
41
+ spring
24
42
  };
25
- export const motion = { duration, easing, spring };
@@ -0,0 +1,14 @@
1
+ export type Attempt<T> = {
2
+ ok: true;
3
+ value: T;
4
+ } | {
5
+ ok: false;
6
+ error: unknown;
7
+ };
8
+ /**
9
+ * Runs `task` and reports how it ended instead of throwing. Component handlers use it
10
+ * so they need no `try/finally`: React Compiler skips any component whose body has one.
11
+ */
12
+ export declare function attempt<T>(task: () => T | Promise<T>): Promise<Attempt<T>>;
13
+ /** The error's own message when it has one, else the fallback shown to the user. */
14
+ export declare function errorMessage(cause: unknown, fallback: string): string;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Runs `task` and reports how it ended instead of throwing. Component handlers use it
3
+ * so they need no `try/finally`: React Compiler skips any component whose body has one.
4
+ */
5
+ export async function attempt(task) {
6
+ try {
7
+ return {
8
+ ok: true,
9
+ value: await task()
10
+ };
11
+ } catch (error) {
12
+ return {
13
+ ok: false,
14
+ error
15
+ };
16
+ }
17
+ }
18
+
19
+ /** The error's own message when it has one, else the fallback shown to the user. */
20
+ export function errorMessage(cause, fallback) {
21
+ return cause instanceof Error && cause.message.trim() ? cause.message : fallback;
22
+ }