@oxyhq/bloom 0.19.1 → 0.20.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 (170) hide show
  1. package/lib/commonjs/benefit-list/index.js +119 -0
  2. package/lib/commonjs/benefit-list/index.js.map +1 -0
  3. package/lib/commonjs/benefit-list/types.js +6 -0
  4. package/lib/commonjs/benefit-list/types.js.map +1 -0
  5. package/lib/commonjs/connection-dots/ConnectionDots.js +112 -0
  6. package/lib/commonjs/connection-dots/ConnectionDots.js.map +1 -0
  7. package/lib/commonjs/connection-dots/ConnectionDots.web.js +116 -0
  8. package/lib/commonjs/connection-dots/ConnectionDots.web.js.map +1 -0
  9. package/lib/commonjs/connection-dots/ConnectionDotsBase.js +55 -0
  10. package/lib/commonjs/connection-dots/ConnectionDotsBase.js.map +1 -0
  11. package/lib/commonjs/connection-dots/index.js +13 -0
  12. package/lib/commonjs/connection-dots/index.js.map +1 -0
  13. package/lib/commonjs/connection-dots/index.web.js +13 -0
  14. package/lib/commonjs/connection-dots/index.web.js.map +1 -0
  15. package/lib/commonjs/connection-dots/types.js +6 -0
  16. package/lib/commonjs/connection-dots/types.js.map +1 -0
  17. package/lib/commonjs/design-tokens/color-roles.js +112 -0
  18. package/lib/commonjs/design-tokens/color-roles.js.map +1 -0
  19. package/lib/commonjs/design-tokens/index.js +89 -0
  20. package/lib/commonjs/design-tokens/index.js.map +1 -0
  21. package/lib/commonjs/design-tokens/scales.js +168 -0
  22. package/lib/commonjs/design-tokens/scales.js.map +1 -0
  23. package/lib/commonjs/design-tokens/shadows.js +48 -0
  24. package/lib/commonjs/design-tokens/shadows.js.map +1 -0
  25. package/lib/commonjs/design-tokens/shadows.native.js +53 -0
  26. package/lib/commonjs/design-tokens/shadows.native.js.map +1 -0
  27. package/lib/commonjs/design-tokens/tailwind-preset.js +139 -0
  28. package/lib/commonjs/design-tokens/tailwind-preset.js.map +1 -0
  29. package/lib/commonjs/design-tokens/theme-css.js +98 -0
  30. package/lib/commonjs/design-tokens/theme-css.js.map +1 -0
  31. package/lib/commonjs/index.js +208 -93
  32. package/lib/commonjs/index.js.map +1 -1
  33. package/lib/commonjs/index.web.js +223 -108
  34. package/lib/commonjs/index.web.js.map +1 -1
  35. package/lib/commonjs/text-field/index.js +156 -0
  36. package/lib/commonjs/text-field/index.js.map +1 -1
  37. package/lib/module/benefit-list/index.js +114 -0
  38. package/lib/module/benefit-list/index.js.map +1 -0
  39. package/lib/module/benefit-list/types.js +4 -0
  40. package/lib/module/benefit-list/types.js.map +1 -0
  41. package/lib/module/connection-dots/ConnectionDots.js +107 -0
  42. package/lib/module/connection-dots/ConnectionDots.js.map +1 -0
  43. package/lib/module/connection-dots/ConnectionDots.web.js +111 -0
  44. package/lib/module/connection-dots/ConnectionDots.web.js.map +1 -0
  45. package/lib/module/connection-dots/ConnectionDotsBase.js +50 -0
  46. package/lib/module/connection-dots/ConnectionDotsBase.js.map +1 -0
  47. package/lib/module/connection-dots/index.js +4 -0
  48. package/lib/module/connection-dots/index.js.map +1 -0
  49. package/lib/module/connection-dots/index.web.js +4 -0
  50. package/lib/module/connection-dots/index.web.js.map +1 -0
  51. package/lib/module/connection-dots/types.js +4 -0
  52. package/lib/module/connection-dots/types.js.map +1 -0
  53. package/lib/module/design-tokens/color-roles.js +108 -0
  54. package/lib/module/design-tokens/color-roles.js.map +1 -0
  55. package/lib/module/design-tokens/index.js +27 -0
  56. package/lib/module/design-tokens/index.js.map +1 -0
  57. package/lib/module/design-tokens/scales.js +164 -0
  58. package/lib/module/design-tokens/scales.js.map +1 -0
  59. package/lib/module/design-tokens/shadows.js +43 -0
  60. package/lib/module/design-tokens/shadows.js.map +1 -0
  61. package/lib/module/design-tokens/shadows.native.js +48 -0
  62. package/lib/module/design-tokens/shadows.native.js.map +1 -0
  63. package/lib/module/design-tokens/tailwind-preset.js +136 -0
  64. package/lib/module/design-tokens/tailwind-preset.js.map +1 -0
  65. package/lib/module/design-tokens/theme-css.js +94 -0
  66. package/lib/module/design-tokens/theme-css.js.map +1 -0
  67. package/lib/module/index.js +6 -1
  68. package/lib/module/index.js.map +1 -1
  69. package/lib/module/index.web.js +6 -1
  70. package/lib/module/index.web.js.map +1 -1
  71. package/lib/module/text-field/index.js +158 -2
  72. package/lib/module/text-field/index.js.map +1 -1
  73. package/lib/typescript/commonjs/benefit-list/index.d.ts +6 -0
  74. package/lib/typescript/commonjs/benefit-list/index.d.ts.map +1 -0
  75. package/lib/typescript/commonjs/benefit-list/types.d.ts +35 -0
  76. package/lib/typescript/commonjs/benefit-list/types.d.ts.map +1 -0
  77. package/lib/typescript/commonjs/connection-dots/ConnectionDots.d.ts +4 -0
  78. package/lib/typescript/commonjs/connection-dots/ConnectionDots.d.ts.map +1 -0
  79. package/lib/typescript/commonjs/connection-dots/ConnectionDots.web.d.ts +14 -0
  80. package/lib/typescript/commonjs/connection-dots/ConnectionDots.web.d.ts.map +1 -0
  81. package/lib/typescript/commonjs/connection-dots/ConnectionDotsBase.d.ts +18 -0
  82. package/lib/typescript/commonjs/connection-dots/ConnectionDotsBase.d.ts.map +1 -0
  83. package/lib/typescript/commonjs/connection-dots/index.d.ts +3 -0
  84. package/lib/typescript/commonjs/connection-dots/index.d.ts.map +1 -0
  85. package/lib/typescript/commonjs/connection-dots/index.web.d.ts +3 -0
  86. package/lib/typescript/commonjs/connection-dots/index.web.d.ts.map +1 -0
  87. package/lib/typescript/commonjs/connection-dots/types.d.ts +41 -0
  88. package/lib/typescript/commonjs/connection-dots/types.d.ts.map +1 -0
  89. package/lib/typescript/commonjs/design-tokens/color-roles.d.ts +102 -0
  90. package/lib/typescript/commonjs/design-tokens/color-roles.d.ts.map +1 -0
  91. package/lib/typescript/commonjs/design-tokens/index.d.ts +28 -0
  92. package/lib/typescript/commonjs/design-tokens/index.d.ts.map +1 -0
  93. package/lib/typescript/commonjs/design-tokens/scales.d.ts +152 -0
  94. package/lib/typescript/commonjs/design-tokens/scales.d.ts.map +1 -0
  95. package/lib/typescript/commonjs/design-tokens/shadows.d.ts +34 -0
  96. package/lib/typescript/commonjs/design-tokens/shadows.d.ts.map +1 -0
  97. package/lib/typescript/commonjs/design-tokens/shadows.native.d.ts +19 -0
  98. package/lib/typescript/commonjs/design-tokens/shadows.native.d.ts.map +1 -0
  99. package/lib/typescript/commonjs/design-tokens/tailwind-preset.d.ts +59 -0
  100. package/lib/typescript/commonjs/design-tokens/tailwind-preset.d.ts.map +1 -0
  101. package/lib/typescript/commonjs/design-tokens/theme-css.d.ts +39 -0
  102. package/lib/typescript/commonjs/design-tokens/theme-css.d.ts.map +1 -0
  103. package/lib/typescript/commonjs/index.d.ts +6 -0
  104. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  105. package/lib/typescript/commonjs/index.web.d.ts +6 -0
  106. package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
  107. package/lib/typescript/commonjs/text-field/index.d.ts +14 -1
  108. package/lib/typescript/commonjs/text-field/index.d.ts.map +1 -1
  109. package/lib/typescript/module/benefit-list/index.d.ts +6 -0
  110. package/lib/typescript/module/benefit-list/index.d.ts.map +1 -0
  111. package/lib/typescript/module/benefit-list/types.d.ts +35 -0
  112. package/lib/typescript/module/benefit-list/types.d.ts.map +1 -0
  113. package/lib/typescript/module/connection-dots/ConnectionDots.d.ts +4 -0
  114. package/lib/typescript/module/connection-dots/ConnectionDots.d.ts.map +1 -0
  115. package/lib/typescript/module/connection-dots/ConnectionDots.web.d.ts +14 -0
  116. package/lib/typescript/module/connection-dots/ConnectionDots.web.d.ts.map +1 -0
  117. package/lib/typescript/module/connection-dots/ConnectionDotsBase.d.ts +18 -0
  118. package/lib/typescript/module/connection-dots/ConnectionDotsBase.d.ts.map +1 -0
  119. package/lib/typescript/module/connection-dots/index.d.ts +3 -0
  120. package/lib/typescript/module/connection-dots/index.d.ts.map +1 -0
  121. package/lib/typescript/module/connection-dots/index.web.d.ts +3 -0
  122. package/lib/typescript/module/connection-dots/index.web.d.ts.map +1 -0
  123. package/lib/typescript/module/connection-dots/types.d.ts +41 -0
  124. package/lib/typescript/module/connection-dots/types.d.ts.map +1 -0
  125. package/lib/typescript/module/design-tokens/color-roles.d.ts +102 -0
  126. package/lib/typescript/module/design-tokens/color-roles.d.ts.map +1 -0
  127. package/lib/typescript/module/design-tokens/index.d.ts +28 -0
  128. package/lib/typescript/module/design-tokens/index.d.ts.map +1 -0
  129. package/lib/typescript/module/design-tokens/scales.d.ts +152 -0
  130. package/lib/typescript/module/design-tokens/scales.d.ts.map +1 -0
  131. package/lib/typescript/module/design-tokens/shadows.d.ts +34 -0
  132. package/lib/typescript/module/design-tokens/shadows.d.ts.map +1 -0
  133. package/lib/typescript/module/design-tokens/shadows.native.d.ts +19 -0
  134. package/lib/typescript/module/design-tokens/shadows.native.d.ts.map +1 -0
  135. package/lib/typescript/module/design-tokens/tailwind-preset.d.ts +59 -0
  136. package/lib/typescript/module/design-tokens/tailwind-preset.d.ts.map +1 -0
  137. package/lib/typescript/module/design-tokens/theme-css.d.ts +39 -0
  138. package/lib/typescript/module/design-tokens/theme-css.d.ts.map +1 -0
  139. package/lib/typescript/module/index.d.ts +6 -0
  140. package/lib/typescript/module/index.d.ts.map +1 -1
  141. package/lib/typescript/module/index.web.d.ts +6 -0
  142. package/lib/typescript/module/index.web.d.ts.map +1 -1
  143. package/lib/typescript/module/text-field/index.d.ts +14 -1
  144. package/lib/typescript/module/text-field/index.d.ts.map +1 -1
  145. package/package.json +50 -1
  146. package/src/__tests__/BenefitList.test.tsx +108 -0
  147. package/src/__tests__/ConnectionDots.test.tsx +86 -0
  148. package/src/__tests__/TextFieldFloatingLabel.test.tsx +93 -0
  149. package/src/__tests__/design-tokens.test.ts +169 -0
  150. package/src/benefit-list/BenefitList.stories.tsx +50 -0
  151. package/src/benefit-list/index.tsx +133 -0
  152. package/src/benefit-list/types.ts +36 -0
  153. package/src/connection-dots/ConnectionDots.stories.tsx +54 -0
  154. package/src/connection-dots/ConnectionDots.tsx +139 -0
  155. package/src/connection-dots/ConnectionDots.web.tsx +122 -0
  156. package/src/connection-dots/ConnectionDotsBase.tsx +62 -0
  157. package/src/connection-dots/index.ts +2 -0
  158. package/src/connection-dots/index.web.ts +2 -0
  159. package/src/connection-dots/types.ts +41 -0
  160. package/src/design-tokens/color-roles.ts +110 -0
  161. package/src/design-tokens/index.ts +45 -0
  162. package/src/design-tokens/scales.ts +134 -0
  163. package/src/design-tokens/shadows.native.ts +44 -0
  164. package/src/design-tokens/shadows.ts +42 -0
  165. package/src/design-tokens/tailwind-preset.ts +139 -0
  166. package/src/design-tokens/theme-css.ts +99 -0
  167. package/src/index.ts +33 -0
  168. package/src/index.web.ts +33 -0
  169. package/src/text-field/TextField.stories.tsx +46 -0
  170. package/src/text-field/index.tsx +212 -1
