@kbach/react 0.6.50 → 1.0.0-beta.0

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/dist/index.d.ts CHANGED
@@ -1,165 +1,26 @@
1
- import * as React from 'react';
2
- import React__default, { Context, ReactNode, ComponentType, ForwardRefExoticComponent } from 'react';
3
- import * as __core from './core';
4
- import { ThemeMode, ResolvedConfig, DefaultColorName, defaultColors, ThemeColors, DefaultSpacingKey, ThemeSpacing, FrameworkConfig, StyleValue, ResolvedStyle } from './core';
5
- export { DefaultColorName, DefaultSpacingKey, FrameworkConfig, ParsedClass, PluginAPI, ResolvedConfig, ResolvedStyle, StyleValue, ThemeConfig, ThemeMode, buildConfig, clearCache, defaultColors, defaultTheme, disableRuntimeCSS, flatten, generateKbachTypesDts, getConfig, initConfig, normalizeClassString, parseClass, parseClasses, resolve, setResolveTarget, splitClassTokens, updateConfig } from './core';
6
- export { r as registerWebElement } from './web-substitute-CfpYFnE7.js';
1
+ export { T as ThemeConfig, d as defaultTheme, g as getTheme, s as setTheme } from './theme-BKAb78KP.js';
2
+ import * as react from 'react';
3
+ import { ReactNode } from 'react';
7
4
 
8
- interface ThemeContextValue {
9
- /** The user-selected mode ('light' | 'dark' | 'system') */
10
- mode: ThemeMode;
11
- /** The effective resolved mode (never 'system') */
12
- resolvedMode: 'light' | 'dark';
13
- /** Convenience boolean */
14
- isDark: boolean;
15
- /** Change the theme mode programmatically */
16
- setMode: (mode: ThemeMode) => void;
17
- /** Toggle between light and dark (ignores system) */
18
- toggle: () => void;
19
- /** The fully resolved framework config (theme values, darkMode strategy, etc.) */
20
- config: ResolvedConfig;
21
- }
22
- declare global {
23
- var __kbachThemeContext: Context<ThemeContextValue | null> | undefined;
24
- }
25
- declare const ThemeContext: Context<ThemeContextValue | null>;
26
- declare function useTheme(): ThemeContextValue;
27
- declare function useIsDark(): boolean;
28
-
29
- interface ColorScale {
30
- /** `colors.blue[6]` → raw hex string */
31
- readonly [shade: number]: string;
32
- /** `colors.blue['6/50']` → shade 6 at 50% opacity */
33
- readonly [key: string]: string;
34
- }
35
5
  /**
36
- * Empty on purpose — augment it via declaration merging so `useColors()` (and
37
- * `useSpacing()`'s equivalent, `KbachCustomSpacing`) know about a project's
38
- * `kbach.config.js` colors without repeating a type parameter at every call
39
- * site. `kbach.config.js` is a plain runtime-loaded .js file, so TypeScript
40
- * can't see into it on its own — this is the same declaration-merging pattern
41
- * styled-components' `DefaultTheme` and i18next's resource typing use for the
42
- * identical problem. Put this in any .d.ts your tsconfig includes:
43
- *
44
- * ```ts
45
- * import '@kbach/react'; // or '@kbach/native' — either works, native re-exports react's types
46
- * declare module '@kbach/react' {
47
- * interface KbachCustomColors {
48
- * primary: string; // a flat color, like the built-in `white`/`black`
49
- * brand: ColorScale; // a 1–12 shade scale, like the built-in `blue`/`red`
50
- * }
51
- * }
52
- * ```
53
- *
54
- * A mode-aware `{ light, dark }` config color (see ColorValue) still resolves
55
- * to a flat `string` at read time — declare those as `string` here too, not
56
- * as the config shape.
57
- */
58
- interface KbachCustomColors {
59
- }
60
- type ColorValueFor<K extends string> = K extends keyof typeof defaultColors ? (typeof defaultColors)[K] extends string ? string : ColorScale : K extends keyof KbachCustomColors ? KbachCustomColors[K] : ColorScale | string;
61
- /** Every color name TypeScript knows about without an explicit type parameter: the built-in theme plus whatever's been added via the KbachCustomColors augmentation above. */
62
- type KnownColorName = DefaultColorName | Extract<keyof KbachCustomColors, string>;
63
- /**
64
- * `ColorName` defaults to `KnownColorName` (the built-in theme's color names
65
- * plus anything augmented onto `KbachCustomColors` above), so `useColors()`
66
- * gets full autocomplete and typo-catching out of the box — including custom
67
- * `kbach.config.js` colors, once augmented once project-wide. Without that
68
- * augmentation, a project with extra colors can still widen per call instead:
69
- * `useColors<DefaultColorName | 'brand'>()`.
6
+ * Loads and instantiates the Rust/WASM core engine. Call once at startup
7
+ * (e.g. before `ReactDOM.createRoot(...).render(...)`) and await it — every
8
+ * subsequent call returns the same memoized promise, so it's safe to call
9
+ * again from multiple entry points.
70
10
  */
