@oxyhq/bloom 0.16.1 → 0.17.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 (59) hide show
  1. package/README.md +54 -16
  2. package/lib/commonjs/dialog/Dialog.js +7 -7
  3. package/lib/commonjs/dialog/Dialog.js.map +1 -1
  4. package/lib/commonjs/dialog/Dialog.web.js +9 -5
  5. package/lib/commonjs/dialog/Dialog.web.js.map +1 -1
  6. package/lib/commonjs/dialog/DialogBottomSheet.js +18 -7
  7. package/lib/commonjs/dialog/DialogBottomSheet.js.map +1 -1
  8. package/lib/commonjs/dialog/placement.js +12 -1
  9. package/lib/commonjs/dialog/placement.js.map +1 -1
  10. package/lib/commonjs/theme/index.js +7 -0
  11. package/lib/commonjs/theme/index.js.map +1 -1
  12. package/lib/commonjs/theme/use-navigation-theme.js +54 -0
  13. package/lib/commonjs/theme/use-navigation-theme.js.map +1 -0
  14. package/lib/module/dialog/Dialog.js +8 -8
  15. package/lib/module/dialog/Dialog.js.map +1 -1
  16. package/lib/module/dialog/Dialog.web.js +10 -6
  17. package/lib/module/dialog/Dialog.web.js.map +1 -1
  18. package/lib/module/dialog/DialogBottomSheet.js +19 -8
  19. package/lib/module/dialog/DialogBottomSheet.js.map +1 -1
  20. package/lib/module/dialog/placement.js +11 -0
  21. package/lib/module/dialog/placement.js.map +1 -1
  22. package/lib/module/theme/index.js +1 -0
  23. package/lib/module/theme/index.js.map +1 -1
  24. package/lib/module/theme/use-navigation-theme.js +50 -0
  25. package/lib/module/theme/use-navigation-theme.js.map +1 -0
  26. package/lib/typescript/commonjs/dialog/Dialog.d.ts.map +1 -1
  27. package/lib/typescript/commonjs/dialog/Dialog.web.d.ts.map +1 -1
  28. package/lib/typescript/commonjs/dialog/DialogBottomSheet.d.ts +2 -2
  29. package/lib/typescript/commonjs/dialog/DialogBottomSheet.d.ts.map +1 -1
  30. package/lib/typescript/commonjs/dialog/placement.d.ts +10 -0
  31. package/lib/typescript/commonjs/dialog/placement.d.ts.map +1 -1
  32. package/lib/typescript/commonjs/dialog/types.d.ts +12 -0
  33. package/lib/typescript/commonjs/dialog/types.d.ts.map +1 -1
  34. package/lib/typescript/commonjs/theme/index.d.ts +2 -0
  35. package/lib/typescript/commonjs/theme/index.d.ts.map +1 -1
  36. package/lib/typescript/commonjs/theme/use-navigation-theme.d.ts +31 -0
  37. package/lib/typescript/commonjs/theme/use-navigation-theme.d.ts.map +1 -0
  38. package/lib/typescript/module/dialog/Dialog.d.ts.map +1 -1
  39. package/lib/typescript/module/dialog/Dialog.web.d.ts.map +1 -1
  40. package/lib/typescript/module/dialog/DialogBottomSheet.d.ts +2 -2
  41. package/lib/typescript/module/dialog/DialogBottomSheet.d.ts.map +1 -1
  42. package/lib/typescript/module/dialog/placement.d.ts +10 -0
  43. package/lib/typescript/module/dialog/placement.d.ts.map +1 -1
  44. package/lib/typescript/module/dialog/types.d.ts +12 -0
  45. package/lib/typescript/module/dialog/types.d.ts.map +1 -1
  46. package/lib/typescript/module/theme/index.d.ts +2 -0
  47. package/lib/typescript/module/theme/index.d.ts.map +1 -1
  48. package/lib/typescript/module/theme/use-navigation-theme.d.ts +31 -0
  49. package/lib/typescript/module/theme/use-navigation-theme.d.ts.map +1 -0
  50. package/package.json +1 -1
  51. package/src/__tests__/DialogBottomSheet.test.tsx +129 -0
  52. package/src/__tests__/public-api-contract.test.ts +42 -0
  53. package/src/dialog/Dialog.tsx +7 -5
  54. package/src/dialog/Dialog.web.tsx +10 -5
  55. package/src/dialog/DialogBottomSheet.tsx +22 -7
  56. package/src/dialog/placement.ts +11 -0
  57. package/src/dialog/types.ts +12 -0
  58. package/src/theme/index.ts +2 -0
  59. package/src/theme/use-navigation-theme.ts +59 -0
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/theme/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EACV,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACpE,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACrE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7D,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAClF,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/theme/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EACV,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACpE,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AACnF,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7D,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAClF,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,31 @@
1
+ export interface NavigationThemeFont {
2
+ fontFamily: string;
3
+ fontWeight: '400' | '500' | '700' | '900';
4
+ }
5
+ export interface NavigationTheme {
6
+ dark: boolean;
7
+ colors: {
8
+ primary: string;
9
+ background: string;
10
+ card: string;
11
+ text: string;
12
+ border: string;
13
+ notification: string;
14
+ };
15
+ fonts: {
16
+ regular: NavigationThemeFont;
17
+ medium: NavigationThemeFont;
18
+ bold: NavigationThemeFont;
19
+ heavy: NavigationThemeFont;
20
+ };
21
+ }
22
+ /**
23
+ * Projects Bloom's resolved theme into a react-navigation `Theme` POJO for
24
+ * expo-router's <ThemeProvider>. Keeps Bloom the single source of truth for
25
+ * navigator chrome (Stack headers, screen backgrounds, modal) so it tracks
26
+ * the active color preset. Returns a structural object — no react-navigation
27
+ * import — so Bloom stays decoupled. Replaces the per-app copies in
28
+ * test-app-expo / inbox / accounts.
29
+ */
30
+ export declare function useNavigationTheme(): NavigationTheme;
31
+ //# sourceMappingURL=use-navigation-theme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-navigation-theme.d.ts","sourceRoot":"","sources":["../../../../src/theme/use-navigation-theme.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;CAC3C;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE;QACN,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,KAAK,EAAE;QACL,OAAO,EAAE,mBAAmB,CAAC;QAC7B,MAAM,EAAE,mBAAmB,CAAC;QAC5B,IAAI,EAAE,mBAAmB,CAAC;QAC1B,KAAK,EAAE,mBAAmB,CAAC;KAC5B,CAAC;CACH;AASD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,CAiBpD"}
@@ -1 +1 @@
1
- {"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../../../../src/dialog/Dialog.tsx"],"names":[],"mappings":"AAgCA,OAAO,EAGL,4BAA4B,EAK7B,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,YAAY,EAGZ,WAAW,EACZ,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,4BAA4B,EAAE,CAAC;AAExC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,MAAM,CAAC,EACrB,SAAS,EACT,GAAG,IAAI,EACR,EAAE,WAAW,2CAab;AAoZD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,WAAW,EACX,OAAO,EACP,SAAS,GACV,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,2CAoBA"}
1
+ {"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../../../../src/dialog/Dialog.tsx"],"names":[],"mappings":"AAgCA,OAAO,EAIL,4BAA4B,EAK7B,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,YAAY,EAGZ,WAAW,EACZ,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,4BAA4B,EAAE,CAAC;AAExC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,MAAM,CAAC,EACrB,SAAS,EACT,GAAG,IAAI,EACR,EAAE,WAAW,2CAab;AAqZD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,WAAW,EACX,OAAO,EACP,SAAS,GACV,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,2CAoBA"}
@@ -1 +1 @@
1
- {"version":3,"file":"Dialog.web.d.ts","sourceRoot":"","sources":["../../../../src/dialog/Dialog.web.tsx"],"names":[],"mappings":"AAyBA,OAAO,EAKL,4BAA4B,EAM7B,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,YAAY,EAGZ,WAAW,EACZ,MAAM,SAAS,CAAC;AASjB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,WAAW,2CAazD;AA+RD,OAAO,EAAE,4BAA4B,EAAE,CAAC;AAmNxC;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,WAAW,EACX,OAAO,EACP,SAAS,GACV,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,2CAgBA;AAgCD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,mZAK5B,CAAC"}
1
+ {"version":3,"file":"Dialog.web.d.ts","sourceRoot":"","sources":["../../../../src/dialog/Dialog.web.tsx"],"names":[],"mappings":"AAyBA,OAAO,EAML,4BAA4B,EAM7B,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,YAAY,EAGZ,WAAW,EACZ,MAAM,SAAS,CAAC;AASjB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,WAAW,2CAazD;AAoSD,OAAO,EAAE,4BAA4B,EAAE,CAAC;AAqNxC;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,WAAW,EACX,OAAO,EACP,SAAS,GACV,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,2CAgBA;AA6BD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,mZAK5B,CAAC"}
@@ -19,11 +19,11 @@ import type { DialogProps } from './types';
19
19
  * so a host's CSS-var theme scope (`vars()`) still applies to descendants