@@ -3,6 +3,8 @@ export { atoms, flatten, Z_INDEX, zIndex } from './styles';
3
3
  export type { ViewStyleProp, TextStyleProp } from './styles';
4
4
  export * as tokens from './styles/tokens';
5
5
  export { web, native, ios, android, platform, select } from './styles/platform';
6
+ export { bloomTailwindPreset, bloomThemeCss, bloomThemeBlock, FILL_ROLES, TEXT_ROLES, BORDER_ROLES, SPACING, RADIUS, BORDER_WIDTH, TYPOGRAPHY, FONT_FAMILY_VARS, SHADOW_BOX, bloomShadowStyle, } from './design-tokens';
7
+ export type { TailwindPreset, TailwindPresetThemeExtend, FillRole, TextRole, BorderRole, TypeRole, TypeRoleName, ShadowRole, } from './design-tokens';
6
8
  export { useInteractionState } from './hooks/useInteractionState';
7
9
  export { useDelayedLoading } from './hooks/useDelayedLoading';
8
10
  export { useThrottledValue } from './hooks/useThrottledValue';
@@ -34,6 +36,10 @@ export * as Skeleton from './skeleton';
34
36
  export * as Grid from './grid';
35
37
  export { Fill } from './fill';
36
38
  export { IconCircle } from './icon-circle';
