@oxyhq/bloom 0.27.0 → 0.28.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 (253) hide show
  1. package/lib/commonjs/animated-check/AnimatedCheck.js +162 -0
  2. package/lib/commonjs/animated-check/AnimatedCheck.js.map +1 -0
  3. package/lib/commonjs/animated-check/index.js +13 -0
  4. package/lib/commonjs/animated-check/index.js.map +1 -0
  5. package/lib/commonjs/dialog/placement.js +9 -7
  6. package/lib/commonjs/dialog/placement.js.map +1 -1
  7. package/lib/commonjs/fill/index.js.map +1 -1
  8. package/lib/commonjs/hooks/index.js +20 -0
  9. package/lib/commonjs/hooks/index.js.map +1 -1
  10. package/lib/commonjs/hooks/useGutters.js +106 -0
  11. package/lib/commonjs/hooks/useGutters.js.map +1 -0
  12. package/lib/commonjs/hooks/useHaptics.js +69 -0
  13. package/lib/commonjs/hooks/useHaptics.js.map +1 -0
  14. package/lib/commonjs/hooks/useInteractionState.js +8 -2
  15. package/lib/commonjs/hooks/useInteractionState.js.map +1 -1
  16. package/lib/commonjs/index.js +155 -63
  17. package/lib/commonjs/index.js.map +1 -1
  18. package/lib/commonjs/index.web.js +155 -63
  19. package/lib/commonjs/index.web.js.map +1 -1
  20. package/lib/commonjs/media-inset-border/MediaInsetBorder.js +50 -0
  21. package/lib/commonjs/media-inset-border/MediaInsetBorder.js.map +1 -0
  22. package/lib/commonjs/media-inset-border/index.js +13 -0
  23. package/lib/commonjs/media-inset-border/index.js.map +1 -0
  24. package/lib/commonjs/motion/ScreenTransition.js +46 -0
  25. package/lib/commonjs/motion/ScreenTransition.js.map +1 -0
  26. package/lib/commonjs/motion/index.js +32 -0
  27. package/lib/commonjs/motion/index.js.map +1 -0
  28. package/lib/commonjs/motion/motion.js +93 -0
  29. package/lib/commonjs/motion/motion.js.map +1 -0
  30. package/lib/commonjs/pressable-scale/PressableScale.js +75 -0
  31. package/lib/commonjs/pressable-scale/PressableScale.js.map +1 -0
  32. package/lib/commonjs/pressable-scale/index.js +13 -0
  33. package/lib/commonjs/pressable-scale/index.js.map +1 -0
  34. package/lib/commonjs/pressable-with-hover/PressableWithHover.js +36 -0
  35. package/lib/commonjs/pressable-with-hover/PressableWithHover.js.map +1 -0
  36. package/lib/commonjs/pressable-with-hover/index.js +13 -0
  37. package/lib/commonjs/pressable-with-hover/index.js.map +1 -0
  38. package/lib/commonjs/styles/breakpoints.js +24 -0
  39. package/lib/commonjs/styles/breakpoints.js.map +1 -0
  40. package/lib/commonjs/styles/index.js +7 -0
  41. package/lib/commonjs/styles/index.js.map +1 -1
  42. package/lib/commonjs/subtle-hover/SubtleHover.js +69 -0
  43. package/lib/commonjs/subtle-hover/SubtleHover.js.map +1 -0
  44. package/lib/commonjs/subtle-hover/index.js +13 -0
  45. package/lib/commonjs/subtle-hover/index.js.map +1 -0
  46. package/lib/commonjs/theme/build-theme.js +2 -0
  47. package/lib/commonjs/theme/build-theme.js.map +1 -1
  48. package/lib/commonjs/theme/gradients.js +39 -0
  49. package/lib/commonjs/theme/gradients.js.map +1 -0
  50. package/lib/commonjs/theme/index.js +7 -0
  51. package/lib/commonjs/theme/index.js.map +1 -1
  52. package/lib/module/animated-check/AnimatedCheck.js +157 -0
  53. package/lib/module/animated-check/AnimatedCheck.js.map +1 -0
  54. package/lib/module/animated-check/index.js +4 -0
  55. package/lib/module/animated-check/index.js.map +1 -0
  56. package/lib/module/dialog/placement.js +9 -7
  57. package/lib/module/dialog/placement.js.map +1 -1
  58. package/lib/module/fill/index.js.map +1 -1
  59. package/lib/module/hooks/index.js +2 -0
  60. package/lib/module/hooks/index.js.map +1 -1
  61. package/lib/module/hooks/useGutters.js +103 -0
  62. package/lib/module/hooks/useGutters.js.map +1 -0
  63. package/lib/module/hooks/useHaptics.js +65 -0
  64. package/lib/module/hooks/useHaptics.js.map +1 -0
  65. package/lib/module/hooks/useInteractionState.js +10 -3
  66. package/lib/module/hooks/useInteractionState.js.map +1 -1
  67. package/lib/module/index.js +10 -1
  68. package/lib/module/index.js.map +1 -1
  69. package/lib/module/index.web.js +10 -1
  70. package/lib/module/index.web.js.map +1 -1
  71. package/lib/module/media-inset-border/MediaInsetBorder.js +47 -0
  72. package/lib/module/media-inset-border/MediaInsetBorder.js.map +1 -0
  73. package/lib/module/media-inset-border/index.js +4 -0
  74. package/lib/module/media-inset-border/index.js.map +1 -0
  75. package/lib/module/motion/ScreenTransition.js +40 -0
  76. package/lib/module/motion/ScreenTransition.js.map +1 -0
  77. package/lib/module/motion/index.js +5 -0
  78. package/lib/module/motion/index.js.map +1 -0
  79. package/lib/module/motion/motion.js +87 -0
  80. package/lib/module/motion/motion.js.map +1 -0
  81. package/lib/module/pressable-scale/PressableScale.js +72 -0
  82. package/lib/module/pressable-scale/PressableScale.js.map +1 -0
  83. package/lib/module/pressable-scale/index.js +4 -0
  84. package/lib/module/pressable-scale/index.js.map +1 -0
  85. package/lib/module/pressable-with-hover/PressableWithHover.js +31 -0
  86. package/lib/module/pressable-with-hover/PressableWithHover.js.map +1 -0
  87. package/lib/module/pressable-with-hover/index.js +4 -0
  88. package/lib/module/pressable-with-hover/index.js.map +1 -0
  89. package/lib/module/styles/breakpoints.js +20 -0
  90. package/lib/module/styles/breakpoints.js.map +1 -0
  91. package/lib/module/styles/index.js +1 -0
  92. package/lib/module/styles/index.js.map +1 -1
  93. package/lib/module/subtle-hover/SubtleHover.js +66 -0
  94. package/lib/module/subtle-hover/SubtleHover.js.map +1 -0
  95. package/lib/module/subtle-hover/index.js +4 -0
  96. package/lib/module/subtle-hover/index.js.map +1 -0
  97. package/lib/module/theme/build-theme.js +2 -0
  98. package/lib/module/theme/build-theme.js.map +1 -1
  99. package/lib/module/theme/gradients.js +35 -0
  100. package/lib/module/theme/gradients.js.map +1 -0
  101. package/lib/module/theme/index.js +1 -0
  102. package/lib/module/theme/index.js.map +1 -1
  103. package/lib/typescript/commonjs/animated-check/AnimatedCheck.d.ts +18 -0
  104. package/lib/typescript/commonjs/animated-check/AnimatedCheck.d.ts.map +1 -0
  105. package/lib/typescript/commonjs/animated-check/index.d.ts +3 -0
  106. package/lib/typescript/commonjs/animated-check/index.d.ts.map +1 -0
  107. package/lib/typescript/commonjs/dialog/placement.d.ts +8 -7
  108. package/lib/typescript/commonjs/dialog/placement.d.ts.map +1 -1
  109. package/lib/typescript/commonjs/fill/index.d.ts +2 -2
  110. package/lib/typescript/commonjs/fill/index.d.ts.map +1 -1
  111. package/lib/typescript/commonjs/hooks/index.d.ts +4 -0
  112. package/lib/typescript/commonjs/hooks/index.d.ts.map +1 -1
  113. package/lib/typescript/commonjs/hooks/useGutters.d.ts +32 -0
  114. package/lib/typescript/commonjs/hooks/useGutters.d.ts.map +1 -0
  115. package/lib/typescript/commonjs/hooks/useHaptics.d.ts +28 -0
  116. package/lib/typescript/commonjs/hooks/useHaptics.d.ts.map +1 -0
  117. package/lib/typescript/commonjs/hooks/useInteractionState.d.ts +6 -0
  118. package/lib/typescript/commonjs/hooks/useInteractionState.d.ts.map +1 -1
  119. package/lib/typescript/commonjs/index.d.ts +16 -0
  120. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  121. package/lib/typescript/commonjs/index.web.d.ts +16 -0
  122. package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
  123. package/lib/typescript/commonjs/media-inset-border/MediaInsetBorder.d.ts +26 -0
  124. package/lib/typescript/commonjs/media-inset-border/MediaInsetBorder.d.ts.map +1 -0
  125. package/lib/typescript/commonjs/media-inset-border/index.d.ts +3 -0
  126. package/lib/typescript/commonjs/media-inset-border/index.d.ts.map +1 -0
  127. package/lib/typescript/commonjs/motion/ScreenTransition.d.ts +33 -0
  128. package/lib/typescript/commonjs/motion/ScreenTransition.d.ts.map +1 -0
  129. package/lib/typescript/commonjs/motion/index.d.ts +4 -0
  130. package/lib/typescript/commonjs/motion/index.d.ts.map +1 -0
  131. package/lib/typescript/commonjs/motion/motion.d.ts +24 -0
  132. package/lib/typescript/commonjs/motion/motion.d.ts.map +1 -0
  133. package/lib/typescript/commonjs/pressable-scale/PressableScale.d.ts +21 -0
  134. package/lib/typescript/commonjs/pressable-scale/PressableScale.d.ts.map +1 -0
  135. package/lib/typescript/commonjs/pressable-scale/index.d.ts +3 -0
  136. package/lib/typescript/commonjs/pressable-scale/index.d.ts.map +1 -0
  137. package/lib/typescript/commonjs/pressable-with-hover/PressableWithHover.d.ts +18 -0
  138. package/lib/typescript/commonjs/pressable-with-hover/PressableWithHover.d.ts.map +1 -0
  139. package/lib/typescript/commonjs/pressable-with-hover/index.d.ts +3 -0
  140. package/lib/typescript/commonjs/pressable-with-hover/index.d.ts.map +1 -0
  141. package/lib/typescript/commonjs/styles/breakpoints.d.ts +18 -0
  142. package/lib/typescript/commonjs/styles/breakpoints.d.ts.map +1 -0
  143. package/lib/typescript/commonjs/styles/index.d.ts +2 -0
  144. package/lib/typescript/commonjs/styles/index.d.ts.map +1 -1
  145. package/lib/typescript/commonjs/subtle-hover/SubtleHover.d.ts +32 -0
  146. package/lib/typescript/commonjs/subtle-hover/SubtleHover.d.ts.map +1 -0
  147. package/lib/typescript/commonjs/subtle-hover/index.d.ts +3 -0
  148. package/lib/typescript/commonjs/subtle-hover/index.d.ts.map +1 -0
  149. package/lib/typescript/commonjs/theme/build-theme.d.ts.map +1 -1
  150. package/lib/typescript/commonjs/theme/gradients.d.ts +12 -0
  151. package/lib/typescript/commonjs/theme/gradients.d.ts.map +1 -0
  152. package/lib/typescript/commonjs/theme/index.d.ts +2 -1
  153. package/lib/typescript/commonjs/theme/index.d.ts.map +1 -1
  154. package/lib/typescript/commonjs/theme/types.d.ts +11 -0
  155. package/lib/typescript/commonjs/theme/types.d.ts.map +1 -1
  156. package/lib/typescript/module/animated-check/AnimatedCheck.d.ts +18 -0
  157. package/lib/typescript/module/animated-check/AnimatedCheck.d.ts.map +1 -0
  158. package/lib/typescript/module/animated-check/index.d.ts +3 -0
  159. package/lib/typescript/module/animated-check/index.d.ts.map +1 -0
  160. package/lib/typescript/module/dialog/placement.d.ts +8 -7
  161. package/lib/typescript/module/dialog/placement.d.ts.map +1 -1
  162. package/lib/typescript/module/fill/index.d.ts +2 -2
  163. package/lib/typescript/module/fill/index.d.ts.map +1 -1
  164. package/lib/typescript/module/hooks/index.d.ts +4 -0
  165. package/lib/typescript/module/hooks/index.d.ts.map +1 -1
  166. package/lib/typescript/module/hooks/useGutters.d.ts +32 -0
  167. package/lib/typescript/module/hooks/useGutters.d.ts.map +1 -0
  168. package/lib/typescript/module/hooks/useHaptics.d.ts +28 -0
  169. package/lib/typescript/module/hooks/useHaptics.d.ts.map +1 -0
  170. package/lib/typescript/module/hooks/useInteractionState.d.ts +6 -0
  171. package/lib/typescript/module/hooks/useInteractionState.d.ts.map +1 -1
  172. package/lib/typescript/module/index.d.ts +16 -0
  173. package/lib/typescript/module/index.d.ts.map +1 -1
  174. package/lib/typescript/module/index.web.d.ts +16 -0
  175. package/lib/typescript/module/index.web.d.ts.map +1 -1
  176. package/lib/typescript/module/media-inset-border/MediaInsetBorder.d.ts +26 -0
  177. package/lib/typescript/module/media-inset-border/MediaInsetBorder.d.ts.map +1 -0
  178. package/lib/typescript/module/media-inset-border/index.d.ts +3 -0
  179. package/lib/typescript/module/media-inset-border/index.d.ts.map +1 -0
  180. package/lib/typescript/module/motion/ScreenTransition.d.ts +33 -0
  181. package/lib/typescript/module/motion/ScreenTransition.d.ts.map +1 -0
  182. package/lib/typescript/module/motion/index.d.ts +4 -0
  183. package/lib/typescript/module/motion/index.d.ts.map +1 -0
  184. package/lib/typescript/module/motion/motion.d.ts +24 -0
  185. package/lib/typescript/module/motion/motion.d.ts.map +1 -0
  186. package/lib/typescript/module/pressable-scale/PressableScale.d.ts +21 -0
  187. package/lib/typescript/module/pressable-scale/PressableScale.d.ts.map +1 -0
  188. package/lib/typescript/module/pressable-scale/index.d.ts +3 -0
  189. package/lib/typescript/module/pressable-scale/index.d.ts.map +1 -0
  190. package/lib/typescript/module/pressable-with-hover/PressableWithHover.d.ts +18 -0
  191. package/lib/typescript/module/pressable-with-hover/PressableWithHover.d.ts.map +1 -0
  192. package/lib/typescript/module/pressable-with-hover/index.d.ts +3 -0
  193. package/lib/typescript/module/pressable-with-hover/index.d.ts.map +1 -0
  194. package/lib/typescript/module/styles/breakpoints.d.ts +18 -0
  195. package/lib/typescript/module/styles/breakpoints.d.ts.map +1 -0
  196. package/lib/typescript/module/styles/index.d.ts +2 -0
  197. package/lib/typescript/module/styles/index.d.ts.map +1 -1
  198. package/lib/typescript/module/subtle-hover/SubtleHover.d.ts +32 -0
  199. package/lib/typescript/module/subtle-hover/SubtleHover.d.ts.map +1 -0
  200. package/lib/typescript/module/subtle-hover/index.d.ts +3 -0
  201. package/lib/typescript/module/subtle-hover/index.d.ts.map +1 -0
  202. package/lib/typescript/module/theme/build-theme.d.ts.map +1 -1
  203. package/lib/typescript/module/theme/gradients.d.ts +12 -0
  204. package/lib/typescript/module/theme/gradients.d.ts.map +1 -0
  205. package/lib/typescript/module/theme/index.d.ts +2 -1
  206. package/lib/typescript/module/theme/index.d.ts.map +1 -1
  207. package/lib/typescript/module/theme/types.d.ts +11 -0
  208. package/lib/typescript/module/theme/types.d.ts.map +1 -1
  209. package/package.json +72 -1
  210. package/src/__tests__/AnimatedCheck.test.tsx +31 -0
  211. package/src/__tests__/MediaInsetBorder.test.tsx +36 -0
  212. package/src/__tests__/Motion.test.tsx +58 -0
  213. package/src/__tests__/PressableScale.test.tsx +29 -0
  214. package/src/__tests__/PressableWithHover.test.tsx +48 -0
  215. package/src/__tests__/SubtleHover.test.tsx +42 -0
  216. package/src/__tests__/theme-gradients.test.ts +31 -0
  217. package/src/__tests__/theme.test.ts +3 -0
  218. package/src/__tests__/useGutters.test.ts +58 -0
  219. package/src/__tests__/useHaptics.test.tsx +58 -0
  220. package/src/__tests__/useInteractionState.test.ts +27 -0
  221. package/src/animated-check/AnimatedCheck.stories.tsx +60 -0
  222. package/src/animated-check/AnimatedCheck.tsx +173 -0
  223. package/src/animated-check/index.ts +2 -0
  224. package/src/dialog/placement.ts +10 -7
  225. package/src/fill/index.tsx +2 -2
  226. package/src/hooks/index.ts +4 -0
  227. package/src/hooks/useGutters.ts +99 -0
  228. package/src/hooks/useHaptics.tsx +69 -0
  229. package/src/hooks/useInteractionState.ts +8 -2
  230. package/src/index.ts +20 -0
  231. package/src/index.web.ts +20 -0
  232. package/src/media-inset-border/MediaInsetBorder.stories.tsx +60 -0
  233. package/src/media-inset-border/MediaInsetBorder.tsx +72 -0
  234. package/src/media-inset-border/index.ts +2 -0
  235. package/src/motion/Motion.stories.tsx +99 -0
  236. package/src/motion/ScreenTransition.tsx +71 -0
  237. package/src/motion/index.ts +3 -0
  238. package/src/motion/motion.ts +75 -0
  239. package/src/pressable-scale/PressableScale.stories.tsx +71 -0
  240. package/src/pressable-scale/PressableScale.tsx +103 -0
  241. package/src/pressable-scale/index.ts +2 -0
  242. package/src/pressable-with-hover/PressableWithHover.stories.tsx +43 -0
  243. package/src/pressable-with-hover/PressableWithHover.tsx +43 -0
  244. package/src/pressable-with-hover/index.ts +2 -0
  245. package/src/styles/breakpoints.ts +18 -0
  246. package/src/styles/index.ts +2 -0
  247. package/src/subtle-hover/SubtleHover.stories.tsx +72 -0
  248. package/src/subtle-hover/SubtleHover.tsx +90 -0
  249. package/src/subtle-hover/index.ts +2 -0
  250. package/src/theme/build-theme.ts +2 -0
  251. package/src/theme/gradients.ts +61 -0
  252. package/src/theme/index.ts +2 -1
  253. package/src/theme/types.ts +13 -0
