@oxyhq/bloom 0.16.2 → 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.
package/README.md CHANGED
@@ -50,19 +50,21 @@ const theme = useTheme();
50
50
 
51
51
  4 modes: `light`, `dark`, `system`, `adaptive` (uses iOS/Android native dynamic colors when available).
52
52
 
53
- ### Modal & feedback components: Dialog, BottomSheet, toast / alert
53
+ ### Overlay components: Dialog, BottomSheet, toast / alert
54
54
 
55
- Bloom ships three primitives for modal, sheet, and feedback presentation. Pick the one that matches your use case:
55
+ Bloom ships **two** overlay surface components plus feedback helpers:
56
56
 
57
- | Component | Native | Web | Use when |
58
- |-----------|--------|-----|----------|
59
- | `Dialog` | Bottom-sheet card (Bloom's own) | Centered modal | Confirmation flows AND custom modal content — same component handles both |
60
- | `BottomSheet` | Draggable sheet | RN `Modal` polyfill | You need direct control over snap points, scroll handoff, keyboard, detached/flush layout |
61
- | `toast` / `alert` | Sonner-native + Bloom theme | Sonner + Bloom theme | Passive feedback (`toast`) or one-shot confirmations (`alert`) called imperatively from anywhere |
57
+ | Component | Use when |
58
+ |-----------|----------|
59
+ | `Dialog` | All modal surfaces — centered card, side-sheet (left/right), or bottom-sheet via the `placement` prop. Confirmation flows AND custom content. |
60
+ | `BottomSheet` | You need direct control over snap points, scroll handoff, gesture coordination, or detached presentation. |
61
+ | `toast` / `alert` | Passive feedback (`toast`) or one-shot confirmations (`alert`) called imperatively from anywhere. |
62
+
63
+ > **Removed in 0.16.x:** `CenteredDialog` and `ResponsiveSheet` no longer exist. Migrate: `<CenteredDialog visible={v} onClose={c}>…</CenteredDialog>` → `<Dialog placement="center" open={v} onClose={c}>…</Dialog>`; `<ResponsiveSheet side="left" open={o} onClose={c}>…</ResponsiveSheet>` → `<Dialog placement={{ base:'bottom', md:'left' }} open={o} onClose={c}>…</Dialog>`.
62
64
 
63
65
  ### Dialog
64
66
 
65
- A single `<Dialog>` component for both confirmation flows AND custom modal content. Bottom-sheet card on native, centered modal on web.
67
+ A single `<Dialog>` component for every overlay surface centered modal, side-sheet, or bottom-sheet selected by the `placement` prop. Same component and same props on every platform.
66
68
 
67
69
  > **Required providers (native).** Your app root **must** be wrapped with `GestureHandlerRootView` from `react-native-gesture-handler` for the bottom-sheet pan gestures to work.
68
70
 
@@ -83,7 +85,7 @@ export default function Root() {
83
85
  }
84
86
  ```
85
87
 
86
- `BloomDialogProvider` powers the imperative `alert()` helper (see below) — mount it once near the app root.
88
+ `BloomDialogProvider` powers the imperative `alert()` helper — mount it once near the app root.
87
89
 
88
90
  #### Declarative (the 90% case)
89
91
 
@@ -110,12 +112,38 @@ function SignOutButton() {
110
112
  }
111
113
  ```
112
114
 
115
+ #### Controlled open state
116
+
117
+ ```tsx
118
+ <Dialog
119
+ placement="center"
120
+ open={isOpen}
121
+ onClose={() => setIsOpen(false)}
122
+ title="Confirm?"
123
+ actions={[{ label: 'OK', onPress: handleOk }, { label: 'Cancel', color: 'cancel' }]}
124
+ />
125
+ ```
126
+
127
+ #### Side-sheet (drawer)
128
+
129
+ ```tsx
130
+ // Fixed left side-sheet
131
+ <Dialog placement="left" control={control} title="Filters">
132
+ <FilterPanel />
133
+ </Dialog>
134
+
135
+ // Responsive: bottom-sheet on mobile, left drawer on desktop
136
+ <Dialog placement={{ base: 'bottom', md: 'left' }} control={control} title="Filters">
137
+ <FilterPanel />
138
+ </Dialog>
139
+ ```
140
+
113
141
  #### Custom content
114
142
 
115
- Provide any JSX as `children`. Combine with `title` to keep a consistent header.
143
+ Provide any JSX as `children`. Combine with `title` to keep a consistent header. Set `contentPadding={0}` when the children own their own insets.
116
144
 
117
145
  ```tsx
118
- <Dialog control={control} title="Pick a tag">
146
+ <Dialog control={control} title="Pick a tag" contentPadding={0}>
119
147
  <YourCustomBody />
120
148
  </Dialog>
121
149
  ```
@@ -132,19 +160,29 @@ Drop the declarative props entirely — `children` owns every pixel.
132
160
 
133
161
  #### Props
134
162
 
135
- - `control` — from `useDialogControl()`.
163
+ - `control?` — from `useDialogControl()`. Omit when using controlled `open` instead.
164
+ - `open?` — controlled open state. When provided, wins over `control`.
165
+ - `placement?` — `'center' | 'left' | 'right' | 'bottom'` or a responsive map `{ base; sm?; md?; lg?; xl? }`. Default: `'center'`. Breakpoints: sm 640 / md 768 / lg 1024 / xl 1280 px.
136
166
  - `title?: string` — header text.
137
167
  - `description?: string` — supporting copy rendered below the title.
138
- - `actions?: DialogAction[]` — confirmation buttons. Each action accepts:
139
- - `label: string` — button text.
168
+ - `actions?: DialogAction[]` — confirmation buttons. Each action:
169
+ - `label: string`
140
170
  - `color?: 'default' | 'cancel' | 'destructive'` — defaults to `'default'`.
141
171
  - `onPress?: (e) => void` — invoked after the dialog finishes closing.
142
172
  - `disabled?: boolean`
143
173
  - `shouldCloseOnPress?: boolean` — defaults to `true`. Set `false` while an async action is in flight.
144
174
  - `testID?: string`
145
175
  - `children?: React.ReactNode` — custom content rendered after the description.
146
- - `onClose?: () => void` — fires after the dialog has finished closing.
147
- - `style?`applied to the dialog content container.
176
+ - `onClose?: () => void` — fires after the dialog has finished closing. In controlled mode, the host must flip `open` to `false`.
177
+ - `contentPadding?: number` inner padding of the dialog body. Default `20`. Set `0` for custom children that own their own insets.
178
+ - `width?: number` — side-sheet width (px). Default `460`.
179
+ - `maxWidth?: number` — centered-card max width (px). Default `480`.
180
+ - `maxHeightRatio?: number` — bottom-sheet height as a fraction of viewport height. Default `0.9`.
181
+ - `inset?: { top?; bottom?; left?; right? }` — side-sheet inset (px) from the overlay container edges.
182
+ - `showHandle?: boolean` — show drag handle in bottom-sheet mode. Default `true`.
183
+ - `dismissOnBackdrop?: boolean` — tap backdrop to dismiss. Default `true`.
184
+ - `panelStyle? / panelClassName?` — style/class for the panel surface.
185
+ - `containerStyle? / containerClassName?` — style/class for the root overlay (e.g. rail offset, theme-var scope).
148
186
  - `label?: string` — accessibility label.
149
187
  - `testID?: string`
150
188
 
@@ -99,6 +99,12 @@ Object.defineProperty(exports, "useColorScopeStyle", {
99
99
  return _colorScope.useColorScopeStyle;
100
100
  }
101
101
  });
