@oxy.so/bloom 6.1.0 → 6.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 (205) hide show
  1. package/README.md +1 -1
  2. package/docs/adoption-matrix.mdx +3 -2
  3. package/docs/chat-composer.mdx +5 -1
  4. package/docs/cover-header.mdx +56 -0
  5. package/lib/commonjs/ai-profile-card/AiProfileCard.js +186 -263
  6. package/lib/commonjs/ai-profile-card/AiProfileCard.js.map +1 -1
  7. package/lib/commonjs/chat-composer/SuggestionList.js +3 -1
  8. package/lib/commonjs/chat-composer/SuggestionList.js.map +1 -1
  9. package/lib/commonjs/chat-composer/SuggestionList.web.js +3 -1
  10. package/lib/commonjs/chat-composer/SuggestionList.web.js.map +1 -1
  11. package/lib/commonjs/chat-composer/create-suggestion-list.js +64 -10
  12. package/lib/commonjs/chat-composer/create-suggestion-list.js.map +1 -1
  13. package/lib/commonjs/chat-composer/messages.js +7 -0
  14. package/lib/commonjs/chat-composer/messages.js.map +1 -1
  15. package/lib/commonjs/cover-header/CoverHeader.js +83 -0
  16. package/lib/commonjs/cover-header/CoverHeader.js.map +1 -0
  17. package/lib/commonjs/cover-header/CoverImage.js +72 -0
  18. package/lib/commonjs/cover-header/CoverImage.js.map +1 -0
  19. package/lib/commonjs/cover-header/index.js +13 -0
  20. package/lib/commonjs/cover-header/index.js.map +1 -0
  21. package/lib/commonjs/cover-header/types.js +6 -0
  22. package/lib/commonjs/cover-header/types.js.map +1 -0
  23. package/lib/commonjs/index.js +951 -939
  24. package/lib/commonjs/index.js.map +1 -1
  25. package/lib/commonjs/index.web.js +951 -939
  26. package/lib/commonjs/index.web.js.map +1 -1
  27. package/lib/commonjs/locale/translations/ar.js +7 -0
  28. package/lib/commonjs/locale/translations/ar.js.map +1 -1
  29. package/lib/commonjs/locale/translations/bn.js +7 -0
  30. package/lib/commonjs/locale/translations/bn.js.map +1 -1
  31. package/lib/commonjs/locale/translations/ca.js +7 -0
  32. package/lib/commonjs/locale/translations/ca.js.map +1 -1
  33. package/lib/commonjs/locale/translations/de.js +7 -0
  34. package/lib/commonjs/locale/translations/de.js.map +1 -1
  35. package/lib/commonjs/locale/translations/es.js +7 -0
  36. package/lib/commonjs/locale/translations/es.js.map +1 -1
  37. package/lib/commonjs/locale/translations/fr.js +7 -0
  38. package/lib/commonjs/locale/translations/fr.js.map +1 -1
  39. package/lib/commonjs/locale/translations/hi.js +7 -0
  40. package/lib/commonjs/locale/translations/hi.js.map +1 -1
  41. package/lib/commonjs/locale/translations/id.js +7 -0
  42. package/lib/commonjs/locale/translations/id.js.map +1 -1
  43. package/lib/commonjs/locale/translations/it.js +7 -0
  44. package/lib/commonjs/locale/translations/it.js.map +1 -1
  45. package/lib/commonjs/locale/translations/ja.js +7 -0
  46. package/lib/commonjs/locale/translations/ja.js.map +1 -1
  47. package/lib/commonjs/locale/translations/pt.js +7 -0
  48. package/lib/commonjs/locale/translations/pt.js.map +1 -1
  49. package/lib/commonjs/locale/translations/ru.js +7 -0
  50. package/lib/commonjs/locale/translations/ru.js.map +1 -1
  51. package/lib/commonjs/locale/translations/tr.js +7 -0
  52. package/lib/commonjs/locale/translations/tr.js.map +1 -1
  53. package/lib/commonjs/locale/translations/zh.js +7 -0
  54. package/lib/commonjs/locale/translations/zh.js.map +1 -1
  55. package/lib/module/ai-profile-card/AiProfileCard.js +185 -262
  56. package/lib/module/ai-profile-card/AiProfileCard.js.map +1 -1
  57. package/lib/module/chat-composer/SuggestionList.js +3 -1
  58. package/lib/module/chat-composer/SuggestionList.js.map +1 -1
  59. package/lib/module/chat-composer/SuggestionList.web.js +3 -1
  60. package/lib/module/chat-composer/SuggestionList.web.js.map +1 -1
  61. package/lib/module/chat-composer/create-suggestion-list.js +64 -10
  62. package/lib/module/chat-composer/create-suggestion-list.js.map +1 -1
  63. package/lib/module/chat-composer/messages.js +7 -0
  64. package/lib/module/chat-composer/messages.js.map +1 -1
  65. package/lib/module/cover-header/CoverHeader.js +78 -0
  66. package/lib/module/cover-header/CoverHeader.js.map +1 -0
  67. package/lib/module/cover-header/CoverImage.js +68 -0
  68. package/lib/module/cover-header/CoverImage.js.map +1 -0
  69. package/lib/module/cover-header/index.js +4 -0
  70. package/lib/module/cover-header/index.js.map +1 -0
  71. package/lib/module/cover-header/types.js +4 -0
  72. package/lib/module/cover-header/types.js.map +1 -0
  73. package/lib/module/index.js +1 -0
  74. package/lib/module/index.js.map +1 -1
  75. package/lib/module/index.web.js +1 -0
  76. package/lib/module/index.web.js.map +1 -1
  77. package/lib/module/locale/translations/ar.js +7 -0
  78. package/lib/module/locale/translations/ar.js.map +1 -1
  79. package/lib/module/locale/translations/bn.js +7 -0
  80. package/lib/module/locale/translations/bn.js.map +1 -1
  81. package/lib/module/locale/translations/ca.js +7 -0
  82. package/lib/module/locale/translations/ca.js.map +1 -1
  83. package/lib/module/locale/translations/de.js +7 -0
  84. package/lib/module/locale/translations/de.js.map +1 -1
  85. package/lib/module/locale/translations/es.js +7 -0
  86. package/lib/module/locale/translations/es.js.map +1 -1
  87. package/lib/module/locale/translations/fr.js +7 -0
  88. package/lib/module/locale/translations/fr.js.map +1 -1
  89. package/lib/module/locale/translations/hi.js +7 -0
  90. package/lib/module/locale/translations/hi.js.map +1 -1
  91. package/lib/module/locale/translations/id.js +7 -0
  92. package/lib/module/locale/translations/id.js.map +1 -1
  93. package/lib/module/locale/translations/it.js +7 -0
  94. package/lib/module/locale/translations/it.js.map +1 -1
  95. package/lib/module/locale/translations/ja.js +7 -0
  96. package/lib/module/locale/translations/ja.js.map +1 -1
  97. package/lib/module/locale/translations/pt.js +7 -0
  98. package/lib/module/locale/translations/pt.js.map +1 -1
  99. package/lib/module/locale/translations/ru.js +7 -0
  100. package/lib/module/locale/translations/ru.js.map +1 -1
  101. package/lib/module/locale/translations/tr.js +7 -0
  102. package/lib/module/locale/translations/tr.js.map +1 -1
  103. package/lib/module/locale/translations/zh.js +7 -0
  104. package/lib/module/locale/translations/zh.js.map +1 -1
  105. package/lib/typescript/commonjs/ai-profile-card/AiProfileCard.d.ts.map +1 -1
  106. package/lib/typescript/commonjs/chat-composer/SuggestionList.d.ts +1 -1
  107. package/lib/typescript/commonjs/chat-composer/SuggestionList.d.ts.map +1 -1
  108. package/lib/typescript/commonjs/chat-composer/SuggestionList.web.d.ts +1 -1
  109. package/lib/typescript/commonjs/chat-composer/SuggestionList.web.d.ts.map +1 -1
  110. package/lib/typescript/commonjs/chat-composer/create-suggestion-list.d.ts +4 -2
  111. package/lib/typescript/commonjs/chat-composer/create-suggestion-list.d.ts.map +1 -1
  112. package/lib/typescript/commonjs/chat-composer/messages.d.ts +6 -0
  113. package/lib/typescript/commonjs/chat-composer/messages.d.ts.map +1 -1
  114. package/lib/typescript/commonjs/chat-composer/types.d.ts +17 -0
  115. package/lib/typescript/commonjs/chat-composer/types.d.ts.map +1 -1
  116. package/lib/typescript/commonjs/cover-header/CoverHeader.d.ts +21 -0
  117. package/lib/typescript/commonjs/cover-header/CoverHeader.d.ts.map +1 -0
  118. package/lib/typescript/commonjs/cover-header/CoverImage.d.ts +11 -0
  119. package/lib/typescript/commonjs/cover-header/CoverImage.d.ts.map +1 -0
  120. package/lib/typescript/commonjs/cover-header/index.d.ts +3 -0
  121. package/lib/typescript/commonjs/cover-header/index.d.ts.map +1 -0
  122. package/lib/typescript/commonjs/cover-header/types.d.ts +33 -0
  123. package/lib/typescript/commonjs/cover-header/types.d.ts.map +1 -0
  124. package/lib/typescript/commonjs/index.d.ts +1 -0
  125. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  126. package/lib/typescript/commonjs/index.web.d.ts +1 -0
  127. package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
  128. package/lib/typescript/commonjs/locale/translations/ar.d.ts.map +1 -1
  129. package/lib/typescript/commonjs/locale/translations/bn.d.ts.map +1 -1
  130. package/lib/typescript/commonjs/locale/translations/ca.d.ts.map +1 -1
  131. package/lib/typescript/commonjs/locale/translations/de.d.ts.map +1 -1
  132. package/lib/typescript/commonjs/locale/translations/es.d.ts.map +1 -1
  133. package/lib/typescript/commonjs/locale/translations/fr.d.ts.map +1 -1
  134. package/lib/typescript/commonjs/locale/translations/hi.d.ts.map +1 -1
  135. package/lib/typescript/commonjs/locale/translations/id.d.ts.map +1 -1
  136. package/lib/typescript/commonjs/locale/translations/it.d.ts.map +1 -1
  137. package/lib/typescript/commonjs/locale/translations/ja.d.ts.map +1 -1
  138. package/lib/typescript/commonjs/locale/translations/pt.d.ts.map +1 -1
  139. package/lib/typescript/commonjs/locale/translations/ru.d.ts.map +1 -1
  140. package/lib/typescript/commonjs/locale/translations/tr.d.ts.map +1 -1
  141. package/lib/typescript/commonjs/locale/translations/zh.d.ts.map +1 -1
  142. package/lib/typescript/module/ai-profile-card/AiProfileCard.d.ts.map +1 -1
  143. package/lib/typescript/module/chat-composer/SuggestionList.d.ts +1 -1
  144. package/lib/typescript/module/chat-composer/SuggestionList.d.ts.map +1 -1
  145. package/lib/typescript/module/chat-composer/SuggestionList.web.d.ts +1 -1
  146. package/lib/typescript/module/chat-composer/SuggestionList.web.d.ts.map +1 -1
  147. package/lib/typescript/module/chat-composer/create-suggestion-list.d.ts +4 -2
  148. package/lib/typescript/module/chat-composer/create-suggestion-list.d.ts.map +1 -1
  149. package/lib/typescript/module/chat-composer/messages.d.ts +6 -0
  150. package/lib/typescript/module/chat-composer/messages.d.ts.map +1 -1
  151. package/lib/typescript/module/chat-composer/types.d.ts +17 -0
  152. package/lib/typescript/module/chat-composer/types.d.ts.map +1 -1
  153. package/lib/typescript/module/cover-header/CoverHeader.d.ts +21 -0
  154. package/lib/typescript/module/cover-header/CoverHeader.d.ts.map +1 -0
  155. package/lib/typescript/module/cover-header/CoverImage.d.ts +11 -0
  156. package/lib/typescript/module/cover-header/CoverImage.d.ts.map +1 -0
  157. package/lib/typescript/module/cover-header/index.d.ts +3 -0
  158. package/lib/typescript/module/cover-header/index.d.ts.map +1 -0
  159. package/lib/typescript/module/cover-header/types.d.ts +33 -0
  160. package/lib/typescript/module/cover-header/types.d.ts.map +1 -0
  161. package/lib/typescript/module/index.d.ts +1 -0
  162. package/lib/typescript/module/index.d.ts.map +1 -1
  163. package/lib/typescript/module/index.web.d.ts +1 -0
  164. package/lib/typescript/module/index.web.d.ts.map +1 -1
  165. package/lib/typescript/module/locale/translations/ar.d.ts.map +1 -1
  166. package/lib/typescript/module/locale/translations/bn.d.ts.map +1 -1
  167. package/lib/typescript/module/locale/translations/ca.d.ts.map +1 -1
  168. package/lib/typescript/module/locale/translations/de.d.ts.map +1 -1
  169. package/lib/typescript/module/locale/translations/es.d.ts.map +1 -1
  170. package/lib/typescript/module/locale/translations/fr.d.ts.map +1 -1
  171. package/lib/typescript/module/locale/translations/hi.d.ts.map +1 -1
  172. package/lib/typescript/module/locale/translations/id.d.ts.map +1 -1
  173. package/lib/typescript/module/locale/translations/it.d.ts.map +1 -1
  174. package/lib/typescript/module/locale/translations/ja.d.ts.map +1 -1
  175. package/lib/typescript/module/locale/translations/pt.d.ts.map +1 -1
  176. package/lib/typescript/module/locale/translations/ru.d.ts.map +1 -1
  177. package/lib/typescript/module/locale/translations/tr.d.ts.map +1 -1
  178. package/lib/typescript/module/locale/translations/zh.d.ts.map +1 -1
  179. package/package.json +18 -1
  180. package/src/ai-profile-card/AiProfileCard.tsx +159 -241
  181. package/src/chat-composer/SuggestionList.tsx +2 -1
  182. package/src/chat-composer/SuggestionList.web.tsx +2 -1
  183. package/src/chat-composer/create-suggestion-list.tsx +54 -8
  184. package/src/chat-composer/messages.ts +9 -0
  185. package/src/chat-composer/types.ts +17 -0
  186. package/src/cover-header/CoverHeader.tsx +75 -0
  187. package/src/cover-header/CoverImage.tsx +60 -0
  188. package/src/cover-header/index.ts +3 -0
  189. package/src/cover-header/types.ts +30 -0
  190. package/src/index.ts +1 -0
  191. package/src/index.web.ts +1 -0
  192. package/src/locale/translations/ar.ts +3 -0
  193. package/src/locale/translations/bn.ts +3 -0
  194. package/src/locale/translations/ca.ts +3 -0
  195. package/src/locale/translations/de.ts +3 -0
  196. package/src/locale/translations/es.ts +3 -0
  197. package/src/locale/translations/fr.ts +3 -0
  198. package/src/locale/translations/hi.ts +3 -0
  199. package/src/locale/translations/id.ts +3 -0
  200. package/src/locale/translations/it.ts +3 -0
  201. package/src/locale/translations/ja.ts +3 -0
  202. package/src/locale/translations/pt.ts +3 -0
  203. package/src/locale/translations/ru.ts +3 -0
  204. package/src/locale/translations/tr.ts +3 -0
  205. package/src/locale/translations/zh.ts +3 -0