@@ -34,9 +34,20 @@ export interface ThemeColors {
34
34
  shadow: string;
35
35
  overlay: string;
36
36
  }
37
+ /**
38
+ * A single named gradient: an ordered list of `[offset, color]` stops where
39
+ * `offset` runs 0–1 along the gradient axis and `color` is any RN/CSS color.
40
+ */
41
+ export interface ThemeGradient {
42
+ values: Array<[number, string]>;
43
+ }
44
+ /** Map of named gradients exposed on the theme (`theme.gradients`). */
45
+ export type ThemeGradients = Record<string, ThemeGradient>;
37
46
  export interface Theme {
38
47
  mode: 'light' | 'dark';
39
48
  colors: ThemeColors;
49
+ /** Brand-neutral named linear-gradient palettes. Theme-agnostic (same in light/dark). */
50
+ gradients: ThemeGradients;
40
51
  isDark: boolean;
41
52
  isLight: boolean;
42
53
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/theme/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEjE,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kBAAkB,EAAE,MAAM,CAAC;IAE3B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IAErB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IAEpB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;IAEpB,iFAAiF;IACjF,SAAS,EAAE,MAAM,CAAC;IAElB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IAEnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IAEb,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,EAAE,MAAM,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,wBAAwB,EAAE,MAAM,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;IAEnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IACvB,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CAClB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/theme/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEjE,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kBAAkB,EAAE,MAAM,CAAC;IAE3B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IAErB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IAEpB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;IAEpB,iFAAiF;IACjF,SAAS,EAAE,MAAM,CAAC;IAElB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IAEnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IAEb,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,EAAE,MAAM,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,wBAAwB,EAAE,MAAM,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;IAEnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACjC;AAED,uEAAuE;AACvE,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAE3D,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IACvB,MAAM,EAAE,WAAW,CAAC;IACpB,yFAAyF;IACzF,SAAS,EAAE,cAAc,CAAC;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CAClB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/bloom",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "description": "Bloom UI — Oxy ecosystem component library for React Native + Expo + Web",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -371,6 +371,72 @@
371
371
  "default": "./lib/commonjs/fill/index.js"
372
372
  }