71
- type ColorsAPI<ColorName extends string = KnownColorName> = {
72
- readonly [K in ColorName]: ColorValueFor<K>;
73
- } & {
74
- /**
75
- * Pass any CSS color through, optionally applying an opacity (0–100).
76
- * - `colors.alpha('#3b82f6', 50)` → `'rgba(59,130,246,0.5)'`
77
- * - `colors.alpha('rgb(0,0,0)', 10)` → `'rgba(0,0,0,0.1)'`
78
- * - `colors.alpha('rgba(0,0,0,0.5)')` → `'rgba(0,0,0,0.5)'` (passthrough)
79
- */
80
- readonly alpha: (color: string, opacity?: number) => string;
81
- };
82
- declare function wrapColors<ColorName extends string = KnownColorName>(rawColors: ThemeColors, isDark?: boolean): ColorsAPI<ColorName>;
83
- declare function useColors<ColorName extends string = KnownColorName>(): ColorsAPI<ColorName>;
11
+ declare function initKbach(): Promise<void>;
12
+ declare function isKbachReady(): boolean;
84
13
 
14
+ declare function disableRuntimeCSS(): void;
15
+ declare function isRuntimeCSSDisabled(): boolean;
85
16
  /**
86
- * Empty on purpose — augment it via declaration merging so `useSpacing()`
87
- * (like `useColors()`'s `KbachCustomColors`) knows about a project's
88
- * `kbach.config.js` spacing keys without repeating a type parameter at every
89
- * call site:
17
+ * Resolves a Kbach class string via the Rust/WASM engine, cascade-order-safe
18
+ * injects the resulting CSS rules into a single `<style data-kbach>` tag,
19
+ * and returns the class string unchanged for use as `className`.
90
20
  *
91
- * ```ts
92
- * import '@kbach/react'; // or '@kbach/native'
93
- * declare module '@kbach/react' {
94
- * interface KbachCustomSpacing {
95
- * 18: true; // value doesn't matter — only the key is read (see SpacingAPI)
96
- * }
97
- * }
98
- * ```
21
+ * Must be called after `await initKbach()` resolves — see wasmLoader.ts.
99
22
  */