20
20
  * - `title` / `description` / `actions` / `children` → declarative body
21
21
  */
22
- export declare function DialogBottomSheet({ control, open: controlledOpen, onClose, testID, title, description, actions, maxHeightRatio, showHandle, dismissOnBackdrop, style, panelStyle, panelClassName, containerStyle, containerClassName, label, children, }: DialogBottomSheetProps): import("react/jsx-runtime").JSX.Element;
22
+ export declare function DialogBottomSheet({ control, open: controlledOpen, onClose, testID, title, description, actions, maxHeightRatio, showHandle, dismissOnBackdrop, contentPadding, style, panelStyle, panelClassName, containerStyle, containerClassName, label, children, }: DialogBottomSheetProps): import("react/jsx-runtime").JSX.Element;
23
23
  /**
24
24
  * Props the shared bottom-sheet surface consumes — the `bottom`-relevant subset
25
25
  * of `DialogProps`. `width`, `maxWidth`, and `inset` are placement-specific to
26
26
  * the side/centered surfaces and intentionally excluded.
27
27
  */
28
- export type DialogBottomSheetProps = Pick<DialogProps, 'control' | 'open' | 'onClose' | 'testID' | 'title' | 'description' | 'actions' | 'maxHeightRatio' | 'showHandle' | 'dismissOnBackdrop' | 'style' | 'panelStyle' | 'panelClassName' | 'containerStyle' | 'containerClassName' | 'label' | 'children'>;
28
+ export type DialogBottomSheetProps = Pick<DialogProps, 'control' | 'open' | 'onClose' | 'testID' | 'title' | 'description' | 'actions' | 'maxHeightRatio' | 'showHandle' | 'dismissOnBackdrop' | 'contentPadding' | 'style' | 'panelStyle' | 'panelClassName' | 'containerStyle' | 'containerClassName' | 'label' | 'children'>;
29
29
  //# sourceMappingURL=DialogBottomSheet.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DialogBottomSheet.d.ts","sourceRoot":"","sources":["../../../../src/dialog/DialogBottomSheet.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAsB,WAAW,EAAE,MAAM,SAAS,CAAC;AAQ/D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,IAAI,EAAE,cAAc,EACpB,OAAO,EACP,MAAM,EACN,KAAK,EACL,WAAW,EACX,OAAO,EACP,cAAyC,EACzC,UAAiB,EACjB,iBAAwB,EACxB,KAAK,EACL,UAAU,EACV,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,KAAK,EACL,QAAQ,GACT,EAAE,sBAAsB,2CA+IxB;AAED;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,IAAI,CACvC,WAAW,EACT,SAAS,GACT,MAAM,GACN,SAAS,GACT,QAAQ,GACR,OAAO,GACP,aAAa,GACb,SAAS,GACT,gBAAgB,GAChB,YAAY,GACZ,mBAAmB,GACnB,OAAO,GACP,YAAY,GACZ,gBAAgB,GAChB,gBAAgB,GAChB,oBAAoB,GACpB,OAAO,GACP,UAAU,CACb,CAAC"}