373
373
  },
374
+ "./media-inset-border": {
375
+ "react-native": "./src/media-inset-border/index.ts",
376
+ "import": {
377
+ "types": "./lib/typescript/module/media-inset-border/index.d.ts",
378
+ "default": "./lib/module/media-inset-border/index.js"
379
+ },
380
+ "require": {
381
+ "types": "./lib/typescript/commonjs/media-inset-border/index.d.ts",
382
+ "default": "./lib/commonjs/media-inset-border/index.js"
383
+ }
384
+ },
385
+ "./pressable-scale": {
386
+ "react-native": "./src/pressable-scale/index.ts",
387
+ "import": {
388
+ "types": "./lib/typescript/module/pressable-scale/index.d.ts",
389
+ "default": "./lib/module/pressable-scale/index.js"
390
+ },
391
+ "require": {
392
+ "types": "./lib/typescript/commonjs/pressable-scale/index.d.ts",
393
+ "default": "./lib/commonjs/pressable-scale/index.js"
394
+ }
395
+ },
396
+ "./pressable-with-hover": {
397
+ "react-native": "./src/pressable-with-hover/index.ts",
398
+ "import": {
399
+ "types": "./lib/typescript/module/pressable-with-hover/index.d.ts",
400
+ "default": "./lib/module/pressable-with-hover/index.js"
401
+ },
402
+ "require": {
403
+ "types": "./lib/typescript/commonjs/pressable-with-hover/index.d.ts",
404
+ "default": "./lib/commonjs/pressable-with-hover/index.js"
405
+ }
406
+ },
407
+ "./subtle-hover": {
408
+ "react-native": "./src/subtle-hover/index.ts",
409
+ "import": {
410
+ "types": "./lib/typescript/module/subtle-hover/index.d.ts",
411
+ "default": "./lib/module/subtle-hover/index.js"
412
+ },
413
+ "require": {
414
+ "types": "./lib/typescript/commonjs/subtle-hover/index.d.ts",
415
+ "default": "./lib/commonjs/subtle-hover/index.js"
416
+ }
417
+ },
418
+ "./motion": {
419
+ "react-native": "./src/motion/index.ts",
420
+ "import": {
421
+ "types": "./lib/typescript/module/motion/index.d.ts",
422
+ "default": "./lib/module/motion/index.js"
423
+ },
424
+ "require": {
425
+ "types": "./lib/typescript/commonjs/motion/index.d.ts",
426
+ "default": "./lib/commonjs/motion/index.js"
427
+ }
428
+ },
429
+ "./animated-check": {
430
+ "react-native": "./src/animated-check/index.ts",
431
+ "import": {
432
+ "types": "./lib/typescript/module/animated-check/index.d.ts",
433
+ "default": "./lib/module/animated-check/index.js"
434
+ },
435
+ "require": {
436
+ "types": "./lib/typescript/commonjs/animated-check/index.d.ts",
437
+ "default": "./lib/commonjs/animated-check/index.js"
438
+ }
439
+ },
374
440
  "./icon-circle": {
375
441
  "react-native": "./src/icon-circle/index.tsx",
376
442
  "import": {
@@ -927,6 +993,7 @@
927
993
  "@types/react-native": "*",
928
994
  "@vitejs/plugin-react": "^6.0.2",
929
995
  "expo-font": "^56.0.5",
996
+ "expo-haptics": "~56.0.3",
930
997
  "expo-router": "^56.0.0",
931
998
  "jest": "^30.3.0",
932
999
  "jest-environment-jsdom": "^30.4.1",
@@ -951,6 +1018,7 @@
951
1018
  "peerDependencies": {
952
1019
  "expo": "*",
953
1020
  "expo-font": "*",
1021
+ "expo-haptics": "*",
954
1022
  "expo-router": ">=3.0.0",
955
1023
  "react": ">=18.0.0",
956
1024
  "react-dom": ">=18.0.0",
@@ -969,6 +1037,9 @@
969
1037
  "expo-font": {
970
1038
  "optional": true
971
1039
  },
1040
+ "expo-haptics": {
1041
+ "optional": true
1042
+ },
972
1043
  "expo-router": {
973
1044
  "optional": true
974
1045
  },
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import { act, render } from '@testing-library/react-native';
3
+
4
+ import { BloomThemeProvider } from '../theme/BloomThemeProvider';
5
+ import { AnimatedCheck, type AnimatedCheckRef } from '../animated-check';
6
+
7
+ function renderWithTheme(ui: React.ReactElement) {
8
+ return render(
9
+ <BloomThemeProvider mode="light" colorPreset="teal">
10
+ {ui}
11
+ </BloomThemeProvider>,
12
+ );
13
+ }
14
+
15
+ describe('AnimatedCheck', () => {
16
+ it('carries a displayName', () => {
17
+ expect(AnimatedCheck.displayName).toBe('AnimatedCheck');
18
+ });
19
+
20
+ it('renders the animated SVG when react-native-svg is available', () => {
21
+ const { toJSON } = renderWithTheme(<AnimatedCheck size={48} />);
22
+ expect(toJSON()).not.toBeNull();
23
+ });
24
+
25
+ it('exposes an imperative play() that runs without throwing', () => {
26
+ const ref = React.createRef<AnimatedCheckRef>();
27
+ renderWithTheme(<AnimatedCheck ref={ref} size={48} color="#6366f1" />);
28
+ expect(ref.current).not.toBeNull();
29
+ expect(() => act(() => ref.current?.play())).not.toThrow();
30
+ });
31
+ });
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react-native';
3
+
4
+ import { BloomThemeProvider } from '../theme/BloomThemeProvider';
5
+ import { MediaInsetBorder } from '../media-inset-border';
6
+
7
+ function renderWithTheme(ui: React.ReactElement) {
8
+ return render(
9
+ <BloomThemeProvider mode="light" colorPreset="teal">
10
+ {ui}
11
+ </BloomThemeProvider>,
12
+ );
13
+ }
14
+
15
+ describe('MediaInsetBorder', () => {
16
+ it('carries a displayName', () => {
17
+ expect(MediaInsetBorder.displayName).toBe('MediaInsetBorder');
18
+ });
19
+
20
+ it('renders the softened border by default', () => {
21
+ const { UNSAFE_root } = renderWithTheme(<MediaInsetBorder />);
22
+ expect(UNSAFE_root).toBeTruthy();
23
+ });
24
+
25
+ it('renders the opaque variant', () => {
26
+ const { UNSAFE_root } = renderWithTheme(<MediaInsetBorder opaque />);
27
+ expect(UNSAFE_root).toBeTruthy();
28
+ });
29
+
30
+ it('accepts a style override for borderRadius', () => {
31
+ const { UNSAFE_root } = renderWithTheme(
32
+ <MediaInsetBorder style={{ borderRadius: 999 }} />,
33
+ );
34
+ expect(UNSAFE_root).toBeTruthy();
35
+ });
36
+ });
@@ -0,0 +1,58 @@
1
+ import React from 'react';
2
+ import { Text } from 'react-native';
3
+ import { render } from '@testing-library/react-native';
4
+
5
+ import {
6
+ ScaleAndFadeIn,
7
+ ScaleAndFadeOut,
8
+ ScreenTransition,
9
+ ShrinkAndPop,
10
+ } from '../motion';
11
+
12
+ // The reanimated mock resolves `withTiming(v)` -> v, `withDelay(d, v)` -> v and
13
+ // `withSequence(...v)` -> last, so the builder output is deterministic.
14
+
15
+ describe('motion presets', () => {
16
+ it('ScaleAndFadeIn enters from 70% scale and transparent', () => {
17
+ const { initialValues, animations } = ScaleAndFadeIn();
18
+ expect(initialValues).toEqual({ opacity: 0, transform: [{ scale: 0.7 }] });
19
+ expect(animations).toEqual({ opacity: 1, transform: [{ scale: 1 }] });
20
+ });
21
+
22
+ it('ScaleAndFadeOut exits to 70% scale and transparent', () => {
23
+ const { initialValues, animations } = ScaleAndFadeOut();
24
+ expect(initialValues).toEqual({ opacity: 1, transform: [{ scale: 1 }] });
25
+ expect(animations).toEqual({ opacity: 0, transform: [{ scale: 0.7 }] });
26
+ });
27
+
28
+ it('ShrinkAndPop overshoots to 110% before leaving', () => {
29
+ const { initialValues, animations } = ShrinkAndPop();
30
+ expect(initialValues).toEqual({ opacity: 1, transform: [{ scale: 1 }] });
31
+ // withSequence resolves to its final keyframe (the 1.1 overshoot) in the mock.
32
+ expect(animations.transform).toEqual([{ scale: 1.1 }]);
33
+ });
34
+ });
35
+
36
+ describe('ScreenTransition', () => {
37
+ it('carries a displayName', () => {
38
+ expect(ScreenTransition.displayName).toBe('ScreenTransition');
39
+ });
40
+
41
+ it('renders its children for a forward transition', () => {
42
+ const { getByText } = render(
43
+ <ScreenTransition direction="forward">
44
+ <Text>Screen body</Text>
45
+ </ScreenTransition>,
46
+ );
47
+ expect(getByText('Screen body')).toBeTruthy();
48
+ });
49
+
50
+ it('renders its children for a backward transition', () => {
51
+ const { getByText } = render(
52
+ <ScreenTransition direction="backward" enabledWeb>
53
+ <Text>Back</Text>
54
+ </ScreenTransition>,
55
+ );
56
+ expect(getByText('Back')).toBeTruthy();
57
+ });
58
+ });
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+ import { Text } from 'react-native';
3
+ import { render } from '@testing-library/react-native';
4
+
5
+ import { PressableScale } from '../pressable-scale';
6
+
7
+ describe('PressableScale', () => {
8
+ it('carries a displayName', () => {
9
+ expect(PressableScale.displayName).toBe('PressableScale');
10
+ });
11
+
12
+ it('renders its children', () => {
13
+ const { getByText } = render(
14
+ <PressableScale accessibilityLabel="Press me">
15
+ <Text>Tap target</Text>
16
+ </PressableScale>,
17
+ );
18
+ expect(getByText('Tap target')).toBeTruthy();
19
+ });
20
+
21
+ it('renders with a custom target scale', () => {
22
+ const { getByText } = render(
23
+ <PressableScale targetScale={0.9} accessibilityLabel="Press me">
24
+ <Text>Scaled</Text>
25
+ </PressableScale>,
26
+ );
27
+ expect(getByText('Scaled')).toBeTruthy();
28
+ });
29
+ });
@@ -0,0 +1,48 @@
1
+ import React from 'react';
2
+ import { Text } from 'react-native';
3
+ import { fireEvent, render } from '@testing-library/react-native';
4
+
5
+ import { PressableWithHover } from '../pressable-with-hover';
6
+
7
+ describe('PressableWithHover', () => {
8
+ it('carries a displayName', () => {
9
+ expect(PressableWithHover.displayName).toBe('PressableWithHover');
10
+ });
11
+
12
+ it('renders its children', () => {
13
+ const { getByText } = render(
14
+ <PressableWithHover
15
+ accessibilityLabel="Row"
16
+ style={{ backgroundColor: 'white' }}
17
+ hoverStyle={{ backgroundColor: 'gray' }}
18
+ >
19
+ <Text>Row</Text>
20
+ </PressableWithHover>,
21
+ );
22
+ expect(getByText('Row')).toBeTruthy();
23
+ });
24
+
25
+ it('merges hoverStyle only while hovered', () => {
26
+ const { getByTestId } = render(
27
+ <PressableWithHover
28
+ testID="row"
29
+ style={{ backgroundColor: 'white' }}
30
+ hoverStyle={{ backgroundColor: 'gray' }}
31
+ >
32
+ <Text>Row</Text>
33
+ </PressableWithHover>,
34
+ );
35
+
36
+ const row = getByTestId('row');
37
+ expect(row.props.style).toEqual({ backgroundColor: 'white' });
38
+
39
+ fireEvent(row, 'hoverIn');
40
+ expect(getByTestId('row').props.style).toEqual([
41
+ { backgroundColor: 'white' },
42
+ { backgroundColor: 'gray' },
43
+ ]);
44
+
45
+ fireEvent(row, 'hoverOut');
46
+ expect(getByTestId('row').props.style).toEqual({ backgroundColor: 'white' });
47
+ });
48
+ });
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react-native';
3
+
4
+ import { BloomThemeProvider } from '../theme/BloomThemeProvider';
5
+ import { SubtleHover } from '../subtle-hover';
6
+
7
+ // Default test Platform.OS is 'ios' (see __mocks__/react-native.ts), so the
8
+ // web-only overlay is suppressed unless `native` is opted in.
9
+ function renderWithTheme(ui: React.ReactElement) {
10
+ return render(
11
+ <BloomThemeProvider mode="light" colorPreset="teal">
12
+ {ui}
13
+ </BloomThemeProvider>,
14
+ );
15
+ }
16
+
17
+ describe('SubtleHover', () => {
18
+ it('carries a displayName', () => {
19
+ expect(SubtleHover.displayName).toBe('SubtleHover');
20
+ });
21
+
22
+ // `renderWithTheme` wraps the subject in the provider's flex View, so the
23
+ // overlay's presence is read from that wrapper's `children` rather than the
24
+ // root being null.
25
+ it('renders nothing on native by default', () => {
26
+ const tree = renderWithTheme(<SubtleHover hover />).toJSON();
27
+ expect(tree).not.toBeNull();
28
+ expect(tree?.children).toBeNull();
29
+ });
30
+
31
+ it('renders the overlay on native when opted in', () => {
32
+ const tree = renderWithTheme(<SubtleHover hover native />).toJSON();
33
+ expect(tree?.children).not.toBeNull();
34
+ });
35
+
36
+ it('accepts an opacity override and a style', () => {
37
+ const tree = renderWithTheme(
38
+ <SubtleHover hover native opacity={0.9} style={{ borderRadius: 12 }} />,
39
+ ).toJSON();
40
+ expect(tree?.children).not.toBeNull();
41
+ });
42
+ });
@@ -0,0 +1,31 @@
1
+ import { buildTheme } from '../theme/build-theme';
2
+ import { THEME_GRADIENTS } from '../theme/gradients';
3
+
4
+ describe('theme gradients', () => {
5
+ it('are exposed on the built theme', () => {
6
+ const theme = buildTheme('teal', 'light');
7
+ expect(theme.gradients).toBe(THEME_GRADIENTS);
8
+ expect(Object.keys(theme.gradients).length).toBeGreaterThan(0);
9
+ });
10
+
11
+ it('every gradient has ordered [offset, color] stops', () => {
12
+ for (const [name, gradient] of Object.entries(THEME_GRADIENTS)) {
13
+ expect(name).toBeTruthy();
14
+ expect(Array.isArray(gradient.values)).toBe(true);
15
+ expect(gradient.values.length).toBeGreaterThanOrEqual(2);
16
+
17
+ for (const [offset, color] of gradient.values) {
18
+ expect(typeof offset).toBe('number');
19
+ expect(offset).toBeGreaterThanOrEqual(0);
20
+ expect(offset).toBeLessThanOrEqual(1);
21
+ expect(color).toMatch(/^#[0-9a-f]{6}$/i);
22
+ }
23
+ }
24
+ });
25
+
26
+ it('are identical across light and dark mode (theme-agnostic)', () => {
27
+ expect(buildTheme('teal', 'dark').gradients).toBe(
28
+ buildTheme('teal', 'light').gradients,
29
+ );
30
+ });
31
+ });
@@ -5,6 +5,7 @@ import {
5
5
  hexToAppColorName,
6
6
  } from '../theme/color-presets';
7
7
  import { buildTheme } from '../theme/build-theme';
8
+ import { THEME_GRADIENTS } from '../theme/gradients';
8
9
  import type { Theme, ThemeColors, ThemeMode } from '../theme/types';
9
10
 
10
11
  describe('Theme system', () => {
@@ -111,6 +112,7 @@ describe('Theme system', () => {
111
112
  isDark: false,
112
113
  isLight: true,
113
114
  colors: createMockColors(),
115
+ gradients: THEME_GRADIENTS,
114
116
  };
115
117
  expect(lightTheme.isDark).toBe(false);
116
118
  expect(lightTheme.isLight).toBe(true);
@@ -120,6 +122,7 @@ describe('Theme system', () => {
120
122
  isDark: true,
121
123
  isLight: false,
122
124
  colors: createMockColors(),
125
+ gradients: THEME_GRADIENTS,
123
126
  };
124
127
  expect(darkTheme.isDark).toBe(true);
125
128
  expect(darkTheme.isLight).toBe(false);
@@ -0,0 +1,58 @@
1
+ import { renderHook } from '@testing-library/react-native';
2
+
3
+ import { useGutters } from '../hooks/useGutters';
4
+ import { space } from '../styles/tokens';
5
+
6
+ // The RN mock reports a 375px-wide window (below the `sm` 640 breakpoint), so
7
+ // every case resolves to the `base` tier.
8
+
9
+ describe('useGutters', () => {
10
+ it('expands a single size to all four edges', () => {
11
+ const { result } = renderHook(() => useGutters(['base']));
12
+ expect(result.current).toEqual({
13
+ paddingTop: space.lg,
14
+ paddingRight: space.lg,
15
+ paddingBottom: space.lg,
16
+ paddingLeft: space.lg,
17
+ });
18
+ });
19
+
20
+ it('treats a two-value list as [vertical, horizontal]', () => {
21
+ const { result } = renderHook(() => useGutters([0, 'wide']));
22
+ expect(result.current).toEqual({
23
+ paddingTop: 0,
24
+ paddingRight: space.xl,
25
+ paddingBottom: 0,
26
+ paddingLeft: space.xl,
27
+ });
28
+ });
29
+
30
+ it('maps a four-value list to [top, right, bottom, left]', () => {
31
+ const { result } = renderHook(() =>
32
+ useGutters(['compact', 'base', 0, 'wide']),
33
+ );
34
+ expect(result.current).toEqual({
35
+ paddingTop: space.sm,
36
+ paddingRight: space.lg,
37
+ paddingBottom: 0,
38
+ paddingLeft: space.xl,
39
+ });
40
+ });
41
+
42
+ it('returns zero padding for a `0` gutter', () => {
43
+ const { result } = renderHook(() => useGutters([0]));
44
+ expect(result.current).toEqual({
45
+ paddingTop: 0,
46
+ paddingRight: 0,
47
+ paddingBottom: 0,
48
+ paddingLeft: 0,
49
+ });
50
+ });
51
+
52
+ it('keeps a stable object identity across re-renders at the same width', () => {
53
+ const { result, rerender } = renderHook(() => useGutters(['base']));
54
+ const first = result.current;
55
+ rerender({});
56
+ expect(result.current).toBe(first);
57
+ });
58
+ });
@@ -0,0 +1,58 @@
1
+ import React from 'react';
2
+ import { Platform } from 'react-native';
3
+ import { act, renderHook } from '@testing-library/react-native';
4
+ import { ImpactFeedbackStyle, impactAsync } from 'expo-haptics';
5
+
6
+ import { BloomHapticsProvider, useHaptics } from '../hooks/useHaptics';
7
+
8
+ const originalOS = Platform.OS;
9
+
10
+ afterEach(() => {
11
+ Platform.OS = originalOS;
12
+ jest.clearAllMocks();
13
+ });
14
+
15
+ describe('useHaptics', () => {
16
+ it('returns a callback', () => {
17
+ const { result } = renderHook(() => useHaptics());
18
+ expect(typeof result.current).toBe('function');
19
+ });
20
+
21
+ it('fires the mapped impact style on iOS', () => {
22
+ Platform.OS = 'ios';
23
+ const { result } = renderHook(() => useHaptics());
24
+ act(() => result.current('heavy'));
25
+ expect(impactAsync).toHaveBeenCalledWith(ImpactFeedbackStyle.Heavy);
26
+ });
27
+
28
+ it('defaults to the light impact', () => {
29
+ Platform.OS = 'ios';
30
+ const { result } = renderHook(() => useHaptics());
31
+ act(() => result.current());
32
+ expect(impactAsync).toHaveBeenCalledWith(ImpactFeedbackStyle.Light);
33
+ });
34
+
35
+ it('clamps to the light impact on Android regardless of strength', () => {
36
+ Platform.OS = 'android';
37
+ const { result } = renderHook(() => useHaptics());
38
+ act(() => result.current('heavy'));
39
+ expect(impactAsync).toHaveBeenCalledWith(ImpactFeedbackStyle.Light);
40
+ });
41
+
42
+ it('no-ops on web', () => {
43
+ Platform.OS = 'web';
44
+ const { result } = renderHook(() => useHaptics());
45
+ act(() => result.current('medium'));
46
+ expect(impactAsync).not.toHaveBeenCalled();
47
+ });
48
+
49
+ it('no-ops when disabled via BloomHapticsProvider', () => {
50
+ Platform.OS = 'ios';
51
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
52
+ <BloomHapticsProvider enabled={false}>{children}</BloomHapticsProvider>
53
+ );
54
+ const { result } = renderHook(() => useHaptics(), { wrapper });
55
+ act(() => result.current('medium'));
56
+ expect(impactAsync).not.toHaveBeenCalled();
57
+ });
58
+ });
@@ -0,0 +1,27 @@
1
+ import { act, renderHook } from '@testing-library/react-native';
2
+
3
+ import { useInteractionState } from '../hooks/useInteractionState';
4
+
5
+ describe('useInteractionState', () => {
6
+ it('starts inactive and toggles on onIn/onOut', () => {
7
+ const { result } = renderHook(() => useInteractionState());
8
+ expect(result.current.state).toBe(false);
9
+
10
+ act(() => result.current.onIn());
11
+ expect(result.current.state).toBe(true);
12
+
13
+ act(() => result.current.onOut());
14
+ expect(result.current.state).toBe(false);
15
+ });
16
+
17
+ it('keeps handler identities stable across renders', () => {
18
+ const { result, rerender } = renderHook(() => useInteractionState());
19
+ const firstOnIn = result.current.onIn;
20
+ const firstOnOut = result.current.onOut;
21
+
22
+ rerender({});
23
+
24
+ expect(result.current.onIn).toBe(firstOnIn);
25
+ expect(result.current.onOut).toBe(firstOnOut);
26
+ });
27
+ });
@@ -0,0 +1,60 @@
1
+ import React, { useRef } from 'react';
2
+ import { Pressable, Text, View } from 'react-native';
3
+ import type { Meta, StoryObj } from '@storybook/react-vite';
4
+
5
+ import { AnimatedCheck, type AnimatedCheckRef } from './index';
6
+
7
+ const meta: Meta<typeof AnimatedCheck> = {
8
+ title: 'Components/AnimatedCheck',
9
+ component: AnimatedCheck,
10
+ argTypes: {
11
+ size: { control: { type: 'range', min: 16, max: 128, step: 4 } },
12
+ color: { control: 'color' },
13
+ },
14
+ };
15
+
16
+ export default meta;
17
+
18
+ type Story = StoryObj<typeof AnimatedCheck>;
19
+
20
+ export const Playable: Story = {
21
+ args: { size: 64 },
22
+ render: (args) => {
23
+ function Demo() {
24
+ const ref = useRef<AnimatedCheckRef>(null);
25
+ return (
26
+ <View style={{ gap: 16, alignItems: 'center' }}>
27
+ <AnimatedCheck ref={ref} {...args} />
28
+ <Pressable
29
+ accessibilityLabel="Play"
30
+ onPress={() => ref.current?.play()}
31
+ style={{
32
+ paddingVertical: 8,
33
+ paddingHorizontal: 16,
34
+ borderRadius: 8,
35
+ backgroundColor: '#111827',
36
+ }}
37
+ >
38
+ <Text style={{ color: 'white', fontWeight: '600' }}>Play</Text>
39
+ </Pressable>
40
+ </View>
41
+ );
42
+ }
43
+ return <Demo />;
44
+ },
45
+ };
46
+
47
+ export const CustomColor: Story = {
48
+ args: { size: 80, color: '#6366f1' },
49
+ render: (args) => {
50
+ function Demo() {
51
+ const ref = useRef<AnimatedCheckRef>(null);
52
+ return (
53
+ <Pressable accessibilityLabel="Replay" onPress={() => ref.current?.play()}>
54
+ <AnimatedCheck ref={ref} {...args} />
55
+ </Pressable>
56
+ );
57
+ }
58
+ return <Demo />;
59
+ },
60
+ };