@kbach/ui 0.1.0-beta.0 → 0.1.0-beta.2
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/KBACH.md +14 -6
- package/README.md +35 -303
- package/dist/{chunk-BPCFICND.mjs → chunk-LY753XBX.mjs} +90 -18
- package/dist/{chunk-UE54W6ZG.mjs → chunk-UVXTWGHQ.mjs} +1 -1
- package/dist/core/index.d.ts +74 -16
- package/dist/core/index.js +84 -18
- package/dist/index.d.ts +5 -11
- package/dist/index.js +103 -94
- package/dist/index.mjs +14 -4
- package/dist/jsx-dev-runtime.mjs +2 -2
- package/dist/jsx-runtime.mjs +2 -2
- package/dist/native.js +0 -21
- package/dist/native.mjs +49 -0
- package/dist/vite-plugin.js +105 -15
- package/dist/vite-plugin.mjs +105 -15
- package/kbach-ui.md +14 -3
- package/package.json +3 -2
- package/scripts/postinstall.js +17 -0
- package/dist/native.d.ts +0 -314
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
2
8
|
|
|
3
9
|
// src/core/platform.ts
|
|
4
10
|
var isWeb = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
|
|
@@ -19,7 +25,10 @@ function toNativeValue(raw) {
|
|
|
19
25
|
return raw;
|
|
20
26
|
}
|
|
21
27
|
function escapeCSSSelector(cls) {
|
|
22
|
-
|
|
28
|
+
const escaped = cls.replace(/[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g, "\\$&");
|
|
29
|
+
if (!/^[0-9]/.test(escaped)) return escaped;
|
|
30
|
+
const hex = escaped.charCodeAt(0).toString(16);
|
|
31
|
+
return `\\${hex} ${escaped.slice(1)}`;
|
|
23
32
|
}
|
|
24
33
|
|
|
25
34
|
// src/core/devWarn.ts
|
|
@@ -1857,9 +1866,23 @@ var effectResolvers = {
|
|
|
1857
1866
|
return isNaN(n) ? null : { opacity: n > 1 ? n / 100 : n };
|
|
1858
1867
|
},
|
|
1859
1868
|
// ── Shadow ─────────────────────────────────────────────────────────────────
|
|
1869
|
+
// Presets carry BOTH a web `boxShadow` string and native's shadow*/elevation
|
|
1870
|
+
// properties (theme.ts) — picked apart here rather than left for
|
|
1871
|
+
// styleValueToCSS's RN_ONLY_PROPS filter to sort out, since shadowOffset is
|
|
1872
|
+
// an object (filtered out as "not a flat style value" regardless of
|
|
1873
|
+
// platform) and boxShadow would otherwise leak into native's style prop as
|
|
1874
|
+
// an RN doesn't understand (silently ignored there, but better to not hand
|
|
1875
|
+
// native a property that was never meant for it — same branch-on-platform
|
|
1876
|
+
// pattern as bg-opacity/transition/animate above).
|
|
1860
1877
|
shadow: ({ value }, { shadow }) => {
|
|
1861
1878
|
const key = value === "" ? "DEFAULT" : value;
|
|
1862
|
-
|
|
1879
|
+
const preset = shadow[key];
|
|
1880
|
+
if (!preset) return null;
|
|
1881
|
+
if (getEffectiveIsWeb()) {
|
|
1882
|
+
return preset.boxShadow !== void 0 ? { boxShadow: preset.boxShadow } : null;
|
|
1883
|
+
}
|
|
1884
|
+
const { boxShadow, ...native } = preset;
|
|
1885
|
+
return Object.keys(native).length > 0 ? native : null;
|
|
1863
1886
|
},
|
|
1864
1887
|
// ── Animations (CSS keyframe animations, web-only) ────────────────────────
|
|
1865
1888
|
// Each variant injects a @keyframes rule once via the __keyframe marker.
|
|
@@ -2738,55 +2761,78 @@ var defaultTheme = {
|
|
|
2738
2761
|
none: "none"
|
|
2739
2762
|
// CSS: flex: none = 0 0 auto; native: mapped to 0
|
|
2740
2763
|
},
|
|
2764
|
+
// Each preset carries BOTH React Native's shadow*/elevation properties
|
|
2765
|
+
// (used on native) and a `boxShadow` string (used on web) — see the
|
|
2766
|
+
// `shadow` resolver in resolvers/effects.ts, which picks one or the other
|
|
2767
|
+
// via getEffectiveIsWeb() and strips the rest. The boxShadow values are
|
|
2768
|
+
// Tailwind's own default shadow scale verbatim (same offsets/blur/spread/
|
|
2769
|
+
// opacity), so shadow-sm/md/lg/xl/2xl look the same as their Tailwind
|
|
2770
|
+
// counterparts on web — the native shadowOffset/shadowRadius/shadowOpacity
|
|
2771
|
+
// numbers were tuned independently for RN's elevation model and don't need
|
|
2772
|
+
// to (and can't exactly) match the CSS values pixel-for-pixel.
|
|
2741
2773
|
shadow: {
|
|
2742
2774
|
sm: {
|
|
2743
2775
|
shadowColor: "#000",
|
|
2744
2776
|
shadowOffset: { width: 0, height: 1 },
|
|
2745
2777
|
shadowOpacity: 0.05,
|
|
2746
2778
|
shadowRadius: 2,
|
|
2747
|
-
elevation: 1
|
|
2779
|
+
elevation: 1,
|
|
2780
|
+
boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)"
|
|
2748
2781
|
},
|
|
2749
2782
|
DEFAULT: {
|
|
2750
2783
|
shadowColor: "#000",
|
|
2751
2784
|
shadowOffset: { width: 0, height: 2 },
|
|
2752
2785
|
shadowOpacity: 0.1,
|
|
2753
2786
|
shadowRadius: 4,
|
|
2754
|
-
elevation: 2
|
|
2787
|
+
elevation: 2,
|
|
2788
|
+
boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)"
|
|
2755
2789
|
},
|
|
2756
2790
|
md: {
|
|
2757
2791
|
shadowColor: "#000",
|
|
2758
2792
|
shadowOffset: { width: 0, height: 4 },
|
|
2759
2793
|
shadowOpacity: 0.1,
|
|
2760
2794
|
shadowRadius: 8,
|
|
2761
|
-
elevation: 3
|
|
2795
|
+
elevation: 3,
|
|
2796
|
+
boxShadow: "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)"
|
|
2762
2797
|
},
|
|
2763
2798
|
lg: {
|
|
2764
2799
|
shadowColor: "#000",
|
|
2765
2800
|
shadowOffset: { width: 0, height: 8 },
|
|
2766
2801
|
shadowOpacity: 0.1,
|
|
2767
2802
|
shadowRadius: 15,
|
|
2768
|
-
elevation: 4
|
|
2803
|
+
elevation: 4,
|
|
2804
|
+
boxShadow: "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)"
|
|
2769
2805
|
},
|
|
2770
2806
|
xl: {
|
|
2771
2807
|
shadowColor: "#000",
|
|
2772
2808
|
shadowOffset: { width: 0, height: 16 },
|
|
2773
2809
|
shadowOpacity: 0.1,
|
|
2774
2810
|
shadowRadius: 24,
|
|
2775
|
-
elevation: 6
|
|
2811
|
+
elevation: 6,
|
|
2812
|
+
boxShadow: "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)"
|
|
2776
2813
|
},
|
|
2777
2814
|
"2xl": {
|
|
2778
2815
|
shadowColor: "#000",
|
|
2779
2816
|
shadowOffset: { width: 0, height: 24 },
|
|
2780
2817
|
shadowOpacity: 0.25,
|
|
2781
2818
|
shadowRadius: 48,
|
|
2782
|
-
elevation: 8
|
|
2819
|
+
elevation: 8,
|
|
2820
|
+
boxShadow: "0 25px 50px -12px rgb(0 0 0 / 0.25)"
|
|
2821
|
+
},
|
|
2822
|
+
// Web-only — React Native has no inset-shadow equivalent (shadow*/
|
|
2823
|
+
// elevation only ever draw an outer shadow), so the native side of this
|
|
2824
|
+
// preset is intentionally empty rather than approximating something RN
|
|
2825
|
+
// can't actually do.
|
|
2826
|
+
inner: {
|
|
2827
|
+
boxShadow: "inset 0 2px 4px 0 rgb(0 0 0 / 0.05)"
|
|
2783
2828
|
},
|
|
2784
2829
|
none: {
|
|
2785
2830
|
shadowColor: "transparent",
|
|
2786
2831
|
shadowOffset: { width: 0, height: 0 },
|
|
2787
2832
|
shadowOpacity: 0,
|
|
2788
2833
|
shadowRadius: 0,
|
|
2789
|
-
elevation: 0
|
|
2834
|
+
elevation: 0,
|
|
2835
|
+
boxShadow: "0 0 #0000"
|
|
2790
2836
|
}
|
|
2791
2837
|
},
|
|
2792
2838
|
screens: {
|
|
@@ -2900,8 +2946,25 @@ var BASE_RESET = [
|
|
|
2900
2946
|
"table { border-collapse: collapse; border-spacing: 0; }"
|
|
2901
2947
|
].join("\n");
|
|
2902
2948
|
|
|
2949
|
+
// src/core/globalSingleton.ts
|
|
2950
|
+
function getGlobalSingleton(key, create) {
|
|
2951
|
+
const symbolKey = /* @__PURE__ */ Symbol.for(`__kbach_${key}__`);
|
|
2952
|
+
const g = globalThis;
|
|
2953
|
+
let value = g[symbolKey];
|
|
2954
|
+
if (value === void 0) {
|
|
2955
|
+
value = create();
|
|
2956
|
+
g[symbolKey] = value;
|
|
2957
|
+
}
|
|
2958
|
+
return value;
|
|
2959
|
+
}
|
|
2960
|
+
|
|
2903
2961
|
// src/core/responsiveStore.ts
|
|
2904
|
-
var store =
|
|
2962
|
+
var store = getGlobalSingleton("responsiveStore", () => ({
|
|
2963
|
+
width: 0,
|
|
2964
|
+
notifiedWidth: 0,
|
|
2965
|
+
screens: {},
|
|
2966
|
+
listeners: /* @__PURE__ */ new Set()
|
|
2967
|
+
}));
|
|
2905
2968
|
function syncGlobalWidth(width) {
|
|
2906
2969
|
store.width = width;
|
|
2907
2970
|
}
|
|
@@ -2959,12 +3022,12 @@ function getSortedEntries(resolved) {
|
|
|
2959
3022
|
}
|
|
2960
3023
|
return sorted;
|
|
2961
3024
|
}
|
|
2962
|
-
var
|
|
3025
|
+
var _fontFamilyHolder = getGlobalSingleton("defaultFontFamily", () => ({ value: void 0 }));
|
|
2963
3026
|
function setDefaultFontFamily(font) {
|
|
2964
|
-
|
|
3027
|
+
_fontFamilyHolder.value = font;
|
|
2965
3028
|
}
|
|
2966
3029
|
function getDefaultFontFamily() {
|
|
2967
|
-
return
|
|
3030
|
+
return _fontFamilyHolder.value;
|
|
2968
3031
|
}
|
|
2969
3032
|
var _styleEl = null;
|
|
2970
3033
|
var _ruleIndexByKey = /* @__PURE__ */ new Map();
|
|
@@ -2997,12 +3060,12 @@ function evictInjectedRule(rule) {
|
|
|
2997
3060
|
}
|
|
2998
3061
|
}
|
|
2999
3062
|
var _injectedRules = new LRUCache(5e4, evictInjectedRule);
|
|
3000
|
-
var
|
|
3063
|
+
var _runtimeCSSHolder = getGlobalSingleton("runtimeCSSDisabled", () => ({ value: false }));
|
|
3001
3064
|
function disableRuntimeCSS() {
|
|
3002
|
-
|
|
3065
|
+
_runtimeCSSHolder.value = true;
|
|
3003
3066
|
}
|
|
3004
3067
|
function isRuntimeCSSDisabled() {
|
|
3005
|
-
return
|
|
3068
|
+
return _runtimeCSSHolder.value;
|
|
3006
3069
|
}
|
|
3007
3070
|
function getStyleEl() {
|
|
3008
3071
|
if (_styleEl) return _styleEl;
|
|
@@ -3362,7 +3425,10 @@ function deepMerge(base, override) {
|
|
|
3362
3425
|
}
|
|
3363
3426
|
return result;
|
|
3364
3427
|
}
|
|
3365
|
-
var configStore =
|
|
3428
|
+
var configStore = getGlobalSingleton("configStore", () => ({
|
|
3429
|
+
resolved: null,
|
|
3430
|
+
listeners: /* @__PURE__ */ new Set()
|
|
3431
|
+
}));
|
|
3366
3432
|
function getConfig() {
|
|
3367
3433
|
if (configStore.resolved) return configStore.resolved;
|
|
3368
3434
|
configStore.resolved = buildConfig({});
|
|
@@ -3531,7 +3597,11 @@ function initConfig(userConfig) {
|
|
|
3531
3597
|
}
|
|
3532
3598
|
|
|
3533
3599
|
// src/core/darkModeStore.ts
|
|
3534
|
-
var store2 =
|
|
3600
|
+
var store2 = getGlobalSingleton("darkModeStore", () => ({
|
|
3601
|
+
isDark: false,
|
|
3602
|
+
notifiedIsDark: false,
|
|
3603
|
+
subscribers: /* @__PURE__ */ new Set()
|
|
3604
|
+
}));
|
|
3535
3605
|
function syncGlobalDarkMode(isDark) {
|
|
3536
3606
|
store2.isDark = isDark;
|
|
3537
3607
|
}
|
|
@@ -4128,6 +4198,8 @@ var InteractiveWrapper = forwardRef(
|
|
|
4128
4198
|
InteractiveWrapper.displayName = "Kbach.InteractiveWrapper";
|
|
4129
4199
|
|
|
4130
4200
|
export {
|
|
4201
|
+
__require,
|
|
4202
|
+
getGlobalSingleton,
|
|
4131
4203
|
isModeAwareColor,
|
|
4132
4204
|
isWeb,
|
|
4133
4205
|
isNative,
|
package/dist/core/index.d.ts
CHANGED
|
@@ -188,6 +188,43 @@ interface ResolvedConfig {
|
|
|
188
188
|
plugins: Array<(api: PluginAPI) => void>;
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Returns a value that's the SAME object across every physical copy of core/
|
|
193
|
+
* a bundler might produce for a single running process — not just across
|
|
194
|
+
* dist/index.js / dist/jsx-runtime.js / dist/jsx-dev-runtime.js (the CJS
|
|
195
|
+
* build, which already shares one real dist/core/index.js via
|
|
196
|
+
* externalization — see tsup.config.ts's CORE_EXTERNAL), but ALSO across
|
|
197
|
+
* that CJS build and the separate ESM build (dist/index.mjs etc., which
|
|
198
|
+
* inlines its own copy of core/ — a deliberate, accepted trade-off for
|
|
199
|
+
* Rollup/Vite compatibility, see context.tsx's ThemeContext comment for the
|
|
200
|
+
* full story).
|
|
201
|
+
*
|
|
202
|
+
* Confirmed as a real, live bug (not just theoretical), back when ThemeProvider
|
|
203
|
+
* still had a separate native-specific wrapper at the now-removed
|
|
204
|
+
* '@kbach/ui/native' subpath (see native/index.ts): a React Native app that
|
|
205
|
+
* reached @kbach/ui through more than one physical dist file — e.g.
|
|
206
|
+
* `import { ThemeProvider } from '@kbach/ui/native'` in one file and
|
|
207
|
+
* `import { useIsDark } from '@kbach/ui'` in another, which Metro can resolve
|
|
208
|
+
* to DIFFERENT dist files per call site — got dark:/light: classes silently
|
|
209
|
+
* inert from one instance while useIsDark() read a different,
|
|
210
|
+
* correctly-updating instance (or vice versa), depending on which module
|
|
211
|
+
* each half of the app happened to load through. ThemeProvider now auto-
|
|
212
|
+
* detects native itself (no separate wrapper/import path to diverge on), but
|
|
213
|
+
* the underlying dual-build split (ESM for Rollup/Vite, CJS for Metro) is
|
|
214
|
+
* still real, so darkModeStore/responsiveStore/config/etc. still need this —
|
|
215
|
+
* jsx-runtime.tsx, for instance, still reaches core/ as its own call site.
|
|
216
|
+
* See RULES.md rule 3: this is exactly the "globalThis-singleton hack" it
|
|
217
|
+
* allows when a real fix (one guaranteed physical module) isn't available —
|
|
218
|
+
* which it isn't here, short of giving up either Rollup or Metro compatibility
|
|
219
|
+
* entirely.
|
|
220
|
+
*
|
|
221
|
+
* Keyed on Symbol.for() (the well-known global symbol registry, shared
|
|
222
|
+
* across realms/module copies by spec) rather than a plain string property,
|
|
223
|
+
* so this can never collide with anything else that happens to touch
|
|
224
|
+
* globalThis.
|
|
225
|
+
*/
|
|
226
|
+
declare function getGlobalSingleton<T>(key: string, create: () => T): T;
|
|
227
|
+
|
|
191
228
|
/**
|
|
192
229
|
* True for a mode-aware color pair (`{ light, dark }`), as opposed to a plain
|
|
193
230
|
* hex/rgb/alias string. Shared by config.ts (alias-chain resolution),
|
|
@@ -1036,6 +1073,7 @@ declare const defaultTheme: {
|
|
|
1036
1073
|
shadowOpacity: number;
|
|
1037
1074
|
shadowRadius: number;
|
|
1038
1075
|
elevation: number;
|
|
1076
|
+
boxShadow: string;
|
|
1039
1077
|
};
|
|
1040
1078
|
DEFAULT: {
|
|
1041
1079
|
shadowColor: string;
|
|
@@ -1046,6 +1084,7 @@ declare const defaultTheme: {
|
|
|
1046
1084
|
shadowOpacity: number;
|
|
1047
1085
|
shadowRadius: number;
|
|
1048
1086
|
elevation: number;
|
|
1087
|
+
boxShadow: string;
|
|
1049
1088
|
};
|
|
1050
1089
|
md: {
|
|
1051
1090
|
shadowColor: string;
|
|
@@ -1056,6 +1095,7 @@ declare const defaultTheme: {
|
|
|
1056
1095
|
shadowOpacity: number;
|
|
1057
1096
|
shadowRadius: number;
|
|
1058
1097
|
elevation: number;
|
|
1098
|
+
boxShadow: string;
|
|
1059
1099
|
};
|
|
1060
1100
|
lg: {
|
|
1061
1101
|
shadowColor: string;
|
|
@@ -1066,6 +1106,7 @@ declare const defaultTheme: {
|
|
|
1066
1106
|
shadowOpacity: number;
|
|
1067
1107
|
shadowRadius: number;
|
|
1068
1108
|
elevation: number;
|
|
1109
|
+
boxShadow: string;
|
|
1069
1110
|
};
|
|
1070
1111
|
xl: {
|
|
1071
1112
|
shadowColor: string;
|
|
@@ -1076,6 +1117,7 @@ declare const defaultTheme: {
|
|
|
1076
1117
|
shadowOpacity: number;
|
|
1077
1118
|
shadowRadius: number;
|
|
1078
1119
|
elevation: number;
|
|
1120
|
+
boxShadow: string;
|
|
1079
1121
|
};
|
|
1080
1122
|
'2xl': {
|
|
1081
1123
|
shadowColor: string;
|
|
@@ -1086,6 +1128,10 @@ declare const defaultTheme: {
|
|
|
1086
1128
|
shadowOpacity: number;
|
|
1087
1129
|
shadowRadius: number;
|
|
1088
1130
|
elevation: number;
|
|
1131
|
+
boxShadow: string;
|
|
1132
|
+
};
|
|
1133
|
+
inner: {
|
|
1134
|
+
boxShadow: string;
|
|
1089
1135
|
};
|
|
1090
1136
|
none: {
|
|
1091
1137
|
shadowColor: string;
|
|
@@ -1096,6 +1142,7 @@ declare const defaultTheme: {
|
|
|
1096
1142
|
shadowOpacity: number;
|
|
1097
1143
|
shadowRadius: number;
|
|
1098
1144
|
elevation: number;
|
|
1145
|
+
boxShadow: string;
|
|
1099
1146
|
};
|
|
1100
1147
|
};
|
|
1101
1148
|
screens: {
|
|
@@ -1147,6 +1194,15 @@ declare function toNativeValue(raw: string): string | number;
|
|
|
1147
1194
|
/**
|
|
1148
1195
|
* Escape a class name for use inside a CSS selector.
|
|
1149
1196
|
* e.g. 'bg-[#fff]' → 'bg-\\[\\#fff\\]'
|
|
1197
|
+
*
|
|
1198
|
+
* Also escapes a leading digit: a CSS identifier can't start with an
|
|
1199
|
+
* unescaped digit — `.2xl\:text-lg { ... }` is invalid CSS and every browser
|
|
1200
|
+
* silently fails to match it, which is exactly what happens for a screen/
|
|
1201
|
+
* breakpoint literally named "2xl" (or any other numeric-leading class).
|
|
1202
|
+
* Escaped per the CSS spec — a backslash plus the character's hex code
|
|
1203
|
+
* point, followed by one space to terminate the hex escape (always safe to
|
|
1204
|
+
* include, and needed here since "2xl"'s next character "x" isn't itself a
|
|
1205
|
+
* hex digit that could otherwise be read as part of the escape by mistake).
|
|
1150
1206
|
*/
|
|
1151
1207
|
declare function escapeCSSSelector(cls: string): string;
|
|
1152
1208
|
|
|
@@ -1271,14 +1327,17 @@ declare function initConfig(userConfig: FrameworkConfig): void;
|
|
|
1271
1327
|
* synchronously during render without needing context. ThemeProvider writes to it;
|
|
1272
1328
|
* DarkWrapper / InteractiveWrapper subscribe via useSyncExternalStore.
|
|
1273
1329
|
*
|
|
1274
|
-
*
|
|
1275
|
-
*
|
|
1276
|
-
*
|
|
1277
|
-
*
|
|
1278
|
-
*
|
|
1279
|
-
*
|
|
1280
|
-
*
|
|
1281
|
-
*
|
|
1330
|
+
* Backed by getGlobalSingleton() (globalThis-keyed), not a plain module-level
|
|
1331
|
+
* variable — core/ being built as its own shared dist/core/ entry (see
|
|
1332
|
+
* tsup.config.ts's CORE_EXTERNAL) only guarantees one instance across the CJS
|
|
1333
|
+
* build (dist/index.js, dist/jsx-runtime.js, dist/jsx-dev-runtime.js). The
|
|
1334
|
+
* separate ESM build (dist/index.mjs etc., required for Rollup/Vite — see
|
|
1335
|
+
* context.tsx's ThemeContext comment) inlines its own copy, and Metro can
|
|
1336
|
+
* route different call sites in the same app through either one. Confirmed
|
|
1337
|
+
* as a real bug this way: dark:/light: classes went completely inert (while
|
|
1338
|
+
* useIsDark() kept reading correctly) in an app mixing
|
|
1339
|
+
* `import ... from '@kbach/ui/native'` and `import ... from '@kbach/ui'`
|
|
1340
|
+
* — the two ended up on different physical copies of this store.
|
|
1282
1341
|
*/
|
|
1283
1342
|
/**
|
|
1284
1343
|
* Silently update isDark without notifying subscribers.
|
|
@@ -1308,13 +1367,12 @@ declare function subscribeGlobalDarkMode(callback: () => void): () => void;
|
|
|
1308
1367
|
/**
|
|
1309
1368
|
* Global responsive width store.
|
|
1310
1369
|
*
|
|
1311
|
-
*
|
|
1312
|
-
*
|
|
1313
|
-
*
|
|
1314
|
-
*
|
|
1315
|
-
*
|
|
1316
|
-
*
|
|
1317
|
-
* this module to begin with — a plain module-level object is enough.
|
|
1370
|
+
* Backed by getGlobalSingleton() (globalThis-keyed) — see
|
|
1371
|
+
* darkModeStore.ts's header comment for why a plain module-level object
|
|
1372
|
+
* isn't enough: core/ being its own shared dist/core/ entry only covers the
|
|
1373
|
+
* CJS build (dist/index.js, dist/jsx-runtime.js, dist/jsx-dev-runtime.js);
|
|
1374
|
+
* the separate ESM build inlines its own copy, and Metro can route
|
|
1375
|
+
* different call sites in the same app through either one.
|
|
1318
1376
|
*/
|
|
1319
1377
|
type WidthListener = () => void;
|
|
1320
1378
|
/** Synchronous write for use in the render phase. */
|
|
@@ -1343,4 +1401,4 @@ declare function subscribeGlobalWidth(listener: WidthListener): () => void;
|
|
|
1343
1401
|
*/
|
|
1344
1402
|
declare function getActiveBreakpoints(width?: number, screens?: Record<string, number>): Set<string>;
|
|
1345
1403
|
|
|
1346
|
-
export { BASE_RESET, type ColorShades, type ColorValue, type DarkMode, type DefaultColorName, type DefaultSpacingKey, type FrameworkConfig, LRUCache, type ParsedClass, type Platform, type PluginAPI, RESET_STYLE_ID, type ResolvedConfig, type ResolvedStyle, type StyleValue, type ThemeColors, type ThemeConfig, type ThemeMode, type ThemeSpacing, buildConfig, clearCache, clearPluginModifiers, defaultColors, defaultTheme, disableRuntimeCSS, escapeCSSSelector, expandModeAwareColorClasses, flatten, generateClassCSS, generateKbachTypesDts, getActiveBreakpoints, getAllModifierNames, getBuiltinStandaloneNames, getBuiltinUtilityPrefixes, getConfig, getDefaultFontFamily, getEffectiveIsWeb, getGlobalDarkMode, getGlobalScreens, getGlobalWidth, getInteractiveModifiers, getModeModifiers, getModifier, getResponsiveModifiers, initConfig, isKnownModifier, isKnownUtility, isModeAwareColor, isNative, isRuntimeCSSDisabled, isWeb, kbachWarn, matchModifier, normalizeClassString, onConfigChange, parseClass, parseClasses, parseHexRgb, registerModifier, resetConfig, resolve, resolveColor, resolveSizing, resolveSpacing, resolveUtility, setDefaultFontFamily, setGlobalDarkMode, setGlobalWidth, setResolveTarget, splitClassTokens, subscribeGlobalDarkMode, subscribeGlobalWidth, syncGlobalDarkMode, syncGlobalScreens, syncGlobalWidth, toNativeValue, updateConfig };
|
|
1404
|
+
export { BASE_RESET, type ColorShades, type ColorValue, type DarkMode, type DefaultColorName, type DefaultSpacingKey, type FrameworkConfig, LRUCache, type ParsedClass, type Platform, type PluginAPI, RESET_STYLE_ID, type ResolvedConfig, type ResolvedStyle, type StyleValue, type ThemeColors, type ThemeConfig, type ThemeMode, type ThemeSpacing, buildConfig, clearCache, clearPluginModifiers, defaultColors, defaultTheme, disableRuntimeCSS, escapeCSSSelector, expandModeAwareColorClasses, flatten, generateClassCSS, generateKbachTypesDts, getActiveBreakpoints, getAllModifierNames, getBuiltinStandaloneNames, getBuiltinUtilityPrefixes, getConfig, getDefaultFontFamily, getEffectiveIsWeb, getGlobalDarkMode, getGlobalScreens, getGlobalSingleton, getGlobalWidth, getInteractiveModifiers, getModeModifiers, getModifier, getResponsiveModifiers, initConfig, isKnownModifier, isKnownUtility, isModeAwareColor, isNative, isRuntimeCSSDisabled, isWeb, kbachWarn, matchModifier, normalizeClassString, onConfigChange, parseClass, parseClasses, parseHexRgb, registerModifier, resetConfig, resolve, resolveColor, resolveSizing, resolveSpacing, resolveUtility, setDefaultFontFamily, setGlobalDarkMode, setGlobalWidth, setResolveTarget, splitClassTokens, subscribeGlobalDarkMode, subscribeGlobalWidth, syncGlobalDarkMode, syncGlobalScreens, syncGlobalWidth, toNativeValue, updateConfig };
|
package/dist/core/index.js
CHANGED
|
@@ -43,6 +43,7 @@ __export(index_exports, {
|
|
|
43
43
|
getEffectiveIsWeb: () => getEffectiveIsWeb,
|
|
44
44
|
getGlobalDarkMode: () => getGlobalDarkMode,
|
|
45
45
|
getGlobalScreens: () => getGlobalScreens,
|
|
46
|
+
getGlobalSingleton: () => getGlobalSingleton,
|
|
46
47
|
getGlobalWidth: () => getGlobalWidth,
|
|
47
48
|
getInteractiveModifiers: () => getInteractiveModifiers,
|
|
48
49
|
getModeModifiers: () => getModeModifiers,
|
|
@@ -84,6 +85,18 @@ __export(index_exports, {
|
|
|
84
85
|
});
|
|
85
86
|
module.exports = __toCommonJS(index_exports);
|
|
86
87
|
|
|
88
|
+
// src/core/globalSingleton.ts
|
|
89
|
+
function getGlobalSingleton(key, create) {
|
|
90
|
+
const symbolKey = /* @__PURE__ */ Symbol.for(`__kbach_${key}__`);
|
|
91
|
+
const g = globalThis;
|
|
92
|
+
let value = g[symbolKey];
|
|
93
|
+
if (value === void 0) {
|
|
94
|
+
value = create();
|
|
95
|
+
g[symbolKey] = value;
|
|
96
|
+
}
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
|
|
87
100
|
// src/core/colorValue.ts
|
|
88
101
|
function isModeAwareColor(v) {
|
|
89
102
|
return typeof v === "object" && v !== null && "light" in v && "dark" in v;
|
|
@@ -113,7 +126,10 @@ function toNativeValue(raw) {
|
|
|
113
126
|
return raw;
|
|
114
127
|
}
|
|
115
128
|
function escapeCSSSelector(cls) {
|
|
116
|
-
|
|
129
|
+
const escaped = cls.replace(/[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g, "\\$&");
|
|
130
|
+
if (!/^[0-9]/.test(escaped)) return escaped;
|
|
131
|
+
const hex = escaped.charCodeAt(0).toString(16);
|
|
132
|
+
return `\\${hex} ${escaped.slice(1)}`;
|
|
117
133
|
}
|
|
118
134
|
|
|
119
135
|
// src/core/devWarn.ts
|
|
@@ -1945,9 +1961,23 @@ var effectResolvers = {
|
|
|
1945
1961
|
return isNaN(n) ? null : { opacity: n > 1 ? n / 100 : n };
|
|
1946
1962
|
},
|
|
1947
1963
|
// ── Shadow ─────────────────────────────────────────────────────────────────
|
|
1964
|
+
// Presets carry BOTH a web `boxShadow` string and native's shadow*/elevation
|
|
1965
|
+
// properties (theme.ts) — picked apart here rather than left for
|
|
1966
|
+
// styleValueToCSS's RN_ONLY_PROPS filter to sort out, since shadowOffset is
|
|
1967
|
+
// an object (filtered out as "not a flat style value" regardless of
|
|
1968
|
+
// platform) and boxShadow would otherwise leak into native's style prop as
|
|
1969
|
+
// an RN doesn't understand (silently ignored there, but better to not hand
|
|
1970
|
+
// native a property that was never meant for it — same branch-on-platform
|
|
1971
|
+
// pattern as bg-opacity/transition/animate above).
|
|
1948
1972
|
shadow: ({ value }, { shadow }) => {
|
|
1949
1973
|
const key = value === "" ? "DEFAULT" : value;
|
|
1950
|
-
|
|
1974
|
+
const preset = shadow[key];
|
|
1975
|
+
if (!preset) return null;
|
|
1976
|
+
if (getEffectiveIsWeb()) {
|
|
1977
|
+
return preset.boxShadow !== void 0 ? { boxShadow: preset.boxShadow } : null;
|
|
1978
|
+
}
|
|
1979
|
+
const { boxShadow, ...native } = preset;
|
|
1980
|
+
return Object.keys(native).length > 0 ? native : null;
|
|
1951
1981
|
},
|
|
1952
1982
|
// ── Animations (CSS keyframe animations, web-only) ────────────────────────
|
|
1953
1983
|
// Each variant injects a @keyframes rule once via the __keyframe marker.
|
|
@@ -2826,55 +2856,78 @@ var defaultTheme = {
|
|
|
2826
2856
|
none: "none"
|
|
2827
2857
|
// CSS: flex: none = 0 0 auto; native: mapped to 0
|
|
2828
2858
|
},
|
|
2859
|
+
// Each preset carries BOTH React Native's shadow*/elevation properties
|
|
2860
|
+
// (used on native) and a `boxShadow` string (used on web) — see the
|
|
2861
|
+
// `shadow` resolver in resolvers/effects.ts, which picks one or the other
|
|
2862
|
+
// via getEffectiveIsWeb() and strips the rest. The boxShadow values are
|
|
2863
|
+
// Tailwind's own default shadow scale verbatim (same offsets/blur/spread/
|
|
2864
|
+
// opacity), so shadow-sm/md/lg/xl/2xl look the same as their Tailwind
|
|
2865
|
+
// counterparts on web — the native shadowOffset/shadowRadius/shadowOpacity
|
|
2866
|
+
// numbers were tuned independently for RN's elevation model and don't need
|
|
2867
|
+
// to (and can't exactly) match the CSS values pixel-for-pixel.
|
|
2829
2868
|
shadow: {
|
|
2830
2869
|
sm: {
|
|
2831
2870
|
shadowColor: "#000",
|
|
2832
2871
|
shadowOffset: { width: 0, height: 1 },
|
|
2833
2872
|
shadowOpacity: 0.05,
|
|
2834
2873
|
shadowRadius: 2,
|
|
2835
|
-
elevation: 1
|
|
2874
|
+
elevation: 1,
|
|
2875
|
+
boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)"
|
|
2836
2876
|
},
|
|
2837
2877
|
DEFAULT: {
|
|
2838
2878
|
shadowColor: "#000",
|
|
2839
2879
|
shadowOffset: { width: 0, height: 2 },
|
|
2840
2880
|
shadowOpacity: 0.1,
|
|
2841
2881
|
shadowRadius: 4,
|
|
2842
|
-
elevation: 2
|
|
2882
|
+
elevation: 2,
|
|
2883
|
+
boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)"
|
|
2843
2884
|
},
|
|
2844
2885
|
md: {
|
|
2845
2886
|
shadowColor: "#000",
|
|
2846
2887
|
shadowOffset: { width: 0, height: 4 },
|
|
2847
2888
|
shadowOpacity: 0.1,
|
|
2848
2889
|
shadowRadius: 8,
|
|
2849
|
-
elevation: 3
|
|
2890
|
+
elevation: 3,
|
|
2891
|
+
boxShadow: "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)"
|
|
2850
2892
|
},
|
|
2851
2893
|
lg: {
|
|
2852
2894
|
shadowColor: "#000",
|
|
2853
2895
|
shadowOffset: { width: 0, height: 8 },
|
|
2854
2896
|
shadowOpacity: 0.1,
|
|
2855
2897
|
shadowRadius: 15,
|
|
2856
|
-
elevation: 4
|
|
2898
|
+
elevation: 4,
|
|
2899
|
+
boxShadow: "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)"
|
|
2857
2900
|
},
|
|
2858
2901
|
xl: {
|
|
2859
2902
|
shadowColor: "#000",
|
|
2860
2903
|
shadowOffset: { width: 0, height: 16 },
|
|
2861
2904
|
shadowOpacity: 0.1,
|
|
2862
2905
|
shadowRadius: 24,
|
|
2863
|
-
elevation: 6
|
|
2906
|
+
elevation: 6,
|
|
2907
|
+
boxShadow: "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)"
|
|
2864
2908
|
},
|
|
2865
2909
|
"2xl": {
|
|
2866
2910
|
shadowColor: "#000",
|
|
2867
2911
|
shadowOffset: { width: 0, height: 24 },
|
|
2868
2912
|
shadowOpacity: 0.25,
|
|
2869
2913
|
shadowRadius: 48,
|
|
2870
|
-
elevation: 8
|
|
2914
|
+
elevation: 8,
|
|
2915
|
+
boxShadow: "0 25px 50px -12px rgb(0 0 0 / 0.25)"
|
|
2916
|
+
},
|
|
2917
|
+
// Web-only — React Native has no inset-shadow equivalent (shadow*/
|
|
2918
|
+
// elevation only ever draw an outer shadow), so the native side of this
|
|
2919
|
+
// preset is intentionally empty rather than approximating something RN
|
|
2920
|
+
// can't actually do.
|
|
2921
|
+
inner: {
|
|
2922
|
+
boxShadow: "inset 0 2px 4px 0 rgb(0 0 0 / 0.05)"
|
|
2871
2923
|
},
|
|
2872
2924
|
none: {
|
|
2873
2925
|
shadowColor: "transparent",
|
|
2874
2926
|
shadowOffset: { width: 0, height: 0 },
|
|
2875
2927
|
shadowOpacity: 0,
|
|
2876
2928
|
shadowRadius: 0,
|
|
2877
|
-
elevation: 0
|
|
2929
|
+
elevation: 0,
|
|
2930
|
+
boxShadow: "0 0 #0000"
|
|
2878
2931
|
}
|
|
2879
2932
|
},
|
|
2880
2933
|
screens: {
|
|
@@ -2989,7 +3042,12 @@ var BASE_RESET = [
|
|
|
2989
3042
|
].join("\n");
|
|
2990
3043
|
|
|
2991
3044
|
// src/core/responsiveStore.ts
|
|
2992
|
-
var store =
|
|
3045
|
+
var store = getGlobalSingleton("responsiveStore", () => ({
|
|
3046
|
+
width: 0,
|
|
3047
|
+
notifiedWidth: 0,
|
|
3048
|
+
screens: {},
|
|
3049
|
+
listeners: /* @__PURE__ */ new Set()
|
|
3050
|
+
}));
|
|
2993
3051
|
function syncGlobalWidth(width) {
|
|
2994
3052
|
store.width = width;
|
|
2995
3053
|
}
|
|
@@ -3047,12 +3105,12 @@ function getSortedEntries(resolved) {
|
|
|
3047
3105
|
}
|
|
3048
3106
|
return sorted;
|
|
3049
3107
|
}
|
|
3050
|
-
var
|
|
3108
|
+
var _fontFamilyHolder = getGlobalSingleton("defaultFontFamily", () => ({ value: void 0 }));
|
|
3051
3109
|
function setDefaultFontFamily(font) {
|
|
3052
|
-
|
|
3110
|
+
_fontFamilyHolder.value = font;
|
|
3053
3111
|
}
|
|
3054
3112
|
function getDefaultFontFamily() {
|
|
3055
|
-
return
|
|
3113
|
+
return _fontFamilyHolder.value;
|
|
3056
3114
|
}
|
|
3057
3115
|
var _styleEl = null;
|
|
3058
3116
|
var _ruleIndexByKey = /* @__PURE__ */ new Map();
|
|
@@ -3085,12 +3143,12 @@ function evictInjectedRule(rule) {
|
|
|
3085
3143
|
}
|
|
3086
3144
|
}
|
|
3087
3145
|
var _injectedRules = new LRUCache(5e4, evictInjectedRule);
|
|
3088
|
-
var
|
|
3146
|
+
var _runtimeCSSHolder = getGlobalSingleton("runtimeCSSDisabled", () => ({ value: false }));
|
|
3089
3147
|
function disableRuntimeCSS() {
|
|
3090
|
-
|
|
3148
|
+
_runtimeCSSHolder.value = true;
|
|
3091
3149
|
}
|
|
3092
3150
|
function isRuntimeCSSDisabled() {
|
|
3093
|
-
return
|
|
3151
|
+
return _runtimeCSSHolder.value;
|
|
3094
3152
|
}
|
|
3095
3153
|
function getStyleEl() {
|
|
3096
3154
|
if (_styleEl) return _styleEl;
|
|
@@ -3460,7 +3518,10 @@ function deepMerge(base, override) {
|
|
|
3460
3518
|
}
|
|
3461
3519
|
return result;
|
|
3462
3520
|
}
|
|
3463
|
-
var configStore =
|
|
3521
|
+
var configStore = getGlobalSingleton("configStore", () => ({
|
|
3522
|
+
resolved: null,
|
|
3523
|
+
listeners: /* @__PURE__ */ new Set()
|
|
3524
|
+
}));
|
|
3464
3525
|
function getConfig() {
|
|
3465
3526
|
if (configStore.resolved) return configStore.resolved;
|
|
3466
3527
|
configStore.resolved = buildConfig({});
|
|
@@ -3629,7 +3690,11 @@ function initConfig(userConfig) {
|
|
|
3629
3690
|
}
|
|
3630
3691
|
|
|
3631
3692
|
// src/core/darkModeStore.ts
|
|
3632
|
-
var store2 =
|
|
3693
|
+
var store2 = getGlobalSingleton("darkModeStore", () => ({
|
|
3694
|
+
isDark: false,
|
|
3695
|
+
notifiedIsDark: false,
|
|
3696
|
+
subscribers: /* @__PURE__ */ new Set()
|
|
3697
|
+
}));
|
|
3633
3698
|
function syncGlobalDarkMode(isDark) {
|
|
3634
3699
|
store2.isDark = isDark;
|
|
3635
3700
|
}
|
|
@@ -3671,6 +3736,7 @@ function subscribeGlobalDarkMode(callback) {
|
|
|
3671
3736
|
getEffectiveIsWeb,
|
|
3672
3737
|
getGlobalDarkMode,
|
|
3673
3738
|
getGlobalScreens,
|
|
3739
|
+
getGlobalSingleton,
|
|
3674
3740
|
getGlobalWidth,
|
|
3675
3741
|
getInteractiveModifiers,
|
|
3676
3742
|
getModeModifiers,
|
package/dist/index.d.ts
CHANGED
|
@@ -19,9 +19,6 @@ interface ThemeContextValue {
|
|
|
19
19
|
/** The fully resolved framework config (theme values, darkMode strategy, etc.) */
|
|
20
20
|
config: ResolvedConfig;
|
|
21
21
|
}
|
|
22
|
-
declare global {
|
|
23
|
-
var __kbachThemeContext: Context<ThemeContextValue | null> | undefined;
|
|
24
|
-
}
|
|
25
22
|
declare const ThemeContext: Context<ThemeContextValue | null>;
|
|
26
23
|
declare function useTheme(): ThemeContextValue;
|
|
27
24
|
declare function useIsDark(): boolean;
|
|
@@ -136,22 +133,19 @@ interface ThemeProviderProps {
|
|
|
136
133
|
/**
|
|
137
134
|
* System color scheme for native `defaultMode="system"`.
|
|
138
135
|
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
136
|
+
* On React Native this is detected automatically via `useColorScheme()` —
|
|
137
|
+
* pass this prop only to override that (e.g. in tests, or Storybook).
|
|
141
138
|
*
|
|
142
139
|
* @example
|
|
143
140
|
* ```tsx
|
|
144
|
-
*
|
|
145
|
-
* const colorScheme = useColorScheme();
|
|
146
|
-
* <ThemeProvider defaultMode="system" colorScheme={colorScheme}>…</ThemeProvider>
|
|
141
|
+
* <ThemeProvider defaultMode="system" colorScheme="dark">…</ThemeProvider>
|
|
147
142
|
* ```
|
|
148
143
|
*/
|
|
149
144
|
colorScheme?: 'light' | 'dark' | null;
|
|
150
145
|
/**
|
|
151
146
|
* Current window/screen width in pixels for responsive breakpoints.
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
* automatically from `useWindowDimensions()`.
|
|
147
|
+
* Detected automatically on both web (`window.innerWidth`) and React
|
|
148
|
+
* Native (`useWindowDimensions()`) — pass this prop only to override that.
|
|
155
149
|
*/
|
|
156
150
|
windowWidth?: number;
|
|
157
151
|
/** Override the config (useful for per-tree config). Defaults to global getConfig(). */
|