@oxyhq/bloom 0.7.1 → 0.7.3
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/lib/commonjs/portal/index.js +18 -1
- package/lib/commonjs/portal/index.js.map +1 -1
- package/lib/commonjs/theme/BloomThemeProvider.js +21 -1
- package/lib/commonjs/theme/BloomThemeProvider.js.map +1 -1
- package/lib/commonjs/theme/apply-dark-class.js +6 -3
- package/lib/commonjs/theme/apply-dark-class.js.map +1 -1
- package/lib/commonjs/theme/native-root-vars.js +6 -1
- package/lib/commonjs/theme/native-root-vars.js.map +1 -1
- package/lib/commonjs/theme/preset-vars.js +67 -1
- package/lib/commonjs/theme/preset-vars.js.map +1 -1
- package/lib/module/portal/index.js +18 -1
- package/lib/module/portal/index.js.map +1 -1
- package/lib/module/theme/BloomThemeProvider.js +21 -1
- package/lib/module/theme/BloomThemeProvider.js.map +1 -1
- package/lib/module/theme/apply-dark-class.js +6 -3
- package/lib/module/theme/apply-dark-class.js.map +1 -1
- package/lib/module/theme/native-root-vars.js +6 -1
- package/lib/module/theme/native-root-vars.js.map +1 -1
- package/lib/module/theme/preset-vars.js +66 -1
- package/lib/module/theme/preset-vars.js.map +1 -1
- package/lib/typescript/commonjs/portal/index.d.ts +10 -0
- package/lib/typescript/commonjs/portal/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/BloomThemeProvider.d.ts +23 -0
- package/lib/typescript/commonjs/theme/BloomThemeProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/apply-dark-class.d.ts +6 -3
- package/lib/typescript/commonjs/theme/apply-dark-class.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/native-root-vars.d.ts +6 -1
- package/lib/typescript/commonjs/theme/native-root-vars.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/preset-vars.d.ts +35 -2
- package/lib/typescript/commonjs/theme/preset-vars.d.ts.map +1 -1
- package/lib/typescript/module/portal/index.d.ts +10 -0
- package/lib/typescript/module/portal/index.d.ts.map +1 -1
- package/lib/typescript/module/theme/BloomThemeProvider.d.ts +23 -0
- package/lib/typescript/module/theme/BloomThemeProvider.d.ts.map +1 -1
- package/lib/typescript/module/theme/apply-dark-class.d.ts +6 -3
- package/lib/typescript/module/theme/apply-dark-class.d.ts.map +1 -1
- package/lib/typescript/module/theme/native-root-vars.d.ts +6 -1
- package/lib/typescript/module/theme/native-root-vars.d.ts.map +1 -1
- package/lib/typescript/module/theme/preset-vars.d.ts +35 -2
- package/lib/typescript/module/theme/preset-vars.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/native-root-vars.test.ts +4 -2
- package/src/__tests__/preset-vars.test.ts +45 -4
- package/src/portal/index.tsx +26 -1
- package/src/react-native-css.d.ts +3 -2
- package/src/theme/BloomThemeProvider.tsx +30 -1
- package/src/theme/apply-dark-class.ts +6 -3
- package/src/theme/native-root-vars.ts +6 -1
- package/src/theme/preset-vars.ts +84 -3
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
declare function createPortalGroup(): {
|
|
3
|
+
Provider: (props: React.PropsWithChildren<object>) => import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
Outlet: () => import("react/jsx-runtime").JSX.Element | null;
|
|
5
|
+
Portal: ({ children }: React.PropsWithChildren<object>) => null;
|
|
6
|
+
};
|
|
7
|
+
type PortalGroup = ReturnType<typeof createPortalGroup>;
|
|
8
|
+
declare global {
|
|
9
|
+
var __oxyhq_bloom_portal_group__: PortalGroup | undefined;
|
|
10
|
+
}
|
|
2
11
|
export declare const Provider: (props: React.PropsWithChildren<object>) => import("react/jsx-runtime").JSX.Element;
|
|
3
12
|
export declare const Outlet: React.MemoExoticComponent<() => import("react/jsx-runtime").JSX.Element | null>;
|
|
4
13
|
export declare const Portal: ({ children }: React.PropsWithChildren<object>) => null;
|
|
14
|
+
export {};
|
|
5
15
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/portal/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAWN,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/portal/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAWN,MAAM,OAAO,CAAC;AAef,iBAAS,iBAAiB;sBAQC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC;;2BAkC1B,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC;EAa9D;AAED,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAExD,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,4BAA4B,EAAE,WAAW,GAAG,SAAS,CAAC;CAC3D;AAsBD,eAAO,MAAM,QAAQ,UA5EM,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,4CA4EZ,CAAC;AAC/C,eAAO,MAAM,MAAM,iFAA6B,CAAC;AACjD,eAAO,MAAM,MAAM,iBA5Ca,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,SA4CrB,CAAC"}
|
|
@@ -17,6 +17,29 @@ export interface BloomThemeContextValue {
|
|
|
17
17
|
*/
|
|
18
18
|
resetTheme: () => void;
|
|
19
19
|
}
|
|
20
|
+
declare global {
|
|
21
|
+
var __oxyhq_bloom_theme_context__: React.Context<BloomThemeContextValue | null> | undefined;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* `BloomThemeContext` is a `globalThis`-guarded singleton so that every physical
|
|
25
|
+
* copy of this module returns the SAME React context object.
|
|
26
|
+
*
|
|
27
|
+
* Why this is required: `package.json`'s `exports["./theme"]` ships a
|
|
28
|
+
* `react-native` → `./src/...` condition alongside the `import` → `./lib/module/...`
|
|
29
|
+
* and `require` → `./lib/commonjs/...` forks, so up to three physical copies of the
|
|
30
|
+
* theme module can coexist in a single bundle. A bundler can resolve them through
|
|
31
|
+
* DIFFERENT conditions across import paths — e.g. an app importing `@oxyhq/bloom/theme`
|
|
32
|
+
* lands on the `src` copy while `@oxyhq/bloom/toast`'s `Toast.tsx` imports
|
|
33
|
+
* `../theme/use-theme` relatively and lands on a different physical copy. With a plain
|
|
34
|
+
* module-level `createContext(...)`, each copy creates its OWN context object: the
|
|
35
|
+
* mounted `<BloomThemeProvider>` writes into context A while `useTheme`/`useBloomTheme`/
|
|
36
|
+
* `Toast`/`Dialog` read context B, so `useContext` returns `null` and they throw
|
|
37
|
+
* "must be used within a <BloomThemeProvider>" even when correctly wrapped. This is the
|
|
38
|
+
* same src-vs-lib dual-instance class react-native-css guards its `StyleCollection`/
|
|
39
|
+
* root-variable families against. Anchoring the context on `globalThis` (keyed by the
|
|
40
|
+
* `__oxyhq_bloom_theme_context__` property) collapses all copies onto one object. The
|
|
41
|
+
* `??=` runs `createContext` at most once per process.
|
|
42
|
+
*/
|
|
20
43
|
export declare const BloomThemeContext: React.Context<BloomThemeContextValue | null>;
|
|
21
44
|
export interface BloomThemeProviderProps {
|
|
22
45
|
/** Controlled mode. Omit to use Bloom's internal state (with optional persistence). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BloomThemeProvider.d.ts","sourceRoot":"","sources":["../../../../src/theme/BloomThemeProvider.tsx"],"names":[],"mappings":"AAIA,OAAO,oBAAoB,CAAC;AAE5B,OAAO,KAQN,MAAM,OAAO,CAAC;AACf,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAOtB,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGpD,OAAO,EAKL,KAAK,iBAAiB,EAEvB,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAKhD,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,WAAW,EAAE,YAAY,CAAC;IAC1B,OAAO,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IACnC,cAAc,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IAC/C;;;;OAIG;IACH,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,eAAO,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"BloomThemeProvider.d.ts","sourceRoot":"","sources":["../../../../src/theme/BloomThemeProvider.tsx"],"names":[],"mappings":"AAIA,OAAO,oBAAoB,CAAC;AAE5B,OAAO,KAQN,MAAM,OAAO,CAAC;AACf,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAOtB,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGpD,OAAO,EAKL,KAAK,iBAAiB,EAEvB,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAKhD,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,WAAW,EAAE,YAAY,CAAC;IAC1B,OAAO,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IACnC,cAAc,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IAC/C;;;;OAIG;IACH,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,6BAA6B,EAC7B,KAAK,CAAC,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,GAC5C,SAAS,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAEpB,CAAC;AAExD,MAAM,WAAW,uBAAuB;IACtC,uFAAuF;IACvF,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,mEAAmE;IACnE,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,mEAAmE;IACnE,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,2EAA2E;IAC3E,kBAAkB,CAAC,EAAE,YAAY,CAAC;IAElC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IACzC,mBAAmB,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IAErD;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iDAAiD;IACjD,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAE9B,2DAA2D;IAC3D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wDAAwD;IACxD,cAAc,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAEjC;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAE1C,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAkJD,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EAAE,cAAc,EACpB,WAAW,EAAE,gBAAgB,EAC7B,WAA0B,EAC1B,kBAAmC,EACnC,YAAY,EACZ,mBAAmB,EACnB,UAAU,EACV,OAAO,EACP,cAAc,EACd,WAAW,EACX,KAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,QAAQ,GACT,EAAE,uBAAuB,2CAwFzB"}
|
|
@@ -5,10 +5,13 @@ export declare function applyDarkClass(resolved: 'light' | 'dark'): void;
|
|
|
5
5
|
* No-op on native — only affects web.
|
|
6
6
|
*
|
|
7
7
|
* Writes both the raw HSL triples (e.g. `--primary: 185 100% 20%`) and the
|
|
8
|
-
* resolved `--color-*` vars (`--color-primary:
|
|
8
|
+
* resolved `--color-*` vars (`--color-primary: rgb(0 94 102)`) so both
|
|
9
9
|
* shadcn-style `hsl(var(--primary))` plumbing and Tailwind v4 `@theme`
|
|
10
|
-
* utilities resolve consistently.
|
|
11
|
-
*
|
|
10
|
+
* utilities resolve consistently. The resolved vars are emitted as `rgb(...)`
|
|
11
|
+
* (not `hsl(...)`) so the alpha utilities (`bg-primary/10`) resolve on native —
|
|
12
|
+
* see `getPresetVars` / `hslTripletToRgb`. Includes extended tokens (card,
|
|
13
|
+
* chart-*, content-area, sidebar-*) so consumer apps don't need to synthesize
|
|
14
|
+
* them.
|
|
12
15
|
*/
|
|
13
16
|
export declare function applyColorPresetVars(preset: AppColorName, resolved: 'light' | 'dark'): void;
|
|
14
17
|
//# sourceMappingURL=apply-dark-class.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply-dark-class.d.ts","sourceRoot":"","sources":["../../../../src/theme/apply-dark-class.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGvE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,QAIxD;AAED
|
|
1
|
+
{"version":3,"file":"apply-dark-class.d.ts","sourceRoot":"","sources":["../../../../src/theme/apply-dark-class.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGvE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,QAIxD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,QAUpF"}
|
|
@@ -24,11 +24,16 @@ import { type AppColorName } from './color-presets';
|
|
|
24
24
|
* families hold only react-native-css's own `__rn-css-rem`/`__rn-css-color` entries
|
|
25
25
|
* (Bloom's `global.css` defines the `hsl(var(--primary))` indirection but not the HSL
|
|
26
26
|
* triples themselves — those are injected at runtime), so the whole tree below the
|
|
27
|
-
* navigator renders monochrome. Writing the
|
|
27
|
+
* navigator renders monochrome. Writing the vars into `rootVariables` here is the
|
|
28
28
|
* exact same mechanism react-native-css uses for compiled `:root {}` / `@theme` vars
|
|
29
29
|
* (`StyleCollection.inject` does `rootVariables(name).set(valueArray)`), so portaled
|
|
30
30
|
* content resolves the palette identically regardless of React-tree position.
|
|
31
31
|
*
|
|
32
|
+
* The raw tokens are written as HSL triples (`primary: '205 87% 53%'`) and the
|
|
33
|
+
* resolved Tailwind v4 `--color-*` vars as sRGB `rgb(...)` (`color-primary:
|
|
34
|
+
* 'rgb(31 153 239)'`) — the latter so `color-mix`-based alpha utilities resolve
|
|
35
|
+
* on native (see `getPresetVars` / `hslTripletToRgb`).
|
|
36
|
+
*
|
|
32
37
|
* Why a STATIC `import`, not a runtime `require` (critical)
|
|
33
38
|
* ---------------------------------------------------------
|
|
34
39
|
* react-native-css@3.0.7 ships both a `dist/commonjs` and a `dist/module` build, and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-root-vars.d.ts","sourceRoot":"","sources":["../../../../src/theme/native-root-vars.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD
|
|
1
|
+
{"version":3,"file":"native-root-vars.d.ts","sourceRoot":"","sources":["../../../../src/theme/native-root-vars.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6EG;AAEH;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAS3F"}
|
|
@@ -1,11 +1,44 @@
|
|
|
1
1
|
import { type AppColorName, type PresetTokens } from './color-presets';
|
|
2
|
+
/**
|
|
3
|
+
* Convert a shadcn-style HSL triple (`'H S% L%'`, optionally with an alpha tail
|
|
4
|
+
* `'H S% L% / A'`) into an sRGB `rgb(...)` string.
|
|
5
|
+
*
|
|
6
|
+
* Why rgb and not hsl (the native alpha-utility bug this fixes)
|
|
7
|
+
* -------------------------------------------------------------
|
|
8
|
+
* Tailwind v4 compiles an alpha-on-theme-color utility (`bg-primary/10`) into a
|
|
9
|
+
* `color-mix(... var(--color-primary) 10%, transparent)` declaration. On native,
|
|
10
|
+
* react-native-css resolves that `color-mix` at runtime via `colorjs.io/fn`, but
|
|
11
|
+
* its `color-mix` implementation registers ONLY the `sRGB`, `P3`, and `OKLab`
|
|
12
|
+
* color spaces — never `HSL`. So if `--color-primary` resolves to an `hsl(...)`
|
|
13
|
+
* string, `parse('hsl(...)')` throws, the mix is swallowed, and the utility
|
|
14
|
+
* silently produces no color. Emitting the resolved var as `rgb(...)` (a space
|
|
15
|
+
* colorjs.io always has registered) makes the alpha utilities resolve on native,
|
|
16
|
+
* matching web. Full (non-alpha) utilities already worked because React Native's
|
|
17
|
+
* native color parser handles `hsl()` directly; only `color-mix` was affected.
|
|
18
|
+
*
|
|
19
|
+
* Output is space-separated channels (`rgb(31 153 239)`), the modern CSS syntax
|
|
20
|
+
* parsed by both `colorjs.io/fn` (with only sRGB/P3/OKLab registered) and
|
|
21
|
+
* react-native-web / browsers. An alpha tail emits `rgb(r g b / a)`, likewise
|
|
22
|
+
* parsed by both.
|
|
23
|
+
*
|
|
24
|
+
* Pure function — standard HSL→sRGB conversion, channels rounded to integers.
|
|
25
|
+
* Tolerates a `deg` suffix on the hue and `%` suffixes on saturation/lightness.
|
|
26
|
+
*/
|
|
27
|
+
export declare function hslTripletToRgb(triplet: string): string;
|
|
2
28
|
export interface PresetVarsOptions {
|
|
3
29
|
/**
|
|
4
|
-
* Also emit Tailwind v4 resolved `--color-*` vars (
|
|
30
|
+
* Also emit Tailwind v4 resolved `--color-*` vars (as sRGB `rgb(...)` strings)
|
|
5
31
|
* alongside the raw HSL triples. Needed when scoping a subtree where
|
|
6
32
|
* Tailwind's `@theme` block has already precomputed `--color-*` at `:root`,
|
|
7
33
|
* so overriding `--background` alone wouldn't cascade to `bg-background`.
|
|
8
|
-
*
|
|
34
|
+
*
|
|
35
|
+
* The resolved vars are emitted as `rgb(...)` rather than `hsl(...)` so the
|
|
36
|
+
* alpha-on-theme-color utilities (`bg-primary/10`, `text-foreground/50`)
|
|
37
|
+
* resolve on native: Tailwind compiles those to a runtime `color-mix(...)` that
|
|
38
|
+
* react-native-css evaluates via `colorjs.io/fn`, which only registers the
|
|
39
|
+
* sRGB/P3/OKLab spaces (not HSL) — so an `hsl(...)` var would throw and the
|
|
40
|
+
* utility would silently render no color. See `hslTripletToRgb`. Default
|
|
41
|
+
* `false`.
|
|
9
42
|
*/
|
|
10
43
|
includeResolvedColorVars?: boolean;
|
|
11
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preset-vars.d.ts","sourceRoot":"","sources":["../../../../src/theme/preset-vars.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"preset-vars.d.ts","sourceRoot":"","sources":["../../../../src/theme/preset-vars.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAU1F;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA+CvD;AAyBD,MAAM,WAAW,iBAAiB;IAChC;;;;;;;;;;;;;OAaG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,YAAY,EACvB,IAAI,EAAE,OAAO,GAAG,MAAM,EACtB,OAAO,GAAE,iBAAsB,GAC9B,YAAY,CAwCd;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,YAAY,EACvB,IAAI,EAAE,OAAO,GAAG,MAAM,EACtB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,IAAI,CAQN"}
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
declare function createPortalGroup(): {
|
|
3
|
+
Provider: (props: React.PropsWithChildren<object>) => import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
Outlet: () => import("react/jsx-runtime").JSX.Element | null;
|
|
5
|
+
Portal: ({ children }: React.PropsWithChildren<object>) => null;
|
|
6
|
+
};
|
|
7
|
+
type PortalGroup = ReturnType<typeof createPortalGroup>;
|
|
8
|
+
declare global {
|
|
9
|
+
var __oxyhq_bloom_portal_group__: PortalGroup | undefined;
|
|
10
|
+
}
|
|
2
11
|
export declare const Provider: (props: React.PropsWithChildren<object>) => import("react/jsx-runtime").JSX.Element;
|
|
3
12
|
export declare const Outlet: React.MemoExoticComponent<() => import("react/jsx-runtime").JSX.Element | null>;
|
|
4
13
|
export declare const Portal: ({ children }: React.PropsWithChildren<object>) => null;
|
|
14
|
+
export {};
|
|
5
15
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/portal/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAWN,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/portal/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAWN,MAAM,OAAO,CAAC;AAef,iBAAS,iBAAiB;sBAQC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC;;2BAkC1B,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC;EAa9D;AAED,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAExD,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,4BAA4B,EAAE,WAAW,GAAG,SAAS,CAAC;CAC3D;AAsBD,eAAO,MAAM,QAAQ,UA5EM,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,4CA4EZ,CAAC;AAC/C,eAAO,MAAM,MAAM,iFAA6B,CAAC;AACjD,eAAO,MAAM,MAAM,iBA5Ca,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,SA4CrB,CAAC"}
|
|
@@ -17,6 +17,29 @@ export interface BloomThemeContextValue {
|
|
|
17
17
|
*/
|
|
18
18
|
resetTheme: () => void;
|
|
19
19
|
}
|
|
20
|
+
declare global {
|
|
21
|
+
var __oxyhq_bloom_theme_context__: React.Context<BloomThemeContextValue | null> | undefined;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* `BloomThemeContext` is a `globalThis`-guarded singleton so that every physical
|
|
25
|
+
* copy of this module returns the SAME React context object.
|
|
26
|
+
*
|
|
27
|
+
* Why this is required: `package.json`'s `exports["./theme"]` ships a
|
|
28
|
+
* `react-native` → `./src/...` condition alongside the `import` → `./lib/module/...`
|
|
29
|
+
* and `require` → `./lib/commonjs/...` forks, so up to three physical copies of the
|
|
30
|
+
* theme module can coexist in a single bundle. A bundler can resolve them through
|
|
31
|
+
* DIFFERENT conditions across import paths — e.g. an app importing `@oxyhq/bloom/theme`
|
|
32
|
+
* lands on the `src` copy while `@oxyhq/bloom/toast`'s `Toast.tsx` imports
|
|
33
|
+
* `../theme/use-theme` relatively and lands on a different physical copy. With a plain
|
|
34
|
+
* module-level `createContext(...)`, each copy creates its OWN context object: the
|
|
35
|
+
* mounted `<BloomThemeProvider>` writes into context A while `useTheme`/`useBloomTheme`/
|
|
36
|
+
* `Toast`/`Dialog` read context B, so `useContext` returns `null` and they throw
|
|
37
|
+
* "must be used within a <BloomThemeProvider>" even when correctly wrapped. This is the
|
|
38
|
+
* same src-vs-lib dual-instance class react-native-css guards its `StyleCollection`/
|
|
39
|
+
* root-variable families against. Anchoring the context on `globalThis` (keyed by the
|
|
40
|
+
* `__oxyhq_bloom_theme_context__` property) collapses all copies onto one object. The
|
|
41
|
+
* `??=` runs `createContext` at most once per process.
|
|
42
|
+
*/
|
|
20
43
|
export declare const BloomThemeContext: React.Context<BloomThemeContextValue | null>;
|
|
21
44
|
export interface BloomThemeProviderProps {
|
|
22
45
|
/** Controlled mode. Omit to use Bloom's internal state (with optional persistence). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BloomThemeProvider.d.ts","sourceRoot":"","sources":["../../../../src/theme/BloomThemeProvider.tsx"],"names":[],"mappings":"AAIA,OAAO,oBAAoB,CAAC;AAE5B,OAAO,KAQN,MAAM,OAAO,CAAC;AACf,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAOtB,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGpD,OAAO,EAKL,KAAK,iBAAiB,EAEvB,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAKhD,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,WAAW,EAAE,YAAY,CAAC;IAC1B,OAAO,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IACnC,cAAc,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IAC/C;;;;OAIG;IACH,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,eAAO,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"BloomThemeProvider.d.ts","sourceRoot":"","sources":["../../../../src/theme/BloomThemeProvider.tsx"],"names":[],"mappings":"AAIA,OAAO,oBAAoB,CAAC;AAE5B,OAAO,KAQN,MAAM,OAAO,CAAC;AACf,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAOtB,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGpD,OAAO,EAKL,KAAK,iBAAiB,EAEvB,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAKhD,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,WAAW,EAAE,YAAY,CAAC;IAC1B,OAAO,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IACnC,cAAc,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IAC/C;;;;OAIG;IACH,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,6BAA6B,EAC7B,KAAK,CAAC,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,GAC5C,SAAS,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAEpB,CAAC;AAExD,MAAM,WAAW,uBAAuB;IACtC,uFAAuF;IACvF,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,mEAAmE;IACnE,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,mEAAmE;IACnE,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,2EAA2E;IAC3E,kBAAkB,CAAC,EAAE,YAAY,CAAC;IAElC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IACzC,mBAAmB,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IAErD;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iDAAiD;IACjD,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAE9B,2DAA2D;IAC3D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wDAAwD;IACxD,cAAc,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAEjC;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAE1C,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAkJD,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EAAE,cAAc,EACpB,WAAW,EAAE,gBAAgB,EAC7B,WAA0B,EAC1B,kBAAmC,EACnC,YAAY,EACZ,mBAAmB,EACnB,UAAU,EACV,OAAO,EACP,cAAc,EACd,WAAW,EACX,KAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,QAAQ,GACT,EAAE,uBAAuB,2CAwFzB"}
|
|
@@ -5,10 +5,13 @@ export declare function applyDarkClass(resolved: 'light' | 'dark'): void;
|
|
|
5
5
|
* No-op on native — only affects web.
|
|
6
6
|
*
|
|
7
7
|
* Writes both the raw HSL triples (e.g. `--primary: 185 100% 20%`) and the
|
|
8
|
-
* resolved `--color-*` vars (`--color-primary:
|
|
8
|
+
* resolved `--color-*` vars (`--color-primary: rgb(0 94 102)`) so both
|
|
9
9
|
* shadcn-style `hsl(var(--primary))` plumbing and Tailwind v4 `@theme`
|
|
10
|
-
* utilities resolve consistently.
|
|
11
|
-
*
|
|
10
|
+
* utilities resolve consistently. The resolved vars are emitted as `rgb(...)`
|
|
11
|
+
* (not `hsl(...)`) so the alpha utilities (`bg-primary/10`) resolve on native —
|
|
12
|
+
* see `getPresetVars` / `hslTripletToRgb`. Includes extended tokens (card,
|
|
13
|
+
* chart-*, content-area, sidebar-*) so consumer apps don't need to synthesize
|
|
14
|
+
* them.
|
|
12
15
|
*/
|
|
13
16
|
export declare function applyColorPresetVars(preset: AppColorName, resolved: 'light' | 'dark'): void;
|
|
14
17
|
//# sourceMappingURL=apply-dark-class.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply-dark-class.d.ts","sourceRoot":"","sources":["../../../../src/theme/apply-dark-class.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGvE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,QAIxD;AAED
|
|
1
|
+
{"version":3,"file":"apply-dark-class.d.ts","sourceRoot":"","sources":["../../../../src/theme/apply-dark-class.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGvE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,QAIxD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,QAUpF"}
|
|
@@ -24,11 +24,16 @@ import { type AppColorName } from './color-presets';
|
|
|
24
24
|
* families hold only react-native-css's own `__rn-css-rem`/`__rn-css-color` entries
|
|
25
25
|
* (Bloom's `global.css` defines the `hsl(var(--primary))` indirection but not the HSL
|
|
26
26
|
* triples themselves — those are injected at runtime), so the whole tree below the
|
|
27
|
-
* navigator renders monochrome. Writing the
|
|
27
|
+
* navigator renders monochrome. Writing the vars into `rootVariables` here is the
|
|
28
28
|
* exact same mechanism react-native-css uses for compiled `:root {}` / `@theme` vars
|
|
29
29
|
* (`StyleCollection.inject` does `rootVariables(name).set(valueArray)`), so portaled
|
|
30
30
|
* content resolves the palette identically regardless of React-tree position.
|
|
31
31
|
*
|
|
32
|
+
* The raw tokens are written as HSL triples (`primary: '205 87% 53%'`) and the
|
|
33
|
+
* resolved Tailwind v4 `--color-*` vars as sRGB `rgb(...)` (`color-primary:
|
|
34
|
+
* 'rgb(31 153 239)'`) — the latter so `color-mix`-based alpha utilities resolve
|
|
35
|
+
* on native (see `getPresetVars` / `hslTripletToRgb`).
|
|
36
|
+
*
|
|
32
37
|
* Why a STATIC `import`, not a runtime `require` (critical)
|
|
33
38
|
* ---------------------------------------------------------
|
|
34
39
|
* react-native-css@3.0.7 ships both a `dist/commonjs` and a `dist/module` build, and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-root-vars.d.ts","sourceRoot":"","sources":["../../../../src/theme/native-root-vars.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD
|
|
1
|
+
{"version":3,"file":"native-root-vars.d.ts","sourceRoot":"","sources":["../../../../src/theme/native-root-vars.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6EG;AAEH;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAS3F"}
|
|
@@ -1,11 +1,44 @@
|
|
|
1
1
|
import { type AppColorName, type PresetTokens } from './color-presets';
|
|
2
|
+
/**
|
|
3
|
+
* Convert a shadcn-style HSL triple (`'H S% L%'`, optionally with an alpha tail
|
|
4
|
+
* `'H S% L% / A'`) into an sRGB `rgb(...)` string.
|
|
5
|
+
*
|
|
6
|
+
* Why rgb and not hsl (the native alpha-utility bug this fixes)
|
|
7
|
+
* -------------------------------------------------------------
|
|
8
|
+
* Tailwind v4 compiles an alpha-on-theme-color utility (`bg-primary/10`) into a
|
|
9
|
+
* `color-mix(... var(--color-primary) 10%, transparent)` declaration. On native,
|
|
10
|
+
* react-native-css resolves that `color-mix` at runtime via `colorjs.io/fn`, but
|
|
11
|
+
* its `color-mix` implementation registers ONLY the `sRGB`, `P3`, and `OKLab`
|
|
12
|
+
* color spaces — never `HSL`. So if `--color-primary` resolves to an `hsl(...)`
|
|
13
|
+
* string, `parse('hsl(...)')` throws, the mix is swallowed, and the utility
|
|
14
|
+
* silently produces no color. Emitting the resolved var as `rgb(...)` (a space
|
|
15
|
+
* colorjs.io always has registered) makes the alpha utilities resolve on native,
|
|
16
|
+
* matching web. Full (non-alpha) utilities already worked because React Native's
|
|
17
|
+
* native color parser handles `hsl()` directly; only `color-mix` was affected.
|
|
18
|
+
*
|
|
19
|
+
* Output is space-separated channels (`rgb(31 153 239)`), the modern CSS syntax
|
|
20
|
+
* parsed by both `colorjs.io/fn` (with only sRGB/P3/OKLab registered) and
|
|
21
|
+
* react-native-web / browsers. An alpha tail emits `rgb(r g b / a)`, likewise
|
|
22
|
+
* parsed by both.
|
|
23
|
+
*
|
|
24
|
+
* Pure function — standard HSL→sRGB conversion, channels rounded to integers.
|
|
25
|
+
* Tolerates a `deg` suffix on the hue and `%` suffixes on saturation/lightness.
|
|
26
|
+
*/
|
|
27
|
+
export declare function hslTripletToRgb(triplet: string): string;
|
|
2
28
|
export interface PresetVarsOptions {
|
|
3
29
|
/**
|
|
4
|
-
* Also emit Tailwind v4 resolved `--color-*` vars (
|
|
30
|
+
* Also emit Tailwind v4 resolved `--color-*` vars (as sRGB `rgb(...)` strings)
|
|
5
31
|
* alongside the raw HSL triples. Needed when scoping a subtree where
|
|
6
32
|
* Tailwind's `@theme` block has already precomputed `--color-*` at `:root`,
|
|
7
33
|
* so overriding `--background` alone wouldn't cascade to `bg-background`.
|
|
8
|
-
*
|
|
34
|
+
*
|
|
35
|
+
* The resolved vars are emitted as `rgb(...)` rather than `hsl(...)` so the
|
|
36
|
+
* alpha-on-theme-color utilities (`bg-primary/10`, `text-foreground/50`)
|
|
37
|
+
* resolve on native: Tailwind compiles those to a runtime `color-mix(...)` that
|
|
38
|
+
* react-native-css evaluates via `colorjs.io/fn`, which only registers the
|
|
39
|
+
* sRGB/P3/OKLab spaces (not HSL) — so an `hsl(...)` var would throw and the
|
|
40
|
+
* utility would silently render no color. See `hslTripletToRgb`. Default
|
|
41
|
+
* `false`.
|
|
9
42
|
*/
|
|
10
43
|
includeResolvedColorVars?: boolean;
|
|
11
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preset-vars.d.ts","sourceRoot":"","sources":["../../../../src/theme/preset-vars.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"preset-vars.d.ts","sourceRoot":"","sources":["../../../../src/theme/preset-vars.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAU1F;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA+CvD;AAyBD,MAAM,WAAW,iBAAiB;IAChC;;;;;;;;;;;;;OAaG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,YAAY,EACvB,IAAI,EAAE,OAAO,GAAG,MAAM,EACtB,OAAO,GAAE,iBAAsB,GAC9B,YAAY,CAwCd;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,YAAY,EACvB,IAAI,EAAE,OAAO,GAAG,MAAM,EACtB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,IAAI,CAQN"}
|
package/package.json
CHANGED
|
@@ -51,9 +51,11 @@ describe('applyNativeRootVars (native variant)', () => {
|
|
|
51
51
|
expect(sets).not.toHaveProperty('--primary');
|
|
52
52
|
expect(sets.primary).toEqual([['205 87% 53%']]);
|
|
53
53
|
|
|
54
|
-
// Resolved Tailwind v4 `--color-*` vars are written too, also bare-keyed
|
|
54
|
+
// Resolved Tailwind v4 `--color-*` vars are written too, also bare-keyed,
|
|
55
|
+
// and as sRGB `rgb(...)` (not `hsl(...)`) so native `color-mix` alpha
|
|
56
|
+
// utilities resolve — `205 87% 53%` → `rgb(31 153 239)`.
|
|
55
57
|
expect(sets).toHaveProperty('color-primary');
|
|
56
|
-
expect(sets['color-primary']).toEqual([['
|
|
58
|
+
expect(sets['color-primary']).toEqual([['rgb(31 153 239)']]);
|
|
57
59
|
|
|
58
60
|
// Every value is a single-element value-array `[[value]]` of strings.
|
|
59
61
|
for (const value of Object.values(sets)) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { APP_COLOR_NAMES, APP_COLOR_PRESETS } from '../theme/color-presets';
|
|
2
|
-
import { getPresetVars } from '../theme/preset-vars';
|
|
2
|
+
import { getPresetVars, hslTripletToRgb } from '../theme/preset-vars';
|
|
3
3
|
|
|
4
4
|
const EXTENDED_KEYS = [
|
|
5
5
|
'--card',
|
|
@@ -50,9 +50,50 @@ describe('getPresetVars', () => {
|
|
|
50
50
|
expect(vars['--color-primary']).toBeUndefined();
|
|
51
51
|
});
|
|
52
52
|
|
|
53
|
-
it('emits resolved --color-* vars
|
|
53
|
+
it('emits resolved --color-* vars as rgb() when requested', () => {
|
|
54
54
|
const vars = getPresetVars('green', 'dark', { includeResolvedColorVars: true });
|
|
55
|
-
|
|
56
|
-
expect(vars['--color-
|
|
55
|
+
const primaryTriple = APP_COLOR_PRESETS.green.dark['--primary'] ?? '';
|
|
56
|
+
expect(vars['--color-primary']).toBe(hslTripletToRgb(primaryTriple));
|
|
57
|
+
expect(vars['--color-primary']).toMatch(/^rgb\(\d+ \d+ \d+\)$/);
|
|
58
|
+
expect(vars['--color-card']).toBe(hslTripletToRgb(vars['--card'] ?? ''));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('resolves --color-primary to rgb for the blue preset (matches web reference)', () => {
|
|
62
|
+
// `blue` preset light `--primary` is `205 87% 53%` → rgb(31 153 239).
|
|
63
|
+
const vars = getPresetVars('blue', 'light', { includeResolvedColorVars: true });
|
|
64
|
+
expect(vars['--color-primary']).toBe('rgb(31 153 239)');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('hslTripletToRgb', () => {
|
|
69
|
+
it('converts a plain HSL triple to a space-separated rgb() string', () => {
|
|
70
|
+
expect(hslTripletToRgb('205 87% 53%')).toBe('rgb(31 153 239)');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('converts the achromatic extremes', () => {
|
|
74
|
+
expect(hslTripletToRgb('0 0% 0%')).toBe('rgb(0 0 0)');
|
|
75
|
+
expect(hslTripletToRgb('0 0% 100%')).toBe('rgb(255 255 255)');
|
|
76
|
+
expect(hslTripletToRgb('0 0% 12%')).toBe('rgb(31 31 31)');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('converts a saturated triple within ±1 per channel of the reference', () => {
|
|
80
|
+
// teal `185 100% 20%` ≈ rgb(0 94 102).
|
|
81
|
+
const match = /^rgb\((\d+) (\d+) (\d+)\)$/.exec(hslTripletToRgb('185 100% 20%'));
|
|
82
|
+
expect(match).not.toBeNull();
|
|
83
|
+
const r = Number(match?.[1] ?? NaN);
|
|
84
|
+
const g = Number(match?.[2] ?? NaN);
|
|
85
|
+
const b = Number(match?.[3] ?? NaN);
|
|
86
|
+
expect(Math.abs(r - 0)).toBeLessThanOrEqual(1);
|
|
87
|
+
expect(Math.abs(g - 94)).toBeLessThanOrEqual(1);
|
|
88
|
+
expect(Math.abs(b - 102)).toBeLessThanOrEqual(1);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('tolerates a deg suffix on the hue', () => {
|
|
92
|
+
expect(hslTripletToRgb('205deg 87% 53%')).toBe('rgb(31 153 239)');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('emits rgb(r g b / a) for an alpha tail (decimal or percentage)', () => {
|
|
96
|
+
expect(hslTripletToRgb('205 87% 53% / 0.5')).toBe('rgb(31 153 239 / 0.5)');
|
|
97
|
+
expect(hslTripletToRgb('205 87% 53% / 50%')).toBe('rgb(31 153 239 / 0.5)');
|
|
57
98
|
});
|
|
58
99
|
});
|
package/src/portal/index.tsx
CHANGED
|
@@ -81,7 +81,32 @@ function createPortalGroup() {
|
|
|
81
81
|
return { Provider, Outlet, Portal };
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
type PortalGroup = ReturnType<typeof createPortalGroup>;
|
|
85
|
+
|
|
86
|
+
declare global {
|
|
87
|
+
// eslint-disable-next-line no-var
|
|
88
|
+
var __oxyhq_bloom_portal_group__: PortalGroup | undefined;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* `DefaultPortal` is a `globalThis`-guarded singleton so that every physical copy of
|
|
93
|
+
* this module shares ONE portal group (one `Context`, one `Provider`/`Outlet`/`Portal`
|
|
94
|
+
* triple).
|
|
95
|
+
*
|
|
96
|
+
* Why this is required: like the theme module, `exports["./portal"]` ships a
|
|
97
|
+
* `react-native` → `./src/...` condition alongside the `lib/module` / `lib/commonjs`
|
|
98
|
+
* forks, and the `Portal` component is consumed cross-subpath from `./tooltip`,
|
|
99
|
+
* `./prompt-input`, `./dialog`, `./select`, `./menu`, `./context-menu`, and `./popover`
|
|
100
|
+
* while the `Provider`/`Outlet` are mounted once at the app root via `@oxyhq/bloom/portal`.
|
|
101
|
+
* A bundler can resolve those subpaths through different export conditions, so without a
|
|
102
|
+
* guard each physical copy runs its own `createPortalGroup()` and a `<Portal>` from copy
|
|
103
|
+
* A would register against the `Provider`'s map in copy B — its content would never reach
|
|
104
|
+
* the `Outlet`. Anchoring the group on `globalThis` (keyed by `__oxyhq_bloom_portal_group__`)
|
|
105
|
+
* collapses all copies onto one group. Same src-vs-lib dual-instance class as
|
|
106
|
+
* `BloomThemeContext`.
|
|
107
|
+
*/
|
|
108
|
+
const DefaultPortal: PortalGroup = (globalThis.__oxyhq_bloom_portal_group__ ??=
|
|
109
|
+
createPortalGroup());
|
|
85
110
|
|
|
86
111
|
export const Provider = DefaultPortal.Provider;
|
|
87
112
|
export const Outlet = memo(DefaultPortal.Outlet);
|
|
@@ -20,8 +20,9 @@
|
|
|
20
20
|
declare module 'react-native-css/native-internal' {
|
|
21
21
|
/**
|
|
22
22
|
* A leaf style value. Mirrors react-native-css's `compiler.types`
|
|
23
|
-
* `StyleDescriptor`. Bloom only ever writes string
|
|
24
|
-
*
|
|
23
|
+
* `StyleDescriptor`. Bloom only ever writes string color values (raw HSL
|
|
24
|
+
* triples for the `--*` vars, sRGB `rgb(...)` for the resolved `--color-*`
|
|
25
|
+
* vars), but the family is typed against the full descriptor union.
|
|
25
26
|
*/
|
|
26
27
|
type StyleDescriptor =
|
|
27
28
|
| string
|
|
@@ -58,7 +58,36 @@ export interface BloomThemeContextValue {
|
|
|
58
58
|
resetTheme: () => void;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
declare global {
|
|
62
|
+
// eslint-disable-next-line no-var
|
|
63
|
+
var __oxyhq_bloom_theme_context__:
|
|
64
|
+
| React.Context<BloomThemeContextValue | null>
|
|
65
|
+
| undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* `BloomThemeContext` is a `globalThis`-guarded singleton so that every physical
|
|
70
|
+
* copy of this module returns the SAME React context object.
|
|
71
|
+
*
|
|
72
|
+
* Why this is required: `package.json`'s `exports["./theme"]` ships a
|
|
73
|
+
* `react-native` → `./src/...` condition alongside the `import` → `./lib/module/...`
|
|
74
|
+
* and `require` → `./lib/commonjs/...` forks, so up to three physical copies of the
|
|
75
|
+
* theme module can coexist in a single bundle. A bundler can resolve them through
|
|
76
|
+
* DIFFERENT conditions across import paths — e.g. an app importing `@oxyhq/bloom/theme`
|
|
77
|
+
* lands on the `src` copy while `@oxyhq/bloom/toast`'s `Toast.tsx` imports
|
|
78
|
+
* `../theme/use-theme` relatively and lands on a different physical copy. With a plain
|
|
79
|
+
* module-level `createContext(...)`, each copy creates its OWN context object: the
|
|
80
|
+
* mounted `<BloomThemeProvider>` writes into context A while `useTheme`/`useBloomTheme`/
|
|
81
|
+
* `Toast`/`Dialog` read context B, so `useContext` returns `null` and they throw
|
|
82
|
+
* "must be used within a <BloomThemeProvider>" even when correctly wrapped. This is the
|
|
83
|
+
* same src-vs-lib dual-instance class react-native-css guards its `StyleCollection`/
|
|
84
|
+
* root-variable families against. Anchoring the context on `globalThis` (keyed by the
|
|
85
|
+
* `__oxyhq_bloom_theme_context__` property) collapses all copies onto one object. The
|
|
86
|
+
* `??=` runs `createContext` at most once per process.
|
|
87
|
+
*/
|
|
88
|
+
export const BloomThemeContext: React.Context<BloomThemeContextValue | null> =
|
|
89
|
+
(globalThis.__oxyhq_bloom_theme_context__ ??=
|
|
90
|
+
createContext<BloomThemeContextValue | null>(null));
|
|
62
91
|
|
|
63
92
|
export interface BloomThemeProviderProps {
|
|
64
93
|
/** Controlled mode. Omit to use Bloom's internal state (with optional persistence). */
|
|
@@ -13,10 +13,13 @@ export function applyDarkClass(resolved: 'light' | 'dark') {
|
|
|
13
13
|
* No-op on native — only affects web.
|
|
14
14
|
*
|
|
15
15
|
* Writes both the raw HSL triples (e.g. `--primary: 185 100% 20%`) and the
|
|
16
|
-
* resolved `--color-*` vars (`--color-primary:
|
|
16
|
+
* resolved `--color-*` vars (`--color-primary: rgb(0 94 102)`) so both
|
|
17
17
|
* shadcn-style `hsl(var(--primary))` plumbing and Tailwind v4 `@theme`
|
|
18
|
-
* utilities resolve consistently.
|
|
19
|
-
*
|
|
18
|
+
* utilities resolve consistently. The resolved vars are emitted as `rgb(...)`
|
|
19
|
+
* (not `hsl(...)`) so the alpha utilities (`bg-primary/10`) resolve on native —
|
|
20
|
+
* see `getPresetVars` / `hslTripletToRgb`. Includes extended tokens (card,
|
|
21
|
+
* chart-*, content-area, sidebar-*) so consumer apps don't need to synthesize
|
|
22
|
+
* them.
|
|
20
23
|
*/
|
|
21
24
|
export function applyColorPresetVars(preset: AppColorName, resolved: 'light' | 'dark') {
|
|
22
25
|
if (Platform.OS !== 'web' || typeof document === 'undefined') return;
|
|
@@ -28,11 +28,16 @@ import { type AppColorName } from './color-presets';
|
|
|
28
28
|
* families hold only react-native-css's own `__rn-css-rem`/`__rn-css-color` entries
|
|
29
29
|
* (Bloom's `global.css` defines the `hsl(var(--primary))` indirection but not the HSL
|
|
30
30
|
* triples themselves — those are injected at runtime), so the whole tree below the
|
|
31
|
-
* navigator renders monochrome. Writing the
|
|
31
|
+
* navigator renders monochrome. Writing the vars into `rootVariables` here is the
|
|
32
32
|
* exact same mechanism react-native-css uses for compiled `:root {}` / `@theme` vars
|
|
33
33
|
* (`StyleCollection.inject` does `rootVariables(name).set(valueArray)`), so portaled
|
|
34
34
|
* content resolves the palette identically regardless of React-tree position.
|
|
35
35
|
*
|
|
36
|
+
* The raw tokens are written as HSL triples (`primary: '205 87% 53%'`) and the
|
|
37
|
+
* resolved Tailwind v4 `--color-*` vars as sRGB `rgb(...)` (`color-primary:
|
|
38
|
+
* 'rgb(31 153 239)'`) — the latter so `color-mix`-based alpha utilities resolve
|
|
39
|
+
* on native (see `getPresetVars` / `hslTripletToRgb`).
|
|
40
|
+
*
|
|
36
41
|
* Why a STATIC `import`, not a runtime `require` (critical)
|
|
37
42
|
* ---------------------------------------------------------
|
|
38
43
|
* react-native-css@3.0.7 ships both a `dist/commonjs` and a `dist/module` build, and
|