100
- interface KbachCustomSpacing {
101
- }
102
- /** Every spacing key TypeScript knows about without an explicit type parameter. */
103
- type KnownSpacingKey = DefaultSpacingKey | Extract<keyof KbachCustomSpacing, string>;
104
- /**
105
- * `SpacingKey` defaults to `KnownSpacingKey` (the built-in theme's spacing keys
106
- * plus anything augmented onto `KbachCustomSpacing` above), so `useSpacing()`
107
- * gets full autocomplete and typo-catching out of the box — including custom
108
- * `kbach.config.js` keys, once augmented once project-wide. Without that
109
- * augmentation, a project with extra keys can still widen per call instead:
110
- * `useSpacing<DefaultSpacingKey | '18'>()`.
111
- */
112
- type SpacingAPI<SpacingKey extends string = KnownSpacingKey> = {
113
- readonly [K in SpacingKey]: number | string;
114
- };
115
- declare function wrapSpacing<SpacingKey extends string = KnownSpacingKey>(rawSpacing: ThemeSpacing): SpacingAPI<SpacingKey>;
116
- /**
117
- * Returns the active theme's spacing scale as a typed, autocomplete-friendly
118
- * object — useful anywhere a raw JS number/string is needed instead of a
119
- * className (Animated API distances, chart dimensions, FlatList separator
120
- * heights, etc.). Values match exactly what `p-`/`m-`/`w-`/`h-`/`gap-` and
121
- * other spacing-scale utilities resolve to.
122
- *
123
- * ```ts
124
- * const spacing = useSpacing();
125
- * spacing[4] // 16
126
- * spacing.full // '100%'
127
- * spacing['1/2'] // '50%'
128
- * ```
129
- */
130
- declare function useSpacing<SpacingKey extends string = KnownSpacingKey>(): SpacingAPI<SpacingKey>;
131
-
132
- interface ThemeProviderProps {
133
- children: ReactNode;
134
- /** Initial mode. Falls back to persisted value, then 'system'. */
135
- defaultMode?: ThemeMode;
136
- /**
137
- * System color scheme for native `defaultMode="system"`.
138
- *
139
- * Pass the value of `useColorScheme()` from `react-native`. When importing
140
- * `ThemeProvider` from `@kbach/react/native` this is handled automatically.
141
- *
142
- * @example
143
- * ```tsx
144
- * import { useColorScheme } from 'react-native';
145
- * const colorScheme = useColorScheme();
146
- * <ThemeProvider defaultMode="system" colorScheme={colorScheme}>…</ThemeProvider>
147
- * ```
148
- */
149
- colorScheme?: 'light' | 'dark' | null;
150
- /**
151
- * Current window/screen width in pixels for responsive breakpoints.
152
- * On web this is read from `window.innerWidth` automatically.
153
- * When importing `ThemeProvider` from `@kbach/react/native` this is provided
154
- * automatically from `useWindowDimensions()`.
155
- */
156
- windowWidth?: number;
157
- /** Override the config (useful for per-tree config). Defaults to global getConfig(). */
158
- config?: FrameworkConfig;
159
- /** Disable persistence to localStorage */
160
- disablePersistence?: boolean;
161
- }
162
- declare function ThemeProvider({ children, defaultMode, colorScheme, windowWidth: windowWidthProp, config: configOverride, disablePersistence, }: ThemeProviderProps): React__default.JSX.Element;
23
+ declare function kb(classString: string): string;
163
24
 
164
25
  /**
165
26
  * Renders Kbach's base browser-default reset (borderless button/input,
@@ -167,162 +28,90 @@ declare function ThemeProvider({ children, defaultMode, colorScheme, windowWidth
167
28
  * tag so it's part of the page's initial HTML.
168
29
  *
169
30
  * Runtime-only setups (no Vite plugin / static kbach.css) otherwise only get
170
- * this reset once ThemeProvider's client-side effect runs — fine for a plain
171
- * CSR app, but under SSR the server has no JS to run it, so the first paint
172
- * ships with raw browser defaults (e.g. the native button border) until
31
+ * this reset once kb()'s first call injects it — fine for a plain CSR app,
32
+ * but under SSR the server has no JS to run it, so the first paint ships
33
+ * with raw browser defaults (e.g. the native button border) until
173
34
  * hydration catches up. Render this once, as high in <head> as your
174
35
  * framework allows, to close that gap.
175
36
  *
176
- * Not needed if you're on the static-CSS setup — kbach.css already includes
177
- * the same reset, and the runtime injector detects this tag and skips adding
178
- * it a second time either way.
37
+ * Skips rendering entirely once disableRuntimeCSS() has fired — the Vite
38
+ * plugin calls it automatically wherever kbach.css gets imported (see
39
+ * vite-plugin/format.ts's own BASE_RESET inlining), and that static file
40
+ * already has this exact reset. No isNative check (unlike old-kbach's
41
+ * version of this component): @kbach/react is a web-only package now, so
42
+ * there's no cross-platform import path for this component to guard against.
179
43
  */