39
+ export { ConnectionDots } from './connection-dots/index.web';
40
+ export type { ConnectionDotsProps } from './connection-dots/index.web';
41
+ export { BenefitRow, BenefitList } from './benefit-list';
42
+ export type { BenefitRowProps, BenefitListProps } from './benefit-list';
37
43
  export * from './text-field';
38
44
  export * from './segmented-control';
39
45
  export { SearchInput } from './search-input';
@@ -1 +1 @@
1
- {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../src/index.web.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../src/index.web.ts"],"names":[],"mappings":"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"}
@@ -18,8 +18,21 @@ export type TextFieldInputProps = Omit<TextInputProps, 'value' | 'onChangeText'
18
18
  isInvalid?: boolean;
19
19
  inputRef?: React.RefObject<TextInput | null> | React.ForwardedRef<TextInput>;
20
20
  placeholder?: string | null | undefined;
21
+ /**
22
+ * Render the field with a Material-style floating label. When `true`, the
23
+ * {@link label} sits inside the field as the placeholder while the input is
24
+ * empty AND unfocused; on focus OR when a value is present it animates up to a
25
+ * small caption pinned to the top of the field and the typed value shows
26
+ * below it. Opt-in — the default (`false`) keeps the existing chrome where the
27
+ * label lives above the field (`TextFieldLabel`) and the placeholder is plain.
28
+ *
29
+ * Cross-platform: the animation is driven by focus + value-presence state (no
30
+ * CSS `:placeholder-shown` / `peer-focus`), so web and native behave
31
+ * identically. Respects reduced-motion (snaps instead of animating).
32
+ */
33
+ floatingLabel?: boolean;
21
34
  };
