@sigx/lynx-daisyui 0.4.0 → 0.4.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.
Files changed (59) hide show
  1. package/README.md +38 -0
  2. package/dist/buttons/Button.js +53 -0
  3. package/dist/data/Avatar.js +46 -0
  4. package/dist/feedback/Alert.js +13 -0
  5. package/dist/feedback/Badge.js +17 -0
  6. package/dist/feedback/Loading.js +16 -0
  7. package/dist/feedback/Modal.js +23 -0
  8. package/dist/feedback/Progress.js +17 -0
  9. package/dist/feedback/Skeleton.js +18 -0
  10. package/dist/feedback/Steps.js +16 -0
  11. package/dist/forms/Checkbox.js +32 -0
  12. package/dist/forms/FormField.js +5 -0
  13. package/dist/forms/Input.js +25 -0
  14. package/dist/forms/Radio.js +28 -0
  15. package/dist/forms/Select.js +33 -0
  16. package/dist/forms/Textarea.js +31 -0
  17. package/dist/forms/Toggle.js +32 -0
  18. package/dist/index.d.ts +66 -58
  19. package/dist/index.js +41 -678
  20. package/dist/layout/Card.js +39 -0
  21. package/dist/layout/Center.d.ts +2 -1
  22. package/dist/layout/Center.js +24 -0
  23. package/dist/layout/Col.d.ts +2 -2
  24. package/dist/layout/Col.js +33 -0
  25. package/dist/layout/Divider.js +27 -0
  26. package/dist/layout/Row.d.ts +2 -2
  27. package/dist/layout/Row.js +33 -0
  28. package/dist/layout/ScrollView.js +18 -0
  29. package/dist/layout/Spacer.js +11 -0
  30. package/dist/navigation/NavDrawer.d.ts +62 -0
  31. package/dist/navigation/NavDrawer.js +205 -0
  32. package/dist/navigation/NavHeader.d.ts +12 -1
  33. package/dist/navigation/NavHeader.js +74 -0
  34. package/dist/navigation/NavTabBar.js +90 -0
  35. package/dist/navigation/SwiperIndicator.d.ts +59 -0
  36. package/dist/navigation/SwiperIndicator.js +232 -0
  37. package/dist/navigation/Tabs.js +18 -0
  38. package/dist/preset/index.js +66 -40
  39. package/dist/shared/press.d.ts +2 -0
  40. package/dist/shared/press.js +6 -0
  41. package/dist/shared/styles.d.ts +29 -1
  42. package/dist/shared/styles.js +90 -0
  43. package/dist/styles/components/typography.css +36 -2
  44. package/dist/styles/index.css +8 -4
  45. package/dist/styles/themes/shapes.css +2 -1
  46. package/dist/styles/themes/{dark.css → tokens.css} +9 -33
  47. package/dist/theme/StatusBarSync.d.ts +41 -0
  48. package/dist/theme/StatusBarSync.js +85 -0
  49. package/dist/theme/ThemeProvider.d.ts +91 -35
  50. package/dist/theme/ThemeProvider.js +183 -0
  51. package/dist/theme/registry.d.ts +101 -0
  52. package/dist/theme/registry.js +185 -0
  53. package/dist/typography/Heading.js +19 -0
  54. package/dist/typography/Text.d.ts +11 -1
  55. package/dist/typography/Text.js +25 -0
  56. package/package.json +12 -10
  57. package/dist/index.js.map +0 -1
  58. package/dist/preset/index.js.map +0 -1
  59. package/dist/styles/themes/light.css +0 -95
@@ -1,5 +1,30 @@
1
- /* Typography — text color utilities for semantic colors */
1
+ /*
2
+ * Daisy semantic color utilities.
3
+ *
4
+ * bg-<token>: every token in DAISY_COLOR_TOKEN_LIST (semantics +
5
+ * their -content variants, plus the three base surfaces
6
+ * and base-content).
7
+ * text-<token>: semantic tokens + their -content variants, plus
8
+ * base-content. The three base surface tokens
9
+ * (base-100/200/300) are deliberately omitted — text the
10
+ * colour of the surface it sits on is never useful, and
11
+ * keeping the class out prevents accidental "invisible
12
+ * text" footguns.
13
+ *
14
+ * Hand-written rules rather than Tailwind-generated utilities, so they
15
+ * survive purge regardless of whether a consumer's source statically
16
+ * references the class. This matters for daisy components that compose
17
+ * the class dynamically — e.g. `class={`bg-${props.background}`}` in
18
+ * `<NavDrawer>` — which Tailwind's source scanner can't see. Without
19
+ * these always-on rules, `<NavDrawer background="primary">` paints
20
+ * transparent because `bg-primary` got purged.
21
+ *
22
+ * Lynx specifics: `var(--color-*)` resolves only from CSS-pipeline rules
23
+ * (this file, daisy themes), never from inline `style.backgroundColor`.
24
+ * Keep daisy surface tokens flowing through a class.
25
+ */
2
26
 