180
- declare function KbachReset(): React.JSX.Element | null;
44
+ declare function KbachReset(): react.JSX.Element | null;
181
45
 
182
- interface StyledProps {
183
- /** Additional utility classes applied on top of the base classes */
184
- kb?: string;
185
- /** Merged with resolved kb styles; applied last */
186
- style?: StyleValue | StyleValue[];
187
- }
188
- type OmittedKeys = 'style';
189
46
  /**
190
- * Create a styled component from any React / React Native component.
191
- *
192
- * ```tsx
193
- * const Card = styled(View, 'bg-white dark:bg-gray-800 rounded-xl p-4 shadow-md');
194
- * const Button = styled(TouchableOpacity, 'bg-blue-500 pressed:bg-blue-700 dark:bg-blue-600 rounded-lg p-3');
195
- *
196
- * // Use it:
197
- * <Card kb="mt-4">...</Card>
198
- * <Button onPress={handlePress} kb="w-full" />
199
- * ```
47
+ * Port of old-kbach's dark-mode architecture (`packages/ui/src/core/
48
+ * darkModeStore.ts` + `ThemeProvider.tsx`), simplified for this package's
49
+ * web-only scope: a single module-level store is the actual source of
50
+ * truth, and `<ThemeProvider>`/`useTheme()` (see `ThemeProvider.tsx`) are a
51
+ * thin, OPTIONAL React wrapper around it — every function here works
52
+ * standalone, with no provider mounted anywhere, which is the whole point:
53
+ * a plain script, an event handler, or a component that never wants
54
+ * Context can call `toggleGlobalDarkMode()` directly.
200
55
  */
201
- declare function styled<T extends ComponentType<any>>(Component: T, baseClasses?: string): ForwardRefExoticComponent<Omit<React__default.ComponentPropsWithRef<T>, OmittedKeys> & StyledProps>;
56
+ type ThemeMode = 'light' | 'dark' | 'system';
57
+ declare function getGlobalDarkMode(): boolean;
58
+ declare function getGlobalThemeMode(): ThemeMode;
59
+ /** Explicit user choice — persisted, and takes priority over any later `<ThemeProvider defaultMode>` seed (see `seedDefaultMode` below). */
60
+ declare function setGlobalThemeMode(nextMode: ThemeMode): void;
61
+ declare function toggleGlobalDarkMode(): void;
62
+ declare function subscribeGlobalDarkMode(listener: () => void): () => void;
202
63
 
203
- interface InteractionState {
204
- hover?: boolean;
205
- focus?: boolean;
206
- /** Maps to 'pressed' and 'active' modifiers */
207
- pressed?: boolean;
208
- active?: boolean;
209
- disabled?: boolean;
210
- checked?: boolean;
211
- visited?: boolean;
212
- placeholder?: boolean;
213
- }
214
64
  /**
215
- * Resolve a utility class string to a style object for the current theme + state.
216
- *
217
- * ```tsx
218
- * // Basic usage
219
- * const styles = useStyles('bg-white dark:bg-gray-900 p-4');
220
- *
221
- * // With interaction state
222
- * const [pressed, setPressed] = useState(false);
223
- * const styles = useStyles('bg-blue-500 pressed:bg-blue-700', { pressed });
224
- *
225
- * // Multiple class strings (merged left-to-right)
226
- * const styles = useStyles(['bg-white p-4', 'dark:bg-gray-900 rounded-xl']);
227
- * ```
228
- */
229
- declare function useStyles(classString: string | string[], state?: InteractionState): StyleValue;
230
- /**
231
- * Returns the full ResolvedStyle bucket map (base, dark, hover, …).
232
- * Useful when you need to apply styles selectively or pass them to Animated.
233
- */
234
- declare function useResolvedStyle(classString: string | string[]): __core.ResolvedStyle;
235
-
236
- /**
237
- * Subscribe to the global dark-mode store with React's concurrent-safe
238
- * useSyncExternalStore so that any component using className/kb re-renders
239
- * immediately when the theme changes — without needing ThemeContext.
65
+ * Reactive read of the global dark-mode store — works with or without a
66
+ * `<ThemeProvider>` mounted anywhere in the tree, since it subscribes to
67
+ * `darkModeStore.ts` directly. `useSyncExternalStore`'s SSR snapshot always
68
+ * returns `false` (light), matching `applyToDom`'s own server no-op — the
69
+ * server and the client's very first render agree, so there's no
70
+ * hydration-mismatch warning; the real value (if different) applies on the
71
+ * client immediately after.
240
72
  */