22
- export declare function TextFieldInput({ label, placeholder, value, onChangeText, onFocus, onBlur, isInvalid, inputRef, style, ...rest }: TextFieldInputProps): import("react/jsx-runtime").JSX.Element;
35
+ export declare function TextFieldInput({ label, placeholder, value, onChangeText, onFocus, onBlur, isInvalid, inputRef, style, floatingLabel, ...rest }: TextFieldInputProps): import("react/jsx-runtime").JSX.Element;
23
36
  export declare function TextFieldLabel({ nativeID, children, }: React.PropsWithChildren<{
24
37
  nativeID?: string;
25
38
  }>): import("react/jsx-runtime").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/text-field/index.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,kBAAkB,EAGvB,SAAS,EACT,KAAK,cAAc,EAGnB,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAKtB,OAAO,EAML,KAAK,aAAa,EAClB,KAAK,aAAa,EACnB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAkB,KAAK,KAAK,IAAI,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAwB7E,MAAM,MAAM,cAAc,GAAG,KAAK,CAAC,iBAAiB,CAClD;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,aAAa,CACxC,CAAC;AAEF,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,SAAiB,EAAE,KAAK,EAAE,EAAE,cAAc,2CA0C/E;AAED,wBAAgB,oBAAoB;;;;;EA0BnC;AAED,MAAM,MAAM,mBAAmB,GAAG,IAAI,CACpC,cAAc,EACd,OAAO,GAAG,cAAc,GAAG,aAAa,CACzC,GAAG;IACF,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;IAC7E,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACzC,CAAC;AAEF,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,WAAW,EACX,KAAK,EACL,YAAY,EACZ,OAAO,EACP,MAAM,EACN,SAAS,EACT,QAAQ,EACR,KAAK,EACL,GAAG,IAAI,EACR,EAAE,mBAAmB,2CAmGrB;AAED,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,QAAQ,GACT,EAAE,KAAK,CAAC,iBAAiB,CAAC;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,2CAchD;AAED,wBAAgB,aAAa,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,KAAK,CAAC,aAAa,CAAC,YAAY,CAAC,CAAA;CAAE,2CA8BxF;AAED,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,KAAK,EACL,iBAAiB,EACjB,KAAK,GACN,EAAE,KAAK,CAAC,iBAAiB,CACxB,aAAa,GAAG;IACd,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB,CAAC,EAAE,kBAAkB,CAAC,mBAAmB,CAAC,CAAC;CAC7D,CACF,2CAqBA;AAED,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf,2CAkDA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/text-field/index.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,kBAAkB,EAIvB,SAAS,EACT,KAAK,cAAc,EAGnB,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAMtB,OAAO,EAML,KAAK,aAAa,EAClB,KAAK,aAAa,EACnB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAkB,KAAK,KAAK,IAAI,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAwB7E,MAAM,MAAM,cAAc,GAAG,KAAK,CAAC,iBAAiB,CAClD;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,aAAa,CACxC,CAAC;AAEF,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,SAAiB,EAAE,KAAK,EAAE,EAAE,cAAc,2CA0C/E;AAED,wBAAgB,oBAAoB;;;;;EA0BnC;AAED,MAAM,MAAM,mBAAmB,GAAG,IAAI,CACpC,cAAc,EACd,OAAO,GAAG,cAAc,GAAG,aAAa,CACzC,GAAG;IACF,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;IAC7E,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACxC;;;;;;;;;;;OAWG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,WAAW,EACX,KAAK,EACL,YAAY,EACZ,OAAO,EACP,MAAM,EACN,SAAS,EACT,QAAQ,EACR,KAAK,EACL,aAAqB,EACrB,GAAG,IAAI,EACR,EAAE,mBAAmB,2CA4HrB;AA4KD,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,QAAQ,GACT,EAAE,KAAK,CAAC,iBAAiB,CAAC;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,2CAchD;AAED,wBAAgB,aAAa,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,KAAK,CAAC,aAAa,CAAC,YAAY,CAAC,CAAA;CAAE,2CA8BxF;AAED,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,KAAK,EACL,iBAAiB,EACjB,KAAK,GACN,EAAE,KAAK,CAAC,iBAAiB,CACxB,aAAa,GAAG;IACd,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB,CAAC,EAAE,kBAAkB,CAAC,mBAAmB,CAAC,CAAC;CAC7D,CACF,2CAqBA;AAED,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf,2CAkDA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/bloom",
3
- "version": "0.19.1",
3
+ "version": "0.20.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",
@@ -61,6 +61,28 @@
61
61
  "default": "./lib/commonjs/theme/color-presets.js"
62
62
  }
63
63
  },
64
+ "./design-tokens": {
65
+ "react-native": "./src/design-tokens/index.ts",
66
+ "import": {
67
+ "types": "./lib/typescript/module/design-tokens/index.d.ts",
68
+ "default": "./lib/module/design-tokens/index.js"
69
+ },
70
+ "require": {
71
+ "types": "./lib/typescript/commonjs/design-tokens/index.d.ts",
72
+ "default": "./lib/commonjs/design-tokens/index.js"
73
+ }
74
+ },
75
+ "./tailwind-preset": {
76
+ "react-native": "./src/design-tokens/tailwind-preset.ts",
77
+ "import": {
78
+ "types": "./lib/typescript/module/design-tokens/tailwind-preset.d.ts",
79
+ "default": "./lib/module/design-tokens/tailwind-preset.js"
80
+ },
81
+ "require": {
82
+ "types": "./lib/typescript/commonjs/design-tokens/tailwind-preset.d.ts",
83
+ "default": "./lib/commonjs/design-tokens/tailwind-preset.js"
84
+ }
85
+ },
64
86
  "./portal": {
65
87
  "react-native": "./src/portal/index.tsx",
66
88
  "browser": {
@@ -360,6 +382,33 @@
360
382
  "default": "./lib/commonjs/icon-circle/index.js"
361
383
  }
362
384
  },
