@oxyhq/bloom 0.48.0 → 0.49.1

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 (88) hide show
  1. package/lib/commonjs/bottom-sheet/BottomSheetBase.js +3 -1
  2. package/lib/commonjs/bottom-sheet/BottomSheetBase.js.map +1 -1
  3. package/lib/commonjs/dialog/Dialog.js +35 -4
  4. package/lib/commonjs/dialog/Dialog.js.map +1 -1
  5. package/lib/commonjs/dialog/Dialog.web.js +48 -14
  6. package/lib/commonjs/dialog/Dialog.web.js.map +1 -1
  7. package/lib/commonjs/dialog/DialogBottomSheet.js +30 -5
  8. package/lib/commonjs/dialog/DialogBottomSheet.js.map +1 -1
  9. package/lib/commonjs/dialog/DialogHeader.js +12 -6
  10. package/lib/commonjs/dialog/DialogHeader.js.map +1 -1
  11. package/lib/commonjs/dialog/DialogMorph.js +458 -0
  12. package/lib/commonjs/dialog/DialogMorph.js.map +1 -0
  13. package/lib/commonjs/dialog/index.js +7 -0
  14. package/lib/commonjs/dialog/index.js.map +1 -1
  15. package/lib/commonjs/dialog/index.web.js +7 -0
  16. package/lib/commonjs/dialog/index.web.js.map +1 -1
  17. package/lib/commonjs/settings-list/SettingsList.js +1 -1
  18. package/lib/commonjs/surfaces/SurfaceHost.js +1 -0
  19. package/lib/commonjs/surfaces/SurfaceHost.js.map +1 -1
  20. package/lib/module/bottom-sheet/BottomSheetBase.js +3 -1
  21. package/lib/module/bottom-sheet/BottomSheetBase.js.map +1 -1
  22. package/lib/module/dialog/Dialog.js +35 -4
  23. package/lib/module/dialog/Dialog.js.map +1 -1
  24. package/lib/module/dialog/Dialog.web.js +48 -15
  25. package/lib/module/dialog/Dialog.web.js.map +1 -1
  26. package/lib/module/dialog/DialogBottomSheet.js +31 -6
  27. package/lib/module/dialog/DialogBottomSheet.js.map +1 -1
  28. package/lib/module/dialog/DialogHeader.js +12 -6
  29. package/lib/module/dialog/DialogHeader.js.map +1 -1
  30. package/lib/module/dialog/DialogMorph.js +451 -0
  31. package/lib/module/dialog/DialogMorph.js.map +1 -0
  32. package/lib/module/dialog/index.js +1 -0
  33. package/lib/module/dialog/index.js.map +1 -1
  34. package/lib/module/dialog/index.web.js +1 -0
  35. package/lib/module/dialog/index.web.js.map +1 -1
  36. package/lib/module/settings-list/SettingsList.js +1 -1
  37. package/lib/module/surfaces/SurfaceHost.js +1 -0
  38. package/lib/module/surfaces/SurfaceHost.js.map +1 -1
  39. package/lib/typescript/commonjs/bottom-sheet/BottomSheetBase.d.ts +10 -3
  40. package/lib/typescript/commonjs/bottom-sheet/BottomSheetBase.d.ts.map +1 -1
  41. package/lib/typescript/commonjs/dialog/Dialog.d.ts.map +1 -1
  42. package/lib/typescript/commonjs/dialog/Dialog.web.d.ts.map +1 -1
  43. package/lib/typescript/commonjs/dialog/DialogBottomSheet.d.ts +2 -2
  44. package/lib/typescript/commonjs/dialog/DialogBottomSheet.d.ts.map +1 -1
  45. package/lib/typescript/commonjs/dialog/DialogHeader.d.ts.map +1 -1
  46. package/lib/typescript/commonjs/dialog/DialogMorph.d.ts +150 -0
  47. package/lib/typescript/commonjs/dialog/DialogMorph.d.ts.map +1 -0
  48. package/lib/typescript/commonjs/dialog/index.d.ts +2 -0
  49. package/lib/typescript/commonjs/dialog/index.d.ts.map +1 -1
  50. package/lib/typescript/commonjs/dialog/index.web.d.ts +2 -0
  51. package/lib/typescript/commonjs/dialog/index.web.d.ts.map +1 -1
  52. package/lib/typescript/commonjs/dialog/types.d.ts +30 -0
  53. package/lib/typescript/commonjs/dialog/types.d.ts.map +1 -1
  54. package/lib/typescript/commonjs/surfaces/SurfaceHost.d.ts.map +1 -1
  55. package/lib/typescript/commonjs/surfaces/types.d.ts +9 -0
  56. package/lib/typescript/commonjs/surfaces/types.d.ts.map +1 -1
  57. package/lib/typescript/module/bottom-sheet/BottomSheetBase.d.ts +10 -3
  58. package/lib/typescript/module/bottom-sheet/BottomSheetBase.d.ts.map +1 -1
  59. package/lib/typescript/module/dialog/Dialog.d.ts.map +1 -1
  60. package/lib/typescript/module/dialog/Dialog.web.d.ts.map +1 -1
  61. package/lib/typescript/module/dialog/DialogBottomSheet.d.ts +2 -2
  62. package/lib/typescript/module/dialog/DialogBottomSheet.d.ts.map +1 -1
  63. package/lib/typescript/module/dialog/DialogHeader.d.ts.map +1 -1
  64. package/lib/typescript/module/dialog/DialogMorph.d.ts +150 -0
  65. package/lib/typescript/module/dialog/DialogMorph.d.ts.map +1 -0
  66. package/lib/typescript/module/dialog/index.d.ts +2 -0
  67. package/lib/typescript/module/dialog/index.d.ts.map +1 -1
  68. package/lib/typescript/module/dialog/index.web.d.ts +2 -0
  69. package/lib/typescript/module/dialog/index.web.d.ts.map +1 -1
  70. package/lib/typescript/module/dialog/types.d.ts +30 -0
  71. package/lib/typescript/module/dialog/types.d.ts.map +1 -1
  72. package/lib/typescript/module/surfaces/SurfaceHost.d.ts.map +1 -1
  73. package/lib/typescript/module/surfaces/types.d.ts +9 -0
  74. package/lib/typescript/module/surfaces/types.d.ts.map +1 -1
  75. package/package.json +1 -1
  76. package/src/__tests__/DialogMorph.test.tsx +230 -0
  77. package/src/bottom-sheet/BottomSheetBase.tsx +15 -2
  78. package/src/dialog/Dialog.tsx +43 -11
  79. package/src/dialog/Dialog.web.tsx +40 -10
  80. package/src/dialog/DialogBottomSheet.tsx +37 -6
  81. package/src/dialog/DialogHeader.tsx +14 -5
  82. package/src/dialog/DialogMorph.tsx +576 -0
  83. package/src/dialog/index.ts +2 -0
  84. package/src/dialog/index.web.ts +2 -0
  85. package/src/dialog/types.ts +30 -0
  86. package/src/settings-list/SettingsList.tsx +1 -1
  87. package/src/surfaces/SurfaceHost.tsx +1 -0
  88. package/src/surfaces/types.ts +9 -0