102
+ Object.defineProperty(exports, "useNavigationTheme", {
103
+ enumerable: true,
104
+ get: function () {
105
+ return _useNavigationTheme.useNavigationTheme;
106
+ }
107
+ });
102
108
  Object.defineProperty(exports, "useTheme", {
103
109
  enumerable: true,
104
110
  get: function () {
@@ -121,6 +127,7 @@ var _BloomThemeProvider = require("./BloomThemeProvider.js");
121
127
  var _colorScope = require("./color-scope");
122
128
  var _buildTheme = require("./build-theme.js");
123
129
  var _useTheme = require("./use-theme.js");
130
+ var _useNavigationTheme = require("./use-navigation-theme.js");
124
131
  var _colorPresets = require("./color-presets.js");
125
132
  var _presetVars = require("./preset-vars.js");
126
133
  var _applyDarkClass = require("./apply-dark-class.js");
@@ -1 +1 @@
1
- {"version":3,"names":["_BloomThemeProvider","require","_colorScope","_buildTheme","_useTheme","_colorPresets","_presetVars","_applyDarkClass","_setColorSchemeSafe","_initCssInterop","_persistence"],"sourceRoot":"../../../src","sources":["theme/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,mBAAA,GAAAC,OAAA;AAKA,IAAAC,WAAA,GAAAD,OAAA;AAEA,IAAAE,WAAA,GAAAF,OAAA;AACA,IAAAG,SAAA,GAAAH,OAAA;AAGA,IAAAI,aAAA,GAAAJ,OAAA;AAOA,IAAAK,WAAA,GAAAL,OAAA;AACA,IAAAM,eAAA,GAAAN,OAAA;AACA,IAAAO,mBAAA,GAAAP,OAAA;AACA,IAAAQ,eAAA,GAAAR,OAAA;AAEA,IAAAS,YAAA,GAAAT,OAAA","ignoreList":[]}
1
+ {"version":3,"names":["_BloomThemeProvider","require","_colorScope","_buildTheme","_useTheme","_useNavigationTheme","_colorPresets","_presetVars","_applyDarkClass","_setColorSchemeSafe","_initCssInterop","_persistence"],"sourceRoot":"../../../src","sources":["theme/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,mBAAA,GAAAC,OAAA;AAKA,IAAAC,WAAA,GAAAD,OAAA;AAEA,IAAAE,WAAA,GAAAF,OAAA;AACA,IAAAG,SAAA,GAAAH,OAAA;AACA,IAAAI,mBAAA,GAAAJ,OAAA;AAIA,IAAAK,aAAA,GAAAL,OAAA;AAOA,IAAAM,WAAA,GAAAN,OAAA;AACA,IAAAO,eAAA,GAAAP,OAAA;AACA,IAAAQ,mBAAA,GAAAR,OAAA;AACA,IAAAS,eAAA,GAAAT,OAAA;AAEA,IAAAU,YAAA,GAAAV,OAAA","ignoreList":[]}
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useNavigationTheme = useNavigationTheme;
7
+ var _react = require("react");
8
+ var _useTheme = require("./use-theme.js");
9
+ const NAV_FONTS = {
10
+ regular: {
11
+ fontFamily: 'System',
12
+ fontWeight: '400'
13
+ },
14
+ medium: {
15
+ fontFamily: 'System',
16
+ fontWeight: '500'
17
+ },
18
+ bold: {
19
+ fontFamily: 'System',
20
+ fontWeight: '700'
21
+ },
22
+ heavy: {
23
+ fontFamily: 'System',
24
+ fontWeight: '900'
25
+ }
26
+ };
27
+
28
+ /**
29
+ * Projects Bloom's resolved theme into a react-navigation `Theme` POJO for
30
+ * expo-router's <ThemeProvider>. Keeps Bloom the single source of truth for
31
+ * navigator chrome (Stack headers, screen backgrounds, modal) so it tracks
32
+ * the active color preset. Returns a structural object — no react-navigation
33
+ * import — so Bloom stays decoupled. Replaces the per-app copies in
34
+ * test-app-expo / inbox / accounts.
35
+ */
36
+ function useNavigationTheme() {
37
+ const {
38
+ mode,
39
+ colors
40
+ } = (0, _useTheme.useTheme)();
41
+ return (0, _react.useMemo)(() => ({
42
+ dark: mode === 'dark',
43
+ colors: {
44
+ primary: colors.primary,
45
+ background: colors.background,
46
+ card: colors.card,
47
+ text: colors.text,
48
+ border: colors.border,
49
+ notification: colors.error
50
+ },
51
+ fonts: NAV_FONTS
52
+ }), [mode, colors]);
53
+ }
54
+ //# sourceMappingURL=use-navigation-theme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_react","require","_useTheme","NAV_FONTS","regular","fontFamily","fontWeight","medium","bold","heavy","useNavigationTheme","mode","colors","useTheme","useMemo","dark","primary","background","card","text","border","notification","error","fonts"],"sourceRoot":"../../../src","sources":["theme/use-navigation-theme.ts"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,SAAA,GAAAD,OAAA;AAyBA,MAAME,SAAmC,GAAG;EAC1CC,OAAO,EAAE;IAAEC,UAAU,EAAE,QAAQ;IAAEC,UAAU,EAAE;EAAM,CAAC;EACpDC,MAAM,EAAE;IAAEF,UAAU,EAAE,QAAQ;IAAEC,UAAU,EAAE;EAAM,CAAC;EACnDE,IAAI,EAAE;IAAEH,UAAU,EAAE,QAAQ;IAAEC,UAAU,EAAE;EAAM,CAAC;EACjDG,KAAK,EAAE;IAAEJ,UAAU,EAAE,QAAQ;IAAEC,UAAU,EAAE;EAAM;AACnD,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASI,kBAAkBA,CAAA,EAAoB;EACpD,MAAM;IAAEC,IAAI;IAAEC;EAAO,CAAC,GAAG,IAAAC,kBAAQ,EAAC,CAAC;EACnC,OAAO,IAAAC,cAAO,EACZ,OAAO;IACLC,IAAI,EAAEJ,IAAI,KAAK,MAAM;IACrBC,MAAM,EAAE;MACNI,OAAO,EAAEJ,MAAM,CAACI,OAAO;MACvBC,UAAU,EAAEL,MAAM,CAACK,UAAU;MAC7BC,IAAI,EAAEN,MAAM,CAACM,IAAI;MACjBC,IAAI,EAAEP,MAAM,CAACO,IAAI;MACjBC,MAAM,EAAER,MAAM,CAACQ,MAAM;MACrBC,YAAY,EAAET,MAAM,CAACU;IACvB,CAAC;IACDC,KAAK,EAAEpB;EACT,CAAC,CAAC,EACF,CAACQ,IAAI,EAAEC,MAAM,CACf,CAAC;AACH","ignoreList":[]}
@@ -4,6 +4,7 @@ export { BloomThemeProvider } from "./BloomThemeProvider.js";
4
4
  export { BloomColorScope, useColorScopeStyle } from './color-scope';
5
5
  export { buildTheme, STATUS_COLORS } from "./build-theme.js";
6
6
  export { useTheme, useThemeColor, useBloomTheme } from "./use-theme.js";
7
+ export { useNavigationTheme } from "./use-navigation-theme.js";
7
8
  export { APP_COLOR_NAMES, PREMIUM_COLOR_NAMES, APP_COLOR_PRESETS, HEX_TO_APP_COLOR, hexToAppColorName } from "./color-presets.js";
8
9
  export { getPresetVars, applyPresetVarsToDocument } from "./preset-vars.js";
9
10
  export { applyDarkClass } from "./apply-dark-class.js";
@@ -1 +1 @@
1
- {"version":3,"names":["BloomThemeProvider","BloomColorScope","useColorScopeStyle","buildTheme","STATUS_COLORS","useTheme","useThemeColor","useBloomTheme","APP_COLOR_NAMES","PREMIUM_COLOR_NAMES","APP_COLOR_PRESETS","HEX_TO_APP_COLOR","hexToAppColorName","getPresetVars","applyPresetVarsToDocument","applyDarkClass","setColorSchemeSafe","initCssInteropDarkMode","webLocalStorage"],"sourceRoot":"../../../src","sources":["theme/index.ts"],"mappings":";;AAAA,SAASA,kBAAkB,QAAQ,yBAAsB;AAKzD,SAASC,eAAe,EAAEC,kBAAkB,QAAQ,eAAe;AAEnE,SAASC,UAAU,EAAEC,aAAa,QAAQ,kBAAe;AACzD,SAASC,QAAQ,EAAEC,aAAa,EAAEC,aAAa,QAAQ,gBAAa;AAGpE,SACEC,eAAe,EACfC,mBAAmB,EACnBC,iBAAiB,EACjBC,gBAAgB,EAChBC,iBAAiB,QACZ,oBAAiB;AACxB,SAASC,aAAa,EAAEC,yBAAyB,QAAQ,kBAAe;AACxE,SAASC,cAAc,QAAQ,uBAAoB;AACnD,SAASC,kBAAkB,QAAQ,4BAAyB;AAC5D,SAASC,sBAAsB,QAAQ,uBAAoB;AAE3D,SAASC,eAAe,QAAQ,kBAAe","ignoreList":[]}
1
+ {"version":3,"names":["BloomThemeProvider","BloomColorScope","useColorScopeStyle","buildTheme","STATUS_COLORS","useTheme","useThemeColor","useBloomTheme","useNavigationTheme","APP_COLOR_NAMES","PREMIUM_COLOR_NAMES","APP_COLOR_PRESETS","HEX_TO_APP_COLOR","hexToAppColorName","getPresetVars","applyPresetVarsToDocument","applyDarkClass","setColorSchemeSafe","initCssInteropDarkMode","webLocalStorage"],"sourceRoot":"../../../src","sources":["theme/index.ts"],"mappings":";;AAAA,SAASA,kBAAkB,QAAQ,yBAAsB;AAKzD,SAASC,eAAe,EAAEC,kBAAkB,QAAQ,eAAe;AAEnE,SAASC,UAAU,EAAEC,aAAa,QAAQ,kBAAe;AACzD,SAASC,QAAQ,EAAEC,aAAa,EAAEC,aAAa,QAAQ,gBAAa;AACpE,SAASC,kBAAkB,QAAQ,2BAAwB;AAI3D,SACEC,eAAe,EACfC,mBAAmB,EACnBC,iBAAiB,EACjBC,gBAAgB,EAChBC,iBAAiB,QACZ,oBAAiB;AACxB,SAASC,aAAa,EAAEC,yBAAyB,QAAQ,kBAAe;AACxE,SAASC,cAAc,QAAQ,uBAAoB;AACnD,SAASC,kBAAkB,QAAQ,4BAAyB;AAC5D,SAASC,sBAAsB,QAAQ,uBAAoB;AAE3D,SAASC,eAAe,QAAQ,kBAAe","ignoreList":[]}
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+
3
+ import { useMemo } from 'react';
4
+ import { useTheme } from "./use-theme.js";
5
+ const NAV_FONTS = {
6
+ regular: {
7
+ fontFamily: 'System',
8
+ fontWeight: '400'
9
+ },
10
+ medium: {
11
+ fontFamily: 'System',
12
+ fontWeight: '500'
13
+ },
14
+ bold: {
15
+ fontFamily: 'System',
16
+ fontWeight: '700'
17
+ },
18
+ heavy: {
19
+ fontFamily: 'System',
20
+ fontWeight: '900'
21
+ }
22
+ };
23
+
24
+ /**
25
+ * Projects Bloom's resolved theme into a react-navigation `Theme` POJO for
26
+ * expo-router's <ThemeProvider>. Keeps Bloom the single source of truth for
27
+ * navigator chrome (Stack headers, screen backgrounds, modal) so it tracks
28
+ * the active color preset. Returns a structural object — no react-navigation
29
+ * import — so Bloom stays decoupled. Replaces the per-app copies in
30
+ * test-app-expo / inbox / accounts.
31
+ */
32
+ export function useNavigationTheme() {
33
+ const {
34
+ mode,
35
+ colors
36
+ } = useTheme();
37
+ return useMemo(() => ({
38
+ dark: mode === 'dark',
39
+ colors: {
40
+ primary: colors.primary,
41
+ background: colors.background,
42
+ card: colors.card,
43
+ text: colors.text,
44
+ border: colors.border,
45
+ notification: colors.error
46
+ },
47
+ fonts: NAV_FONTS
48
+ }), [mode, colors]);
49
+ }
50
+ //# sourceMappingURL=use-navigation-theme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["useMemo","useTheme","NAV_FONTS","regular","fontFamily","fontWeight","medium","bold","heavy","useNavigationTheme","mode","colors","dark","primary","background","card","text","border","notification","error","fonts"],"sourceRoot":"../../../src","sources":["theme/use-navigation-theme.ts"],"mappings":";;AAAA,SAASA,OAAO,QAAQ,OAAO;AAC/B,SAASC,QAAQ,QAAQ,gBAAa;AAyBtC,MAAMC,SAAmC,GAAG;EAC1CC,OAAO,EAAE;IAAEC,UAAU,EAAE,QAAQ;IAAEC,UAAU,EAAE;EAAM,CAAC;EACpDC,MAAM,EAAE;IAAEF,UAAU,EAAE,QAAQ;IAAEC,UAAU,EAAE;EAAM,CAAC;EACnDE,IAAI,EAAE;IAAEH,UAAU,EAAE,QAAQ;IAAEC,UAAU,EAAE;EAAM,CAAC;EACjDG,KAAK,EAAE;IAAEJ,UAAU,EAAE,QAAQ;IAAEC,UAAU,EAAE;EAAM;AACnD,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASI,kBAAkBA,CAAA,EAAoB;EACpD,MAAM;IAAEC,IAAI;IAAEC;EAAO,CAAC,GAAGV,QAAQ,CAAC,CAAC;EACnC,OAAOD,OAAO,CACZ,OAAO;IACLY,IAAI,EAAEF,IAAI,KAAK,MAAM;IACrBC,MAAM,EAAE;MACNE,OAAO,EAAEF,MAAM,CAACE,OAAO;MACvBC,UAAU,EAAEH,MAAM,CAACG,UAAU;MAC7BC,IAAI,EAAEJ,MAAM,CAACI,IAAI;MACjBC,IAAI,EAAEL,MAAM,CAACK,IAAI;MACjBC,MAAM,EAAEN,MAAM,CAACM,MAAM;MACrBC,YAAY,EAAEP,MAAM,CAACQ;IACvB,CAAC;IACDC,KAAK,EAAElB;EACT,CAAC,CAAC,EACF,CAACQ,IAAI,EAAEC,MAAM,CACf,CAAC;AACH","ignoreList":[]}
@@ -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"}
@@ -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.2",
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",
@@ -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
+ });
@@ -7,6 +7,8 @@ export { BloomColorScope, useColorScopeStyle } from './color-scope';
7
7
  export type { BloomColorScopeProps } from './color-scope';
8
8
  export { buildTheme, STATUS_COLORS } from './build-theme';
9
9
  export { useTheme, useThemeColor, useBloomTheme } from './use-theme';
10
+ export { useNavigationTheme } from './use-navigation-theme';
11
+ export type { NavigationTheme, NavigationThemeFont } from './use-navigation-theme';
10
12
  export type { Theme, ThemeColors, ThemeMode } from './types';
11
13
  export type { AppColorName, AppColorPreset, PresetTokens } from './color-presets';
12
14
  export {
@@ -0,0 +1,59 @@
1
+ import { useMemo } from 'react';
2
+ import { useTheme } from './use-theme';
3
+
4
+ export interface NavigationThemeFont {
5
+ fontFamily: string;
6
+ fontWeight: '400' | '500' | '700' | '900';
7
+ }
8
+
9
+ export interface NavigationTheme {
10
+ dark: boolean;
11
+ colors: {
12
+ primary: string;
13
+ background: string;
14
+ card: string;
15
+ text: string;
16
+ border: string;
17
+ notification: string;
18
+ };
19
+ fonts: {
20
+ regular: NavigationThemeFont;
21
+ medium: NavigationThemeFont;
22
+ bold: NavigationThemeFont;
23
+ heavy: NavigationThemeFont;
24
+ };
25
+ }
26
+
27
+ const NAV_FONTS: NavigationTheme['fonts'] = {
28
+ regular: { fontFamily: 'System', fontWeight: '400' },
29
+ medium: { fontFamily: 'System', fontWeight: '500' },
30
+ bold: { fontFamily: 'System', fontWeight: '700' },
31
+ heavy: { fontFamily: 'System', fontWeight: '900' },
32
+ };
33
+
34
+ /**
35
+ * Projects Bloom's resolved theme into a react-navigation `Theme` POJO for
36
+ * expo-router's <ThemeProvider>. Keeps Bloom the single source of truth for
37
+ * navigator chrome (Stack headers, screen backgrounds, modal) so it tracks
38
+ * the active color preset. Returns a structural object — no react-navigation
39
+ * import — so Bloom stays decoupled. Replaces the per-app copies in
40
+ * test-app-expo / inbox / accounts.
41
+ */
42
+ export function useNavigationTheme(): NavigationTheme {
43
+ const { mode, colors } = useTheme();
44
+ return useMemo(
45
+ () => ({
46
+ dark: mode === 'dark',
47
+ colors: {
48
+ primary: colors.primary,
49
+ background: colors.background,
50
+ card: colors.card,
51
+ text: colors.text,
52
+ border: colors.border,
53
+ notification: colors.error,
54
+ },
55
+ fonts: NAV_FONTS,
56
+ }),
57
+ [mode, colors],
58
+ );
59
+ }