@kbach/react-native 1.0.0-beta.8 → 1.0.0-beta.9

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.
@@ -1,98 +0,0 @@
1
- import * as react from 'react';
2
- import { ReactNode } from 'react';
3
-
4
- interface ThemeConfig {
5
- /** Plain hex only, this pass — e.g. "blue-6" -> "#2563eb" */
6
- colors: Record<string, string>;
7
- spacing: Record<string, number>;
8
- screens: Record<string, number>;
9
- darkMode: 'attribute' | 'class' | 'media';
10
- }
11
- declare const defaultTheme: ThemeConfig;
12
- declare function setTheme(theme: ThemeConfig): void;
13
- declare function getTheme(): ThemeConfig;
14
-
15
- /**
16
- * Native port of @kbach/react's darkModeStore.ts — same architecture (a
17
- * single module-level store is the real source of truth;
18
- * ThemeProvider/useTheme are a thin, OPTIONAL wrapper around it; every
19
- * function here works standalone with no provider mounted anywhere), with
20
- * two real platform differences from the web version:
21
- *
22
- * 1. No persistence. There's no React-Native-builtin equivalent of
23
- * localStorage — adding one would mean forcing a real native dependency
24
- * (e.g. @react-native-async-storage/async-storage) on every consumer,
25
- * even ones that don't want it. The mode always starts at 'system' on a
26
- * fresh app launch. If you want persistence, read your own storage at
27
- * startup (before your first render) and call setGlobalThemeMode() with
28
- * the result — same "seed once, no built-in write-back" shape as
29
- * seedDefaultMode() below, just driven by your own storage instead of
30
- * one this package picks for you.
31
- * 2. No DOM to "apply" the resolved mode to. On web, applyToDom() flips a
32
- * class/attribute the generated CSS's `dark:` selectors match against.
33
- * On native there's no CSS at all — nativeBridge.ts's resolveStyle()
34
- * reads getGlobalDarkMode() directly on every call instead of querying
35
- * Appearance.getColorScheme() itself, which is what actually makes an
36
- * explicit override affect rendering (previously that function always
37
- * read the raw OS setting, so it had no way to reflect a user choice —
38
- * see nativeBridge.ts's own comment on this for why that's the one
39
- * genuinely fixed bug this change introduces, not just a new feature).
40
- */
41
- type ThemeMode = 'light' | 'dark' | 'system';
42
- declare function getGlobalDarkMode(): boolean;
43
- declare function getGlobalThemeMode(): ThemeMode;
44
- /** Explicit user choice — takes priority over any later `<ThemeProvider defaultMode>` seed (see `seedDefaultMode` below). Not persisted; see this module's own doc comment. */
45
- declare function setGlobalThemeMode(nextMode: ThemeMode): void;
46
- declare function toggleGlobalDarkMode(): void;
47
- declare function subscribeGlobalDarkMode(listener: () => void): () => void;
48
-
49
- /**
50
- * Reactive read of the global dark-mode store — works with or without a
51
- * `<ThemeProvider>` mounted anywhere in the tree, since it subscribes to
52
- * `darkModeStore.ts` directly. No server-snapshot argument (unlike
53
- * @kbach/react's web version) — React Native has no SSR concept at all,
54
- * so there's no third argument for `useSyncExternalStore` to need.
55
- */
56
- declare function useGlobalDarkMode(): boolean;
57
-
58
- interface ThemeProviderProps {
59
- children: ReactNode;
60
- /**
61
- * Applied once, on this provider's first mount, but ONLY if nothing has
62
- * made an explicit mode choice yet (see
63
- * `darkModeStore.ts::seedDefaultMode`) — an explicit prior choice always
64
- * wins over a component's own default. Unlike @kbach/react's web
65
- * version, there's no persisted value to check here — see
66
- * darkModeStore.ts's own doc comment on why this package doesn't persist
67
- * the mode across app launches.
68
- */
69
- defaultMode?: ThemeMode;
70
- }
71
- /**
72
- * Native port of @kbach/react's `ThemeProvider` — a thin, OPTIONAL React
73
- * Context wrapper around `darkModeStore.ts`'s single global store, not the
74
- * source of truth itself. Because the store is global, MULTIPLE mounted
75
- * providers share the exact same `mode`/`isDark` state rather than each
76
- * getting their own scoped value — mounting more than one is very likely a
77
- * mistake (each fighting over the same state via a different
78
- * `defaultMode`), so this warns once in dev, matching the web version's
79
- * identical guard.
80
- */
81
- declare function ThemeProvider({ children, defaultMode }: ThemeProviderProps): react.JSX.Element;
82
-
83
- interface ThemeContextValue {
84
- mode: ThemeMode;
85
- isDark: boolean;
86
- setMode: (mode: ThemeMode) => void;
87
- toggle: () => void;
88
- }
89
- /**
90
- * Requires a `<ThemeProvider>` ancestor — throws otherwise, since a
91
- * component that destructures `{ mode, setMode }` genuinely needs Context
92
- * (unlike a component that just wants the dark/light boolean, which should
93
- * reach for `useGlobalDarkMode()` instead and never needs a provider at
94
- * all).
95
- */
96
- declare function useTheme(): ThemeContextValue;
97
-
98
- export { type ThemeConfig as T, type ThemeContextValue as a, type ThemeMode as b, ThemeProvider as c, type ThemeProviderProps as d, defaultTheme as e, getGlobalThemeMode as f, getGlobalDarkMode as g, getTheme as h, setTheme as i, subscribeGlobalDarkMode as j, useTheme as k, setGlobalThemeMode as s, toggleGlobalDarkMode as t, useGlobalDarkMode as u };
@@ -1,98 +0,0 @@
1
- import * as react from 'react';
2
- import { ReactNode } from 'react';
3
-
4
- interface ThemeConfig {
5
- /** Plain hex only, this pass — e.g. "blue-6" -> "#2563eb" */
6
- colors: Record<string, string>;
7
- spacing: Record<string, number>;
8
- screens: Record<string, number>;
9
- darkMode: 'attribute' | 'class' | 'media';
10
- }
11
- declare const defaultTheme: ThemeConfig;
12
- declare function setTheme(theme: ThemeConfig): void;
13
- declare function getTheme(): ThemeConfig;
14
-
15
- /**
16
- * Native port of @kbach/react's darkModeStore.ts — same architecture (a
17
- * single module-level store is the real source of truth;
18
- * ThemeProvider/useTheme are a thin, OPTIONAL wrapper around it; every
19
- * function here works standalone with no provider mounted anywhere), with
20
- * two real platform differences from the web version:
21
- *
22
- * 1. No persistence. There's no React-Native-builtin equivalent of
23
- * localStorage — adding one would mean forcing a real native dependency
24
- * (e.g. @react-native-async-storage/async-storage) on every consumer,
25
- * even ones that don't want it. The mode always starts at 'system' on a
26
- * fresh app launch. If you want persistence, read your own storage at
27
- * startup (before your first render) and call setGlobalThemeMode() with
28
- * the result — same "seed once, no built-in write-back" shape as
29
- * seedDefaultMode() below, just driven by your own storage instead of
30
- * one this package picks for you.
31
- * 2. No DOM to "apply" the resolved mode to. On web, applyToDom() flips a
32
- * class/attribute the generated CSS's `dark:` selectors match against.
33
- * On native there's no CSS at all — nativeBridge.ts's resolveStyle()
34
- * reads getGlobalDarkMode() directly on every call instead of querying
35
- * Appearance.getColorScheme() itself, which is what actually makes an
36
- * explicit override affect rendering (previously that function always
37
- * read the raw OS setting, so it had no way to reflect a user choice —
38
- * see nativeBridge.ts's own comment on this for why that's the one
39
- * genuinely fixed bug this change introduces, not just a new feature).
40
- */
41
- type ThemeMode = 'light' | 'dark' | 'system';
42
- declare function getGlobalDarkMode(): boolean;
43
- declare function getGlobalThemeMode(): ThemeMode;
44
- /** Explicit user choice — takes priority over any later `<ThemeProvider defaultMode>` seed (see `seedDefaultMode` below). Not persisted; see this module's own doc comment. */
45
- declare function setGlobalThemeMode(nextMode: ThemeMode): void;
46
- declare function toggleGlobalDarkMode(): void;
47
- declare function subscribeGlobalDarkMode(listener: () => void): () => void;
48
-
49
- /**
50
- * Reactive read of the global dark-mode store — works with or without a
51
- * `<ThemeProvider>` mounted anywhere in the tree, since it subscribes to
52
- * `darkModeStore.ts` directly. No server-snapshot argument (unlike
53
- * @kbach/react's web version) — React Native has no SSR concept at all,
54
- * so there's no third argument for `useSyncExternalStore` to need.
55
- */
56
- declare function useGlobalDarkMode(): boolean;
57
-
58
- interface ThemeProviderProps {
59
- children: ReactNode;
60
- /**
61
- * Applied once, on this provider's first mount, but ONLY if nothing has
62
- * made an explicit mode choice yet (see
63
- * `darkModeStore.ts::seedDefaultMode`) — an explicit prior choice always
64
- * wins over a component's own default. Unlike @kbach/react's web
65
- * version, there's no persisted value to check here — see
66
- * darkModeStore.ts's own doc comment on why this package doesn't persist
67
- * the mode across app launches.
68
- */
69
- defaultMode?: ThemeMode;
70
- }
71
- /**
72
- * Native port of @kbach/react's `ThemeProvider` — a thin, OPTIONAL React
73
- * Context wrapper around `darkModeStore.ts`'s single global store, not the
74
- * source of truth itself. Because the store is global, MULTIPLE mounted
75
- * providers share the exact same `mode`/`isDark` state rather than each
76
- * getting their own scoped value — mounting more than one is very likely a
77
- * mistake (each fighting over the same state via a different
78
- * `defaultMode`), so this warns once in dev, matching the web version's
79
- * identical guard.
80
- */
81
- declare function ThemeProvider({ children, defaultMode }: ThemeProviderProps): react.JSX.Element;
82
-
83
- interface ThemeContextValue {
84
- mode: ThemeMode;
85
- isDark: boolean;
86
- setMode: (mode: ThemeMode) => void;
87
- toggle: () => void;
88
- }
89
- /**
90
- * Requires a `<ThemeProvider>` ancestor — throws otherwise, since a
91
- * component that destructures `{ mode, setMode }` genuinely needs Context
92
- * (unlike a component that just wants the dark/light boolean, which should
93
- * reach for `useGlobalDarkMode()` instead and never needs a provider at
94
- * all).
95
- */
96
- declare function useTheme(): ThemeContextValue;
97
-
98
- export { type ThemeConfig as T, type ThemeContextValue as a, type ThemeMode as b, ThemeProvider as c, type ThemeProviderProps as d, defaultTheme as e, getGlobalThemeMode as f, getGlobalDarkMode as g, getTheme as h, setTheme as i, subscribeGlobalDarkMode as j, useTheme as k, setGlobalThemeMode as s, toggleGlobalDarkMode as t, useGlobalDarkMode as u };
@@ -1,66 +0,0 @@
1
- import {
2
- getGlobalDarkMode,
3
- getGlobalThemeMode,
4
- seedDefaultMode,
5
- setGlobalThemeMode,
6
- subscribeGlobalDarkMode,
7
- toggleGlobalDarkMode
8
- } from "./chunk-Z664AKW7.mjs";
9
-
10
- // src/useGlobalDarkMode.ts
11
- import { useSyncExternalStore } from "react";
12
- function useGlobalDarkMode() {
13
- return useSyncExternalStore(subscribeGlobalDarkMode, getGlobalDarkMode);
14
- }
15
-
16
- // src/ThemeContext.tsx
17
- import { createContext, useContext } from "react";
18
- var ThemeContext = createContext(null);
19
- function useTheme() {
20
- const ctx = useContext(ThemeContext);
21
- if (!ctx) {
22
- throw new Error(
23
- 'useTheme() must be called within a <ThemeProvider>. For dark-mode state without a provider, use useGlobalDarkMode() (read) or toggleGlobalDarkMode()/setGlobalThemeMode() (write) from "@kbach/react-native" instead.'
24
- );
25
- }
26
- return ctx;
27
- }
28
-
29
- // src/ThemeProvider.tsx
30
- import { useEffect, useMemo, useRef, useSyncExternalStore as useSyncExternalStore2 } from "react";
31
- import { jsx } from "react/jsx-runtime";
32
- var mountedProviderCount = 0;
33
- var warnedMultipleProviders = false;
34
- function ThemeProvider({ children, defaultMode = "system" }) {
35
- useEffect(() => {
36
- mountedProviderCount++;
37
- if (mountedProviderCount > 1 && !warnedMultipleProviders) {
38
- warnedMultipleProviders = true;
39
- console.warn(
40
- "[kbach] Multiple <ThemeProvider> instances are mounted at once. Dark-mode state is one global store (the same one useGlobalDarkMode()/toggleGlobalDarkMode() read and write), not scoped per-provider, so they will fight over the same state instead of each getting their own."
41
- );
42
- }
43
- return () => {
44
- mountedProviderCount--;
45
- };
46
- }, []);
47
- const seeded = useRef(false);
48
- useEffect(() => {
49
- if (seeded.current) return;
50
- seeded.current = true;
51
- seedDefaultMode(defaultMode);
52
- }, []);
53
- const isDark = useSyncExternalStore2(subscribeGlobalDarkMode, getGlobalDarkMode);
54
- const mode = useSyncExternalStore2(subscribeGlobalDarkMode, getGlobalThemeMode);
55
- const value = useMemo(
56
- () => ({ mode, isDark, setMode: setGlobalThemeMode, toggle: toggleGlobalDarkMode }),
57
- [mode, isDark]
58
- );
59
- return /* @__PURE__ */ jsx(ThemeContext.Provider, { value, children });
60
- }
61
-
62
- export {
63
- useGlobalDarkMode,
64
- useTheme,
65
- ThemeProvider
66
- };