@@ -6,7 +6,13 @@ import React, {
6
6
  useMemo,
7
7
  useRef,
8
8
  } from 'react';
9
- import { View, type StyleProp, type ViewStyle } from 'react-native';
9
+ import {
10
+ useWindowDimensions,
11
+ View,
12
+ type StyleProp,
13
+ type ViewStyle,
14
+ } from 'react-native';
15
+ import { type AnimatedStyle } from 'react-native-reanimated';
10
16
 
11
17
  import { BottomSheet, type BottomSheetRef } from '../bottom-sheet';
12
18
  import { useTheme } from '../theme/use-theme';
@@ -19,6 +25,7 @@ import {
19
25
  DialogNavHeader,
20
26
  useDialogHeaderController,
21
27
  } from './DialogHeader';
28
+ import { DialogMorphContent, useDialogMorph } from './DialogMorph';
22
29
  import {
23
30
  DEFAULT_DIALOG_CONTENT_PADDING,
24
31
  DEFAULT_MAX_HEIGHT_RATIO,
@@ -62,6 +69,7 @@ export function DialogBottomSheet({
62
69
  dismissOnBackdrop = true,
63
70
  contentPadding = DEFAULT_DIALOG_CONTENT_PADDING,
64
71
  scrollable,
72
+ morph,
65
73
  style,
66
74
  panelStyle,
67
75
  panelClassName,
@@ -72,6 +80,7 @@ export function DialogBottomSheet({
72
80
  }: DialogBottomSheetProps) {
73
81
  const isControlled = controlledOpen !== undefined;
74
82
  const theme = useTheme();
83
+ const { height: viewportHeight } = useWindowDimensions();
75
84
  const ref = useRef<BottomSheetRef>(null);
76
85
  const closeCallbacks = useRef<(() => void)[]>([]);
77
86
  const titleId = useId();
@@ -155,10 +164,23 @@ export function DialogBottomSheet({
155
164
  [dismissOnBackdrop],
156
165
  );
157
166
 
167
+ const scrollableResolved = scrollable ?? true;
168
+
169
+ // Size morphing across an in-place content swap. The sheet is anchored at the
170
+ // bottom, so animating its height makes it grow/shrink from that edge; its
171
+ // width is fixed by the placement. The sheet's own `maxHeight` (the ratio
172
+ // percentage + the safe-area clamp inside `BottomSheet`) does the exact
173
+ // capping — the viewport is just the bound the reshape may not exceed.
174
+ const morphState = useDialogMorph({
175
+ enabled: morph !== false,
176
+ measurable: scrollableResolved,
177
+ maxHeight: viewportHeight,
178
+ });
179
+
158
180
  // `panelStyle` paints the sheet surface (e.g. a brand background). It composes
159
181
  // AFTER the BottomSheet's internal background so a host palette wins; the
160
182
  // shape (radius, max height) is supplied here and remains overridable.
161
- const sheetStyle = useMemo<StyleProp<ViewStyle>>(() => {
183
+ const sheetStyle = useMemo<StyleProp<AnimatedStyle<ViewStyle>>>(() => {
162
184
  const maxHeightPercent: `${number}%` = `${Math.round(maxHeightRatio * 100)}%`;
163
185
  return [
164
186
  {
@@ -168,8 +190,11 @@ export function DialogBottomSheet({
168
190
  maxHeight: maxHeightPercent,
169
191
  },
170
192
  panelStyle,
193
+ // Drives the card's `height` only while a morph is in flight; at rest it
194
+ // resolves to `height: 'auto'` — the sheet's normal content sizing.
195
+ morphState.panelStyle,
171
196
  ];
172
- }, [theme.colors.background, maxHeightRatio, panelStyle]);
197
+ }, [theme.colors.background, maxHeightRatio, panelStyle, morphState.panelStyle]);
173
198
 
174
199
  // Does this dialog render bloom's declarative chrome (title / description /
175
200
  // actions)? This no longer gates the SCROLL decision (that is now the explicit
@@ -183,8 +208,6 @@ export function DialogBottomSheet({
183
208
  description !== undefined ||
184
209
  (actions !== undefined && actions.length > 0);
185
210
 
186
- const scrollableResolved = scrollable ?? true;
187
-
188
211
  const dialogBody = (
189
212
  <DialogBody
190
213
  titleId={titleId}
@@ -264,6 +287,8 @@ export function DialogBottomSheet({
264
287
  // Stronger dim than a lone sheet so an underlying sheet's handle/content
265
288
  // doesn't bleed through when a Dialog is stacked over one.
266
289
  backdropOpacity={SHEET_BACKDROP_OPACITY + 0.3}
290
+ // Measures the sheet card — the size a morph starts from.
291
+ onLayout={morphState.onPanelLayout}
267
292
  style={sheetStyle}
268
293
  >
269
294
  <Context.Provider value={context}>
@@ -285,7 +310,12 @@ export function DialogBottomSheet({
285
310
  panelStyle,
286
311
  ]}
287
312
  >
288
- {header ? headerBody : dialogBody}
313
+ {/* The morph layer is its own node INSIDE the class-name'd container:
314
+ a className'd component's `onLayout` never fires on web, and this
315
+ one has to measure the incoming frame. */}
316
+ <DialogMorphContent morph={morphState}>
317
+ {header ? headerBody : dialogBody}
318
+ </DialogMorphContent>
289
319
  </View>
290
320
  </Context.Provider>
291
321
  </BottomSheet>
@@ -313,6 +343,7 @@ export type DialogBottomSheetProps = Pick<
313
343
  | 'dismissOnBackdrop'
314
344
  | 'contentPadding'
315
345
  | 'scrollable'
346
+ | 'morph'
316
347
  | 'style'
317
348
  | 'panelStyle'
318
349
  | 'panelClassName'
@@ -89,6 +89,7 @@ function configsEqual(a: DialogHeaderConfig | null, b: DialogHeaderConfig | null
89
89
  if (!a || !b) return false;
90
90
  return (
91
91
  a.title === b.title &&
92
+ a.titleContent === b.titleContent &&
92
93
  a.subtitle === b.subtitle &&
93
94
  a.largeTitle === b.largeTitle &&
94
95
  a.left === b.left &&
@@ -181,6 +182,7 @@ export function useDialogHeader(config: DialogHeaderConfig | null | undefined):
181
182
  }, [
182
183
  store,
183
184
  config?.title,
185
+ config?.titleContent,
184
186
  config?.subtitle,
185
187
  config?.largeTitle,
186
188
  config?.left,
@@ -235,7 +237,10 @@ export const DialogNavHeader = memo(function DialogNavHeader({
235
237
  const config = useMergedHeaderConfig(header, controller.store);
236
238
  const { scrollY, largeTitleHeight } = controller;
237
239
 
238
- const hasLargeTitle = collapse && (config.largeTitle ?? true) && !!config.title;
240
+ // A branded `titleContent` owns the bar centre outright: it is always visible,
241
+ // so there is nothing to collapse and no large in-content title to collapse under.
242
+ const hasLargeTitle =
243
+ collapse && !config.titleContent && (config.largeTitle ?? true) && !!config.title;
239
244
 
240
245
  const titleStyle = useAnimatedStyle(() => {
241
246
  // When there is no large title to collapse under, the bar title is always
@@ -294,15 +299,17 @@ export const DialogNavHeader = memo(function DialogNavHeader({
294
299
  testID="dialog-nav-title"
295
300
  style={[styles.centerTitle, titleStyle]}
296
301
  >
297
- {config.title ? (
302
+ {config.titleContent ?? null}
303
+ {!config.titleContent && config.title ? (
298
304
  <Text numberOfLines={1} style={[styles.smallTitle, { color: theme.colors.text }]}>
299
305
  {config.title}
300
306
  </Text>
301
307
  ) : null}
302
308
  {/* The subtitle rides along ONLY in the static (non-collapsing) bar —
303
309
  own-scroller screens whose subtitle is a short count. A large-title
304
- screen keeps a clean single-line collapsed title. */}
305
- {!hasLargeTitle && config.subtitle ? (
310
+ screen keeps a clean single-line collapsed title, and a branded
311
+ `titleContent` bar carries no supporting copy at all. */}
312
+ {!hasLargeTitle && !config.titleContent && config.subtitle ? (
306
313
  <Text
307
314
  numberOfLines={1}
308
315
  style={[styles.smallSubtitle, { color: theme.colors.textSecondary }]}
@@ -336,7 +343,9 @@ export const DialogLargeTitle = memo(function DialogLargeTitle({
336
343
  const config = useMergedHeaderConfig(header, controller.store);
337
344
  const { largeTitleHeight } = controller;
338
345
 
339
- const hasLargeTitle = (config.largeTitle ?? true) && !!config.title;
346
+ // A branded `titleContent` lives in the bar and never collapses, so the
347
+ // surface starts flush under the bar with no large title above it.
348
+ const hasLargeTitle = !config.titleContent && (config.largeTitle ?? true) && !!config.title;
340
349
 
341
350
  const onLayout = (e: LayoutChangeEvent) => {
342
351
  largeTitleHeight.value = e.nativeEvent.layout.height;