27
+ /* Text */
3
28
  .text-primary { color: var(--color-primary); }
4
29
  .text-primary-content { color: var(--color-primary-content); }
5
30
  .text-secondary { color: var(--color-secondary); }
@@ -18,15 +43,24 @@
18
43
  .text-error { color: var(--color-error); }
19
44
  .text-error-content { color: var(--color-error-content); }
20
45
 
21
- /* Background color utilities for semantic colors */
46
+ /* Background */
22
47
  .bg-primary { background-color: var(--color-primary); }
48
+ .bg-primary-content { background-color: var(--color-primary-content); }
23
49
  .bg-secondary { background-color: var(--color-secondary); }
50
+ .bg-secondary-content { background-color: var(--color-secondary-content); }
24
51
  .bg-accent { background-color: var(--color-accent); }
52
+ .bg-accent-content { background-color: var(--color-accent-content); }
25
53
  .bg-neutral { background-color: var(--color-neutral); }
54
+ .bg-neutral-content { background-color: var(--color-neutral-content); }
26
55
  .bg-base-100 { background-color: var(--color-base-100); }
27
56
  .bg-base-200 { background-color: var(--color-base-200); }
28
57
  .bg-base-300 { background-color: var(--color-base-300); }
58
+ .bg-base-content { background-color: var(--color-base-content); }
29
59
  .bg-info { background-color: var(--color-info); }
60
+ .bg-info-content { background-color: var(--color-info-content); }
30
61
  .bg-success { background-color: var(--color-success); }
62
+ .bg-success-content { background-color: var(--color-success-content); }
31
63
  .bg-warning { background-color: var(--color-warning); }
64
+ .bg-warning-content { background-color: var(--color-warning-content); }
32
65
  .bg-error { background-color: var(--color-error); }
66
+ .bg-error-content { background-color: var(--color-error-content); }
@@ -1,8 +1,12 @@
1
- /* @sigx/lynx-daisyui — all themes + component styles */
1
+ /* @sigx/lynx-daisyui — base tokens + component styles.
2
+ *
3
+ * Theme COLORS live in the theme registry (src/theme/registry.ts) and are
4
+ * applied as inline CSS custom properties by <ThemeProvider>; only the
5
+ * theme-agnostic structural tokens ship as CSS, under the `.daisy` base
6
+ * class. */
2
7
 
3
- /* Themes */
4
- @import './themes/light.css';
5
- @import './themes/dark.css';
8
+ /* Structural design tokens (.daisy) + composable shape modifiers */
9
+ @import './themes/tokens.css';
6
10
  @import './themes/shapes.css';
7
11
 
8
12
  /* Base reset */
@@ -1,5 +1,6 @@
1
1
  /* Shape variants — override roundness tokens */
2
- /* Combine with a color theme: class="daisy-light daisy-rounded" */
2
+ /* Compose with the base via ThemeProvider's `class` prop, e.g.
3
+ <ThemeProvider class="daisy-rounded"> → host class="daisy daisy-rounded" */
3
4
 