1
+ {"version":3,"file":"DialogBottomSheet.d.ts","sourceRoot":"","sources":["../../../../src/dialog/DialogBottomSheet.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAsB,WAAW,EAAE,MAAM,SAAS,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,IAAI,EAAE,cAAc,EACpB,OAAO,EACP,MAAM,EACN,KAAK,EACL,WAAW,EACX,OAAO,EACP,cAAyC,EACzC,UAAiB,EACjB,iBAAwB,EACxB,cAA+C,EAC/C,KAAK,EACL,UAAU,EACV,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,KAAK,EACL,QAAQ,GACT,EAAE,sBAAsB,2CAiKxB;AAED;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,IAAI,CACvC,WAAW,EACT,SAAS,GACT,MAAM,GACN,SAAS,GACT,QAAQ,GACR,OAAO,GACP,aAAa,GACb,SAAS,GACT,gBAAgB,GAChB,YAAY,GACZ,mBAAmB,GACnB,gBAAgB,GAChB,OAAO,GACP,YAAY,GACZ,gBAAgB,GAChB,gBAAgB,GAChB,oBAAoB,GACpB,OAAO,GACP,UAAU,CACb,CAAC"}
@@ -40,6 +40,16 @@ export declare const DEFAULT_CENTER_MAX_WIDTH = 480;
40
40
  export declare const DEFAULT_MAX_HEIGHT_RATIO = 0.9;
41
41
  /** Corner radius shared by the side-sheet panel and the bottom-sheet top. */
42
42
  export declare const PANEL_RADIUS = 20;
43
+ /**
44
+ * Default inner padding (px) of the dialog content container, applied uniformly
45
+ * across every placement (centered panel, side-sheet body, bottom-sheet body)
46
+ * so the chrome reads identically regardless of which surface resolves. This is
47
+ * the legacy value — omitting the `contentPadding` prop keeps 20px everywhere,
48
+ * byte-for-byte unchanged. A host that owns its own insets (e.g. custom
49
+ * `children` that already manage padding) can override it via
50
+ * `Dialog`'s `contentPadding` prop (e.g. `contentPadding={0}`).
51
+ */
52
+ export declare const DEFAULT_DIALOG_CONTENT_PADDING = 20;
43
53
  /** Open/close transition duration (ms). ~280ms ease-out reads smooth. */
44
54
  export declare const ANIMATION_DURATION = 280;
45
55
  /** Centered-card fade-out duration (ms) — preserves the legacy timing. */
@@ -1 +1 @@
1
- {"version":3,"file":"placement.d.ts","sourceRoot":"","sources":["../../../../src/dialog/placement.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAErE;;;;;;;;;GASG;AACH,MAAM,MAAM,yBAAyB,GACjC,eAAe,GACf;IACE,IAAI,EAAE,eAAe,CAAC;IACtB,EAAE,CAAC,EAAE,eAAe,CAAC;IACrB,EAAE,CAAC,EAAE,eAAe,CAAC;IACrB,EAAE,CAAC,EAAE,eAAe,CAAC;IACrB,EAAE,CAAC,EAAE,eAAe,CAAC;CACtB,CAAC;AAEN;;;;GAIG;AACH,eAAO,MAAM,aAAa,MAAM,CAAC;AACjC,eAAO,MAAM,aAAa,MAAM,CAAC;AACjC,eAAO,MAAM,aAAa,OAAO,CAAC;AAClC,eAAO,MAAM,aAAa,OAAO,CAAC;AAUlC,qDAAqD;AACrD,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,gFAAgF;AAChF,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,4EAA4E;AAC5E,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,6EAA6E;AAC7E,eAAO,MAAM,YAAY,KAAK,CAAC;AAE/B,yEAAyE;AACzE,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,0EAA0E;AAC1E,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,oEAAoE;AACpE,eAAO,MAAM,QAAQ,mCAAmC,CAAC;AAEzD,qEAAqE;AACrE,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,+EAA+E;AAC/E,eAAO,MAAM,4BAA4B,gCAAgC,CAAC;AAE1E,sDAAsD;AACtD,eAAO,MAAM,YAAY,KAAK,CAAC;AAC/B,eAAO,MAAM,aAAa,IAAI,CAAC;AAC/B,eAAO,MAAM,aAAa,IAAI,CAAC;AAE/B;;;GAGG;AACH,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,yBAAyB,GAAG,SAAS,GAC/C,eAAe,CAcjB"}
1
+ {"version":3,"file":"placement.d.ts","sourceRoot":"","sources":["../../../../src/dialog/placement.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAErE;;;;;;;;;GASG;AACH,MAAM,MAAM,yBAAyB,GACjC,eAAe,GACf;IACE,IAAI,EAAE,eAAe,CAAC;IACtB,EAAE,CAAC,EAAE,eAAe,CAAC;IACrB,EAAE,CAAC,EAAE,eAAe,CAAC;IACrB,EAAE,CAAC,EAAE,eAAe,CAAC;IACrB,EAAE,CAAC,EAAE,eAAe,CAAC;CACtB,CAAC;AAEN;;;;GAIG;AACH,eAAO,MAAM,aAAa,MAAM,CAAC;AACjC,eAAO,MAAM,aAAa,MAAM,CAAC;AACjC,eAAO,MAAM,aAAa,OAAO,CAAC;AAClC,eAAO,MAAM,aAAa,OAAO,CAAC;AAUlC,qDAAqD;AACrD,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,gFAAgF;AAChF,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,4EAA4E;AAC5E,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,6EAA6E;AAC7E,eAAO,MAAM,YAAY,KAAK,CAAC;AAE/B;;;;;;;;GAQG;AACH,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAEjD,yEAAyE;AACzE,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,0EAA0E;AAC1E,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,oEAAoE;AACpE,eAAO,MAAM,QAAQ,mCAAmC,CAAC;AAEzD,qEAAqE;AACrE,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,+EAA+E;AAC/E,eAAO,MAAM,4BAA4B,gCAAgC,CAAC;AAE1E,sDAAsD;AACtD,eAAO,MAAM,YAAY,KAAK,CAAC;AAC/B,eAAO,MAAM,aAAa,IAAI,CAAC;AAC/B,eAAO,MAAM,aAAa,IAAI,CAAC;AAE/B;;;GAGG;AACH,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,yBAAyB,GAAG,SAAS,GAC/C,eAAe,CAcjB"}
@@ -125,6 +125,18 @@ export type DialogProps = React.PropsWithChildren<{
125
125
  inset?: DialogInset;
126
126
  /** Whether to render the drag handle in bottom-sheet mode. Defaults to `true`. */
127
127
  showHandle?: boolean;
128
+ /**
129
+ * Inner padding (px) of the dialog content container, applied uniformly across
130
+ * every placement (centered panel, side-sheet body, bottom-sheet body).
131
+ * Defaults to `20`.
132
+ *
133
+ * The default chrome padding is correct for the declarative
134
+ * `title`/`description`/`actions` layout, but a host passing pure custom
135
+ * `children` that already own their insets can set `contentPadding={0}` to
136
+ * render flush content and manage padding itself. Omitting the prop keeps the
137
+ * 20px default on every surface.
138
+ */
139
+ contentPadding?: number;
128
140
  /** Whether tapping the backdrop dismisses the dialog. Defaults to `true`. */
129
141
  dismissOnBackdrop?: boolean;
130
142
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/dialog/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/dialog/types.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,8 @@ export { BloomColorScope, useColorScopeStyle } from './color-scope';
4
4
  export type { BloomColorScopeProps } from './color-scope';
5
5
  export { buildTheme, STATUS_COLORS } from './build-theme';
6
6
  export { useTheme, useThemeColor, useBloomTheme } from './use-theme';
7
+ export { useNavigationTheme } from './use-navigation-theme';
8
+ export type { NavigationTheme, NavigationThemeFont } from './use-navigation-theme';
7
9
  export type { Theme, ThemeColors, ThemeMode } from './types';
8
10
  export type { AppColorName, AppColorPreset, PresetTokens } from './color-presets';
9
11
  export { APP_COLOR_NAMES, PREMIUM_COLOR_NAMES, APP_COLOR_PRESETS, HEX_TO_APP_COLOR, hexToAppColorName, } from './color-presets';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/theme/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EACV,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACpE,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACrE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7D,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAClF,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/theme/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EACV,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACpE,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AACnF,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7D,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAClF,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,31 @@
1
+ export interface NavigationThemeFont {
2
+ fontFamily: string;
3
+ fontWeight: '400' | '500' | '700' | '900';
4
+ }
5
+ export interface NavigationTheme {
6
+ dark: boolean;
7
+ colors: {
8
+ primary: string;
9
+ background: string;
10
+ card: string;
11
+ text: string;
12
+ border: string;
13
+ notification: string;
14
+ };
15
+ fonts: {
16
+ regular: NavigationThemeFont;
17
+ medium: NavigationThemeFont;
18
+ bold: NavigationThemeFont;
19
+ heavy: NavigationThemeFont;
20
+ };
21
+ }
22
+ /**
23
+ * Projects Bloom's resolved theme into a react-navigation `Theme` POJO for
24
+ * expo-router's <ThemeProvider>. Keeps Bloom the single source of truth for
25
+ * navigator chrome (Stack headers, screen backgrounds, modal) so it tracks
26
+ * the active color preset. Returns a structural object — no react-navigation
27
+ * import — so Bloom stays decoupled. Replaces the per-app copies in
28
+ * test-app-expo / inbox / accounts.
29
+ */
30
+ export declare function useNavigationTheme(): NavigationTheme;
31
+ //# sourceMappingURL=use-navigation-theme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-navigation-theme.d.ts","sourceRoot":"","sources":["../../../../src/theme/use-navigation-theme.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;CAC3C;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE;QACN,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,KAAK,EAAE;QACL,OAAO,EAAE,mBAAmB,CAAC;QAC7B,MAAM,EAAE,mBAAmB,CAAC;QAC5B,IAAI,EAAE,mBAAmB,CAAC;QAC1B,KAAK,EAAE,mBAAmB,CAAC;KAC5B,CAAC;CACH;AASD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,CAiBpD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/bloom",
3
- "version": "0.16.1",
3
+ "version": "0.17.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",
@@ -5,6 +5,34 @@ import { act, fireEvent, render } from '@testing-library/react-native';
5
5
  import { Dialog, useDialogControl } from '../dialog';
6
6
  import { BloomThemeProvider } from '../theme/BloomThemeProvider';
7
7
 
8
+ /**
9
+ * Minimal shape of a `react-test-renderer` JSON node — enough to walk the tree
10
+ * by host `type` string without depending on `@types/react-test-renderer`.
11
+ */
12
+ type RenderedNode = {
13
+ type: string;
14
+ children: Array<RenderedNode | string> | null;
15
+ };
16
+
17
+ /** Count host nodes of a given `type` in a rendered `toJSON()` tree. */
18
+ function countNodesByType(
19
+ tree: RenderedNode | RenderedNode[] | null,
20
+ type: string,
21
+ ): number {
22
+ if (tree === null) return 0;
23
+ const roots = Array.isArray(tree) ? tree : [tree];
24
+ let count = 0;
25
+ for (const node of roots) {
26
+ if (node.type === type) count += 1;
27
+ if (node.children) {
28
+ for (const child of node.children) {
29
+ if (typeof child !== 'string') count += countNodesByType(child, type);
30
+ }
31
+ }
32
+ }
33
+ return count;
34
+ }
35
+
8
36
  /**
9
37
  * The Dialog `bottom` placement must delegate to the shared cross-platform
10
38
  * `BottomSheet` (via `DialogBottomSheet`) — the SAME surface native and web use
@@ -127,4 +155,105 @@ describe('Dialog bottom placement delegates to BottomSheet', () => {
127
155
  expect(flattened.backgroundColor).toBe('rebeccapurple');
128
156
  expect(flattened.paddingTop).toBe(7);
129
157
  });
158
+
159
+ it('applies the default 20px content padding when contentPadding is omitted', () => {
160
+ let control: ReturnType<typeof useDialogControl> | undefined;
161
+ const { getByTestId } = renderWithTheme(
162
+ <Harness>
163
+ {(c) => {
164
+ control = c;
165
+ return (
166
+ <Dialog control={c} placement="bottom" testID="default-pad">
167
+ <Text>Body</Text>
168
+ </Dialog>
169
+ );
170
+ }}
171
+ </Harness>,
172
+ );
173
+ act(() => {
174
+ control?.open();
175
+ });
176
+ const content = getByTestId('default-pad');
177
+ const flattened = Array.isArray(content.props.style)
178
+ ? Object.assign({}, ...content.props.style.filter(Boolean))
179
+ : content.props.style;
180
+ // Omitting `contentPadding` keeps the legacy 20px chrome padding.
181
+ expect(flattened.padding).toBe(20);
182
+ });
183
+
184
+ it('removes the body padding when contentPadding={0} (host owns its insets)', () => {
185
+ let control: ReturnType<typeof useDialogControl> | undefined;
186
+ const { getByTestId } = renderWithTheme(
187
+ <Harness>
188
+ {(c) => {
189
+ control = c;
190
+ return (
191
+ <Dialog control={c} placement="bottom" testID="flush-pad" contentPadding={0}>
192
+ <Text>Flush body</Text>
193
+ </Dialog>
194
+ );
195
+ }}
196
+ </Harness>,
197
+ );
198
+ act(() => {
199
+ control?.open();
200
+ });
201
+ const content = getByTestId('flush-pad');
202
+ const flattened = Array.isArray(content.props.style)
203
+ ? Object.assign({}, ...content.props.style.filter(Boolean))
204
+ : content.props.style;
205
+ // `contentPadding={0}` yields flush content the host pads itself.
206
+ expect(flattened.padding).toBe(0);
207
+ });
208
+
209
+ it('does NOT wrap pure custom children in a second scroll container (no scroll-in-scroll)', () => {
210
+ let control: ReturnType<typeof useDialogControl> | undefined;
211
+ const result = renderWithTheme(
212
+ <Harness>
213
+ {(c) => {
214
+ control = c;
215
+ return (
216
+ <Dialog control={c} placement="bottom">
217
+ <Text>Custom children own their own scrolling</Text>
218
+ </Dialog>
219
+ );
220
+ }}
221
+ </Harness>,
222
+ );
223
+ act(() => {
224
+ control?.open();
225
+ });
226
+ // Pure custom children → DialogBottomSheet passes `scrollable={false}` to
227
+ // BottomSheet, so the sheet does NOT add its internal ScrollView. A child
228
+ // that contains its own scroller is then the ONLY scroll container.
229
+ const tree: RenderedNode | RenderedNode[] | null = result.toJSON();
230
+ expect(countNodesByType(tree, 'Animated.ScrollView')).toBe(0);
231
+ });
232
+
233
+ it('keeps the internal scrollable body for declarative dialogs (title/description/actions)', () => {
234
+ let control: ReturnType<typeof useDialogControl> | undefined;
235
+ const result = renderWithTheme(
236
+ <Harness>
237
+ {(c) => {
238
+ control = c;
239
+ return (
240
+ <Dialog
241
+ control={c}
242
+ placement="bottom"
243
+ title="Confirm"
244
+ description="Short declarative body."
245
+ actions={[{ label: 'OK' }]}
246
+ />
247
+ );
248
+ }}
249
+ </Harness>,
250
+ );
251
+ act(() => {
252
+ control?.open();
253
+ });
254
+ // Declarative chrome → the default scrollable body is retained, so a long
255
+ // title/description list still scrolls gracefully on a small viewport.
256
+ const tree: RenderedNode | RenderedNode[] | null = result.toJSON();
257
+ expect(countNodesByType(tree, 'Animated.ScrollView')).toBe(1);
258
+ });
130
259
  });
@@ -0,0 +1,42 @@
1
+ import * as Bloom from '../index';
2
+
3
+ type AnyExports = Record<string, unknown>;
4
+ const api = Bloom as AnyExports;
5
+
6
+ // The ONLY families allowed to remain namespace objects (collections /
7
+ // generic-colliding part names). Everything else is flat-with-prefix.
8
+ const ALLOWED_NAMESPACES = ['Icons', 'Typography', 'Skeleton', 'Grid', 'Code', 'Fonts'] as const;
9
+
10
+ // Compound families converted to flat: their top-level name becomes the
11
+ // root component (a function), no longer a namespace object.
12
+ const FLATTENED_ROOTS = [
13
+ 'Tabs', 'Accordion', 'Select', 'Menu', 'ContextMenu', 'Popover',
14
+ 'Tooltip', 'SegmentedControl', 'TextField', 'Admonition', 'PromptInput',
15
+ ] as const;
16
+
17
+ // One renamed part per converted family — proves the flat rename landed.
18
+ const REPRESENTATIVE_PARTS = [
19
+ 'TabsTrigger', 'AccordionItem', 'SelectItem', 'MenuItem', 'ContextMenuItem',
20
+ 'PopoverTrigger', 'TooltipTrigger', 'SegmentedControlItem', 'TextFieldInput',
21
+ 'AdmonitionIcon', 'PromptInputTextarea',
22
+ ] as const;
23
+
24
+ describe('public API contract', () => {
25
+ it('keeps the six collection families as namespace objects', () => {
26
+ for (const ns of ALLOWED_NAMESPACES) {
27
+ expect(typeof api[ns]).toBe('object');
28
+ }
29
+ });
30
+
31
+ it('exposes each compound family root as a flat component (not a namespace)', () => {
32
+ for (const name of FLATTENED_ROOTS) {
33
+ expect(typeof api[name]).toBe('function');
34
+ }
35
+ });
36
+
37
+ it('exposes compound parts as flat prefixed components', () => {
38
+ for (const name of REPRESENTATIVE_PARTS) {
39
+ expect(typeof api[name]).toBe('function');
40
+ }
41
+ });
42
+ });
@@ -32,6 +32,7 @@ import { DialogBody } from './DialogContent';
32
32
  import { DialogBottomSheet } from './DialogBottomSheet';
33
33
  import {
34
34
  ANIMATION_DURATION,
35
+ DEFAULT_DIALOG_CONTENT_PADDING,
35
36
  DEFAULT_SIDE_WIDTH,
36
37
  DIALOG_SHEET_BACKDROP_TESTID,
37
38
  PANEL_RADIUS,
@@ -106,6 +107,7 @@ function CenteredOrSideDialog({
106
107
  width = DEFAULT_SIDE_WIDTH,
107
108
  inset,
108
109
  dismissOnBackdrop = true,
110
+ contentPadding = DEFAULT_DIALOG_CONTENT_PADDING,
109
111
  style,
110
112
  panelStyle,
111
113
  panelClassName,
@@ -242,6 +244,7 @@ function CenteredOrSideDialog({
242
244
  width={width}
243
245
  inset={inset}
244
246
  dismissOnBackdrop={dismissOnBackdrop}
247
+ contentPadding={contentPadding}
245
248
  testID={testID}
246
249
  label={label}
247
250
  title={title}
@@ -283,7 +286,7 @@ function CenteredOrSideDialog({
283
286
  // Detached BottomSheet already adds `marginBottom: insets.bottom + 16`
284
287
  // to the sheet container — the floating card sits ABOVE the
285
288
  // system gesture bar, so we don't add `insets.bottom` here.
286
- { paddingTop: 20, paddingHorizontal: 20, paddingBottom: 20 },
289
+ { padding: contentPadding },
287
290
  { backgroundColor: theme.colors.background },
288
291
  style,
289
292
  panelStyle,
@@ -311,6 +314,7 @@ function SideSheet({
311
314
  width,
312
315
  inset,
313
316
  dismissOnBackdrop,
317
+ contentPadding,
314
318
  testID,
315
319
  label,
316
320
  title,
@@ -330,6 +334,7 @@ function SideSheet({
330
334
  width: number;
331
335
  inset?: DialogInset;
332
336
  dismissOnBackdrop: boolean;
337
+ contentPadding: number;
333
338
  testID?: string;
334
339
  label?: string;
335
340
  title?: string;
@@ -461,7 +466,7 @@ function SideSheet({
461
466
  ]}
462
467
  pointerEvents="auto"
463
468
  >
464
- <View style={sideStyles.body}>{children}</View>
469
+ <View style={{ padding: contentPadding }}>{children}</View>
465
470
  </Animated.View>
466
471
  </View>
467
472
  );
@@ -484,9 +489,6 @@ const sideStyles = StyleSheet.create({
484
489
  shadowOffset: { width: 0, height: 8 },
485
490
  elevation: 12,
486
491
  },
487
- body: {
488
- padding: 20,
489
- },
490
492
  });
491
493
 
492
494
  /**
@@ -27,6 +27,7 @@ import {
27
27
  ANIMATION_DURATION,
28
28
  CENTER_FADE_OUT_DURATION,
29
29
  DEFAULT_CENTER_MAX_WIDTH,
30
+ DEFAULT_DIALOG_CONTENT_PADDING,
30
31
  DEFAULT_SIDE_WIDTH,
31
32
  DIALOG_SHEET_BACKDROP_TESTID,
32
33
  EASE_OUT,
@@ -106,6 +107,7 @@ function CenterOrSideDialog({
106
107
  maxWidth = DEFAULT_CENTER_MAX_WIDTH,
107
108
  inset,
108
109
  dismissOnBackdrop = true,
110
+ contentPadding = DEFAULT_DIALOG_CONTENT_PADDING,
109
111
  style,
110
112
  panelStyle,
111
113
  panelClassName,
@@ -260,6 +262,7 @@ function CenterOrSideDialog({
260
262
  actions={actions}
261
263
  style={style}
262
264
  maxWidth={maxWidth}
265
+ contentPadding={contentPadding}
263
266
  isClosing={isClosing}
264
267
  >
265
268
  {children}
@@ -287,6 +290,7 @@ function CenterOrSideDialog({
287
290
  width={width}
288
291
  inset={inset}
289
292
  dismissOnBackdrop={dismissOnBackdrop}
293
+ contentPadding={contentPadding}
290
294
  onDismiss={close}
291
295
  panelStyle={panelStyle}
292
296
  panelClassName={panelClassName}
@@ -310,6 +314,7 @@ function DialogPanel({
310
314
  actions,
311
315
  style,
312
316
  maxWidth,
317
+ contentPadding,
313
318
  isClosing,
314
319
  children,
315
320
  }: {
@@ -320,6 +325,7 @@ function DialogPanel({
320
325
  actions?: DialogAction[];
321
326
  style?: DialogProps['style'];
322
327
  maxWidth: number;
328
+ contentPadding: number;
323
329
  isClosing: boolean;
324
330
  children?: React.ReactNode;
325
331
  }) {
@@ -350,7 +356,7 @@ function DialogPanel({
350
356
  shadowOpacity: theme.isDark ? 0.4 : 0.1,
351
357
  shadowRadius: 30,
352
358
  shadowOffset: { width: 0, height: 4 },
353
- padding: 20,
359
+ padding: contentPadding,
354
360
  zIndex: dialogZIndex.surface,
355
361
  },
356
362
  isClosing
@@ -394,6 +400,7 @@ function SheetSurface({
394
400
  width,
395
401
  inset,
396
402
  dismissOnBackdrop,
403
+ contentPadding,
397
404
  onDismiss,
398
405
  panelStyle,
399
406
  panelClassName,
@@ -412,6 +419,7 @@ function SheetSurface({
412
419
  width: number;
413
420
  inset?: DialogInset;
414
421
  dismissOnBackdrop: boolean;
422
+ contentPadding: number;
415
423
  onDismiss: () => void;
416
424
  panelStyle?: StyleProp<ViewStyle>;
417
425
  panelClassName?: string;
@@ -528,7 +536,7 @@ function SheetSurface({
528
536
  ]}
529
537
  pointerEvents="auto"
530
538
  >
531
- <View style={sheetStyles.body}>
539
+ <View style={{ padding: contentPadding }}>
532
540
  <DialogBody
533
541
  titleId={titleId}
534
542
  descriptionId={descriptionId}
@@ -641,9 +649,6 @@ const sheetStyles = {
641
649
  shadowOffset: { width: 0, height: 8 },
642
650
  zIndex: dialogZIndex.surface,
643
651
  } as ViewStyle,
644
- body: {
645
- padding: 20,
646
- } as ViewStyle,
647
652
  };
648
653
 
649
654
  /**
@@ -13,18 +13,13 @@ import { useTheme } from '../theme/use-theme';
13
13
  import { Context } from './context';
14
14
  import { DialogBody } from './DialogContent';
15
15
  import {
16
+ DEFAULT_DIALOG_CONTENT_PADDING,
16
17
  DEFAULT_MAX_HEIGHT_RATIO,
17
18
  PANEL_RADIUS,
18
19
  SHEET_BACKDROP_OPACITY,
19
20
  } from './placement';
20
21
  import type { DialogControlProps, DialogProps } from './types';
21
22
 
22
- /**
23
- * Padding of the bottom-sheet content container (px). Matches the side-sheet
24
- * and centered-panel body padding so every placement reads identically.
25
- */
26
- const SHEET_CONTENT_PADDING = 20;
27
-
28
23
  /**
29
24
  * Shared `BottomSheet`-backed surface for the `bottom` placement, used by BOTH
30
25
  * `Dialog.tsx` (native) and `Dialog.web.tsx` (web). `BottomSheet` is itself a
@@ -56,6 +51,7 @@ export function DialogBottomSheet({
56
51
  maxHeightRatio = DEFAULT_MAX_HEIGHT_RATIO,
57
52
  showHandle = true,
58
53
  dismissOnBackdrop = true,
54
+ contentPadding = DEFAULT_DIALOG_CONTENT_PADDING,
59
55
  style,
60
56
  panelStyle,
61
57
  panelClassName,
@@ -164,6 +160,21 @@ export function DialogBottomSheet({
164
160
  ];
165
161
  }, [theme.colors.background, maxHeightRatio, panelStyle]);
166
162
 
163
+ // Does this dialog render bloom's declarative chrome (title / description /
164
+ // actions), or is it pure custom `children`? Pure custom children own their
165
+ // own layout and scrolling, so we render them through a NON-scrollable
166
+ // BottomSheet body — otherwise the sheet's internal ScrollView would wrap a
167
+ // child that already contains its own scroller, producing a scroll-in-scroll
168
+ // (double scroll container). Declarative dialogs are short and keep the
169
+ // sheet's default scrollable body so they degrade gracefully on small
170
+ // viewports. This only changes what `DialogBottomSheet` passes to
171
+ // `BottomSheet`; the standalone `BottomSheet` API and its `scrollable` default
172
+ // are unchanged for direct consumers.
173
+ const hasDeclarativeChrome =
174
+ title !== undefined ||
175
+ description !== undefined ||
176
+ (actions !== undefined && actions.length > 0);
177
+
167
178
  return (
168
179
  <BottomSheet
169
180
  ref={ref}
@@ -171,6 +182,9 @@ export function DialogBottomSheet({
171
182
  onDismissAttempt={onDismissAttempt}
172
183
  enablePanDownToClose
173
184
  showHandle={showHandle}
185
+ // Pure custom children own scrolling → opt OUT of BottomSheet's internal
186
+ // ScrollView so there is exactly ONE scroll container (no scroll-in-scroll).
187
+ scrollable={hasDeclarativeChrome}
174
188
  // Stronger dim than a lone sheet so an underlying sheet's handle/content
175
189
  // doesn't bleed through when a Dialog is stacked over one.
176
190
  backdropOpacity={SHEET_BACKDROP_OPACITY + 0.3}
@@ -185,7 +199,7 @@ export function DialogBottomSheet({
185
199
  {...(containerClassName ? ({ className: containerClassName } as Record<string, string>) : {})}
186
200
  {...(panelClassName ? ({ className: panelClassName } as Record<string, string>) : {})}
187
201
  style={[
188
- { padding: SHEET_CONTENT_PADDING },
202
+ { padding: contentPadding },
189
203
  // `containerStyle` carries the host's CSS-var theme scope; it wraps
190
204
  // the content subtree so descendants read the scoped palette.
191
205
  containerStyle,
@@ -225,6 +239,7 @@ export type DialogBottomSheetProps = Pick<
225
239
  | 'maxHeightRatio'
226
240
  | 'showHandle'
227
241
  | 'dismissOnBackdrop'
242
+ | 'contentPadding'
228
243
  | 'style'
229
244
  | 'panelStyle'
230
245
  | 'panelClassName'
@@ -60,6 +60,17 @@ export const DEFAULT_MAX_HEIGHT_RATIO = 0.9;
60
60
  /** Corner radius shared by the side-sheet panel and the bottom-sheet top. */
61
61
  export const PANEL_RADIUS = 20;
62
62
 
63
+ /**
64
+ * Default inner padding (px) of the dialog content container, applied uniformly
65
+ * across every placement (centered panel, side-sheet body, bottom-sheet body)
66
+ * so the chrome reads identically regardless of which surface resolves. This is
67
+ * the legacy value — omitting the `contentPadding` prop keeps 20px everywhere,
68
+ * byte-for-byte unchanged. A host that owns its own insets (e.g. custom
69
+ * `children` that already manage padding) can override it via
70
+ * `Dialog`'s `contentPadding` prop (e.g. `contentPadding={0}`).
71
+ */
72
+ export const DEFAULT_DIALOG_CONTENT_PADDING = 20;
73
+
63
74
  /** Open/close transition duration (ms). ~280ms ease-out reads smooth. */
64
75
  export const ANIMATION_DURATION = 280;
65
76