@react-native-rethemed/core 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/color-mode-store.cjs +57 -0
- package/dist/color-mode-store.d.cts +11 -0
- package/dist/color-mode-store.d.mts +11 -0
- package/dist/color-mode-store.mjs +56 -0
- package/dist/config.cjs +26 -0
- package/dist/config.d.cts +8 -0
- package/dist/config.d.mts +8 -0
- package/dist/config.mjs +7 -0
- package/dist/create-themed-styles.cjs +31 -0
- package/dist/{esm/create-themed-styles.d.ts → create-themed-styles.d.cts} +4 -2
- package/dist/{cjs/create-themed-styles.d.ts → create-themed-styles.d.mts} +4 -2
- package/dist/create-themed-styles.mjs +31 -0
- package/dist/create-themed.cjs +93 -0
- package/dist/create-themed.d.cts +48 -0
- package/dist/create-themed.d.mts +48 -0
- package/dist/create-themed.mjs +93 -0
- package/dist/define-theme.cjs +15 -0
- package/dist/{esm/define-theme.d.ts → define-theme.d.cts} +3 -1
- package/dist/{cjs/define-theme.d.ts → define-theme.d.mts} +3 -1
- package/dist/define-theme.mjs +15 -0
- package/dist/extend-theme.cjs +109 -0
- package/dist/{esm/extend-theme.d.ts → extend-theme.d.cts} +3 -1
- package/dist/{cjs/extend-theme.d.ts → extend-theme.d.mts} +3 -1
- package/dist/extend-theme.mjs +109 -0
- package/dist/index.cjs +30 -0
- package/dist/index.d.cts +13 -0
- package/dist/index.d.mts +13 -0
- package/dist/index.mjs +9 -0
- package/dist/resolvers/color-resolver.cjs +23 -0
- package/dist/resolvers/color-resolver.d.cts +4 -0
- package/dist/resolvers/color-resolver.d.mts +4 -0
- package/dist/resolvers/color-resolver.mjs +23 -0
- package/dist/resolvers/line-height-resolver.cjs +17 -0
- package/dist/{esm/resolvers/line-height-resolver.d.ts → resolvers/line-height-resolver.d.cts} +3 -1
- package/dist/{cjs/resolvers/line-height-resolver.d.ts → resolvers/line-height-resolver.d.mts} +3 -1
- package/dist/resolvers/line-height-resolver.mjs +16 -0
- package/dist/resolvers/style-resolver.cjs +86 -0
- package/dist/resolvers/style-resolver.mjs +86 -0
- package/dist/semantic-colors.cjs +29 -0
- package/dist/{esm/semantic-colors.d.ts → semantic-colors.d.cts} +3 -1
- package/dist/{cjs/semantic-colors.d.ts → semantic-colors.d.mts} +3 -1
- package/dist/semantic-colors.mjs +28 -0
- package/dist/style-props.cjs +103 -0
- package/dist/{cjs/style-props.d.ts → style-props.d.cts} +2 -1
- package/dist/{esm/style-props.d.ts → style-props.d.mts} +2 -1
- package/dist/style-props.mjs +95 -0
- package/dist/text-tree.cjs +70 -0
- package/dist/{cjs/text-tree.d.ts → text-tree.d.cts} +4 -8
- package/dist/{esm/text-tree.d.ts → text-tree.d.mts} +4 -8
- package/dist/text-tree.mjs +66 -0
- package/dist/text-variants.cjs +25 -0
- package/dist/text-variants.mjs +25 -0
- package/dist/types.d.cts +280 -0
- package/dist/types.d.mts +280 -0
- package/package.json +16 -24
- package/dist/cjs/color-mode-store.d.ts +0 -32
- package/dist/cjs/color-mode-store.js +0 -64
- package/dist/cjs/config.d.ts +0 -14
- package/dist/cjs/config.js +0 -35
- package/dist/cjs/create-themed-styles.js +0 -37
- package/dist/cjs/create-themed.d.ts +0 -46
- package/dist/cjs/create-themed.js +0 -118
- package/dist/cjs/define-theme.js +0 -15
- package/dist/cjs/extend-theme.js +0 -118
- package/dist/cjs/index.d.ts +0 -4
- package/dist/cjs/index.js +0 -22
- package/dist/cjs/package.json +0 -1
- package/dist/cjs/resolvers/color-resolver.d.ts +0 -10
- package/dist/cjs/resolvers/color-resolver.js +0 -26
- package/dist/cjs/resolvers/line-height-resolver.js +0 -20
- package/dist/cjs/resolvers/style-resolver.d.ts +0 -21
- package/dist/cjs/resolvers/style-resolver.js +0 -104
- package/dist/cjs/semantic-colors.js +0 -32
- package/dist/cjs/style-props.js +0 -95
- package/dist/cjs/text-tree.js +0 -79
- package/dist/cjs/text-variants.d.ts +0 -22
- package/dist/cjs/text-variants.js +0 -29
- package/dist/cjs/types.d.ts +0 -281
- package/dist/cjs/types.js +0 -2
- package/dist/esm/color-mode-store.d.ts +0 -32
- package/dist/esm/color-mode-store.js +0 -59
- package/dist/esm/config.d.ts +0 -14
- package/dist/esm/config.js +0 -13
- package/dist/esm/create-themed-styles.js +0 -34
- package/dist/esm/create-themed.d.ts +0 -46
- package/dist/esm/create-themed.js +0 -115
- package/dist/esm/define-theme.js +0 -12
- package/dist/esm/extend-theme.js +0 -115
- package/dist/esm/index.d.ts +0 -4
- package/dist/esm/index.js +0 -3
- package/dist/esm/package.json +0 -1
- package/dist/esm/resolvers/color-resolver.d.ts +0 -10
- package/dist/esm/resolvers/color-resolver.js +0 -23
- package/dist/esm/resolvers/line-height-resolver.js +0 -16
- package/dist/esm/resolvers/style-resolver.d.ts +0 -21
- package/dist/esm/resolvers/style-resolver.js +0 -101
- package/dist/esm/semantic-colors.js +0 -28
- package/dist/esm/style-props.js +0 -92
- package/dist/esm/text-tree.js +0 -72
- package/dist/esm/text-variants.d.ts +0 -22
- package/dist/esm/text-variants.js +0 -26
- package/dist/esm/types.d.ts +0 -281
- package/dist/esm/types.js +0 -1
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
export const isColorMode = (value) => value === 'light' || value === 'dark' || value === 'system';
|
|
2
|
-
/**
|
|
3
|
-
* External store holding the uncontrolled color mode. One is created per
|
|
4
|
-
* `createThemed()` call (never at module level) so independent instances
|
|
5
|
-
* don't share state.
|
|
6
|
-
*/
|
|
7
|
-
export function createColorModeStore() {
|
|
8
|
-
let mode = null;
|
|
9
|
-
// Set once the user picks a mode, so a late rehydration can't override it.
|
|
10
|
-
let userSet = false;
|
|
11
|
-
let hydrationStarted = false;
|
|
12
|
-
const listeners = new Set();
|
|
13
|
-
const emit = () => {
|
|
14
|
-
for (const listener of listeners)
|
|
15
|
-
listener();
|
|
16
|
-
};
|
|
17
|
-
return {
|
|
18
|
-
/**
|
|
19
|
-
* Seeds the mode on first use only. Called during render so the very
|
|
20
|
-
* first render already sees `defaultMode`; later calls (e.g. a changed
|
|
21
|
-
* `defaultColorMode` prop) are ignored rather than overwriting the
|
|
22
|
-
* current mode.
|
|
23
|
-
*/
|
|
24
|
-
init(defaultMode) {
|
|
25
|
-
if (mode === null)
|
|
26
|
-
mode = defaultMode;
|
|
27
|
-
},
|
|
28
|
-
subscribe(listener) {
|
|
29
|
-
listeners.add(listener);
|
|
30
|
-
return () => {
|
|
31
|
-
listeners.delete(listener);
|
|
32
|
-
};
|
|
33
|
-
},
|
|
34
|
-
getSnapshot() {
|
|
35
|
-
return mode ?? 'system';
|
|
36
|
-
},
|
|
37
|
-
setMode(next) {
|
|
38
|
-
userSet = true;
|
|
39
|
-
if (next === mode)
|
|
40
|
-
return;
|
|
41
|
-
mode = next;
|
|
42
|
-
emit();
|
|
43
|
-
},
|
|
44
|
-
/** Returns `false` if rehydration already ran for this store. */
|
|
45
|
-
beginHydration() {
|
|
46
|
-
if (hydrationStarted)
|
|
47
|
-
return false;
|
|
48
|
-
hydrationStarted = true;
|
|
49
|
-
return true;
|
|
50
|
-
},
|
|
51
|
-
/** Applies a stored value unless it's malformed or the user already chose. */
|
|
52
|
-
hydrate(saved) {
|
|
53
|
-
if (userSet || !isColorMode(saved) || saved === mode)
|
|
54
|
-
return;
|
|
55
|
-
mode = saved;
|
|
56
|
-
emit();
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
}
|
package/dist/esm/config.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `@react-native-rethemed/core/config` — the React/RN-free part of core.
|
|
3
|
-
*
|
|
4
|
-
* Apps can import everything from the package root: the CLI stubs
|
|
5
|
-
* `react-native` when it evaluates a theme file. Core's own token packages
|
|
6
|
-
* still import from here so they never depend on that stub.
|
|
7
|
-
*/
|
|
8
|
-
export { defineTheme } from './define-theme.js';
|
|
9
|
-
export { extendTheme } from './extend-theme.js';
|
|
10
|
-
export { RN_DEFAULT_FONT_SIZE, resolveBaseFontSize, } from './resolvers/line-height-resolver.js';
|
|
11
|
-
export { isSchemeColor, walkSemanticColors } from './semantic-colors.js';
|
|
12
|
-
export { COLOR_KEYS, FONT_SIZE_KEYS, FONT_WEIGHT_KEYS, LETTER_SPACING_KEYS, LINE_HEIGHT_KEYS, RADIUS_KEYS, SHADOW_STYLE_PROPS, SPACING_KEYS, Z_INDEX_KEYS, } from './style-props.js';
|
|
13
|
-
export { isTextPreset, resolveTextColor, TEXT_TOKEN_FIELDS, walkTextPresets, } from './text-tree.js';
|
|
14
|
-
export type { LooseSchema, SchemeColor, SemanticColors, ShadowToken, TextColor, TextToken, TextTokenTree, ThemeConfig, ThemedSchema, ThemedStyles, TokenizeStyle, } from './types.js';
|
package/dist/esm/config.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `@react-native-rethemed/core/config` — the React/RN-free part of core.
|
|
3
|
-
*
|
|
4
|
-
* Apps can import everything from the package root: the CLI stubs
|
|
5
|
-
* `react-native` when it evaluates a theme file. Core's own token packages
|
|
6
|
-
* still import from here so they never depend on that stub.
|
|
7
|
-
*/
|
|
8
|
-
export { defineTheme } from './define-theme.js';
|
|
9
|
-
export { extendTheme } from './extend-theme.js';
|
|
10
|
-
export { RN_DEFAULT_FONT_SIZE, resolveBaseFontSize, } from './resolvers/line-height-resolver.js';
|
|
11
|
-
export { isSchemeColor, walkSemanticColors } from './semantic-colors.js';
|
|
12
|
-
export { COLOR_KEYS, FONT_SIZE_KEYS, FONT_WEIGHT_KEYS, LETTER_SPACING_KEYS, LINE_HEIGHT_KEYS, RADIUS_KEYS, SHADOW_STYLE_PROPS, SPACING_KEYS, Z_INDEX_KEYS, } from './style-props.js';
|
|
13
|
-
export { isTextPreset, resolveTextColor, TEXT_TOKEN_FIELDS, walkTextPresets, } from './text-tree.js';
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { createStyleResolver } from './resolvers/style-resolver.js';
|
|
2
|
-
import { resolveTextTree } from './text-tree.js';
|
|
3
|
-
import { createTextVariants } from './text-variants.js';
|
|
4
|
-
/**
|
|
5
|
-
* Builds `{ view, text, image }` from a fully resolved `ThemeConfig` for one
|
|
6
|
-
* fixed color scheme. No React here — plain data in, plain functions out.
|
|
7
|
-
* `createThemed` calls this once per scheme and hands the result out through
|
|
8
|
-
* `useThemed()`, so components re-render (and pick up new colors) when the
|
|
9
|
-
* scheme changes.
|
|
10
|
-
*
|
|
11
|
-
* `S` comes from the generated `themed.gen.ts`; without it every token
|
|
12
|
-
* prop is loosely typed (`LooseSchema`).
|
|
13
|
-
*/
|
|
14
|
-
export function createThemedStyles(config, scheme) {
|
|
15
|
-
// One pass over the input's keys per call; see `createStyleResolver`.
|
|
16
|
-
const resolveStyle = createStyleResolver(config, scheme);
|
|
17
|
-
const text = (input = {}) => resolveStyle(input);
|
|
18
|
-
// Preset colors (`{ light, dark }`) are picked for this scheme up front.
|
|
19
|
-
const variants = createTextVariants(resolveTextTree(config.semanticTokens?.text, scheme), (input) => resolveStyle(input));
|
|
20
|
-
// `Object.assign` would throw for roles that collide with non-writable
|
|
21
|
-
// function properties (`name`, `length`); defineProperty overrides them.
|
|
22
|
-
for (const [role, sizes] of Object.entries(variants)) {
|
|
23
|
-
Object.defineProperty(text, role, {
|
|
24
|
-
value: sizes,
|
|
25
|
-
enumerable: true,
|
|
26
|
-
configurable: true,
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
return {
|
|
30
|
-
view: resolveStyle,
|
|
31
|
-
image: resolveStyle,
|
|
32
|
-
text,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { type ReactNode } from 'react';
|
|
2
|
-
import { type ColorMode, type ColorModeStorage } from './color-mode-store.js';
|
|
3
|
-
import { type ColorScheme } from './resolvers/color-resolver.js';
|
|
4
|
-
import type { LooseSchema, ThemeConfig, ThemedSchema, ThemedStyles } from './types.js';
|
|
5
|
-
export type { ColorMode, ColorModeStorage, ColorScheme };
|
|
6
|
-
export type ThemedProviderProps = {
|
|
7
|
-
children?: ReactNode;
|
|
8
|
-
/**
|
|
9
|
-
* Controlled mode: the app owns the scheme and this value is used as-is.
|
|
10
|
-
* When set, `defaultColorMode`/`storage`/`storageKey` are ignored.
|
|
11
|
-
*/
|
|
12
|
-
colorScheme?: ColorScheme;
|
|
13
|
-
/** Uncontrolled initial mode. Only read the first time the store is used. */
|
|
14
|
-
defaultColorMode?: ColorMode;
|
|
15
|
-
/** Uncontrolled persistence, e.g. `expo-secure-store` or `AsyncStorage`. */
|
|
16
|
-
storage?: ColorModeStorage;
|
|
17
|
-
storageKey?: string;
|
|
18
|
-
};
|
|
19
|
-
export type UseThemedResult<S extends ThemedSchema = LooseSchema> = {
|
|
20
|
-
themed: ThemedStyles<S>;
|
|
21
|
-
/** Primitive, scheme-independent tokens exactly as in the config. */
|
|
22
|
-
tokens: S['tokens'];
|
|
23
|
-
/**
|
|
24
|
-
* Semantic tokens with colors (including text preset colors) resolved for
|
|
25
|
-
* the current scheme.
|
|
26
|
-
*/
|
|
27
|
-
semanticTokens: S['semanticTokens'];
|
|
28
|
-
colorScheme: ColorScheme;
|
|
29
|
-
};
|
|
30
|
-
export type UseColorModeResult = {
|
|
31
|
-
mode: ColorMode;
|
|
32
|
-
setMode: (mode: ColorMode) => void;
|
|
33
|
-
};
|
|
34
|
-
/**
|
|
35
|
-
* Binds a theme config to React: returns a provider plus hooks that read
|
|
36
|
-
* the current scheme from it. Everything (store, contexts, precomputed
|
|
37
|
-
* themes) is scoped to this call, so several instances can coexist.
|
|
38
|
-
*
|
|
39
|
-
* `S` is supplied by the generated `themed.gen.ts`
|
|
40
|
-
* (`@react-native-rethemed/cli codegen`); without it tokens are loosely typed.
|
|
41
|
-
*/
|
|
42
|
-
export declare function createThemed<S extends ThemedSchema = LooseSchema>(config: ThemeConfig): {
|
|
43
|
-
ThemedProvider: ({ children, colorScheme, defaultColorMode, storage, storageKey, }: ThemedProviderProps) => import("react").FunctionComponentElement<import("react").ProviderProps<ColorScheme | null>>;
|
|
44
|
-
useThemed: () => UseThemedResult<S>;
|
|
45
|
-
useColorMode: () => UseColorModeResult;
|
|
46
|
-
};
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { createContext, createElement, useCallback, useContext, useEffect, useMemo, useSyncExternalStore, } from 'react';
|
|
2
|
-
import { Appearance, useColorScheme } from 'react-native';
|
|
3
|
-
import { createColorModeStore, } from './color-mode-store.js';
|
|
4
|
-
import { createThemedStyles } from './create-themed-styles.js';
|
|
5
|
-
import { resolveSemanticColors, } from './resolvers/color-resolver.js';
|
|
6
|
-
import { resolveTextTree } from './text-tree.js';
|
|
7
|
-
const DEFAULT_STORAGE_KEY = 'react-native-rethemed.color-mode';
|
|
8
|
-
/** RN 0.86 reports `'unspecified'` (and may report `null`); the theme is binary. */
|
|
9
|
-
function toColorScheme(value) {
|
|
10
|
-
return value === 'dark' ? 'dark' : 'light';
|
|
11
|
-
}
|
|
12
|
-
/** `Appearance.setColorScheme` is RN 0.73+ only and absent on react-native-web. */
|
|
13
|
-
function syncNativeAppearance(mode) {
|
|
14
|
-
if (typeof Appearance?.setColorScheme !== 'function')
|
|
15
|
-
return;
|
|
16
|
-
Appearance.setColorScheme(mode === 'system' ? 'unspecified' : mode);
|
|
17
|
-
}
|
|
18
|
-
const warnControlledSetMode = () => {
|
|
19
|
-
if (__DEV__) {
|
|
20
|
-
console.warn('[react-native-rethemed] setMode() was called while <ThemedProvider> is ' +
|
|
21
|
-
'controlled via the `colorScheme` prop. Update that prop instead.');
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
/**
|
|
25
|
-
* Binds a theme config to React: returns a provider plus hooks that read
|
|
26
|
-
* the current scheme from it. Everything (store, contexts, precomputed
|
|
27
|
-
* themes) is scoped to this call, so several instances can coexist.
|
|
28
|
-
*
|
|
29
|
-
* `S` is supplied by the generated `themed.gen.ts`
|
|
30
|
-
* (`@react-native-rethemed/cli codegen`); without it tokens are loosely typed.
|
|
31
|
-
*/
|
|
32
|
-
export function createThemed(config) {
|
|
33
|
-
// Precomputed once per scheme so `useThemed()` returns referentially
|
|
34
|
-
// stable objects until the scheme actually changes.
|
|
35
|
-
const build = (colorScheme) => ({
|
|
36
|
-
themed: createThemedStyles(config, colorScheme),
|
|
37
|
-
tokens: config.tokens ?? {},
|
|
38
|
-
semanticTokens: {
|
|
39
|
-
colors: resolveSemanticColors(config, colorScheme),
|
|
40
|
-
text: resolveTextTree(config.semanticTokens?.text, colorScheme),
|
|
41
|
-
},
|
|
42
|
-
colorScheme,
|
|
43
|
-
});
|
|
44
|
-
const themes = {
|
|
45
|
-
light: build('light'),
|
|
46
|
-
dark: build('dark'),
|
|
47
|
-
};
|
|
48
|
-
const store = createColorModeStore();
|
|
49
|
-
// Split so a mode change that keeps the same scheme (e.g. 'system' ->
|
|
50
|
-
// 'light' while the OS is light) doesn't re-render `useThemed` consumers.
|
|
51
|
-
const SchemeContext = createContext(null);
|
|
52
|
-
const ModeContext = createContext(null);
|
|
53
|
-
function ThemedProvider({ children, colorScheme, defaultColorMode = 'system', storage, storageKey = DEFAULT_STORAGE_KEY, }) {
|
|
54
|
-
const isControlled = colorScheme !== undefined;
|
|
55
|
-
// Hooks run unconditionally so switching between controlled and
|
|
56
|
-
// uncontrolled never changes hook order or remounts children.
|
|
57
|
-
store.init(defaultColorMode);
|
|
58
|
-
const storedMode = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
59
|
-
const systemScheme = toColorScheme(useColorScheme());
|
|
60
|
-
useEffect(() => {
|
|
61
|
-
if (isControlled || !storage || !store.beginHydration())
|
|
62
|
-
return;
|
|
63
|
-
// The store outlives this provider, so the result is applied even if
|
|
64
|
-
// it resolves after unmount; `hydrate` drops it if the user already
|
|
65
|
-
// picked a mode. `Promise.resolve().then` also catches a sync throw.
|
|
66
|
-
Promise.resolve()
|
|
67
|
-
.then(() => storage.getItem(storageKey))
|
|
68
|
-
.then((saved) => store.hydrate(saved))
|
|
69
|
-
.catch(() => { });
|
|
70
|
-
}, [isControlled, storage, storageKey]);
|
|
71
|
-
useEffect(() => {
|
|
72
|
-
if (!isControlled)
|
|
73
|
-
syncNativeAppearance(storedMode);
|
|
74
|
-
}, [isControlled, storedMode]);
|
|
75
|
-
const setUncontrolledMode = useCallback((next) => {
|
|
76
|
-
// Sync first: when returning to 'system', RN's `useColorScheme()`
|
|
77
|
-
// keeps reporting the old override until it's reset.
|
|
78
|
-
syncNativeAppearance(next);
|
|
79
|
-
store.setMode(next);
|
|
80
|
-
if (storage) {
|
|
81
|
-
Promise.resolve()
|
|
82
|
-
.then(() => storage.setItem(storageKey, next))
|
|
83
|
-
.catch(() => { });
|
|
84
|
-
}
|
|
85
|
-
}, [storage, storageKey]);
|
|
86
|
-
const scheme = isControlled
|
|
87
|
-
? colorScheme
|
|
88
|
-
: storedMode === 'system'
|
|
89
|
-
? systemScheme
|
|
90
|
-
: storedMode;
|
|
91
|
-
const mode = isControlled ? colorScheme : storedMode;
|
|
92
|
-
const setMode = isControlled ? warnControlledSetMode : setUncontrolledMode;
|
|
93
|
-
const modeValue = useMemo(() => ({ mode, setMode }), [mode, setMode]);
|
|
94
|
-
// `createElement` instead of JSX so consumers type-checking this source
|
|
95
|
-
// (it ships as `.ts`) don't need a `jsx` compiler option.
|
|
96
|
-
return createElement(SchemeContext, { value: scheme }, createElement(ModeContext, { value: modeValue }, children));
|
|
97
|
-
}
|
|
98
|
-
function useThemed() {
|
|
99
|
-
const scheme = useContext(SchemeContext);
|
|
100
|
-
if (scheme === null) {
|
|
101
|
-
throw new Error('useThemed() must be used within a <ThemedProvider> returned by the ' +
|
|
102
|
-
'same createThemed() call.');
|
|
103
|
-
}
|
|
104
|
-
return themes[scheme];
|
|
105
|
-
}
|
|
106
|
-
function useColorMode() {
|
|
107
|
-
const value = useContext(ModeContext);
|
|
108
|
-
if (value === null) {
|
|
109
|
-
throw new Error('useColorMode() must be used within a <ThemedProvider> returned by ' +
|
|
110
|
-
'the same createThemed() call.');
|
|
111
|
-
}
|
|
112
|
-
return value;
|
|
113
|
-
}
|
|
114
|
-
return { ThemedProvider, useThemed, useColorMode };
|
|
115
|
-
}
|
package/dist/esm/define-theme.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Identity function used purely as a type gate: checks `config` against
|
|
3
|
-
* `ThemeConfig` (object literals get the usual excess-property check, so a
|
|
4
|
-
* misspelled `radius` or `semanticToken` fails to compile).
|
|
5
|
-
*
|
|
6
|
-
* The return type is deliberately the plain `ThemeConfig` — exact token
|
|
7
|
-
* names come from the generated `themed.gen.ts`, not from literal-type
|
|
8
|
-
* inference here.
|
|
9
|
-
*/
|
|
10
|
-
export function defineTheme(config) {
|
|
11
|
-
return config;
|
|
12
|
-
}
|
package/dist/esm/extend-theme.js
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { isSchemeColor } from './semantic-colors.js';
|
|
2
|
-
import { isTextPreset } from './text-tree.js';
|
|
3
|
-
/**
|
|
4
|
-
* Flat `tokens` categories: a one-level merge (`{ ...a, ...b }`, `b` wins per
|
|
5
|
-
* key) is enough. `semanticTokens.colors` holds groups (merged one level
|
|
6
|
-
* deeper) and top-level colors (replaced whole); `semanticTokens.text` is a tree of any depth — see
|
|
7
|
-
* `mergeTextTree`.
|
|
8
|
-
*
|
|
9
|
-
* Merging is runtime-only: the result is typed as the plain `ThemeConfig`,
|
|
10
|
-
* and exact token names come from the generated `themed.gen.ts`.
|
|
11
|
-
*/
|
|
12
|
-
const FLAT_CATEGORY_KEYS = [
|
|
13
|
-
'colors',
|
|
14
|
-
'radii',
|
|
15
|
-
'spacing',
|
|
16
|
-
'fontSizes',
|
|
17
|
-
'fontWeights',
|
|
18
|
-
'lineHeights',
|
|
19
|
-
'letterSpacings',
|
|
20
|
-
'zIndices',
|
|
21
|
-
'shadows',
|
|
22
|
-
];
|
|
23
|
-
function mergeFlat(a, b) {
|
|
24
|
-
if (!a)
|
|
25
|
-
return b;
|
|
26
|
-
if (!b)
|
|
27
|
-
return a;
|
|
28
|
-
return { ...a, ...b };
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* `semanticTokens.colors`: groups are merged, colors replaced whole, so
|
|
32
|
-
* overriding `bg.default` keeps `bg.subtle` and every other group. A
|
|
33
|
-
* top-level color (`primary`) is replaced like a token; when a name is a
|
|
34
|
-
* color on one side and a group on the other, the later side replaces it.
|
|
35
|
-
*/
|
|
36
|
-
function mergeSemanticColors(a, b) {
|
|
37
|
-
if (!a)
|
|
38
|
-
return b;
|
|
39
|
-
if (!b)
|
|
40
|
-
return a;
|
|
41
|
-
const result = { ...a };
|
|
42
|
-
for (const [key, next] of Object.entries(b)) {
|
|
43
|
-
const prev = result[key];
|
|
44
|
-
result[key] =
|
|
45
|
-
prev && !isSchemeColor(prev) && !isSchemeColor(next)
|
|
46
|
-
? { ...prev, ...next }
|
|
47
|
-
: next;
|
|
48
|
-
}
|
|
49
|
-
return result;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* `semanticTokens.text`: groups merge recursively, presets (leaves) are
|
|
53
|
-
* replaced whole. So overriding `display.lg` keeps `display.md` and every
|
|
54
|
-
* other group, at any depth. When a key is a preset on one side and a group
|
|
55
|
-
* on the other, the later side replaces it.
|
|
56
|
-
*/
|
|
57
|
-
function mergeTextTree(a, b) {
|
|
58
|
-
if (!a)
|
|
59
|
-
return b;
|
|
60
|
-
if (!b)
|
|
61
|
-
return a;
|
|
62
|
-
const result = { ...a };
|
|
63
|
-
for (const [key, next] of Object.entries(b)) {
|
|
64
|
-
const prev = result[key];
|
|
65
|
-
result[key] =
|
|
66
|
-
prev && !isTextPreset(prev) && !isTextPreset(next)
|
|
67
|
-
? (mergeTextTree(prev, next) ?? next)
|
|
68
|
-
: next;
|
|
69
|
-
}
|
|
70
|
-
return result;
|
|
71
|
-
}
|
|
72
|
-
function mergeTokens(a, b) {
|
|
73
|
-
if (!a)
|
|
74
|
-
return b;
|
|
75
|
-
if (!b)
|
|
76
|
-
return a;
|
|
77
|
-
const merged = {};
|
|
78
|
-
for (const key of FLAT_CATEGORY_KEYS) {
|
|
79
|
-
const value = mergeFlat(a[key], b[key]);
|
|
80
|
-
if (value)
|
|
81
|
-
merged[key] = value;
|
|
82
|
-
}
|
|
83
|
-
return merged;
|
|
84
|
-
}
|
|
85
|
-
function mergeSemanticTokens(a, b) {
|
|
86
|
-
const colors = mergeSemanticColors(a?.colors, b?.colors);
|
|
87
|
-
const text = mergeTextTree(a?.text, b?.text);
|
|
88
|
-
return {
|
|
89
|
-
...(colors ? { colors } : {}),
|
|
90
|
-
...(text ? { text } : {}),
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
function mergeThemeConfig(a, b) {
|
|
94
|
-
const defaults = mergeFlat(a.defaults, b.defaults);
|
|
95
|
-
return {
|
|
96
|
-
tokens: mergeTokens(a.tokens, b.tokens),
|
|
97
|
-
semanticTokens: mergeSemanticTokens(a.semanticTokens, b.semanticTokens),
|
|
98
|
-
...(defaults ? { defaults } : {}),
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Merges N `ThemeConfig`s, folding left-to-right so later themes override
|
|
103
|
-
* earlier ones per key:
|
|
104
|
-
*
|
|
105
|
-
* ```ts
|
|
106
|
-
* const config = extendTheme(materialDesignTheme, chakraUiTheme, {
|
|
107
|
-
* semanticTokens: {
|
|
108
|
-
* text: { display: { lg: { fontSize: 60, lineHeight: 68 } } },
|
|
109
|
-
* },
|
|
110
|
-
* });
|
|
111
|
-
* ```
|
|
112
|
-
*/
|
|
113
|
-
export function extendTheme(...themes) {
|
|
114
|
-
return themes.reduce(mergeThemeConfig);
|
|
115
|
-
}
|
package/dist/esm/index.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export * from './config.js';
|
|
2
|
-
export type { ColorMode, ColorModeStorage, ColorScheme, ThemedProviderProps, UseColorModeResult, UseThemedResult, } from './create-themed.js';
|
|
3
|
-
export { createThemed } from './create-themed.js';
|
|
4
|
-
export { createThemedStyles } from './create-themed-styles.js';
|
package/dist/esm/index.js
DELETED
package/dist/esm/package.json
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"type":"module"}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { ThemeConfig } from '../types.js';
|
|
2
|
-
export type ColorScheme = 'light' | 'dark';
|
|
3
|
-
/**
|
|
4
|
-
* `config.semanticTokens.colors` for one scheme, keeping its shape: groups
|
|
5
|
-
* become group -> token -> string (`{ fg: { default: '#000' } }`) and
|
|
6
|
-
* top-level colors a string (`{ primary: '#171717' }`). Semantic values are
|
|
7
|
-
* already concrete strings in the config, so this only picks
|
|
8
|
-
* `light`/`dark`.
|
|
9
|
-
*/
|
|
10
|
-
export declare function resolveSemanticColors(config: ThemeConfig, scheme: ColorScheme): Record<string, string | Record<string, string>>;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { walkSemanticColors } from '../semantic-colors.js';
|
|
2
|
-
/**
|
|
3
|
-
* `config.semanticTokens.colors` for one scheme, keeping its shape: groups
|
|
4
|
-
* become group -> token -> string (`{ fg: { default: '#000' } }`) and
|
|
5
|
-
* top-level colors a string (`{ primary: '#171717' }`). Semantic values are
|
|
6
|
-
* already concrete strings in the config, so this only picks
|
|
7
|
-
* `light`/`dark`.
|
|
8
|
-
*/
|
|
9
|
-
export function resolveSemanticColors(config, scheme) {
|
|
10
|
-
const result = {};
|
|
11
|
-
walkSemanticColors(config.semanticTokens?.colors, (token, color, group) => {
|
|
12
|
-
if (group === undefined) {
|
|
13
|
-
result[token] = color[scheme];
|
|
14
|
-
}
|
|
15
|
-
else {
|
|
16
|
-
if (result[group] === undefined)
|
|
17
|
-
result[group] = {};
|
|
18
|
-
const tokens = result[group];
|
|
19
|
-
tokens[token.slice(group.length + 1)] = color[scheme];
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
return result;
|
|
23
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/** React Native's own default `fontSize`. */
|
|
2
|
-
export const RN_DEFAULT_FONT_SIZE = 14;
|
|
3
|
-
/**
|
|
4
|
-
* `defaults.fontSize` as a number: a `tokens.fontSizes` key is looked up,
|
|
5
|
-
* a raw number is used as-is, anything else falls back to RN's default.
|
|
6
|
-
*/
|
|
7
|
-
export function resolveBaseFontSize(config) {
|
|
8
|
-
const base = config.defaults?.fontSize;
|
|
9
|
-
if (typeof base === 'number')
|
|
10
|
-
return base;
|
|
11
|
-
const scale = config.tokens?.fontSizes;
|
|
12
|
-
if (typeof base === 'string' && scale && Object.hasOwn(scale, base)) {
|
|
13
|
-
return scale[base];
|
|
14
|
-
}
|
|
15
|
-
return RN_DEFAULT_FONT_SIZE;
|
|
16
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { ThemeConfig } from '../types.js';
|
|
2
|
-
import type { ColorScheme } from './color-resolver.js';
|
|
3
|
-
/**
|
|
4
|
-
* Builds the resolver behind `themed.view()` / `text()` / `image()` for one
|
|
5
|
-
* color scheme.
|
|
6
|
-
*
|
|
7
|
-
* Everything that doesn't depend on the input is done once here: a single
|
|
8
|
-
* **prop → token table** lookup (`backgroundColor` → the primitive
|
|
9
|
-
* `tokens.colors` plus the flattened `'group.token'` semantic colors for this
|
|
10
|
-
* scheme, `padding` → `tokens.spacing`, …).
|
|
11
|
-
* Each call then makes **one pass over the input's own keys** (usually a
|
|
12
|
-
* handful), rather than scanning every token-aware prop name, and copies the
|
|
13
|
-
* input at most once — or not at all when nothing is a token.
|
|
14
|
-
*
|
|
15
|
-
* Two props need the rest of the style, so they are applied after the pass:
|
|
16
|
-
* - `lineHeight` tokens are ratios of the (resolved) `fontSize`, falling back
|
|
17
|
-
* to `defaults.fontSize`;
|
|
18
|
-
* - the virtual `shadow` prop expands last, so a preset wins over explicit
|
|
19
|
-
* `shadow*` props, and the `shadow` key itself is removed.
|
|
20
|
-
*/
|
|
21
|
-
export declare function createStyleResolver(config: ThemeConfig, scheme: ColorScheme): (input: object) => Record<string, unknown>;
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { walkSemanticColors } from '../semantic-colors.js';
|
|
2
|
-
import { COLOR_KEYS, FONT_SIZE_KEYS, FONT_WEIGHT_KEYS, LETTER_SPACING_KEYS, RADIUS_KEYS, SPACING_KEYS, Z_INDEX_KEYS, } from '../style-props.js';
|
|
3
|
-
import { resolveBaseFontSize } from './line-height-resolver.js';
|
|
4
|
-
/** Avoids float noise like `17.600000000000001` without pixel-snapping. */
|
|
5
|
-
const round2 = (value) => Math.round(value * 100) / 100;
|
|
6
|
-
const hasToken = (table, value) => (typeof value === 'string' || typeof value === 'number') &&
|
|
7
|
-
Object.hasOwn(table, value);
|
|
8
|
-
/**
|
|
9
|
-
* Builds the resolver behind `themed.view()` / `text()` / `image()` for one
|
|
10
|
-
* color scheme.
|
|
11
|
-
*
|
|
12
|
-
* Everything that doesn't depend on the input is done once here: a single
|
|
13
|
-
* **prop → token table** lookup (`backgroundColor` → the primitive
|
|
14
|
-
* `tokens.colors` plus the flattened `'group.token'` semantic colors for this
|
|
15
|
-
* scheme, `padding` → `tokens.spacing`, …).
|
|
16
|
-
* Each call then makes **one pass over the input's own keys** (usually a
|
|
17
|
-
* handful), rather than scanning every token-aware prop name, and copies the
|
|
18
|
-
* input at most once — or not at all when nothing is a token.
|
|
19
|
-
*
|
|
20
|
-
* Two props need the rest of the style, so they are applied after the pass:
|
|
21
|
-
* - `lineHeight` tokens are ratios of the (resolved) `fontSize`, falling back
|
|
22
|
-
* to `defaults.fontSize`;
|
|
23
|
-
* - the virtual `shadow` prop expands last, so a preset wins over explicit
|
|
24
|
-
* `shadow*` props, and the `shadow` key itself is removed.
|
|
25
|
-
*/
|
|
26
|
-
export function createStyleResolver(config, scheme) {
|
|
27
|
-
const tokens = config.tokens ?? {};
|
|
28
|
-
const byProp = Object.create(null);
|
|
29
|
-
const register = (props, table) => {
|
|
30
|
-
if (!table)
|
|
31
|
-
return;
|
|
32
|
-
for (const prop of props)
|
|
33
|
-
byProp[prop] = table;
|
|
34
|
-
};
|
|
35
|
-
// Color props take both kinds of color token. Primitives (`'red.500'`)
|
|
36
|
-
// go in first, so a semantic color with the same name wins (the CLI warns
|
|
37
|
-
// about such collisions).
|
|
38
|
-
const colors = { ...tokens.colors };
|
|
39
|
-
walkSemanticColors(config.semanticTokens?.colors, (token, color) => {
|
|
40
|
-
colors[token] = color[scheme];
|
|
41
|
-
});
|
|
42
|
-
if (tokens.colors || config.semanticTokens?.colors) {
|
|
43
|
-
register(COLOR_KEYS, colors);
|
|
44
|
-
}
|
|
45
|
-
register(RADIUS_KEYS, tokens.radii);
|
|
46
|
-
register(SPACING_KEYS, tokens.spacing);
|
|
47
|
-
register(FONT_SIZE_KEYS, tokens.fontSizes);
|
|
48
|
-
register(FONT_WEIGHT_KEYS, tokens.fontWeights);
|
|
49
|
-
register(LETTER_SPACING_KEYS, tokens.letterSpacings);
|
|
50
|
-
register(Z_INDEX_KEYS, tokens.zIndices);
|
|
51
|
-
const lineHeights = tokens.lineHeights;
|
|
52
|
-
const shadows = tokens.shadows;
|
|
53
|
-
const baseFontSize = resolveBaseFontSize(config);
|
|
54
|
-
return function resolveStyle(input) {
|
|
55
|
-
const source = input;
|
|
56
|
-
let result = null;
|
|
57
|
-
let lineHeightRatio;
|
|
58
|
-
let shadowPreset;
|
|
59
|
-
let hasShadow = false;
|
|
60
|
-
for (const key in source) {
|
|
61
|
-
const value = source[key];
|
|
62
|
-
if (key === 'lineHeight') {
|
|
63
|
-
if (lineHeights && hasToken(lineHeights, value)) {
|
|
64
|
-
lineHeightRatio = lineHeights[value];
|
|
65
|
-
}
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
if (key === 'shadow') {
|
|
69
|
-
if (shadows) {
|
|
70
|
-
hasShadow = true;
|
|
71
|
-
shadowPreset = hasToken(shadows, value)
|
|
72
|
-
? shadows[value]
|
|
73
|
-
: undefined;
|
|
74
|
-
}
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
const table = byProp[key];
|
|
78
|
-
if (table === undefined || !hasToken(table, value))
|
|
79
|
-
continue;
|
|
80
|
-
result ??= { ...source };
|
|
81
|
-
result[key] = table[value];
|
|
82
|
-
}
|
|
83
|
-
if (lineHeightRatio !== undefined) {
|
|
84
|
-
result ??= { ...source };
|
|
85
|
-
const fontSize = typeof result.fontSize === 'number' ? result.fontSize : baseFontSize;
|
|
86
|
-
result.lineHeight = round2(fontSize * lineHeightRatio);
|
|
87
|
-
}
|
|
88
|
-
if (hasShadow) {
|
|
89
|
-
// Rebuild without `shadow` instead of `delete`, which would push the
|
|
90
|
-
// object into V8/Hermes' slow dictionary mode.
|
|
91
|
-
const base = result ?? source;
|
|
92
|
-
const expanded = {};
|
|
93
|
-
for (const key in base) {
|
|
94
|
-
if (key !== 'shadow')
|
|
95
|
-
expanded[key] = base[key];
|
|
96
|
-
}
|
|
97
|
-
result = shadowPreset ? Object.assign(expanded, shadowPreset) : expanded;
|
|
98
|
-
}
|
|
99
|
-
return result ?? source;
|
|
100
|
-
};
|
|
101
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A color of `semanticTokens.colors` (as opposed to a group of them): an
|
|
3
|
-
* object whose `light` or `dark` is a string. A group's values are objects,
|
|
4
|
-
* so a group may still name a token `light` or `dark` (Material UI's
|
|
5
|
-
* `primary.light`).
|
|
6
|
-
*/
|
|
7
|
-
export function isSchemeColor(node) {
|
|
8
|
-
if (typeof node !== 'object' || node === null)
|
|
9
|
-
return false;
|
|
10
|
-
const { light, dark } = node;
|
|
11
|
-
return typeof light === 'string' || typeof dark === 'string';
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Visits every semantic color in definition order with its token name:
|
|
15
|
-
* `'group.token'` inside a group, or the bare name for a top-level color
|
|
16
|
-
* (`'primary'`, `'primary-foreground'`).
|
|
17
|
-
*/
|
|
18
|
-
export function walkSemanticColors(colors, visit) {
|
|
19
|
-
for (const [key, node] of Object.entries(colors ?? {})) {
|
|
20
|
-
if (isSchemeColor(node)) {
|
|
21
|
-
visit(key, node, undefined);
|
|
22
|
-
continue;
|
|
23
|
-
}
|
|
24
|
-
for (const [name, color] of Object.entries(node)) {
|
|
25
|
-
visit(`${key}.${name}`, color, key);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|