@kbach/react-native 1.0.0-beta.3 → 1.0.0-beta.5
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 +44 -13
- package/dist/chunk-4KCCEMEN.mjs +66 -0
- package/dist/{chunk-7KUNKZBM.mjs → chunk-AEVOM2NB.mjs} +2 -12
- package/dist/{chunk-UP5ZN7CN.mjs → chunk-DLIXI5OW.mjs} +23 -14
- package/dist/chunk-FG5E4NLJ.mjs +18 -0
- package/dist/chunk-KSDFO34H.mjs +32 -0
- package/dist/chunk-OJQAJLYT.mjs +14 -0
- package/dist/chunk-ZUE6SCTE.mjs +14 -0
- package/dist/index.mjs +9 -57
- package/dist/index.web.d.mts +7 -0
- package/dist/index.web.d.ts +7 -0
- package/dist/index.web.js +488 -0
- package/dist/index.web.mjs +32 -0
- package/dist/jsx-dev-runtime.d.mts +3 -12
- package/dist/jsx-dev-runtime.d.ts +3 -12
- package/dist/jsx-dev-runtime.js +53 -46
- package/dist/jsx-dev-runtime.mjs +8 -11
- package/dist/jsx-dev-runtime.web.d.mts +10 -0
- package/dist/jsx-dev-runtime.web.d.ts +10 -0
- package/dist/jsx-dev-runtime.web.js +437 -0
- package/dist/jsx-dev-runtime.web.mjs +17 -0
- package/dist/jsx-runtime.d.mts +3 -26
- package/dist/jsx-runtime.d.ts +3 -26
- package/dist/jsx-runtime.js +43 -37
- package/dist/jsx-runtime.mjs +6 -3
- package/dist/jsx-runtime.web.d.mts +7 -0
- package/dist/jsx-runtime.web.d.ts +7 -0
- package/dist/jsx-runtime.web.js +426 -0
- package/dist/jsx-runtime.web.mjs +14 -0
- package/package.json +20 -4
package/README.md
CHANGED
|
@@ -4,11 +4,12 @@ Kbach's utility-class styling engine for React Native — the same class
|
|
|
4
4
|
names as [`@kbach/react`](https://www.npmjs.com/package/@kbach/react),
|
|
5
5
|
resolved to a real `StyleSheet`-ready style object instead of injected CSS.
|
|
6
6
|
|
|
7
|
-
> **Beta
|
|
8
|
-
>
|
|
9
|
-
>
|
|
10
|
-
>
|
|
11
|
-
>
|
|
7
|
+
> **Beta.** Native (Android-only, no iOS bridge yet) resolves through a
|
|
8
|
+
> Kotlin/JNI TurboModule; Expo Web / react-native-web resolves through the
|
|
9
|
+
> same Rust engine compiled to WASM instead — both call the identical
|
|
10
|
+
> resolution logic, so class semantics stay identical everywhere. The
|
|
11
|
+
> native module ships prebuilt (see Setup below) — no Rust toolchain or
|
|
12
|
+
> manual native wiring required on any platform.
|
|
12
13
|
|
|
13
14
|
## Setup
|
|
14
15
|
|
|
@@ -46,13 +47,18 @@ one needs (a plain single-app install doesn't need any of that — it's
|
|
|
46
47
|
only relevant if you're linking packages locally instead of installing
|
|
47
48
|
from npm).
|
|
48
49
|
|
|
49
|
-
### Expo
|
|
50
|
+
### Expo (native — Android)
|
|
50
51
|
|
|
51
|
-
**Expo Go
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
**Expo Go can never work with this package, and that isn't specific to
|
|
53
|
+
Kbach.** Expo Go is a single fixed binary Expo pre-builds and ships
|
|
54
|
+
through the app stores, containing only the native code Expo bundled into
|
|
55
|
+
it at their own build time — there is no mechanism for *any* third-party
|
|
56
|
+
package to add native code to it at runtime, whether that's a raw
|
|
57
|
+
TurboModule (what this package uses) or even Expo's own Modules API. This
|
|
58
|
+
is exactly why the **dev client** concept exists: a custom build of your
|
|
59
|
+
app — built once via `expo prebuild`/`expo run:android`, functionally the
|
|
60
|
+
same "real native project" as the CLI path above — that *does* include
|
|
61
|
+
this module:
|
|
56
62
|
|
|
57
63
|
```js
|
|
58
64
|
// babel.config.js
|
|
@@ -76,11 +82,36 @@ Architecture off, turn it on in `app.json`
|
|
|
76
82
|
EAS Build works the same way — it runs the same `prebuild` step, so no
|
|
77
83
|
extra configuration beyond the two steps above.
|
|
78
84
|
|
|
85
|
+
See `apps/expo-sandbox` in the [repo](https://github.com/semsakadanupol/kbach)
|
|
86
|
+
for a full working reference project covering both this section and Expo
|
|
87
|
+
Web below — including the `metro.config.js` a monorepo setup like this one
|
|
88
|
+
needs (a plain single-app install doesn't need it).
|
|
89
|
+
|
|
90
|
+
### Expo Web
|
|
91
|
+
|
|
92
|
+
**Works — no extra setup, no separate package, no init call.** `expo start
|
|
93
|
+
--web` (and EAS's web builds) render through `react-native-web` —
|
|
94
|
+
`View`/`Text`/etc. become real DOM elements with real CSS, not a
|
|
95
|
+
Kotlin/JNI bridge, so on web this package resolves classes through the
|
|
96
|
+
same Rust engine [`@kbach/react`](https://www.npmjs.com/package/@kbach/react)
|
|
97
|
+
uses — WASM instead of JNI — automatically. Metro picks the right
|
|
98
|
+
implementation per platform on its own via this package's `"browser"`
|
|
99
|
+
export condition; your app code and JSX never change. The WASM module
|
|
100
|
+
initializes itself eagerly (synchronously, at import time — no `await`
|
|
101
|
+
needed before your first render, unlike a from-scratch WASM setup).
|
|
102
|
+
|
|
103
|
+
The only thing this doesn't cover on web is an iOS-parity feature gap —
|
|
104
|
+
everything web-side resolves through the exact same
|
|
105
|
+
[`resolve_style.rs`](https://github.com/semsakadanupol/kbach/blob/main/packages/core-engine/src/resolve_style.rs)
|
|
106
|
+
logic real native uses, so see this README's "What this is" section above
|
|
107
|
+
for what's covered.
|
|
108
|
+
|
|
79
109
|
## What this is
|
|
80
110
|
|
|
81
111
|
- A JSX runtime (`@kbach/react-native/jsx-runtime`) that intercepts
|
|
82
|
-
`className`/`kb` props on every element and calls into the native
|
|
83
|
-
to resolve them to a style object,
|
|
112
|
+
`className`/`kb` props on every element and calls into the native/WASM
|
|
113
|
+
bridge (whichever platform you're on) to resolve them to a style object,
|
|
114
|
+
synchronously, on every render.
|
|
84
115
|
- A Babel plugin (`@kbach/react-native/babel-plugin`) that routes your
|
|
85
116
|
app's own files (never `node_modules`) through that JSX runtime via a
|
|
86
117
|
per-file `@jsxImportSource` pragma.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getGlobalDarkMode,
|
|
3
|
+
getGlobalThemeMode,
|
|
4
|
+
seedDefaultMode,
|
|
5
|
+
setGlobalThemeMode,
|
|
6
|
+
subscribeGlobalDarkMode,
|
|
7
|
+
toggleGlobalDarkMode
|
|
8
|
+
} from "./chunk-AEVOM2NB.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
|
+
};
|
|
@@ -357,25 +357,15 @@ function seedDefaultMode(fallback) {
|
|
|
357
357
|
notify();
|
|
358
358
|
}
|
|
359
359
|
|
|
360
|
-
// src/nativeBridge.ts
|
|
361
|
-
import { Dimensions, TurboModuleRegistry } from "react-native";
|
|
362
|
-
function resolveStyle(classString, pressed = false) {
|
|
363
|
-
const KbachModule = TurboModuleRegistry.getEnforcing("KbachModule");
|
|
364
|
-
const colorScheme = getGlobalDarkMode() ? "dark" : "light";
|
|
365
|
-
const width = Dimensions.get("window").width;
|
|
366
|
-
const json = KbachModule.resolveStyle(classString, getThemeJson(), colorScheme, pressed, width);
|
|
367
|
-
return JSON.parse(json);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
360
|
export {
|
|
371
361
|
defaultTheme,
|
|
372
362
|
setTheme,
|
|
373
363
|
getTheme,
|
|
364
|
+
getThemeJson,
|
|
374
365
|
getGlobalDarkMode,
|
|
375
366
|
getGlobalThemeMode,
|
|
376
367
|
setGlobalThemeMode,
|
|
377
368
|
toggleGlobalDarkMode,
|
|
378
369
|
subscribeGlobalDarkMode,
|
|
379
|
-
seedDefaultMode
|
|
380
|
-
resolveStyle
|
|
370
|
+
seedDefaultMode
|
|
381
371
|
};
|
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
resolveStyle
|
|
3
|
-
} from "./chunk-7KUNKZBM.mjs";
|
|
4
|
-
|
|
5
|
-
// src/jsx-runtime.tsx
|
|
1
|
+
// src/jsxRuntimeCore.ts
|
|
6
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
3
|
import { Fragment } from "react";
|
|
8
4
|
import { Pressable } from "react-native";
|
|
9
5
|
function makeElement(isStaticChildren, type, props, key) {
|
|
10
6
|
return (isStaticChildren ? _jsxs : _jsx)(type, props, key);
|
|
11
7
|
}
|
|
12
|
-
function resolvedStyleFor(classStrRaw, userStyle, pressed) {
|
|
8
|
+
function resolvedStyleFor(resolveStyle, classStrRaw, userStyle, pressed) {
|
|
13
9
|
const resolved = resolveStyle(classStrRaw, pressed);
|
|
14
10
|
if (userStyle === void 0) {
|
|
15
11
|
return resolved;
|
|
@@ -19,7 +15,7 @@ function resolvedStyleFor(classStrRaw, userStyle, pressed) {
|
|
|
19
15
|
}
|
|
20
16
|
return [resolved, userStyle];
|
|
21
17
|
}
|
|
22
|
-
function processElement(type, rawProps, key, isStaticChildren) {
|
|
18
|
+
function processElement(resolveStyle, type, rawProps, key, isStaticChildren) {
|
|
23
19
|
if (type === null || typeof type === "symbol" || !rawProps) {
|
|
24
20
|
return makeElement(isStaticChildren, type, rawProps ?? {}, key);
|
|
25
21
|
}
|
|
@@ -27,18 +23,31 @@ function processElement(type, rawProps, key, isStaticChildren) {
|
|
|
27
23
|
if (typeof classStrRaw !== "string") {
|
|
28
24
|
return makeElement(isStaticChildren, type, rawProps, key);
|
|
29
25
|
}
|
|
30
|
-
const finalStyle = type === Pressable ? (state) => resolvedStyleFor(classStrRaw, userStyle, state.pressed) : resolvedStyleFor(classStrRaw, userStyle, false);
|
|
26
|
+
const finalStyle = type === Pressable ? (state) => resolvedStyleFor(resolveStyle, classStrRaw, userStyle, state.pressed) : resolvedStyleFor(resolveStyle, classStrRaw, userStyle, false);
|
|
31
27
|
return makeElement(isStaticChildren, type, { ...rest, style: finalStyle }, key);
|
|
32
28
|
}
|
|
33
|
-
function
|
|
34
|
-
|
|
29
|
+
function createJsxFunctions(resolveStyle) {
|
|
30
|
+
function jsx(type, props, key) {
|
|
31
|
+
return processElement(resolveStyle, type, props, key, false);
|
|
32
|
+
}
|
|
33
|
+
function jsxs(type, props, key) {
|
|
34
|
+
return processElement(resolveStyle, type, props, key, true);
|
|
35
|
+
}
|
|
36
|
+
return { jsx, jsxs };
|
|
35
37
|
}
|
|
36
|
-
function
|
|
37
|
-
return
|
|
38
|
+
function createJsxDEV(jsx, jsxs) {
|
|
39
|
+
return function jsxDEV(type, props, key, isStaticChildren, source, self) {
|
|
40
|
+
const element = (isStaticChildren ? jsxs : jsx)(type, props, key);
|
|
41
|
+
if (source && element && typeof element === "object" && Object.isExtensible(element)) {
|
|
42
|
+
element._source = source;
|
|
43
|
+
element._self = self;
|
|
44
|
+
}
|
|
45
|
+
return element;
|
|
46
|
+
};
|
|
38
47
|
}
|
|
39
48
|
|
|
40
49
|
export {
|
|
41
50
|
Fragment,
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
createJsxFunctions,
|
|
52
|
+
createJsxDEV
|
|
44
53
|
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getGlobalDarkMode,
|
|
3
|
+
getThemeJson
|
|
4
|
+
} from "./chunk-AEVOM2NB.mjs";
|
|
5
|
+
|
|
6
|
+
// src/nativeBridge.ts
|
|
7
|
+
import { Dimensions, TurboModuleRegistry } from "react-native";
|
|
8
|
+
function resolveStyle(classString, pressed = false) {
|
|
9
|
+
const KbachModule = TurboModuleRegistry.getEnforcing("KbachModule");
|
|
10
|
+
const colorScheme = getGlobalDarkMode() ? "dark" : "light";
|
|
11
|
+
const width = Dimensions.get("window").width;
|
|
12
|
+
const json = KbachModule.resolveStyle(classString, getThemeJson(), colorScheme, pressed, width);
|
|
13
|
+
return JSON.parse(json);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
resolveStyle
|
|
18
|
+
};
|