@oxyhq/bloom 0.6.16 → 0.6.18
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/theme/BloomThemeProvider.js +13 -32
- package/lib/commonjs/theme/BloomThemeProvider.js.map +1 -1
- package/lib/commonjs/theme/apply-dark-class.js +10 -7
- package/lib/commonjs/theme/apply-dark-class.js.map +1 -1
- package/lib/commonjs/theme/color-scope/index.js +59 -0
- package/lib/commonjs/theme/color-scope/index.js.map +1 -0
- package/lib/commonjs/theme/color-scope/index.web.js +58 -0
- package/lib/commonjs/theme/color-scope/index.web.js.map +1 -0
- package/lib/commonjs/theme/color-scope/style-builder.js +36 -0
- package/lib/commonjs/theme/color-scope/style-builder.js.map +1 -0
- package/lib/commonjs/theme/index.js +8 -1
- package/lib/commonjs/theme/index.js.map +1 -1
- package/lib/module/theme/BloomThemeProvider.js +15 -33
- package/lib/module/theme/BloomThemeProvider.js.map +1 -1
- package/lib/module/theme/apply-dark-class.js +10 -7
- package/lib/module/theme/apply-dark-class.js.map +1 -1
- package/lib/module/theme/color-scope/index.js +53 -0
- package/lib/module/theme/color-scope/index.js.map +1 -0
- package/lib/module/theme/color-scope/index.web.js +52 -0
- package/lib/module/theme/color-scope/index.web.js.map +1 -0
- package/lib/module/theme/color-scope/style-builder.js +31 -0
- package/lib/module/theme/color-scope/style-builder.js.map +1 -0
- package/lib/module/theme/index.js +2 -1
- package/lib/module/theme/index.js.map +1 -1
- package/lib/typescript/commonjs/icons/common.d.ts +4 -4
- package/lib/typescript/commonjs/theme/BloomThemeProvider.d.ts +8 -10
- package/lib/typescript/commonjs/theme/BloomThemeProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/apply-dark-class.d.ts +5 -4
- package/lib/typescript/commonjs/theme/apply-dark-class.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/color-scope/index.d.ts +23 -0
- package/lib/typescript/commonjs/theme/color-scope/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/theme/color-scope/index.web.d.ts +21 -0
- package/lib/typescript/commonjs/theme/color-scope/index.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/theme/color-scope/style-builder.d.ts +16 -0
- package/lib/typescript/commonjs/theme/color-scope/style-builder.d.ts.map +1 -0
- package/lib/typescript/commonjs/theme/index.d.ts +4 -2
- package/lib/typescript/commonjs/theme/index.d.ts.map +1 -1
- package/lib/typescript/module/icons/common.d.ts +4 -4
- package/lib/typescript/module/theme/BloomThemeProvider.d.ts +8 -10
- package/lib/typescript/module/theme/BloomThemeProvider.d.ts.map +1 -1
- package/lib/typescript/module/theme/apply-dark-class.d.ts +5 -4
- package/lib/typescript/module/theme/apply-dark-class.d.ts.map +1 -1
- package/lib/typescript/module/theme/color-scope/index.d.ts +23 -0
- package/lib/typescript/module/theme/color-scope/index.d.ts.map +1 -0
- package/lib/typescript/module/theme/color-scope/index.web.d.ts +21 -0
- package/lib/typescript/module/theme/color-scope/index.web.d.ts.map +1 -0
- package/lib/typescript/module/theme/color-scope/style-builder.d.ts +16 -0
- package/lib/typescript/module/theme/color-scope/style-builder.d.ts.map +1 -0
- package/lib/typescript/module/theme/index.d.ts +4 -2
- package/lib/typescript/module/theme/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/BloomColorScope.test.tsx +53 -0
- package/src/theme/BloomThemeProvider.tsx +32 -27
- package/src/theme/apply-dark-class.ts +8 -8
- package/src/theme/color-scope/index.tsx +67 -0
- package/src/theme/color-scope/index.web.tsx +65 -0
- package/src/theme/color-scope/style-builder.ts +39 -0
- package/src/theme/index.ts +3 -2
|
@@ -13,7 +13,13 @@ import React, {
|
|
|
13
13
|
useRef,
|
|
14
14
|
useState,
|
|
15
15
|
} from 'react';
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
Platform,
|
|
18
|
+
View,
|
|
19
|
+
useColorScheme as useRNColorScheme,
|
|
20
|
+
type StyleProp,
|
|
21
|
+
type ViewStyle,
|
|
22
|
+
} from 'react-native';
|
|
17
23
|
|
|
18
24
|
import { useControllableState } from '../hooks/useControllableState';
|
|
19
25
|
import { FontLoader } from '../fonts/FontLoader';
|
|
@@ -21,6 +27,7 @@ import { FontLoader } from '../fonts/FontLoader';
|
|
|
21
27
|
import { applyDarkClass, applyColorPresetVars } from './apply-dark-class';
|
|
22
28
|
import { buildTheme } from './build-theme';
|
|
23
29
|
import { type AppColorName } from './color-presets';
|
|
30
|
+
import { buildNativePresetStyle } from './color-scope/style-builder';
|
|
24
31
|
import {
|
|
25
32
|
readPersistedTheme,
|
|
26
33
|
readPersistedThemeSync,
|
|
@@ -91,6 +98,13 @@ export interface BloomThemeProviderProps {
|
|
|
91
98
|
/** Rendered while native fonts load. Ignored on web. */
|
|
92
99
|
onFontsLoading?: React.ReactNode;
|
|
93
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Style applied to the native `<View>` wrapper that carries the preset's
|
|
103
|
+
* CSS vars. Defaults to `{ flex: 1 }`. Ignored on web (the provider writes
|
|
104
|
+
* vars to `document.documentElement` instead of using a wrapper).
|
|
105
|
+
*/
|
|
106
|
+
nativeWrapperStyle?: StyleProp<ViewStyle>;
|
|
107
|
+
|
|
94
108
|
children: React.ReactNode;
|
|
95
109
|
}
|
|
96
110
|
|
|
@@ -251,6 +265,7 @@ export function BloomThemeProvider({
|
|
|
251
265
|
onHydrating,
|
|
252
266
|
fonts = true,
|
|
253
267
|
onFontsLoading,
|
|
268
|
+
nativeWrapperStyle,
|
|
254
269
|
children,
|
|
255
270
|
}: BloomThemeProviderProps) {
|
|
256
271
|
const rnScheme = useRNColorScheme();
|
|
@@ -295,36 +310,26 @@ export function BloomThemeProvider({
|
|
|
295
310
|
const shouldAwait = awaitHydration ?? Boolean(persistKey && storage);
|
|
296
311
|
const isGated = shouldAwait && !hydrated;
|
|
297
312
|
|
|
313
|
+
const nativeVarsStyle = useMemo(
|
|
314
|
+
() => (Platform.OS === 'web' ? undefined : buildNativePresetStyle(colorPreset, resolved)),
|
|
315
|
+
[colorPreset, resolved],
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
const content = (
|
|
319
|
+
<FontLoader enabled={fonts} fallback={onFontsLoading}>
|
|
320
|
+
{isGated ? onHydrating ?? null : children}
|
|
321
|
+
</FontLoader>
|
|
322
|
+
);
|
|
323
|
+
|
|
298
324
|
return (
|
|
299
325
|
<BloomThemeContext.Provider value={contextValue}>
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
326
|
+
{Platform.OS === 'web' ? (
|
|
327
|
+
content
|
|
328
|
+
) : (
|
|
329
|
+
<View style={[{ flex: 1 }, nativeVarsStyle, nativeWrapperStyle]}>{content}</View>
|
|
330
|
+
)}
|
|
303
331
|
</BloomThemeContext.Provider>
|
|
304
332
|
);
|
|
305
333
|
}
|
|
306
334
|
|
|
307
|
-
/**
|
|
308
|
-
* Scoped color override for a subtree. Inherits the resolved mode from the
|
|
309
|
-
* parent `BloomThemeProvider` but renders descendants with a different preset.
|
|
310
|
-
*/
|
|
311
|
-
export interface BloomColorScopeProps {
|
|
312
|
-
colorPreset: AppColorName;
|
|
313
|
-
children: React.ReactNode;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
export function BloomColorScope({ colorPreset, children }: BloomColorScopeProps) {
|
|
317
|
-
const parent = useContext(BloomThemeContext);
|
|
318
|
-
if (!parent) {
|
|
319
|
-
throw new Error('BloomColorScope must be used within a <BloomThemeProvider>');
|
|
320
|
-
}
|
|
321
335
|
|
|
322
|
-
const contextValue = useMemo<BloomThemeContextValue>(() => {
|
|
323
|
-
const theme = buildTheme(colorPreset, parent.theme.mode);
|
|
324
|
-
return { ...parent, theme, colorPreset };
|
|
325
|
-
}, [colorPreset, parent]);
|
|
326
|
-
|
|
327
|
-
return (
|
|
328
|
-
<BloomThemeContext.Provider value={contextValue}>{children}</BloomThemeContext.Provider>
|
|
329
|
-
);
|
|
330
|
-
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Platform } from 'react-native';
|
|
2
2
|
import { APP_COLOR_PRESETS, type AppColorName } from './color-presets';
|
|
3
|
+
import { getPresetVars } from './preset-vars';
|
|
3
4
|
|
|
4
5
|
export function applyDarkClass(resolved: 'light' | 'dark') {
|
|
5
6
|
if (Platform.OS === 'web' && typeof document !== 'undefined') {
|
|
@@ -11,18 +12,17 @@ export function applyDarkClass(resolved: 'light' | 'dark') {
|
|
|
11
12
|
* Apply a color preset's CSS custom properties to the document root.
|
|
12
13
|
* No-op on native — only affects web.
|
|
13
14
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* `hsl(var(--primary))
|
|
17
|
-
*
|
|
15
|
+
* Writes both the raw HSL triples (e.g. `--primary: 185 100% 20%`) and the
|
|
16
|
+
* resolved `--color-*` vars (`--color-primary: hsl(185 100% 20%)`) so both
|
|
17
|
+
* shadcn-style `hsl(var(--primary))` plumbing and Tailwind v4 `@theme`
|
|
18
|
+
* utilities resolve consistently. Includes extended tokens (card, chart-*,
|
|
19
|
+
* content-area, sidebar-*) so consumer apps don't need to synthesize them.
|
|
18
20
|
*/
|
|
19
21
|
export function applyColorPresetVars(preset: AppColorName, resolved: 'light' | 'dark') {
|
|
20
22
|
if (Platform.OS !== 'web' || typeof document === 'undefined') return;
|
|
23
|
+
if (!APP_COLOR_PRESETS[preset]) return;
|
|
21
24
|
|
|
22
|
-
const
|
|
23
|
-
if (!config) return;
|
|
24
|
-
|
|
25
|
-
const vars = resolved === 'dark' ? config.dark : config.light;
|
|
25
|
+
const vars = getPresetVars(preset, resolved, { includeResolvedColorVars: true });
|
|
26
26
|
const root = document.documentElement.style;
|
|
27
27
|
|
|
28
28
|
for (const [key, value] of Object.entries(vars)) {
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React, { useContext, useMemo } from 'react';
|
|
2
|
+
import { View, type StyleProp, type ViewStyle } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import { BloomThemeContext, type BloomThemeContextValue } from '../BloomThemeProvider';
|
|
5
|
+
import { buildTheme } from '../build-theme';
|
|
6
|
+
import type { AppColorName } from '../color-presets';
|
|
7
|
+
import { buildNativePresetStyle } from './style-builder';
|
|
8
|
+
|
|
9
|
+
export interface BloomColorScopeProps {
|
|
10
|
+
/** Preset to apply within this subtree. */
|
|
11
|
+
colorPreset: AppColorName;
|
|
12
|
+
/**
|
|
13
|
+
* When `true`, do not render a wrapping `<View>`. The caller owns the
|
|
14
|
+
* element that receives the CSS vars (via `useColorScopeStyle`).
|
|
15
|
+
*/
|
|
16
|
+
asChild?: boolean;
|
|
17
|
+
/** Additional style applied to the wrapping `<View>`. Ignored with `asChild`. */
|
|
18
|
+
style?: StyleProp<ViewStyle>;
|
|
19
|
+
children: React.ReactNode;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function BloomColorScope({
|
|
23
|
+
colorPreset,
|
|
24
|
+
asChild = false,
|
|
25
|
+
style,
|
|
26
|
+
children,
|
|
27
|
+
}: BloomColorScopeProps) {
|
|
28
|
+
const parent = useContext(BloomThemeContext);
|
|
29
|
+
if (!parent) {
|
|
30
|
+
throw new Error('BloomColorScope must be used within a <BloomThemeProvider>');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const resolvedMode = parent.theme.mode;
|
|
34
|
+
|
|
35
|
+
const contextValue = useMemo<BloomThemeContextValue>(() => {
|
|
36
|
+
const theme = buildTheme(colorPreset, resolvedMode);
|
|
37
|
+
return { ...parent, theme, colorPreset };
|
|
38
|
+
}, [colorPreset, resolvedMode, parent]);
|
|
39
|
+
|
|
40
|
+
const varsStyle = useMemo(
|
|
41
|
+
() => buildNativePresetStyle(colorPreset, resolvedMode),
|
|
42
|
+
[colorPreset, resolvedMode],
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<BloomThemeContext.Provider value={contextValue}>
|
|
47
|
+
{asChild ? children : <View style={[{ flex: 1 }, varsStyle, style]}>{children}</View>}
|
|
48
|
+
</BloomThemeContext.Provider>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Escape hatch for advanced cases where the wrapping element is owned by the
|
|
54
|
+
* caller. Returns a stable native style object carrying the preset's CSS vars.
|
|
55
|
+
* Returns `undefined` on web or when `nativewind` is not installed.
|
|
56
|
+
*/
|
|
57
|
+
export function useColorScopeStyle(colorPreset: AppColorName): StyleProp<ViewStyle> {
|
|
58
|
+
const parent = useContext(BloomThemeContext);
|
|
59
|
+
if (!parent) {
|
|
60
|
+
throw new Error('useColorScopeStyle must be used within a <BloomThemeProvider>');
|
|
61
|
+
}
|
|
62
|
+
const resolvedMode = parent.theme.mode;
|
|
63
|
+
return useMemo(
|
|
64
|
+
() => buildNativePresetStyle(colorPreset, resolvedMode),
|
|
65
|
+
[colorPreset, resolvedMode],
|
|
66
|
+
);
|
|
67
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React, { useContext, useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import { BloomThemeContext, type BloomThemeContextValue } from '../BloomThemeProvider';
|
|
4
|
+
import { buildTheme } from '../build-theme';
|
|
5
|
+
import type { AppColorName } from '../color-presets';
|
|
6
|
+
import { buildScopeVars } from './style-builder';
|
|
7
|
+
|
|
8
|
+
export interface BloomColorScopeProps {
|
|
9
|
+
/** Preset to apply within this subtree. */
|
|
10
|
+
colorPreset: AppColorName;
|
|
11
|
+
/**
|
|
12
|
+
* When `true`, do not render a wrapping element. The caller owns the
|
|
13
|
+
* DOM node that receives the CSS vars (via `useColorScopeStyle`).
|
|
14
|
+
*/
|
|
15
|
+
asChild?: boolean;
|
|
16
|
+
/** Additional style applied to the wrapping `<div>`. Ignored with `asChild`. */
|
|
17
|
+
style?: React.CSSProperties;
|
|
18
|
+
children: React.ReactNode;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function BloomColorScope({
|
|
22
|
+
colorPreset,
|
|
23
|
+
asChild = false,
|
|
24
|
+
style,
|
|
25
|
+
children,
|
|
26
|
+
}: BloomColorScopeProps) {
|
|
27
|
+
const parent = useContext(BloomThemeContext);
|
|
28
|
+
if (!parent) {
|
|
29
|
+
throw new Error('BloomColorScope must be used within a <BloomThemeProvider>');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const resolvedMode = parent.theme.mode;
|
|
33
|
+
|
|
34
|
+
const contextValue = useMemo<BloomThemeContextValue>(() => {
|
|
35
|
+
const theme = buildTheme(colorPreset, resolvedMode);
|
|
36
|
+
return { ...parent, theme, colorPreset };
|
|
37
|
+
}, [colorPreset, resolvedMode, parent]);
|
|
38
|
+
|
|
39
|
+
const varsStyle = useMemo(
|
|
40
|
+
() => ({ ...(buildScopeVars(colorPreset, resolvedMode) as React.CSSProperties), ...style }),
|
|
41
|
+
[colorPreset, resolvedMode, style],
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<BloomThemeContext.Provider value={contextValue}>
|
|
46
|
+
{asChild ? children : <div style={varsStyle}>{children}</div>}
|
|
47
|
+
</BloomThemeContext.Provider>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Escape hatch for advanced cases where the wrapping element is owned by the
|
|
53
|
+
* caller. Returns a stable React style object carrying the preset's CSS vars.
|
|
54
|
+
*/
|
|
55
|
+
export function useColorScopeStyle(colorPreset: AppColorName): React.CSSProperties {
|
|
56
|
+
const parent = useContext(BloomThemeContext);
|
|
57
|
+
if (!parent) {
|
|
58
|
+
throw new Error('useColorScopeStyle must be used within a <BloomThemeProvider>');
|
|
59
|
+
}
|
|
60
|
+
const resolvedMode = parent.theme.mode;
|
|
61
|
+
return useMemo(
|
|
62
|
+
() => buildScopeVars(colorPreset, resolvedMode) as React.CSSProperties,
|
|
63
|
+
[colorPreset, resolvedMode],
|
|
64
|
+
);
|
|
65
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Platform, type StyleProp, type ViewStyle } from 'react-native';
|
|
2
|
+
|
|
3
|
+
import { getPresetVars } from '../preset-vars';
|
|
4
|
+
import type { AppColorName } from '../color-presets';
|
|
5
|
+
import { lazyRequire } from '../../utils/lazy-require';
|
|
6
|
+
|
|
7
|
+
interface NativeWindVarsModule {
|
|
8
|
+
vars: (record: Record<string, string>) => StyleProp<ViewStyle>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const getNativeWindVars = lazyRequire<NativeWindVarsModule>('nativewind');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Build the CSS custom-property map for a preset, ready to be applied to a
|
|
15
|
+
* subtree. Always includes the resolved `--color-*` vars so Tailwind v4
|
|
16
|
+
* `@theme` utilities (e.g. `bg-background`) honour the scope.
|
|
17
|
+
*/
|
|
18
|
+
export function buildScopeVars(
|
|
19
|
+
colorPreset: AppColorName,
|
|
20
|
+
mode: 'light' | 'dark',
|
|
21
|
+
): Record<string, string> {
|
|
22
|
+
return getPresetVars(colorPreset, mode, { includeResolvedColorVars: true });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Build a native style object carrying every CSS var of the preset, using
|
|
27
|
+
* NativeWind's `vars()` when available. Returns `undefined` on web (where the
|
|
28
|
+
* provider writes vars to `documentElement` instead) or when `nativewind` is
|
|
29
|
+
* not installed.
|
|
30
|
+
*/
|
|
31
|
+
export function buildNativePresetStyle(
|
|
32
|
+
colorPreset: AppColorName,
|
|
33
|
+
mode: 'light' | 'dark',
|
|
34
|
+
): StyleProp<ViewStyle> {
|
|
35
|
+
if (Platform.OS === 'web') return undefined;
|
|
36
|
+
const module = getNativeWindVars();
|
|
37
|
+
if (!module || typeof module.vars !== 'function') return undefined;
|
|
38
|
+
return module.vars(buildScopeVars(colorPreset, mode));
|
|
39
|
+
}
|
package/src/theme/index.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
export { BloomThemeProvider
|
|
1
|
+
export { BloomThemeProvider } from './BloomThemeProvider';
|
|
2
2
|
export type {
|
|
3
3
|
BloomThemeProviderProps,
|
|
4
4
|
BloomThemeContextValue,
|
|
5
|
-
BloomColorScopeProps,
|
|
6
5
|
} from './BloomThemeProvider';
|
|
6
|
+
export { BloomColorScope, useColorScopeStyle } from './color-scope';
|
|
7
|
+
export type { BloomColorScopeProps } from './color-scope';
|
|
7
8
|
export { buildTheme, STATUS_COLORS } from './build-theme';
|
|
8
9
|
export { useTheme, useThemeColor, useBloomTheme } from './use-theme';
|
|
9
10
|
export type { Theme, ThemeColors, ThemeMode } from './types';
|