package/README.md CHANGED
@@ -162,7 +162,7 @@ Icons go one step further: every glyph is its own subpath, `@oxy.so/bloom/icons/
162
162
  | Overlays | `dialog`, `alert-dialog`, `bottom-sheet`, `popover`, `context-menu`, `dropdown-menu`, `menubar`, `tooltip`, `overlay`, `portal`, `teleport`, `surfaces`, `settings-modal` |
163
163
  | Actions | `button`, `button-group`, `social-button`, `fab`, `frosted-icon-button`, `pressable-scale`, `subtle-hover` |
164
164
  | Forms | `text-field`, `textarea`, `phone-input`, `input-otp`, `field`, `input-group`, `label`, `select`, `command`, `checkbox`, `radio`, `switch`, `slider`, `segmented-control`, `search`, `date-picker`, `file-upload`, `auth-card`, `stepper`, `rating`, `wizard` |
165
- | Layout and lists | `grid`, `list`, `scroll`, `settings-list`, `card`, `accordion`, `aspect-ratio`, `divider`, `item`, `carousel` |
165
+ | Layout and lists | `grid`, `list`, `scroll`, `settings-list`, `card`, `cover-header`, `accordion`, `aspect-ratio`, `divider`, `item`, `carousel` |
166
166
  | Data and charts | `table`, `data-table`, `chart-cards`, `stat-cards`, `calendar`, `composition-bar`, `dot-grid-meter`, `stat-bar`, `activity-heatmap`, `recent-hires-card`, `patient-info-card`, `important-alerts-card`, `agent-limits-card`, `ai-profile-card` |
167
167
  | Music and media | `media-card`, `media-controls`, `media-header`, `media-player`, `media-shelf`, `track-list`, `queue-panel`, `lyrics`, `music-library`, `creator-studio`, `sortable-media` |
168
168
  | Housing | `home-search`, `listing-actions`, `listing-editor`, `offering-badge`, `place-reviews`, `property-insights`, `tenancy`, `eviction` |
@@ -32,7 +32,7 @@ The classification is an EQUALITY in `src/__tests__/adoption-matrix.test.ts`, no
32
32
 
33
33
  ## Totals
34
34
 
35
- - **207 families** in `src/`, **205** published (a subpath, the root barrel, or both). **41** carry a web fork.
35
+ - **208 families** in `src/`, **206** published (a subpath, the root barrel, or both). **41** carry a web fork.
36
36
  - Docs, stories and suites are gated as a standard by `src/__tests__/family-coverage.test.ts`; the per-family counts are in the table below.
37
37
 
38
38
  | contract | applies to | reads | delegated | n/a¹ | adaptation |
@@ -163,6 +163,7 @@ The classification is an EQUALITY in `src/__tests__/adoption-matrix.test.ts`, no
163
163
  | `contact-card` | `@oxy.so/bloom/contact-card` + barrel | universal | `contact-card.mdx` | yes | 2 | | | | |
164
164
  | `content-panel` | `@oxy.so/bloom/content-panel` + barrel | web fork | `content-panel.mdx` | yes | 8 | | | | |
165
165
  | `context-menu` | `@oxy.so/bloom/context-menu` + barrel | web fork | `context-menu.mdx` | yes | 5 | | | reads | |
166
+ | `cover-header` | `@oxy.so/bloom/cover-header` + barrel | universal | `cover-header.mdx` | yes | 1 | | | | |
166
167
  | `creator-studio` | `@oxy.so/bloom/creator-studio` + barrel | universal | `creator-studio.mdx` | yes | 4 | | n/a¹ | | **adaptation** |
167
168
  | `data-table` | `@oxy.so/bloom/data-table` + barrel | universal | `data-table.mdx` | yes | 4 | | reads | | reads |
168
169
  | `date-picker` | `@oxy.so/bloom/date-picker` + barrel | universal | `date-picker.mdx` | yes | 5 | reads | reads | | reads |
@@ -303,7 +304,7 @@ The classification is an EQUALITY in `src/__tests__/adoption-matrix.test.ts`, no
303
304
  | `tenancy` | `@oxy.so/bloom/tenancy` + barrel | universal | `tenancy.mdx` | yes | 5 | | reads | | |
304
305
  | `text-field` | `@oxy.so/bloom/text-field` + barrel | universal | `text-field.mdx` | yes | 16 | reads | reads | | |
305
306
  | `textarea` | `@oxy.so/bloom/textarea` + barrel | universal | `textarea.mdx` | yes | 4 | reads | reads | | n/a¹ |
306
- | `theme` | `@oxy.so/bloom/theme` + barrel | web fork | `theme.mdx` | yes | 344 | | | n/a¹ | reads |
307
+ | `theme` | `@oxy.so/bloom/theme` + barrel | web fork | `theme.mdx` | yes | 345 | | | n/a¹ | reads |
307
308
  | `theme-toggle` | `@oxy.so/bloom/theme-toggle` + barrel | universal | `theme-toggle.mdx` | yes | 2 | n/a¹ | | | |
308
309
  | `toast` | `@oxy.so/bloom/toast` + barrel | universal | `toast.mdx` | yes | 17 | | | | |
309
310
  | `tooltip` | `@oxy.so/bloom/tooltip` + barrel | web fork | `tooltip.mdx` | yes | 1 | | | | |
@@ -136,6 +136,10 @@ Times ("12:41", "Yesterday") never appear here. `VoiceRecorder` takes `seconds`
136
136
 
137
137
  One `SuggestionList` serves all three kinds; only the leading slot differs (an avatar, a glyph disc, the emoji). Rows are `role="option"` inside a `role="listbox"` with `aria-selected` — the spelling react-native-web reads; `accessibilityState` alone would render a list with no state on web. Each row is named by its label **and** handle, because "Ana" alone is ambiguous in a group.
138
138
 
139
+ A person marked `verified` gets the accent verified badge after the name, and the row's name says so ("Ana Restrepo, Verified, @ana"); the marker belongs to `kind="mention"` only.
140
+
141
+ The list also answers the two states a search passes through. `loading` replaces the rows with a named, busy "Searching…" line, so a list with nothing *yet* is not read as one that found nothing. An empty list still draws nothing by default — right for a composer that shows the list only while it has rows — and `showEmpty` draws the localized "No people found" line instead (`emptyLabel` replaces it), for a picker the user opened on purpose.
142
+
139
143
  The list renders **in flow** above the bar rather than in a portal. Anchoring it over the conversation is a layout decision an app makes with its own container (`position: absolute` on a wrapper); a portal here would have to guess the scroll parent.
140
144
 
141
145
  ## Voice recorder
@@ -227,7 +231,7 @@ State colours 150ms; the mic ↔ send cross-fade 220ms; the recording dot pulses
227
231
 
228
232
  `ComposerBanner` — `kind`, `title`, `preview`, `thumbnail`, `icon`, `accentColor`, `onClose`, `closeLabel`, `trailing`.
229
233
  `ComposerAttachmentStrip` — `attachments`, `onRemove`, `size` (56), `removeLabel`.
230
- `SuggestionList` — `kind`, `suggestions`, `activeIndex`, `onSelectSuggestion`, `onActiveIndexChange`, `header`, `maxHeight` (232).
234
+ `SuggestionList` — `kind`, `suggestions` (each may carry `verified`), `activeIndex`, `onSelectSuggestion`, `onActiveIndexChange`, `header`, `maxHeight` (232), `loading`, `showEmpty`, `emptyLabel`.
231
235
  `VoiceRecorder` — `state`, `seconds`, `duration`, `amplitudes`, `playing`, `onPlayToggle`, `onCancel`, `onDelete`, `onSend`, `onLock`, `slideToCancel`, `labels`.
232
236
  `AttachmentMenu` — `open`/`defaultOpen`/`onOpenChange`, `items`, `layout`, `columns`, `onSelect`, `recent`, `label`.
233
237
  `EmojiPicker` — `groups`, `onSelectEmoji`, `activeGroup`, `query`, `filter`, `frequentlyUsed`, `skinTone`, `columns` (8), `height` (288), `tabs`, `activeTab`, `labels`.
@@ -0,0 +1,56 @@
1
+ ---
2
+ title: CoverHeader
3
+ description: A cover band with content rising into its bottom edge — a profile's banner and the avatar that overlaps it.
4
+ order: 106
5
+ ---
6
+
7
+ # CoverHeader
8
+
9
+ A cover band (a photo, or anything you draw) and a content column whose top
10
+ rises `overlap` points into the band's bottom edge. It is the geometry of a
11
+ profile page's banner-plus-avatar, and of `AiProfileCard`, which is built on it.
12
+
13
+ ```tsx
14
+ import { CoverHeader } from '@oxy.so/bloom/cover-header';
15
+
16
+ <CoverHeader coverSource={bannerUrl} coverHeight={170} overlap={45}
17
+ contentStyle={{ paddingHorizontal: 16 }}>
18
+ <Avatar size={90} source={avatarUrl} />
19
+ <Text variant="title-2-medium">Maya Collins</Text>
20
+ </CoverHeader>
21
+ ```
22
+
23
+ ## Props
24
+
25
+ | Prop | Default | |
26
+ |---|---|---|
27
+ | `coverSource` | — | photo across the band; without one the band is `background-tertiary` |
28
+ | `coverPosition` | `{ x: 0.5, y: 0.5 }` | where the crop sits, as `object-position` fractions |
29
+ | `cover` | — | drawn in the band instead of the photo |
30
+ | `coverHeight` | `170` | height of the band |
31
+ | `overlap` | `45` | how far the content rises into the band, clamped to `0..coverHeight` |
32
+ | `coverStyle` | — | the band's style (corner radii, another fill) |
33
+ | `contentStyle` | — | the content column's style; its `paddingTop` is the component's |
34
+ | `testID` | — | also names `-cover`, `-cover-image` and `-content` |
35
+
36
+ ## How the overlap is made
37
+
38
+ By layout. Nothing is moved.
39
+
40
+ - The band is the **first** child, positioned absolutely over the top `coverHeight`.
41
+ - The content is the **last** child, in flow, with `paddingTop: coverHeight - overlap`.
42
+
43
+ There is no negative margin, no transform and no `zIndex`, and each of those
44
+ has broken a profile before:
45
+
46
+ - A **transform** keeps the layout box where it was. The content paints 45pt higher,
47
+ but whatever comes next (a tab strip, a list's next row) still starts after the
48
+ un-moved box, which leaves a 45pt hole.
49
+ - A **negative margin** on the content leaves the paint order to whichever
50
+ view happens to create a stacking context first. Inside a native
51
+ virtualized cell, that has put the avatar under the banner.
52
+ - **`zIndex`** belongs to the overlay stack (`docs/overlay.mdx`), never to a component.
53
+
54
+ Because the content is the later sibling, it paints over the band on iOS,
55
+ Android and web. The measured height is the real one. A list header or
56
+ sticky section placed after a `CoverHeader` starts exactly where its content ends.
@@ -13,12 +13,13 @@ var _ContributionsCard = require("../chart-cards/ContributionsCard.js");
13
13
  var _ChartHeader = require("../chart-cards/primitives/ChartHeader.js");
14
14
  var _useCountUp = require("../chart-cards/use-count-up.js");
15
15
  var _index3 = require("../chip/index.js");
16
+ var _index4 = require("../cover-header/index.js");
16
17
  var _useControllableState = require("../hooks/use-controllable-state.js");
17
18
  var _messages = require("../locale/messages.js");
18
- var _index4 = require("../segmented-control/index.js");
19
+ var _index5 = require("../segmented-control/index.js");
19
20
  var _breakpoints = require("../styles/breakpoints.js");
20
21
  var _useTheme = require("../theme/use-theme.js");
21
- var _index5 = require("../typography/index.js");
22
+ var _index6 = require("../typography/index.js");
22
23
  var _messages2 = require("./messages.js");
23
24
  var _formatNumber = require("../locale/format-number.js");
24
25
  var _jsxRuntime = require("react/jsx-runtime");
@@ -31,7 +32,7 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
31
32
  * card radius 24, 1px borderLight, no
32
33
  * fill, clips its children
33
34
  * cover absolute over the top 165px, top corners 23, background-tertiary
34
- * under the photo, `object-position: 50% 45%`
35
+ * under the photo, `object-position: 50% 45%` — a `CoverHeader`
35
36
  * content column 15 apart, padding 124 top / 16 sides / 16 bottom
36
37
  * avatar 80 disc, background-tertiary, initials 30 / 42.5 medium
37
38
  * text-secondary — overlapping the cover's bottom edge by 41
@@ -51,68 +52,8 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
51
52
  * support pair. The contributions heatmap retains its data hue.
52
53
  */const COVER_HEIGHT = 165;
53
54
  const AVATAR_SIZE = 80;
54
-
55
- /** `object-fit: cover` with an `object-position`: the image sized to cover the box, offset by the fractions. */
56
- function CoverImage({
57
- source,
58
- position,
59
- testID
60
- }) {
61
- const [box, setBox] = (0, _react.useState)(null);
62
- const [natural, setNatural] = (0, _react.useState)(null);
63
- const resolved = typeof source === 'string' ? {
64
- uri: source
65
- } : source;
66
- const onLayout = (0, _react.useCallback)(event => {
67
- const {
68
- width,
69
- height
70
- } = event.nativeEvent.layout;
71
- setBox(prev => prev && prev.width === width && prev.height === height ? prev : {
72
- width,
73
- height
74
- });
75
- }, []);
76
- const onLoad = (0, _react.useCallback)(event => {
77
- const s = event.nativeEvent?.source;
78
- if (s && s.width > 0 && s.height > 0) setNatural({
79
- width: s.width,
80
- height: s.height
81
- });
82
- }, []);
83
- const frame = (0, _react.useMemo)(() => {
84
- if (!box || !natural) return null;
85
- const scale = Math.max(box.width / natural.width, box.height / natural.height);
86
- const width = natural.width * scale;
87
- const height = natural.height * scale;
88
- return {
89
- width,
90
- height,
91
- left: (box.width - width) * position.x,
92
- top: (box.height - height) * position.y
93
- };
94
- }, [box, natural, position.x, position.y]);
95
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
96
- style: _reactNative.StyleSheet.absoluteFill,
97
- onLayout: onLayout,
98
- testID: testID ? `${testID}-frame` : undefined,
99
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, {
100
- testID: testID,
101
- source: resolved,
102
- onLoad: onLoad,
103
- accessibilityIgnoresInvertColors: true,
104
- "aria-hidden": true,
105
- resizeMode: "cover",
106
- style: frame ? {
107
- position: 'absolute',
108
- left: frame.left,
109
- top: frame.top,
110
- width: frame.width,
111
- height: frame.height
112
- } : _reactNative.StyleSheet.absoluteFill
113
- })
114
- });
115
- }
55
+ /** Where the content starts: the avatar overlaps the cover's bottom edge by 41. */
56
+ const CONTENT_TOP = 124;
116
57
 
117
58
  /**
118
59
  * The count-up headline, in its own component: the roll re-renders every frame
@@ -130,7 +71,7 @@ function RollingHeadline({
130
71
  const [target, setTarget] = (0, _react.useState)(duration > 0 ? 0 : value);
131
72
  (0, _react.useEffect)(() => setTarget(value), [value]);
132
73
  const rolled = (0, _useCountUp.useCountUp)(target, Math.max(1, duration));
133
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_index5.Text, {
74
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_index6.Text, {
134
75
  variant: "title-1-medium",
135
76
  numberOfLines: 1,
136
77
  testID: testID,
@@ -226,243 +167,225 @@ function AiProfileCardComponent({
226
167
  onActiveCellChange: onActiveCellChange,
227
168
  testID: testID ? `${testID}-grid` : undefined
228
169
  }), [cells, columns, colors.grid, animateIn, wide, activeCell, onActiveCellChange, testID]);
229
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
170
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_index4.CoverHeader, {
230
171
  testID: testID,
172
+ coverSource: coverSource,
173
+ coverPosition: coverPosition,
174
+ coverHeight: COVER_HEIGHT,
175
+ overlap: COVER_HEIGHT - CONTENT_TOP,
231
176
  style: [{
232
- position: 'relative',
233
- width: '100%',
234
177
  minWidth: 0,
235
178
  overflow: 'hidden',
236
179
  borderRadius: 24,
237
180
  borderWidth: 1,
238
181
  borderColor: colors.border
239
182
  }, style],
240
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
241
- testID: testID ? `${testID}-cover` : undefined,
242
- style: {
243
- position: 'absolute',
244
- top: 0,
245
- left: 0,
246
- right: 0,
247
- height: COVER_HEIGHT,
248
- overflow: 'hidden',
249
- borderTopLeftRadius: 23,
250
- borderTopRightRadius: 23,
251
- backgroundColor: colors.tertiary
252
- },
253
- children: coverSource ? /*#__PURE__*/(0, _jsxRuntime.jsx)(CoverImage, {
254
- source: coverSource,
255
- position: coverPosition,
256
- testID: testID ? `${testID}-cover-image` : undefined
257
- }) : null
183
+ coverStyle: {
184
+ borderTopLeftRadius: 23,
185
+ borderTopRightRadius: 23
186
+ },
187
+ contentStyle: {
188
+ flexDirection: 'column',
189
+ gap: 15,
190
+ paddingBottom: 16,
191
+ paddingLeft: 16,
192
+ paddingRight: 16
193
+ },
194
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_index.Avatar, {
195
+ testID: testID ? `${testID}-avatar` : undefined,
196
+ size: AVATAR_SIZE,
197
+ source: avatarSource ?? undefined,
198
+ initials: letters,
199
+ color: "neutral",
200
+ alt: avatarSource ? name : undefined,
201
+ placeholderColor: colors.tertiary,
202
+ placeholderIcon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_index6.Text, {
203
+ allowFontScaling: false,
204
+ numberOfLines: 1,
205
+ style: [styles.initials, {
206
+ color: colors.textSecondary
207
+ }],
208
+ children: letters
209
+ })
258
210
  }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
259
- testID: testID ? `${testID}-content` : undefined,
260
211
  style: {
261
212
  position: 'relative',
262
213
  width: '100%',
263
- flexDirection: 'column',
264
- gap: 15,
265
- paddingTop: 124,
266
- paddingBottom: 16,
267
- paddingLeft: 16,
268
- paddingRight: 16
214
+ flexDirection: 'row',
215
+ alignItems: 'flex-start',
216
+ gap: 15
269
217
  },
270
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_index.Avatar, {
271
- testID: testID ? `${testID}-avatar` : undefined,
272
- size: AVATAR_SIZE,
273
- source: avatarSource ?? undefined,
274
- initials: letters,
275
- color: "neutral",
276
- alt: avatarSource ? name : undefined,
277
- placeholderColor: colors.tertiary,
278
- placeholderIcon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_index5.Text, {
279
- allowFontScaling: false,
280
- numberOfLines: 1,
281
- style: [styles.initials, {
282
- color: colors.textSecondary
283
- }],
284
- children: letters
285
- })
286
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
218
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
287
219
  style: {
288
- position: 'relative',
289
- width: '100%',
290
- flexDirection: 'row',
291
- alignItems: 'flex-start',
292
- gap: 15
220
+ minWidth: 0,
221
+ flex: 1,
222
+ flexDirection: 'column',
223
+ gap: 4
293
224
  },
294
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
225
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_index6.Text, {
226
+ variant: "title-2-medium",
227
+ numberOfLines: 1,
228
+ style: {
229
+ color: colors.text
230
+ },
231
+ children: name
232
+ }), handle != null || badge != null ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
295
233
  style: {
296
- minWidth: 0,
297
- flex: 1,
298
- flexDirection: 'column',
299
- gap: 4
234
+ flexDirection: 'row',
235
+ alignItems: 'center',
236
+ gap: 6
300
237
  },
301
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_index5.Text, {
302
- variant: "title-2-medium",
238
+ children: [handle != null ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_index6.Text, {
239
+ variant: "headline-medium",
303
240
  numberOfLines: 1,
304
241
  style: {
305
- color: colors.text
242
+ color: colors.textSecondary
306
243
  },
307
- children: name
308
- }), handle != null || badge != null ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
244
+ children: handle
245
+ }) : null, badge != null ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_index2.Badge, {
246
+ testID: testID ? `${testID}-badge` : undefined,
247
+ content: badge,
309
248
  style: {
310
- flexDirection: 'row',
311
- alignItems: 'center',
312
- gap: 6
249
+ backgroundColor: colors.tertiary
313
250
  },
314
- children: [handle != null ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_index5.Text, {
315
- variant: "headline-medium",
316
- numberOfLines: 1,
317
- style: {
318
- color: colors.textSecondary
319
- },
320
- children: handle
321
- }) : null, badge != null ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_index2.Badge, {
322
- testID: testID ? `${testID}-badge` : undefined,
323
- content: badge,
324
- style: {
325
- backgroundColor: colors.tertiary
326
- },
327
- textStyle: {
328
- color: colors.textSecondary
329
- }
330
- }) : null]
251
+ textStyle: {
252
+ color: colors.textSecondary
253
+ }
331
254
  }) : null]
332
- }), actions ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
333
- testID: testID ? `${testID}-actions` : undefined,
334
- style: {
335
- position: 'absolute',
336
- top: -34,
337
- right: 4,
338
- flexDirection: 'row',
339
- alignItems: 'center',
340
- justifyContent: 'flex-end',
341
- gap: 10
342
- },
343
- children: actions
344
255
  }) : null]
345
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
256
+ }), actions ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
257
+ testID: testID ? `${testID}-actions` : undefined,
258
+ style: {
259
+ position: 'absolute',
260
+ top: -34,
261
+ right: 4,
262
+ flexDirection: 'row',
263
+ alignItems: 'center',
264
+ justifyContent: 'flex-end',
265
+ gap: 10
266
+ },
267
+ children: actions
268
+ }) : null]
269
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
270
+ style: {
271
+ width: '100%',
272
+ flexDirection: 'column',
273
+ gap: 8
274
+ },
275
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
346
276
  style: {
347
- width: '100%',
348
277
  flexDirection: 'column',
349
- gap: 8
278
+ gap: 2
350
279
  },
351
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
280
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_index6.Text, {
281
+ variant: "body-medium",
282
+ numberOfLines: 1,
352
283
  style: {
353
- flexDirection: 'column',
354
- gap: 2
284
+ color: colors.textSecondary
355
285
  },
356
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_index5.Text, {
357
- variant: "body-medium",
358
- numberOfLines: 1,
359
- style: {
360
- color: colors.textSecondary
361
- },
362
- children: contributionsLabel
363
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
364
- style: {
365
- flexDirection: 'row',
366
- alignItems: 'center',
367
- gap: 8
368
- },
369
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(RollingHeadline, {
370
- value: contributions,
371
- duration: countUpDuration,
372
- format: format,
373
- color: colors.text,
374
- testID: testID ? `${testID}-headline` : undefined
375
- }), delta != null ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_index3.Chip, {
376
- size: "md",
377
- testID: testID ? `${testID}-delta` : undefined,
378
- style: {
379
- alignSelf: 'center',
380
- backgroundColor: colors.chip.background
381
- },
382
- textStyle: {
383
- color: colors.chip.foreground
384
- },
385
- children: delta
386
- }) : null]
387
- })]
388
- }), stats && stats.length > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
389
- testID: testID ? `${testID}-stats` : undefined,
286
+ children: contributionsLabel
287
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
390
288
  style: {
391
- flexDirection: 'column',
289
+ flexDirection: 'row',
290
+ alignItems: 'center',
392
291
  gap: 8
393
292
  },
394
- children: (wide ? [stats.map((_, i) => i)] : pairs(stats.length)).map(row => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
293
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(RollingHeadline, {
294
+ value: contributions,
295
+ duration: countUpDuration,
296
+ format: format,
297
+ color: colors.text,
298
+ testID: testID ? `${testID}-headline` : undefined
299
+ }), delta != null ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_index3.Chip, {
300
+ size: "md",
301
+ testID: testID ? `${testID}-delta` : undefined,
395
302
  style: {
396
- flexDirection: 'row',
397
- alignItems: 'stretch',
398
- gap: 8
303
+ alignSelf: 'center',
304
+ backgroundColor: colors.chip.background
305
+ },
306
+ textStyle: {
307
+ color: colors.chip.foreground
399
308
  },
400
- children: [row.map(i => {
401
- const stat = stats[i];
402
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
403
- testID: testID ? `${testID}-stat-${i}` : undefined,
404
- style: [styles.tile, {
405
- backgroundColor: colors.secondary
406
- }],
407
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_index5.Text, {
408
- variant: "body-medium",
409
- numberOfLines: 1,
410
- style: {
411
- width: '100%',
412
- color: colors.text
413
- },
414
- children: stat.value
415
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_index5.Text, {
416
- variant: "body-2-medium",
417
- numberOfLines: 1,
418
- style: {
419
- width: '100%',
420
- color: colors.textSecondary
421
- },
422
- children: stat.label
423
- })]
424
- }, `${i}-${stat.label}`);
425
- }), !wide && row.length === 1 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
426
- style: styles.tileSpacer
427
- }) : null]
428
- }, row[0]))
429
- }) : null, /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
309
+ children: delta
310
+ }) : null]
311
+ })]
312
+ }), stats && stats.length > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
313
+ testID: testID ? `${testID}-stats` : undefined,
314
+ style: {
315
+ flexDirection: 'column',
316
+ gap: 8
317
+ },
318
+ children: (wide ? [stats.map((_, i) => i)] : pairs(stats.length)).map(row => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
430
319
  style: {
431
- width: '100%',
432
320
  flexDirection: 'row',
433
- alignItems: 'center',
434
- justifyContent: 'space-between',
435
- paddingTop: 6,
436
- paddingLeft: 2
321
+ alignItems: 'stretch',
322
+ gap: 8
437
323
  },
438
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_index5.Text, {
439
- variant: "body-2-medium",
440
- style: {
441
- color: colors.textSecondary
442
- },
443
- children: activityLabel
444
- }), periods.length > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_index4.SegmentedControl, {
445
- label: messages.periodGroup(activityLabel),
446
- type: "radio",
447
- variant: "plain",
448
- value: selectedPeriod,
449
- onValueChange: setPeriod,
450
- children: periods.map(p => /*#__PURE__*/(0, _jsxRuntime.jsx)(_index4.SegmentedControlItem, {
451
- value: p.id,
452
- testID: testID ? `${testID}-period-${p.id}` : undefined,
453
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_index4.SegmentedControlItemText, {
454
- children: p.label
455
- })
456
- }, p.id))
324
+ children: [row.map(i => {
325
+ const stat = stats[i];
326
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
327
+ testID: testID ? `${testID}-stat-${i}` : undefined,
328
+ style: [styles.tile, {
329
+ backgroundColor: colors.secondary
330
+ }],
331
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_index6.Text, {
332
+ variant: "body-medium",
333
+ numberOfLines: 1,
334
+ style: {
335
+ width: '100%',
336
+ color: colors.text
337
+ },
338
+ children: stat.value
339
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_index6.Text, {
340
+ variant: "body-2-medium",
341
+ numberOfLines: 1,
342
+ style: {
343
+ width: '100%',
344
+ color: colors.textSecondary
345
+ },
346
+ children: stat.label
347
+ })]
348
+ }, `${i}-${stat.label}`);
349
+ }), !wide && row.length === 1 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
350
+ style: styles.tileSpacer
457
351
  }) : null]
458
- }), wide ? grid : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
459
- horizontal: true,
460
- showsHorizontalScrollIndicator: false,
352
+ }, row[0]))
353
+ }) : null, /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
354
+ style: {
355
+ width: '100%',
356
+ flexDirection: 'row',
357
+ alignItems: 'center',
358
+ justifyContent: 'space-between',
359
+ paddingTop: 6,
360
+ paddingLeft: 2
361
+ },
362
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_index6.Text, {
363
+ variant: "body-2-medium",
461
364
  style: {
462
- flexGrow: 0
365
+ color: colors.textSecondary
463
366
  },
464
- children: grid
465
- })]
367
+ children: activityLabel
368
+ }), periods.length > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_index5.SegmentedControl, {
369
+ label: messages.periodGroup(activityLabel),
370
+ type: "radio",
371
+ variant: "plain",
372
+ value: selectedPeriod,
373
+ onValueChange: setPeriod,
374
+ children: periods.map(p => /*#__PURE__*/(0, _jsxRuntime.jsx)(_index5.SegmentedControlItem, {
375
+ value: p.id,
376
+ testID: testID ? `${testID}-period-${p.id}` : undefined,
377
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_index5.SegmentedControlItemText, {
378
+ children: p.label
379
+ })
380
+ }, p.id))
381
+ }) : null]
382
+ }), wide ? grid : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
383
+ horizontal: true,
384
+ showsHorizontalScrollIndicator: false,
385
+ style: {
386
+ flexGrow: 0
387
+ },
388
+ children: grid
466
389
  })]
467
390
  })]
468
391
  });