385
+ "./connection-dots": {
386
+ "react-native": "./src/connection-dots/index.ts",
387
+ "browser": {
388
+ "types": "./lib/typescript/module/connection-dots/index.web.d.ts",
389
+ "import": "./lib/module/connection-dots/index.web.js",
390
+ "require": "./lib/commonjs/connection-dots/index.web.js"
391
+ },
392
+ "import": {
393
+ "types": "./lib/typescript/module/connection-dots/index.d.ts",
394
+ "default": "./lib/module/connection-dots/index.js"
395
+ },
396
+ "require": {
397
+ "types": "./lib/typescript/commonjs/connection-dots/index.d.ts",
398
+ "default": "./lib/commonjs/connection-dots/index.js"
399
+ }
400
+ },
401
+ "./benefit-list": {
402
+ "react-native": "./src/benefit-list/index.tsx",
403
+ "import": {
404
+ "types": "./lib/typescript/module/benefit-list/index.d.ts",
405
+ "default": "./lib/module/benefit-list/index.js"
406
+ },
407
+ "require": {
408
+ "types": "./lib/typescript/commonjs/benefit-list/index.d.ts",
409
+ "default": "./lib/commonjs/benefit-list/index.js"
410
+ }
411
+ },
363
412
  "./text-field": {
364
413
  "react-native": "./src/text-field/index.tsx",
365
414
  "import": {
@@ -0,0 +1,108 @@
1
+ import React from 'react';
2
+ import { Text, View } from 'react-native';
3
+ import { render } from '@testing-library/react-native';
4
+
5
+ import { BloomThemeProvider } from '../theme/BloomThemeProvider';
6
+ import { BenefitList, BenefitRow } from '../benefit-list';
7
+
8
+ function renderWithTheme(ui: React.ReactElement) {
9
+ return render(
10
+ <BloomThemeProvider mode="light" colorPreset="teal">
11
+ {ui}
12
+ </BloomThemeProvider>,
13
+ );
14
+ }
15
+
16
+ describe('BenefitRow', () => {
17
+ it('renders the label as caption text', () => {
18
+ const { getByText } = renderWithTheme(
19
+ <BenefitRow icon={<View />} label="Your data stays private" />,
20
+ );
21
+ expect(getByText('Your data stays private')).toBeTruthy();
22
+ });
23
+
24
+ it('renders children when provided instead of label', () => {
25
+ const { getByText, queryByText } = renderWithTheme(
26
+ <BenefitRow icon={<View />} label="ignored">
27
+ Encrypted end to end
28
+ </BenefitRow>,
29
+ );
30
+ expect(getByText('Encrypted end to end')).toBeTruthy();
31
+ expect(queryByText('ignored')).toBeNull();
32
+ });
33
+
34
+ it('renders the (decorative, a11y-hidden) icon slot', () => {
35
+ const { getByText } = renderWithTheme(
36
+ <BenefitRow icon={<Text>ICON</Text>} label="Row" />,
37
+ );
38
+ // The icon square is aria-hidden (the caption carries the meaning), so the
39
+ // icon node is only reachable with hidden elements included.
40
+ expect(getByText('ICON', { includeHiddenElements: true })).toBeTruthy();
41
+ });
42
+
43
+ it('derives an accessibility label from string content', () => {
44
+ const { getByLabelText } = renderWithTheme(
45
+ <BenefitRow icon={<View />} label="Revoke anytime" />,
46
+ );
47
+ const node = getByLabelText('Revoke anytime');
48
+ expect(node.props.accessibilityRole).toBe('text');
49
+ });
50
+
51
+ it('uses an explicit accessibilityLabel over the text content', () => {
52
+ const { getByLabelText } = renderWithTheme(
53
+ <BenefitRow
54
+ icon={<View />}
55
+ label="Revoke anytime"
56
+ accessibilityLabel="Custom label"
57
+ />,
58
+ );
59
+ expect(getByLabelText('Custom label')).toBeTruthy();
60
+ });
61
+ });
62
+
63
+ describe('BenefitList', () => {
64
+ it('renders stacked rows', () => {
65
+ const { getByText } = renderWithTheme(
66
+ <BenefitList accessibilityLabel="Benefits">
67
+ <BenefitRow icon={<View />} label="First benefit" />
68
+ <BenefitRow icon={<View />} label="Second benefit" />
69
+ <BenefitRow icon={<View />} label="Third benefit" />
70
+ </BenefitList>,
71
+ );
72
+ expect(getByText('First benefit')).toBeTruthy();
73
+ expect(getByText('Second benefit')).toBeTruthy();
74
+ expect(getByText('Third benefit')).toBeTruthy();
75
+ });
76
+
77
+ it('applies the card accessibility label', () => {
78
+ const { getByLabelText } = renderWithTheme(
79
+ <BenefitList accessibilityLabel="What you're sharing">
80
+ <BenefitRow icon={<View />} label="Profile photo" />
81
+ </BenefitList>,
82
+ );
83
+ expect(getByLabelText("What you're sharing")).toBeTruthy();
84
+ });
85
+
86
+ it('renders a bordered, rounded card surface using the token roles', () => {
87
+ const { getByLabelText } = renderWithTheme(
88
+ <BenefitList accessibilityLabel="card">
89
+ <BenefitRow icon={<View />} label="Row" />
90
+ </BenefitList>,
91
+ );
92
+ const card = getByLabelText('card');
93
+ // The card style is a nested style array; flatten it fully to assert the
94
+ // radius-20 / border-hairline (0.5px) / border-image token values landed.
95
+ const flat: Record<string, unknown> = {};
96
+ const visit = (s: unknown): void => {
97
+ if (Array.isArray(s)) {
98
+ s.forEach(visit);
99
+ } else if (s && typeof s === 'object') {
100
+ Object.assign(flat, s);
101
+ }
102
+ };
103
+ visit(card.props.style);
104
+ expect(flat.borderRadius).toBe(20);
105
+ expect(flat.borderWidth).toBe(0.5);
106
+ expect(flat.borderColor).toBeTruthy();
107
+ });
108
+ });
@@ -0,0 +1,86 @@
1
+ import React from 'react';
2
+ import { Text, View } from 'react-native';
3
+ import { render } from '@testing-library/react-native';
4
+
5
+ import { BloomThemeProvider } from '../theme/BloomThemeProvider';
6
+ import { ConnectionDots } from '../connection-dots';
7
+
8
+ function renderWithTheme(ui: React.ReactElement) {
9
+ return render(
10
+ <BloomThemeProvider mode="light" colorPreset="teal">
11
+ {ui}
12
+ </BloomThemeProvider>,
13
+ );
14
+ }
15
+
16
+ /**
17
+ * Count animated dot nodes in the rendered tree. On the native fork each dot is
18
+ * an `Animated.View` (the mock emits a host node of that type); the left/right
19
+ * slots use plain `View`s, so the count equals the number of dots.
20
+ */
21
+ function countAnimatedDots(json: unknown): number {
22
+ return (JSON.stringify(json).match(/Animated\.View/g) ?? []).length;
23
+ }
24
+
25
+ describe('ConnectionDots', () => {
26
+ it('renders the left and right slots', () => {
27
+ const { getByText } = renderWithTheme(
28
+ <ConnectionDots left={<Text>LEFT</Text>} right={<Text>RIGHT</Text>} />,
29
+ );
30
+ expect(getByText('LEFT')).toBeTruthy();
31
+ expect(getByText('RIGHT')).toBeTruthy();
32
+ });
33
+
34
+ it('exposes a single accessibility label for the (decorative) dots', () => {
35
+ const { getByLabelText } = renderWithTheme(
36
+ <ConnectionDots
37
+ left={<View />}
38
+ right={<View />}
39
+ accessibilityLabel="Linking your account"
40
+ />,
41
+ );
42
+ const node = getByLabelText('Linking your account');
43
+ expect(node.props.accessibilityRole).toBe('image');
44
+ });
45
+
46
+ it('defaults the accessibility label to "Connecting"', () => {
47
+ const { getByLabelText } = renderWithTheme(
48
+ <ConnectionDots left={<View />} right={<View />} />,
49
+ );
50
+ expect(getByLabelText('Connecting')).toBeTruthy();
51
+ });
52
+
53
+ it('renders dotCount dots', () => {
54
+ const { toJSON } = renderWithTheme(
55
+ <ConnectionDots left={<View />} right={<View />} dotCount={8} />,
56
+ );
57
+ expect(countAnimatedDots(toJSON())).toBe(8);
58
+ });
59
+
60
+ it('renders the default of 6 dots when dotCount is omitted', () => {
61
+ const { toJSON } = renderWithTheme(
62
+ <ConnectionDots left={<View />} right={<View />} />,
63
+ );
64
+ expect(countAnimatedDots(toJSON())).toBe(6);
65
+ });
66
+
67
+ it('renders dots under reduced motion without crashing', () => {
68
+ const { toJSON, getByLabelText } = renderWithTheme(
69
+ <ConnectionDots
70
+ left={<View />}
71
+ right={<View />}
72
+ dotCount={5}
73
+ reducedMotion
74
+ />,
75
+ );
76
+ expect(getByLabelText('Connecting')).toBeTruthy();
77
+ expect(countAnimatedDots(toJSON())).toBe(5);
78
+ });
79
+
80
+ it('clamps a zero/negative dotCount to at least one dot', () => {
81
+ const { toJSON } = renderWithTheme(
82
+ <ConnectionDots left={<View />} right={<View />} dotCount={0} />,
83
+ );
84
+ expect(countAnimatedDots(toJSON())).toBe(1);
85
+ });
86
+ });
@@ -0,0 +1,93 @@
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react-native';
3
+
4
+ import { BloomThemeProvider } from '../theme/BloomThemeProvider';
5
+ import { TextField, TextFieldInput } from '../text-field';
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('TextFieldInput floatingLabel', () => {
16
+ it('renders the floating label text inside the field', () => {
17
+ const { toJSON } = renderWithTheme(
18
+ <TextField>
19
+ <TextFieldInput floatingLabel label="Email" value="" onChangeText={() => {}} />
20
+ </TextField>,
21
+ );
22
+ // The visual label renders in an animated text node; assert it lands in the
23
+ // tree (it's aria-hidden because the input carries the accessibilityLabel).
24
+ expect(JSON.stringify(toJSON())).toContain('Email');
25
+ });
26
+
27
+ it('drives accessibilityLabel from the label', () => {
28
+ const { getByLabelText } = renderWithTheme(
29
+ <TextField>
30
+ <TextFieldInput floatingLabel label="Email" value="" onChangeText={() => {}} />
31
+ </TextField>,
32
+ );
33
+ expect(getByLabelText('Email')).toBeTruthy();
34
+ });
35
+
36
+ it('suppresses the native placeholder (the floating label replaces it)', () => {
37
+ const { getByLabelText } = renderWithTheme(
38
+ <TextField>
39
+ <TextFieldInput floatingLabel label="Email" value="" onChangeText={() => {}} />
40
+ </TextField>,
41
+ );
42
+ const input = getByLabelText('Email');
43
+ expect(input.props.placeholder).toBeUndefined();
44
+ });
45
+
46
+ it('self-wraps in a TextField when rendered without a root', () => {
47
+ const { getByLabelText, toJSON } = renderWithTheme(
48
+ <TextFieldInput floatingLabel label="Standalone" value="" onChangeText={() => {}} />,
49
+ );
50
+ expect(getByLabelText('Standalone')).toBeTruthy();
51
+ expect(JSON.stringify(toJSON())).toContain('Standalone');
52
+ });
53
+
54
+ it('reflects a present value on the input', () => {
55
+ const { getByLabelText } = renderWithTheme(
56
+ <TextField>
57
+ <TextFieldInput
58
+ floatingLabel
59
+ label="Email"
60
+ value="nate@oxy.so"
61
+ onChangeText={() => {}}
62
+ />
63
+ </TextField>,
64
+ );
65
+ expect(getByLabelText('Email').props.value).toBe('nate@oxy.so');
66
+ });
67
+ });
68
+
69
+ describe('TextFieldInput default (non-floating) is unchanged', () => {
70
+ it('falls back to the label as the placeholder when none is given', () => {
71
+ const { getByLabelText } = renderWithTheme(
72
+ <TextField>
73
+ <TextFieldInput label="Username" value="" onChangeText={() => {}} />
74
+ </TextField>,
75
+ );
76
+ // Default variant keeps the existing behavior: placeholder defaults to label.
77
+ expect(getByLabelText('Username').props.placeholder).toBe('Username');
78
+ });
79
+
80
+ it('honors an explicit placeholder', () => {
81
+ const { getByLabelText } = renderWithTheme(
82
+ <TextField>
83
+ <TextFieldInput
84
+ label="Username"
85
+ placeholder="oxylander"
86
+ value=""
87
+ onChangeText={() => {}}
88
+ />
89
+ </TextField>,
90
+ );
91
+ expect(getByLabelText('Username').props.placeholder).toBe('oxylander');
92
+ });
93
+ });
@@ -0,0 +1,169 @@
1
+ import {
2
+ bloomTailwindPreset,
3
+ bloomThemeCss,
4
+ bloomThemeBlock,
5
+ FILL_ROLES,
6
+ TEXT_ROLES,
7
+ BORDER_ROLES,
8
+ SPACING,
9
+ RADIUS,
10
+ BORDER_WIDTH,
11
+ TYPOGRAPHY,
12
+ FONT_FAMILY_VARS,
13
+ SHADOW_BOX,
14
+ bloomShadowStyle,
15
+ } from '../design-tokens';
16
+ import { CANONICAL_TOKENS } from '../theme/token-registry';
17
+
18
+ describe('design-tokens color roles', () => {
19
+ const allRoles = { ...FILL_ROLES, ...TEXT_ROLES, ...BORDER_ROLES };
20
+
21
+ it('every color role is a var(--canonical) reference (no hardcoded color)', () => {
22
+ for (const [role, value] of Object.entries(allRoles)) {
23
+ expect(value).toMatch(/^var\(--[a-z-]+\)$/);
24
+ // Never wrap a base token in hsl() — bloom base tokens are full rgb colors.
25
+ expect(value).not.toContain('hsl(');
26
+ }
27
+ });
28
+
29
+ it('every color role aliases an existing canonical theme token', () => {
30
+ for (const value of Object.values(allRoles)) {
31
+ const token = value.replace(/^var\(--/, '').replace(/\)$/, '');
32
+ expect(CANONICAL_TOKENS).toContain(token as never);
33
+ }
34
+ });
35
+ });
36
+
37
+ describe('design-tokens numeric scales', () => {
38
+ it('exposes the spec spacing scale + screen-margin', () => {
39
+ expect(SPACING).toMatchObject({
40
+ 'space-2': 2,
41
+ 'space-4': 4,
42
+ 'space-8': 8,
43
+ 'space-12': 12,
44
+ 'space-16': 16,
45
+ 'space-20': 20,
46
+ 'space-24': 24,
47
+ 'space-32': 32,
48
+ 'screen-margin': 20,
49
+ });
50
+ });
51
+
52
+ it('exposes the spec radius scale', () => {
53
+ expect(RADIUS).toMatchObject({
54
+ 'radius-8': 8,
55
+ 'radius-12': 12,
56
+ 'radius-20': 20,
57
+ 'radius-28': 28,
58
+ 'radius-max': 9999,
59
+ });
60
+ });
61
+
62
+ it('exposes the 0.5px hairline border width', () => {
63
+ expect(BORDER_WIDTH.hairline).toBe(0.5);
64
+ });
65
+ });
66
+
67
+ describe('design-tokens typography', () => {
68
+ it('covers every spec type role', () => {
69
+ const names = Object.keys(TYPOGRAPHY).sort();
70
+ expect(names).toEqual(
71
+ [
72
+ 'body',
73
+ 'bodySmall',
74
+ 'bodyTitleSmall',
75
+ 'buttonLarge',
76
+ 'caption',
77
+ 'headerBold',
78
+ 'sectionTitle',
79
+ 'subtitle',
80
+ ].sort(),
81
+ );
82
+ });
83
+
84
+ it('each role has a valid size/lineHeight/weight/family', () => {
85
+ for (const role of Object.values(TYPOGRAPHY)) {
86
+ expect(role.size).toBeGreaterThan(0);
87
+ expect(role.lineHeight).toBeGreaterThanOrEqual(role.size);
88
+ expect(['400', '500', '600', '700']).toContain(role.weight);
89
+ expect(FONT_FAMILY_VARS[role.family]).toBeDefined();
90
+ }
91
+ });
92
+ });
93
+
94
+ describe('design-tokens Tailwind preset', () => {
95
+ const ext = bloomTailwindPreset.theme.extend;
96
+
97
+ it('only extends theme (additive, no top-level theme override)', () => {
98
+ expect(Object.keys(bloomTailwindPreset)).toEqual(['theme']);
99
+ expect(Object.keys(bloomTailwindPreset.theme)).toEqual(['extend']);
100
+ });
101
+
102
+ it('color roles reach background/text/border buckets', () => {
103
+ expect(ext.backgroundColor['fill']).toBe('var(--card)');
104
+ expect(ext.textColor['text-tertiary']).toBe('var(--muted-foreground)');
105
+ expect(ext.borderColor['border-input-active']).toBe('var(--ring)');
106
+ });
107
+
108
+ it('spacing/radius are emitted in px', () => {
109
+ expect(ext.spacing['space-8']).toBe('8px');
110
+ expect(ext.spacing['screen-margin']).toBe('20px');
111
+ expect(ext.borderRadius['radius-20']).toBe('20px');
112
+ expect(ext.borderWidth['hairline']).toBe('0.5px');
113
+ });
114
+
115
+ it('fontSize entries are [size, { lineHeight }] tuples', () => {
116
+ expect(ext.fontSize['body']).toEqual(['15px', { lineHeight: '22px' }]);
117
+ expect(ext.fontSize['headerBold']).toEqual(['28px', { lineHeight: '34px' }]);
118
+ });
119
+
120
+ it('fontFamily entries are [family, { fontWeight }] tuples', () => {
121
+ expect(ext.fontFamily['body']).toEqual([
122
+ 'var(--bloom-font-sans)',
123
+ { fontWeight: '400' },
124
+ ]);
125
+ expect(ext.fontFamily['headerBold']).toEqual([
126
+ 'var(--bloom-font-display)',
127
+ { fontWeight: '700' },
128
+ ]);
129
+ });
130
+
131
+ it('registers shadow-s and shadow-m box shadows', () => {
132
+ expect(ext.boxShadow['s']).toBe(SHADOW_BOX.s);
133
+ expect(ext.boxShadow['m']).toBe(SHADOW_BOX.m);
134
+ });
135
+
136
+ it('is frozen so consumer config merges cannot mutate it', () => {
137
+ expect(Object.isFrozen(bloomTailwindPreset)).toBe(true);
138
+ });
139
+ });
140
+
141
+ describe('design-tokens v4 @theme css', () => {
142
+ const css = bloomThemeCss();
143
+
144
+ it('emits --color-<role> aliases as direct var(--x) (never hsl-wrapped)', () => {
145
+ expect(css).toContain('--color-fill: var(--card);');
146
+ expect(css).toContain('--color-text-tertiary: var(--muted-foreground);');
147
+ expect(css).not.toContain('hsl(var(');
148
+ });
149
+
150
+ it('emits spacing, radius, typography and shadow vars', () => {
151
+ expect(css).toContain('--spacing-space-8: 8px;');
152
+ expect(css).toContain('--radius-radius-20: 20px;');
153
+ expect(css).toContain('--text-body: 15px;');
154
+ expect(css).toContain('--text-body--line-height: 22px;');
155
+ expect(css).toContain('--font-body: var(--bloom-font-sans);');
156
+ expect(css).toContain('--shadow-s:');
157
+ });
158
+
159
+ it('wraps the body in a @theme block', () => {
160
+ expect(bloomThemeBlock()).toMatch(/^@theme \{[\s\S]*\}$/);
161
+ });
162
+ });
163
+
164
+ describe('design-tokens shadow style (web default)', () => {
165
+ it('returns a boxShadow style on the web/default fork', () => {
166
+ const style = bloomShadowStyle('m');
167
+ expect(style).toHaveProperty('boxShadow', SHADOW_BOX.m);
168
+ });
169
+ });
@@ -0,0 +1,50 @@
1
+ import React from 'react';
2
+ import { View } from 'react-native';
3
+ import type { Meta, StoryObj } from '@storybook/react-vite';
4
+
5
+ import { BenefitList, BenefitRow } from './index';
6
+ import { Person_Stroke2_Corner0_Rounded as PersonIcon } from '../icons/Person';
7
+ import { Lock_Stroke2_Corner0_Rounded as LockIcon } from '../icons/Lock';
8
+ import { Shield_Stroke2_Corner0_Rounded as ShieldIcon } from '../icons/Shield';
9
+
10
+ const meta: Meta<typeof BenefitList> = {
11
+ title: 'Components/BenefitList',
12
+ component: BenefitList,
13
+ };
14
+
15
+ export default meta;
16
+
17
+ type Story = StoryObj<typeof BenefitList>;
18
+
19
+ export const Consent: Story = {
20
+ render: () => (
21
+ <View style={{ width: 360, padding: 24 }}>
22
+ <BenefitList accessibilityLabel="What you're sharing">
23
+ <BenefitRow
24
+ icon={<PersonIcon size="sm" />}
25
+ label="Share your name and profile photo"
26
+ />
27
+ <BenefitRow
28
+ icon={<LockIcon size="sm" />}
29
+ label="Your password is never shared with this app"
30
+ />
31
+ <BenefitRow
32
+ icon={<ShieldIcon size="sm" />}
33
+ label="You can revoke access at any time from settings"
34
+ />
35
+ </BenefitList>
36
+ </View>
37
+ ),
38
+ };
39
+
40
+ export const SingleRow: Story = {
41
+ render: () => (
42
+ <View style={{ width: 360, padding: 24 }}>
43
+ <BenefitList>
44
+ <BenefitRow icon={<ShieldIcon size="sm" />}>
45
+ End-to-end encrypted by default
46
+ </BenefitRow>
47
+ </BenefitList>
48
+ </View>
49
+ ),
50
+ };