4
5
  .daisy-flat {
5
6
  --rounded-box: 0px;
@@ -1,37 +1,13 @@
1
- /* DaisyUI Dark Theme overrides only color tokens */
2
- /* Scoped under .daisy-dark; CSS inheritance propagates to descendants */
3
-
4
- .daisy-dark {
5
- /* Apply base colors to the theme root */
6
- background-color: #1d232a;
7
- color: #a6adbb;
8
-
9
- /* Semantic colors */
10
- --color-primary: #7582ff;
11
- --color-primary-content: #050617;
12
- --color-secondary: #ff71cf;
13
- --color-secondary-content: #190211;
14
- --color-accent: #00e7d0;
15
- --color-accent-content: #001210;
16
- --color-neutral: #2a323c;
17
- --color-neutral-content: #a6adbb;
18
-
19
- /* Base colors */
20
- --color-base-100: #1d232a;
21
- --color-base-200: #191e24;
22
- --color-base-300: #343b46;
23
- --color-base-content: #a6adbb;
24
-
25
- /* Status colors */
26
- --color-info: #00b4fa;
27
- --color-info-content: #000000;
28
- --color-success: #00a96e;
29
- --color-success-content: #000000;
30
- --color-warning: #ffc100;
31
- --color-warning-content: #000000;
32
- --color-error: #ff676a;
33
- --color-error-content: #000000;
1
+ /* Theme-agnostic structural design tokens.
2
+ *
3
+ * Theme COLORS come from the theme registry (src/theme/registry.ts) and are
4
+ * applied as inline CSS custom properties by <ThemeProvider>. These
5
+ * radius / sizing / component tokens are identical across themes, so they
6
+ * ship once here under the `.daisy` base class that <ThemeProvider> puts on
7
+ * its host view; CSS inheritance propagates them to every descendant. A theme
8
+ * may still override roundness via its `radius` field. */
34
9
 
10
+ .daisy {
35
11
  /* ── Roundness ── */
36
12
  --rounded-box: 16px;
37
13
  --rounded-btn: 8px;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * `<StatusBarSync />` — keeps the device status-bar (and Android's
3
+ * navigation-bar) tint legible against the active daisyui theme.
4
+ *
5
+ * Reads the current theme via `useTheme()`, looks up its variant in the
6
+ * theme registry, and pushes the appropriate tint to the OS via
7
+ * `@sigx/lynx-appearance`:
8
+ *
9
+ * light theme → dark status-bar icons (legible against light bg)
10
+ * dark theme → light status-bar icons (legible against dark bg)
11
+ *
12
+ * Mount once, inside `<ThemeProvider>`:
13
+ *
14
+ * ```tsx
15
+ * <ThemeProvider>
16
+ * <StatusBarSync />
17
+ * <App />
18
+ * </ThemeProvider>
19
+ * ```
20
+ *
21
+ * Renders nothing — it's a side-effect-only component that drives a
22
+ * reactive `effect()` reading `theme.name`. The `matchBackground` prop is
23
+ * reserved for a follow-up that pushes the active theme's
24
+ * `--color-base-100` as the Android system-bar background; today it's a
25
+ * declared no-op so the API surface is stable across the rev that wires
26
+ * CSS-var resolution.
27
+ */
28
+ import { type Define } from '@sigx/lynx';
29
+ export type StatusBarSyncProps =
30
+ /**
31
+ * Reserved — will (in a follow-up) push the active theme's
32
+ * `--color-base-100` as the Android status- and navigation-bar
33
+ * background. Currently a no-op; the prop ships so consumers can opt
34
+ * in without an API break later. iOS and Android 15+ ignore the
35
+ * background regardless (no equivalent on iOS; edge-to-edge on
36
+ * Android 15+).
37
+ */
38
+ Define.Prop<'matchBackground', boolean, false>;
39
+ export declare const StatusBarSync: import("@sigx/runtime-core").ComponentFactory<{
40
+ matchBackground?: boolean | undefined;
41
+ }, void, {}>;
@@ -0,0 +1,85 @@
1
+ import { jsx as _jsx } from "@sigx/lynx/jsx-runtime";
2
+ /**
3
+ * `<StatusBarSync />` — keeps the device status-bar (and Android's
4
+ * navigation-bar) tint legible against the active daisyui theme.
5
+ *
6
+ * Reads the current theme via `useTheme()`, looks up its variant in the
7
+ * theme registry, and pushes the appropriate tint to the OS via
8
+ * `@sigx/lynx-appearance`:
9
+ *
10
+ * light theme → dark status-bar icons (legible against light bg)
11
+ * dark theme → light status-bar icons (legible against dark bg)
12
+ *
13
+ * Mount once, inside `<ThemeProvider>`:
14
+ *
15
+ * ```tsx
16
+ * <ThemeProvider>
17
+ * <StatusBarSync />
18
+ * <App />
19
+ * </ThemeProvider>
20
+ * ```
21
+ *
22
+ * Renders nothing — it's a side-effect-only component that drives a
23
+ * reactive `effect()` reading `theme.name`. The `matchBackground` prop is
24
+ * reserved for a follow-up that pushes the active theme's
25
+ * `--color-base-100` as the Android system-bar background; today it's a
26
+ * declared no-op so the API surface is stable across the rev that wires
27
+ * CSS-var resolution.
28
+ */
29
+ import { component, effect, onMounted, onUnmounted } from '@sigx/lynx';
30
+ import { isAvailable, setSystemBarsStyle } from '@sigx/lynx-appearance';
31
+ import { useTheme } from './ThemeProvider.js';
32
+ import { variantOf } from './registry.js';
33
+ export const StatusBarSync = component(({ props }) => {
34
+ const theme = useTheme();
35
+ let lastApplied = null;
36
+ let runner;
37
+ function apply(name) {
38
+ if (name === lastApplied)
39
+ return;
40
+ lastApplied = name;
41
+ const variant = variantOf(name);
42
+ // For unregistered themes we can't infer a variant — leave the
43
+ // system bars alone. Consumers can register their custom theme via
44
+ // `registerTheme()` to opt in.
45
+ if (!variant)
46
+ return;
47
+ const style = variant === 'dark' ? 'light' : 'dark';
48
+ // Fire-and-forget: `setSystemBarsStyle` is non-throwing (it
49
+ // resolves `{ ok: false, reason: 'unsupported' }` when the native
50
+ // module isn't registered, and silently filters per-leg
51
+ // `unsupported` results — e.g. nav-bar on iOS — so an aggregate
52
+ // `ok: true` is still reachable on partial platforms). Either way,
53
+ // void-discarding the promise here can't surface as an unhandled
54
+ // rejection.
55
+ void setSystemBarsStyle({
56
+ statusBar: style,
57
+ navigationBar: { style },
58
+ });
59
+ }
60
+ onMounted(() => {
61
+ if (!isAvailable())
62
+ return;
63
+ // A reactive effect that reads `theme.name` so the effect re-runs
64
+ // whenever the theme controller's underlying signal changes —
65
+ // including the live system-flip path inside ThemeProvider. No
66
+ // side effects in render; nothing to subscribe/unsubscribe by
67
+ // hand.
68
+ runner = effect(() => {
69
+ apply(theme.name);
70
+ });
71
+ });
72
+ onUnmounted(() => {
73
+ runner?.stop();
74
+ runner = undefined;
75
+ });
76
+ // Reference the prop so the type checker doesn't flag it as unused
77
+ // while it's still reserved. Drop this when the matchBackground
78
+ // implementation lands.
79
+ void props.matchBackground;
80
+ // Zero-size, out-of-flow placeholder. Avoids `display: none` —
81
+ // Lynx can leak unstyled text paint through display:none overlays in
82
+ // some builds (see lynx-display-none caveat); zero-size + absolute is
83
+ // the safer shape.
84
+ return () => (_jsx("view", { style: { position: 'absolute', width: '0px', height: '0px', opacity: 0 } }));
85
+ });
@@ -2,54 +2,92 @@
2
2
  * `<ThemeProvider>` and `useTheme()` — daisyui theme switching for
3
3
  * `@sigx/lynx-daisyui`.
4
4
  *
5
- * The package ships two color themes (`daisy-light`, `daisy-dark`) plus
6
- * style modifier themes (`daisy-rounded`, `daisy-flat`). Each is a CSS
7
- * class containing scoped `--color-*` / `--radius-*` / `--border-*`
8
- * variable definitions; descendants of an element with the class
9
- * inherit those variables (Lynx has `enableCSSInheritance: true` in
10
- * its layout-pipeline defaults), and the daisyui components are built
11
- * to read those vars directly.
5
+ * Themes are CSS classes containing scoped `--color-*` / `--radius-*`
6
+ * variable definitions; descendants of an element with the class inherit
7
+ * those variables (Lynx has `enableCSSInheritance: true` in its
8
+ * layout-pipeline defaults), and the daisyui components are built to read
9
+ * those vars directly.
10
+ *
11
+ * Six color themes ship in the box (`daisy-light`, `daisy-dark`,
12
+ * `daisy-cupcake`, `daisy-emerald`, `daisy-synthwave`, `daisy-dracula`)
13
+ * plus style modifier themes (`daisy-rounded`, `daisy-flat`). Custom themes
14
+ * register their light/dark variant via `registerTheme()` in
15
+ * `./registry.ts` so `followSystem` and `toggle()` know what to pick.
12
16
  *
13
17
  * Usage:
14
18
  *
15
19
  * ```tsx
16
20
  * import { ThemeProvider, useTheme } from '@sigx/lynx-daisyui';
17
21
  *
22
+ * // System-aware (default): picks daisy-light or daisy-dark from the OS,
23
+ * // live-flips when the user toggles dark mode.
18
24
  * defineApp(() => () => (
19
- * <ThemeProvider initial="daisy-light">
25
+ * <ThemeProvider>
20
26
  * <App />
21
27
  * </ThemeProvider>
22
28
  * ));
23
29
  *
24
- * // Anywhere inside:
25
- * const theme = useTheme();
26
- * theme.toggle(); // daisy-light ↔ daisy-dark
27
- * theme.set('daisy-dark'); // explicit
28
- * theme.name; // 'daisy-light' | 'daisy-dark' | custom string
29
- * ```
30
+ * // Pin a specific theme — ignores system appearance.
31
+ * <ThemeProvider initial="daisy-light">…</ThemeProvider>
30
32
  *
31
- * For multi-class compositions (color + modifier), set a custom string:
32
- * `theme.set('daisy-light daisy-rounded')`.
33
+ * // Custom light/dark pair under followSystem.
34
+ * <ThemeProvider light="daisy-cupcake" dark="daisy-synthwave">…</ThemeProvider>
35
+ * ```
33
36
  */
34
37
  import { type Define } from '@sigx/lynx';
38
+ import type { DaisyColor } from '../shared/styles.js';
39
+ /**
40
+ * Declaration-merge extension: add a typed `variant` prop to `<Icon>`,
41
+ * `<FaSolidIcon>`, `<LucideIcon>`, etc. Daisy owns the entire concept
42
+ * — `@sigx/lynx-icons` has no notion of variants. Without this merge
43
+ * being in scope (i.e. an app that doesn't depend on daisy), `<Icon
44
+ * variant="…">` is a compile error: the property doesn't exist.
45
+ *
46
+ * The merge fires the moment any consumer imports anything from
47
+ * `@sigx/lynx-daisyui`. No subpath, no extra import dance.
48
+ */
49
+ declare module '@sigx/lynx-icons' {
50
+ interface IconPropsExtensions {
51
+ /**
52
+ * Daisy color token applied as the icon's `fill`. Resolved at
53
+ * runtime through `useIconColorResolver` (provided by
54
+ * `<ThemeProvider>`) to the current theme's hex value.
55
+ */
56
+ variant?: DaisyColor;
57
+ }
58
+ }
35
59
  /**
36
- * Theme class applied to the provider's host view. The two built-ins
37
- * (`daisy-light` / `daisy-dark`) get autocomplete; arbitrary strings
38
- * are accepted for custom themes or multi-class compositions like
39
- * `'daisy-light daisy-rounded'`.
60
+ * Theme class applied to the provider's host view. The six color themes
61
+ * get autocomplete; arbitrary strings are accepted for custom themes or
62
+ * multi-class compositions like `'daisy-light daisy-rounded'`.
40
63
  */
41
- export type DaisyTheme = 'daisy-light' | 'daisy-dark' | (string & {});
64
+ export type DaisyTheme = 'daisy-light' | 'daisy-dark' | 'daisy-cupcake' | 'daisy-emerald' | 'daisy-synthwave' | 'daisy-dracula' | (string & {});
42
65
  export interface ThemeController {
43
66
  /** Current theme class. Reactive — read inside render/effect to track. */
44
67
  readonly name: DaisyTheme;
45
- /** Replace the active theme. */
68
+ /**
69
+ * Whether the theme is currently being driven by the system color
70
+ * scheme (true when no `initial` was passed and `set()` hasn't been
71
+ * called since mount). UI like a settings screen can read this to show
72
+ * a "Follow system" indicator.
73
+ */
74
+ readonly followingSystem: boolean;
75
+ /**
76
+ * Replace the active theme. Pins the choice — subsequent system
77
+ * appearance changes won't override it (until `followSystem()` is called).
78
+ */
46
79
  set(name: DaisyTheme): void;
47
80
  /**
48
- * Flip between `daisy-light` and `daisy-dark`. When the active
49
- * theme is neither (custom / multi-class), defaults to
50
- * `daisy-dark` on first call.
81
+ * Flip to the paired theme — for built-ins, light dark; for custom
82
+ * themes, follows the `pair` declared in `registerTheme()`, or the
83
+ * first theme of the opposite variant.
51
84
  */
52
85
  toggle(): void;
86
+ /**
87
+ * Resume following system appearance. Equivalent to mounting fresh
88
+ * with no `initial` prop. Useful for a "Reset to system" button.
89
+ */
90
+ followSystem(): void;
53
91
  }
54
92
  /**
55
93
  * Access the enclosing daisyui theme controller. Throws when used
@@ -57,26 +95,44 @@ export interface ThemeController {
57
95
  */
58
96
  export declare const useTheme: import("@sigx/runtime-core").InjectableFunction<ThemeController>;
59
97
  export type ThemeProviderProps =
60
- /** Initial theme class. Defaults to `daisy-light`. */
98
+ /**
99
+ * Pin the initial theme. When set, the provider ignores system
100
+ * appearance until `controller.followSystem()` is called. When
101
+ * omitted, the provider follows the OS color scheme and live-flips
102
+ * with it.
103
+ */
61
104
  Define.Prop<'initial', DaisyTheme, false>
105
+ /**
106
+ * Theme to use when the system color scheme is `'light'`. Defaults to
107
+ * the first registered light theme (`daisy-light`). Only consulted
108
+ * while `followingSystem` is true.
109
+ */
110
+ & Define.Prop<'light', DaisyTheme, false>
111
+ /**
112
+ * Theme to use when the system color scheme is `'dark'`. Defaults to
113
+ * the first registered dark theme (`daisy-dark`). Only consulted
114
+ * while `followingSystem` is true.
115
+ */
116
+ & Define.Prop<'dark', DaisyTheme, false>
62
117
  /** Extra classes appended to the theme class on the host view. */
63
118
  & Define.Prop<'class', string, false>
64
119
  /** Extra inline style on the host view. Merged after the base flex-fill defaults. */
65
120
  & Define.Prop<'style', Record<string, string | number>, false> & Define.Slot<'default'>;
66
121
  /**
67
- * Wraps children in a `<view class={theme}>` so the daisyui CSS
68
- * variables defined inside `.daisy-light` / `.daisy-dark` inherit
69
- * down to every descendant.
122
+ * Wraps children in a `<view class={theme}>` so the daisyui CSS variables
123
+ * defined inside the theme class inherit down to every descendant.
70
124
  *
71
- * The host view defaults to flex-fill long-form so the wrapper doesn't
72
- * collapse between ancestors that flex (e.g. `<SafeAreaProvider>`) and
73
- * descendants that need a sized parent (`<SafeAreaView>`). Consumers
74
- * override the layout via `style`.
125
+ * Layout: defaults to flex-fill long-form so the wrapper doesn't collapse
126
+ * between ancestors that flex (e.g. `<SafeAreaProvider>`) and descendants
127
+ * that need a sized parent (`<SafeAreaView>`). Consumers override via
128
+ * `style`.
75
129
  *
76
130
  * Theme name is held in an *object* signal (not a primitive) so the
77
- * literal-union type survives — `signal<T>` widens primitive literals
78
- * to plain `string` via `Widen<T>`.
131
+ * literal-union type survives — `signal<T>` widens primitive literals to
132
+ * plain `string` via `Widen<T>`.
79
133
  */
80
134
  export declare const ThemeProvider: import("@sigx/runtime-core").ComponentFactory<ThemeProviderProps, void, {
81
135
  default: () => import("@sigx/runtime-core").JSXElement | import("@sigx/runtime-core").JSXElement[] | null;
82
136
  }>;
137
+ export { listThemes, registerTheme, extendTheme, pickThemeFor, pairOf, variantOf, colorsOf, radiusOf, } from './registry.js';
138
+ export type { Theme, ThemePalette, ThemeRadius, ThemeVariant } from './registry.js';
@@ -0,0 +1,183 @@
1
+ import { jsx as _jsx } from "@sigx/lynx/jsx-runtime";
2
+ /**
3
+ * `<ThemeProvider>` and `useTheme()` — daisyui theme switching for
4
+ * `@sigx/lynx-daisyui`.
5
+ *
6
+ * Themes are CSS classes containing scoped `--color-*` / `--radius-*`
7
+ * variable definitions; descendants of an element with the class inherit
8
+ * those variables (Lynx has `enableCSSInheritance: true` in its
9
+ * layout-pipeline defaults), and the daisyui components are built to read
10
+ * those vars directly.
11
+ *
12
+ * Six color themes ship in the box (`daisy-light`, `daisy-dark`,
13
+ * `daisy-cupcake`, `daisy-emerald`, `daisy-synthwave`, `daisy-dracula`)
14
+ * plus style modifier themes (`daisy-rounded`, `daisy-flat`). Custom themes
15
+ * register their light/dark variant via `registerTheme()` in
16
+ * `./registry.ts` so `followSystem` and `toggle()` know what to pick.
17
+ *
18
+ * Usage:
19
+ *
20
+ * ```tsx
21
+ * import { ThemeProvider, useTheme } from '@sigx/lynx-daisyui';
22
+ *
23
+ * // System-aware (default): picks daisy-light or daisy-dark from the OS,
24
+ * // live-flips when the user toggles dark mode.
25
+ * defineApp(() => () => (
26
+ * <ThemeProvider>
27
+ * <App />
28
+ * </ThemeProvider>
29
+ * ));
30
+ *
31
+ * // Pin a specific theme — ignores system appearance.
32
+ * <ThemeProvider initial="daisy-light">…</ThemeProvider>
33
+ *
34
+ * // Custom light/dark pair under followSystem.
35
+ * <ThemeProvider light="daisy-cupcake" dark="daisy-synthwave">…</ThemeProvider>
36
+ * ```
37
+ */
38
+ import { component, defineInjectable, defineProvide, onMounted, onUnmounted, signal, } from '@sigx/lynx';
39
+ import { useIconColorResolver } from '@sigx/lynx-icons';
40
+ import { useSystemColorScheme } from '@sigx/lynx-appearance';
41
+ import { colorsOf, pairOf, pickThemeFor, radiusOf } from './registry.js';
42
+ /**
43
+ * Access the enclosing daisyui theme controller. Throws when used
44
+ * outside `<ThemeProvider>` — install a provider at your app root.
45
+ */
46
+ export const useTheme = defineInjectable(() => {
47
+ throw new Error('[lynx-daisyui] useTheme() called outside <ThemeProvider>. Wrap your app root with `<ThemeProvider>…</ThemeProvider>`.');
48
+ });
49
+ /**
50
+ * Wraps children in a `<view class={theme}>` so the daisyui CSS variables
51
+ * defined inside the theme class inherit down to every descendant.
52
+ *
53
+ * Layout: defaults to flex-fill long-form so the wrapper doesn't collapse
54
+ * between ancestors that flex (e.g. `<SafeAreaProvider>`) and descendants
55
+ * that need a sized parent (`<SafeAreaView>`). Consumers override via
56
+ * `style`.
57
+ *
58
+ * Theme name is held in an *object* signal (not a primitive) so the
59
+ * literal-union type survives — `signal<T>` widens primitive literals to
60
+ * plain `string` via `Widen<T>`.
61
+ */
62
+ export const ThemeProvider = component(({ props, slots }) => {
63
+ const systemScheme = useSystemColorScheme();
64
+ // The underlying signal widens to PrimitiveSignal<string> via Widen<T>;
65
+ // cast at read sites to keep the narrow union throughout the component.
66
+ const readScheme = () => systemScheme.value;
67
+ // Seed: pin to `initial` if set, otherwise follow system.
68
+ const initialState = props.initial
69
+ ? { name: props.initial, following: false }
70
+ : {
71
+ name: readScheme() === 'dark'
72
+ ? (props.dark ?? pickThemeFor('dark'))
73
+ : (props.light ?? pickThemeFor('light')),
74
+ following: true,
75
+ };
76
+ const state = signal(initialState);
77
+ // Guard against re-applying the same theme on stray re-fires.
78
+ let lastApplied = state.following ? readScheme() : null;
79
+ function applySystem(scheme, force = false) {
80
+ if (!state.following)
81
+ return;
82
+ if (!force && lastApplied === scheme)
83
+ return;
84
+ lastApplied = scheme;
85
+ state.name = scheme === 'dark'
86
+ ? (props.dark ?? pickThemeFor('dark'))
87
+ : (props.light ?? pickThemeFor('light'));
88
+ }
89
+ const controller = {
90
+ get name() { return state.name; },
91
+ get followingSystem() { return state.following; },
92
+ set(next) {
93
+ state.name = next;
94
+ state.following = false;
95
+ },
96
+ toggle() {
97
+ state.name = pairOf(state.name);
98
+ state.following = false;
99
+ },
100
+ followSystem() {
101
+ state.following = true;
102
+ applySystem(readScheme(), /* force */ true);
103
+ },
104
+ };
105
+ defineProvide(useTheme, () => controller);
106
+ // Wire the daisy color resolver into `@sigx/lynx-icons`'s injectable
107
+ // so any `<Icon variant="primary">` rendered inside this subtree gets
108
+ // the daisy primary hex automatically. Reading `state.name` inside
109
+ // the resolver makes every icon's render re-run when the theme flips.
110
+ const resolver = (iconProps) => {
111
+ const variant = iconProps.variant;
112
+ if (!variant)
113
+ return undefined;
114
+ // Every theme's palette lives in the registry; fall back to daisy-light
115
+ // if the active theme isn't registered. SVG fills can't read CSS vars,
116
+ // so the resolved hex/rgb is substituted into the fill at render time.
117
+ const palette = colorsOf(state.name) ?? colorsOf('daisy-light');
118
+ return palette?.[variant];
119
+ };
120
+ defineProvide(useIconColorResolver, () => resolver);
121
+ // Subscribe to system color-scheme changes. Both PrimitiveSignal and
122
+ // Computed expose `.subscribe(fn)` returning an unsubscribe handle —
123
+ // we lean on the structural shape so this file doesn't pull
124
+ // @sigx/reactivity into its imports.
125
+ let unsubscribe;
126
+ onMounted(() => {
127
+ // Re-seed once mounted — covers the case where the native publisher
128
+ // populated `__globalProps` between setup and mount.
129
+ applySystem(readScheme());
130
+ const sig = systemScheme;
131
+ if (typeof sig.subscribe === 'function') {
132
+ unsubscribe = sig.subscribe(() => applySystem(readScheme()));
133
+ }
134
+ });
135
+ onUnmounted(() => {
136
+ unsubscribe?.();
137
+ unsubscribe = undefined;
138
+ });
139
+ return () => {
140
+ // Every theme is data. Apply its color tokens as inline CSS custom
141
+ // properties — Lynx inherits custom properties to descendants, so
142
+ // component classes resolve `var(--color-*)` against these (the same
143
+ // mechanism SafeAreaProvider uses for `--sat`/`--sal`). The `daisy`
144
+ // base class supplies theme-agnostic structural tokens (radius,
145
+ // sizing); a theme may override roundness via `radius`. The root
146
+ // background/text are painted from the palette literals (inline
147
+ // `var()` values don't resolve in Lynx).
148
+ const palette = colorsOf(state.name) ?? colorsOf('daisy-light');
149
+ const radius = radiusOf(state.name);
150
+ const style = {
151
+ flexGrow: 1,
152
+ flexShrink: 1,
153
+ flexBasis: 0,
154
+ minHeight: 0,
155
+ display: 'flex',
156
+ flexDirection: 'column',
157
+ backgroundColor: palette['base-100'],
158
+ color: palette['base-content'],
159
+ };
160
+ for (const key in palette) {
161
+ style[`--color-${key}`] = palette[key];
162
+ }
163
+ if (radius) {
164
+ if (radius.box)
165
+ style['--rounded-box'] = radius.box;
166
+ if (radius.btn)
167
+ style['--rounded-btn'] = radius.btn;
168
+ if (radius.badge)
169
+ style['--rounded-badge'] = radius.badge;
170
+ if (radius.tab)
171
+ style['--rounded-tab'] = radius.tab;
172
+ if (radius.selector)
173
+ style['--rounded-selector'] = radius.selector;
174
+ if (radius.toggle)
175
+ style['--rounded-toggle'] = radius.toggle;
176
+ }
177
+ if (props.style)
178
+ Object.assign(style, props.style);
179
+ return (_jsx("view", { class: `daisy${props.class ? ' ' + props.class : ''}`, style: style, children: slots.default?.() }));
180
+ };
181
+ });
182
+ // Re-export registry helpers so consumers only need `@sigx/lynx-daisyui`.
183
+ export { listThemes, registerTheme, extendTheme, pickThemeFor, pairOf, variantOf, colorsOf, radiusOf, } from './registry.js';