241
73
  declare function useGlobalDarkMode(): boolean;
242
74
 
243
- /**
244
- * Returns the name of the currently active breakpoint — the largest breakpoint
245
- * whose min-width the window satisfies, or `'xs'` when below all breakpoints.
246
- *
247
- * ```ts
248
- * const bp = useBreakpoint(); // 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
249
- * ```
250
- */
251
- declare function useBreakpoint(): string;
252
- /**
253
- * Returns a record of boolean flags for each breakpoint — `true` when the
254
- * window width satisfies that breakpoint's min-width threshold.
255
- *
256
- * ```ts
257
- * const { sm, md, lg } = useResponsive();
258
- * const padding = lg ? 32 : sm ? 16 : 8;
259
- * ```
260
- */
261
- declare function useResponsive(): Record<string, boolean>;
262
-
263
- interface InteractiveWrapperProps {
264
- /** The real component to render (View, TouchableOpacity, div, a third-party button…) */
265
- Component: React__default.ComponentType<any> | string;
266
- /** Pre-resolved style buckets from resolve() */
267
- resolvedStyle: ResolvedStyle;
268
- /** On web: original class string so CSS pseudo-rules still fire */
269
- className?: string;
270
- /** Extra style prop passed by the user */
271
- style?: StyleValue | StyleValue[];
272
- children?: React__default.ReactNode;
273
- onPressIn?: (...args: any[]) => void;
274
- onPressOut?: (...args: any[]) => void;
275
- onPointerDown?: (...args: any[]) => void;
276
- onPointerUp?: (...args: any[]) => void;
277
- onPointerLeave?: (...args: any[]) => void;
278
- onPointerCancel?: (...args: any[]) => void;
279
- onMouseEnter?: (...args: any[]) => void;
280
- onMouseLeave?: (...args: any[]) => void;
281
- onFocus?: (...args: any[]) => void;
282
- onBlur?: (...args: any[]) => void;
75
+ interface ThemeProviderProps {
76
+ children: ReactNode;
77
+ /**
78
+ * Applied once, on this provider's first mount, but ONLY if the user has
79
+ * no persisted preference already (see `darkModeStore.ts::seedDefaultMode`)
80
+ * — an explicit prior choice always wins over a component's own default.
81
+ */
82
+ defaultMode?: ThemeMode;
283
83
  }
284
84
  /**
285
- * Thin wrapper rendered automatically by the JSX runtime whenever a className/kb
286
- * string contains interactive modifiers (hover:, pressed:, focus:, active:, …).
287
- *
288
- * Manages interaction state locally and flattens the correct style bucket on
289
- * every render. The wrapped component sees a plain `style` prop — it never
290
- * knows it was wrapped.
291
- *
292
- * Refs are forwarded so the host component's imperative API still works.
85
+ * Optional React Context wrapper around `darkModeStore.ts`'s single global
86
+ * store — port of old-kbach's `ThemeProvider` (`packages/ui/src/
87
+ * ThemeProvider.tsx`), same "provider is a thin wrapper around a global
88
+ * store, not the source of truth itself" shape, simplified to this
89
+ * package's web-only scope (no native `useColorScheme()`/`windowWidth`
90
+ * branch, no `disablePersistence` prop — persistence is always on, since
91
+ * `darkModeStore.ts` already guards every `localStorage` access for
92
+ * environments where it's unavailable).
93
+ *
94
+ * Because the store is global, MULTIPLE mounted providers share the exact
95
+ * same `mode`/`isDark` state rather than each getting their own scoped
96
+ * value — mounting more than one is very likely a mistake (each fighting
97
+ * over the same state via a different `defaultMode`), so this warns once
98
+ * in dev, matching old-kbach's identical guard.
293
99
  */
294
- declare const InteractiveWrapper: React__default.ForwardRefExoticComponent<InteractiveWrapperProps & React__default.RefAttributes<unknown>>;
100
+ declare function ThemeProvider({ children, defaultMode }: ThemeProviderProps): react.JSX.Element;
295
101
 
102
+ interface ThemeContextValue {
103
+ mode: ThemeMode;
104
+ isDark: boolean;
105
+ setMode: (mode: ThemeMode) => void;
106
+ toggle: () => void;
107
+ }
296
108
  /**
297
- * Resolve a utility class string outside of a React component.
298
- *
299
- * On **native** — returns a StyleValue (style object) for the given mode.
300
- * On **web** — injects CSS and returns the original class string (use as className).
301
- *
302
- * ```ts
303
- * // Inside a component use useStyles() instead.
304
- * // kb() is useful for StyleSheet.create() calls and static values.
305
- *
306
- * const styles = StyleSheet.create({
307
- * container: kb('flex-1 bg-white p-4') as any,
308
- * });
309
- *
310
- * // Web: use as className
311
- * <div className={kb('bg-white dark:bg-gray-900 p-4') as string} />
312
- * ```
313
- *
314
- * @param classString Space-separated utility classes
315
- * @param isDark Whether dark mode is active (default: false)
316
- */
317
- declare function kb(classString: string, isDark?: boolean): StyleValue | string;
318
- /**
319
- * Conditionally join class names. Falsy values are ignored.
320
- *
321
- * ```ts
322
- * cx('bg-white p-4', isActive && 'border-2 border-blue-500', undefined)
323
- * // → 'bg-white p-4 border-2 border-blue-500'
324
- * ```
109
+ * Requires a `<ThemeProvider>` ancestor — throws otherwise, since a
110
+ * component that destructures `{ mode, setMode }` genuinely needs Context
111
+ * (unlike a component that just wants the dark/light boolean, which should
112
+ * reach for `useGlobalDarkMode()` instead and never needs a provider at
113
+ * all).
325
114
  */
326
- declare function cx(...classes: Array<string | false | null | undefined>): string;
115
+ declare function useTheme(): ThemeContextValue;
327
116
 
328
- export { type ColorScale, type ColorsAPI, type InteractionState, InteractiveWrapper, type InteractiveWrapperProps, type KbachCustomColors, type KbachCustomSpacing, KbachReset, type SpacingAPI, type StyledProps, ThemeContext, type ThemeContextValue, ThemeProvider, type ThemeProviderProps, cx, kb, styled, useBreakpoint, useColors, useGlobalDarkMode, useIsDark, useResolvedStyle, useResponsive, useSpacing, useStyles, useTheme, wrapColors, wrapSpacing };
117
+ export { KbachReset, type ThemeContextValue, type ThemeMode, ThemeProvider, type ThemeProviderProps, disableRuntimeCSS, getGlobalDarkMode, getGlobalThemeMode, initKbach, isKbachReady, isRuntimeCSSDisabled, kb, setGlobalThemeMode, subscribeGlobalDarkMode, toggleGlobalDarkMode, useGlobalDarkMode, useTheme };