@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
@@ -0,0 +1,90 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@sigx/lynx/jsx-runtime";
2
+ /**
3
+ * `<NavTabBar>` — daisy-themed tab bar for `@sigx/lynx-navigation`.
4
+ *
5
+ * Pairs with `<Tabs>` + `<Tabs.Screen>` from `@sigx/lynx-navigation`:
6
+ * subscribes to `useTabs()` for the active tab + tab list, dispatches
7
+ * tab changes via `setActive`. Pure UI / styling; the navigation
8
+ * package owns state.
9
+ *
10
+ * Default visual treatment: bottom navigation bar — base-200 background,
11
+ * top separator line, active label in primary color.
12
+ *
13
+ * Use the standalone daisy `<Tabs>` / `<Tab>` (also exported from this
14
+ * package) instead when you want generic tab UI not driven by navigation
15
+ * state (e.g. segmented controls inside a settings panel).
16
+ */
17
+ import { component } from '@sigx/lynx';
18
+ import { Pressable } from '@sigx/lynx-gestures';
19
+ import { Icon } from '@sigx/lynx-icons';
20
+ import { useTabs } from '@sigx/lynx-navigation';
21
+ import { PRESSED_SCALE, PRESSED_OPACITY } from '../shared/press.js';
22
+ /** Narrow `TabInfo.icon` to its `IconSpec` variant — the bar renders `<Icon>` for these. */
23
+ const isIconSpec = (v) => typeof v === 'object' && v !== null && 'set' in v && 'name' in v
24
+ && typeof v.set === 'string'
25
+ && typeof v.name === 'string';
26
+ const backgroundClass = {
27
+ 'base-100': 'bg-base-100',
28
+ 'base-200': 'bg-base-200',
29
+ 'base-300': 'bg-base-300',
30
+ 'transparent': '',
31
+ };
32
+ export const NavTabBar = component(({ props }) => {
33
+ const nav = useTabs();
34
+ return () => {
35
+ const tabs = nav.tabs;
36
+ const active = nav.active;
37
+ const renderer = props.renderTab;
38
+ const position = props.position ?? 'bottom';
39
+ const bg = backgroundClass[props.background ?? 'base-200'];
40
+ const bordered = props.bordered ?? true;
41
+ // Bottom tab bar → top border. Top tab bar → bottom border.
42
+ const borderClass = bordered
43
+ ? (position === 'bottom' ? 'border-t border-base-300' : 'border-b border-base-300')
44
+ : '';
45
+ const containerClass = ['flex flex-row', bg, borderClass].filter(Boolean).join(' ');
46
+ return (_jsx("view", { "accessibility-element": false, class: containerClass, children: tabs.map((info) => {
47
+ const isActive = info.name === active;
48
+ const onPress = () => nav.setActive(info.name);
49
+ if (renderer) {
50
+ return renderer(info, { active: isActive, onPress });
51
+ }
52
+ return (_jsx(DefaultNavTab, { info: info, active: isActive, onPress: onPress }));
53
+ }) }));
54
+ };
55
+ });
56
+ /**
57
+ * Pixel size the bar uses when rendering `<Icon>` from an `IconSpec`.
58
+ * Matches the default tab-row height visually.
59
+ */
60
+ const TAB_ICON_SIZE = 22;
61
+ const DefaultNavTab = component(({ props }) => {
62
+ return () => {
63
+ const label = props.info.label ?? props.info.name;
64
+ const a11y = props.info.accessibilityLabel ?? label;
65
+ // Label uses native CSS color via daisy `text-*` classes — Lynx's
66
+ // `<text>` honors color inheritance normally. The icon path below
67
+ // can't rely on the same trick (see comment there for why).
68
+ const labelTone = props.active ? 'text-primary' : 'text-base-content opacity-60';
69
+ const weight = props.active ? 'font-semibold' : '';
70
+ const icon = props.info.icon;
71
+ // For an `IconSpec`, render `<Icon>` with the matching daisy
72
+ // variant. `<ThemeProvider>`'s color resolver maps `'primary'` /
73
+ // `'base-content'` (etc.) to the current theme's hex value, which
74
+ // `<Icon>` substitutes directly into the SVG `fill=` attribute —
75
+ // Lynx's `<svg content=…>` parses inline SVG in isolation and
76
+ // doesn't inherit host `color`, so class-based theming doesn't
77
+ // reach the SVG content. Inactive layers `opacity-60` as a class
78
+ // on the outer element (opacity does propagate to the raster).
79
+ //
80
+ // For a `JSXElement`, the consumer is in charge of styling — we
81
+ // leave it untouched. They can opt into the same theming by
82
+ // passing `variant="primary"` themselves.
83
+ const iconVariant = props.active ? 'primary' : 'base-content';
84
+ const iconClass = props.active ? undefined : 'opacity-60';
85
+ const renderedIcon = isIconSpec(icon)
86
+ ? _jsx(Icon, { set: icon.set, name: icon.name, size: TAB_ICON_SIZE, variant: iconVariant, class: iconClass })
87
+ : (icon ?? null);
88
+ return (_jsxs(Pressable, { class: "flex-1 items-center justify-center py-3", pressedScale: PRESSED_SCALE, pressedOpacity: PRESSED_OPACITY, longPressDuration: 0, "accessibility-element": true, "accessibility-label": a11y, "accessibility-trait": "button", "accessibility-status": props.active ? 'selected' : undefined, onPress: () => props.onPress(), children: [renderedIcon, _jsx("text", { class: `text-sm ${labelTone} ${weight}`, children: label })] }));
89
+ };
90
+ });
@@ -0,0 +1,59 @@
1
+ import { type Define, type PrimitiveSignal, type SharedValue } from '@sigx/lynx';
2
+ import { type DaisyColor } from '../shared/styles.js';
3
+ /**
4
+ * Visual style for the swiper page indicator.
5
+ *
6
+ * - `dots` — equally-spaced circles, the active one fades in via opacity.
7
+ * Today's default. Cheap (opacity-only MT mapper, no layout each frame).
8
+ * - `bar` — fixed track with a single sliding thumb. Single MT binding
9
+ * regardless of page count, so cheapest for very long carousels.
10
+ * - `pill` — the active dot stretches horizontally into a pill while
11
+ * neighbours stay circular. Uses `scaleX` so siblings don't reflow.
12
+ * - `numbered` — text counter like `2 / 5`. Pure BG-thread, no animation.
13
+ * - `scale-pulse` — circles where the active one scales up. No colour
14
+ * crossfade — pairs well with monochrome palettes.
15
+ */
16
+ export type SwiperIndicatorVariant = 'dots' | 'bar' | 'pill' | 'numbered' | 'scale-pulse';
17
+ export type SwiperIndicatorSize = 'xs' | 'sm' | 'md' | 'lg';
18
+ export type SwiperIndicatorProps = Define.Prop<'variant', SwiperIndicatorVariant, false>
19
+ /** Live MT pixel offset from the parent `<Swiper>`. Required for all animated variants. */
20
+ & Define.Prop<'offset', SharedValue<number>, false>
21
+ /** Page width in CSS px. Must match the Swiper's effective page width. */
22
+ & Define.Prop<'pageWidth', number, false>
23
+ /** Total page count. */
24
+ & Define.Prop<'count', number, true>
25
+ /**
26
+ * Current page (whole-units). Required for `numbered`, used by `bar`
27
+ * as fallback when `offset` isn't wired, and consumed by all variants
28
+ * for tap-to-jump.
29
+ */
30
+ & Define.Prop<'index', PrimitiveSignal<number>, false> & Define.Prop<'color', DaisyColor, false> & Define.Prop<'inactiveColor', DaisyColor, false> & Define.Prop<'size', SwiperIndicatorSize, false>
31
+ /**
32
+ * Tap-to-jump handler. The receiver should typically write
33
+ * `index.value = i` to glide the swiper to that page.
34
+ */
35
+ & Define.Prop<'onDotPress', (index: number) => void, false> & Define.Prop<'class', string, false> & Define.Prop<'style', Record<string, string | number>, false>;
36
+ /**
37
+ * Themed swiper page indicator with five preset variants. Each variant
38
+ * is a thin shell over a headless hook from `@sigx/lynx-gestures` (see
39
+ * `useSwiperDotProgress`, `useSwiperDotScale`, `useSwiperDotGrowX`,
40
+ * `useSwiperDotTranslate`). For a fully custom indicator, compose the
41
+ * hooks yourself rather than forking this file.
42
+ *
43
+ * @example
44
+ * ```tsx
45
+ * const offset = useSharedValue(0);
46
+ * const idx = signal({ value: 0 });
47
+ * <Swiper offset={offset} index={idx} width={W}>…</Swiper>
48
+ * <SwiperIndicator
49
+ * variant="pill"
50
+ * offset={offset}
51
+ * pageWidth={W}
52
+ * count={photos.length}
53
+ * index={idx}
54
+ * color="primary"
55
+ * onDotPress={(i) => { idx.value = i; }}
56
+ * />
57
+ * ```
58
+ */
59
+ export declare const SwiperIndicator: import("@sigx/runtime-core").ComponentFactory<SwiperIndicatorProps, void, {}>;
@@ -0,0 +1,232 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@sigx/lynx/jsx-runtime";
2
+ import { component, effect, signal, } from '@sigx/lynx';
3
+ import { useSwiperDotProgress, useSwiperDotScale, useSwiperDotGrowX, useSwiperDotTranslate, } from '@sigx/lynx-gestures';
4
+ import { resolveDaisyColor } from '../shared/styles.js';
5
+ const SIZE_TABLE = {
6
+ xs: { dot: 4, gap: 4, barHeight: 3, fontSize: 11 },
7
+ sm: { dot: 6, gap: 6, barHeight: 4, fontSize: 12 },
8
+ md: { dot: 8, gap: 8, barHeight: 5, fontSize: 14 },
9
+ lg: { dot: 12, gap: 10, barHeight: 6, fontSize: 16 },
10
+ };
11
+ /**
12
+ * Themed swiper page indicator with five preset variants. Each variant
13
+ * is a thin shell over a headless hook from `@sigx/lynx-gestures` (see
14
+ * `useSwiperDotProgress`, `useSwiperDotScale`, `useSwiperDotGrowX`,
15
+ * `useSwiperDotTranslate`). For a fully custom indicator, compose the
16
+ * hooks yourself rather than forking this file.
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * const offset = useSharedValue(0);
21
+ * const idx = signal({ value: 0 });
22
+ * <Swiper offset={offset} index={idx} width={W}>…</Swiper>
23
+ * <SwiperIndicator
24
+ * variant="pill"
25
+ * offset={offset}
26
+ * pageWidth={W}
27
+ * count={photos.length}
28
+ * index={idx}
29
+ * color="primary"
30
+ * onDotPress={(i) => { idx.value = i; }}
31
+ * />
32
+ * ```
33
+ */
34
+ export const SwiperIndicator = component(({ props }) => {
35
+ return () => {
36
+ const variant = props.variant ?? 'dots';
37
+ const size = SIZE_TABLE[props.size ?? 'md'];
38
+ const activeColor = resolveDaisyColor(props.color ?? 'primary');
39
+ const inactiveColor = resolveDaisyColor(props.inactiveColor ?? 'base-content');
40
+ if (variant === 'numbered') {
41
+ return (_jsx(NumberedIndicator, { count: props.count, index: props.index ?? FALLBACK_INDEX, color: activeColor, fontSize: size.fontSize, class: props.class, style: props.style }));
42
+ }
43
+ if (variant === 'bar') {
44
+ if (props.offset == null || props.pageWidth == null)
45
+ return null;
46
+ return (_jsx(BarIndicator, { offset: props.offset, pageWidth: props.pageWidth, count: props.count, activeColor: activeColor, inactiveColor: inactiveColor, barHeight: size.barHeight, dotSize: size.dot, gap: size.gap, onDotPress: props.onDotPress, class: props.class, style: props.style }));
47
+ }
48
+ if (props.offset == null || props.pageWidth == null)
49
+ return null;
50
+ return (_jsx("view", { class: props.class, style: {
51
+ display: 'flex',
52
+ flexDirection: 'row',
53
+ alignItems: 'center',
54
+ justifyContent: 'center',
55
+ gap: size.gap + 'px',
56
+ ...(props.style || {}),
57
+ }, children: Array.from({ length: props.count }, (_, i) => (_jsx(Dot, { index: i, offset: props.offset, pageWidth: props.pageWidth, variant: variant, size: size, activeColor: activeColor, inactiveColor: inactiveColor, onPress: props.onDotPress }, i))) }));
58
+ };
59
+ });
60
+ // ─────────────────────────────────────────────────────────────────────
61
+ // Per-variant pieces. Each owns a single `useAnimatedStyle` call-site
62
+ // (per-iteration call inside `.map()` is fine — call-sites are stable).
63
+ const FALLBACK_INDEX = signal({ value: 0 });
64
+ const Dot = component(({ props }) => {
65
+ // Each branch picks a different headless hook. Variants that need
66
+ // *two* simultaneous channels (opacity AND scale, or scale AND scaleX)
67
+ // need two refs — one per element — because `useAnimatedStyle` is
68
+ // one-binding-per-element.
69
+ if (props.variant === 'dots') {
70
+ return DotsBody(props);
71
+ }
72
+ if (props.variant === 'pill') {
73
+ return PillBody(props);
74
+ }
75
+ // scale-pulse
76
+ return ScalePulseBody(props);
77
+ });
78
+ function DotsBody(props) {
79
+ const overlayRef = useSwiperDotProgress({
80
+ offset: props.offset,
81
+ pageWidth: props.pageWidth,
82
+ index: props.index,
83
+ });
84
+ return () => (_jsx("view", { catchtap: props.onPress ? () => props.onPress?.(props.index) : undefined, style: {
85
+ width: props.size.dot + 'px',
86
+ height: props.size.dot + 'px',
87
+ borderRadius: (props.size.dot / 2) + 'px',
88
+ backgroundColor: withAlpha(props.inactiveColor, 0.4),
89
+ position: 'relative',
90
+ overflow: 'hidden',
91
+ }, children: _jsx("view", { "main-thread:ref": overlayRef, style: {
92
+ position: 'absolute',
93
+ left: '0',
94
+ top: '0',
95
+ right: '0',
96
+ bottom: '0',
97
+ backgroundColor: props.activeColor,
98
+ opacity: '0',
99
+ } }) }));
100
+ }
101
+ function PillBody(props) {
102
+ // Pill stretches horizontally via scaleX (no layout cost) and brightens
103
+ // via opacity on the active-colour overlay. Both channels target the
104
+ // same dot — but each needs its own bound element, so we wrap the
105
+ // overlay inside a scaling shell.
106
+ const shellRef = useSwiperDotGrowX({
107
+ offset: props.offset,
108
+ pageWidth: props.pageWidth,
109
+ index: props.index,
110
+ inactive: 1,
111
+ active: 3,
112
+ });
113
+ const overlayRef = useSwiperDotProgress({
114
+ offset: props.offset,
115
+ pageWidth: props.pageWidth,
116
+ index: props.index,
117
+ });
118
+ return () => (_jsx("view", { catchtap: props.onPress ? () => props.onPress?.(props.index) : undefined, "main-thread:ref": shellRef, style: {
119
+ width: props.size.dot + 'px',
120
+ height: props.size.dot + 'px',
121
+ borderRadius: (props.size.dot / 2) + 'px',
122
+ backgroundColor: withAlpha(props.inactiveColor, 0.4),
123
+ position: 'relative',
124
+ overflow: 'hidden',
125
+ transformOrigin: 'center center',
126
+ }, children: _jsx("view", { "main-thread:ref": overlayRef, style: {
127
+ position: 'absolute',
128
+ left: '0',
129
+ top: '0',
130
+ right: '0',
131
+ bottom: '0',
132
+ backgroundColor: props.activeColor,
133
+ opacity: '0',
134
+ } }) }));
135
+ }
136
+ function ScalePulseBody(props) {
137
+ // No colour crossfade — pure scale. Active dot uses `activeColor`,
138
+ // inactive uses `inactiveColor` at low alpha. Visual is monochrome
139
+ // friendly.
140
+ const scaleRef = useSwiperDotScale({
141
+ offset: props.offset,
142
+ pageWidth: props.pageWidth,
143
+ index: props.index,
144
+ inactive: 1,
145
+ active: 1.6,
146
+ });
147
+ const opacityRef = useSwiperDotProgress({
148
+ offset: props.offset,
149
+ pageWidth: props.pageWidth,
150
+ index: props.index,
151
+ });
152
+ return () => (_jsx("view", { catchtap: props.onPress ? () => props.onPress?.(props.index) : undefined, "main-thread:ref": scaleRef, style: {
153
+ width: props.size.dot + 'px',
154
+ height: props.size.dot + 'px',
155
+ borderRadius: (props.size.dot / 2) + 'px',
156
+ backgroundColor: withAlpha(props.inactiveColor, 0.4),
157
+ position: 'relative',
158
+ overflow: 'hidden',
159
+ }, children: _jsx("view", { "main-thread:ref": opacityRef, style: {
160
+ position: 'absolute',
161
+ left: '0',
162
+ top: '0',
163
+ right: '0',
164
+ bottom: '0',
165
+ backgroundColor: props.activeColor,
166
+ opacity: '0',
167
+ } }) }));
168
+ }
169
+ const BarIndicator = component(({ props }) => {
170
+ // The thumb advances by (dot + gap) per page. We use the headless
171
+ // translate hook — a single MT binding regardless of page count.
172
+ const step = props.dotSize + props.gap;
173
+ const thumbRef = useSwiperDotTranslate({
174
+ offset: props.offset,
175
+ pageWidth: props.pageWidth,
176
+ step,
177
+ });
178
+ return () => {
179
+ const trackWidth = props.count * props.dotSize + Math.max(0, props.count - 1) * props.gap;
180
+ return (_jsxs("view", { class: props.class, style: {
181
+ position: 'relative',
182
+ width: trackWidth + 'px',
183
+ height: props.barHeight + 'px',
184
+ borderRadius: (props.barHeight / 2) + 'px',
185
+ backgroundColor: withAlpha(props.inactiveColor, 0.25),
186
+ overflow: 'visible',
187
+ ...(props.style || {}),
188
+ }, children: [props.onDotPress
189
+ ? (_jsx("view", { style: {
190
+ position: 'absolute',
191
+ inset: '0',
192
+ display: 'flex',
193
+ flexDirection: 'row',
194
+ alignItems: 'center',
195
+ }, children: Array.from({ length: props.count }, (_, i) => (_jsx("view", { catchtap: () => props.onDotPress?.(i), style: {
196
+ width: (props.dotSize + props.gap) + 'px',
197
+ height: '100%',
198
+ } }, i))) }))
199
+ : null, _jsx("view", { "main-thread:ref": thumbRef, style: {
200
+ position: 'absolute',
201
+ left: '0',
202
+ top: '0',
203
+ width: props.dotSize + 'px',
204
+ height: '100%',
205
+ borderRadius: (props.barHeight / 2) + 'px',
206
+ backgroundColor: props.activeColor,
207
+ } })] }));
208
+ };
209
+ });
210
+ const NumberedIndicator = component(({ props }) => {
211
+ const label = signal({ value: '' });
212
+ effect(() => {
213
+ label.value = `${(props.index.value | 0) + 1} / ${props.count}`;
214
+ });
215
+ return () => (_jsx("text", { class: props.class, style: {
216
+ color: props.color,
217
+ fontSize: props.fontSize + 'px',
218
+ fontWeight: '600',
219
+ ...(props.style || {}),
220
+ }, children: label.value }));
221
+ });
222
+ // ─────────────────────────────────────────────────────────────────────
223
+ // Helpers
224
+ /**
225
+ * Apply an alpha to a CSS colour value. Works for `var(--color-*)`
226
+ * (uses `color-mix`) and for raw rgb/hex strings (uses `color-mix`
227
+ * too — broadly supported on the platforms Lynx targets).
228
+ */
229
+ function withAlpha(color, alpha) {
230
+ const pct = Math.round(Math.max(0, Math.min(1, alpha)) * 100);
231
+ return `color-mix(in srgb, ${color} ${pct}%, transparent)`;
232
+ }
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@sigx/lynx/jsx-runtime";
2
+ import { component, compound } from '@sigx/lynx';
3
+ import { Pressable } from '@sigx/lynx-gestures';
4
+ import { PRESSED_SCALE, PRESSED_OPACITY } from '../shared/press.js';
5
+ const _Tabs = component(({ props, slots }) => {
6
+ return () => (_jsx("view", { class: `tabs${props.class ? ' ' + props.class : ''}`, children: slots.default?.() }));
7
+ });
8
+ const Tab = component(({ props, slots }) => {
9
+ return () => {
10
+ const isActive = props.active ?? false;
11
+ return (_jsxs(Pressable, { class: `tab${isActive ? ' tab-active' : ''}${props.class ? ' ' + props.class : ''}`, pressedScale: PRESSED_SCALE, pressedOpacity: PRESSED_OPACITY, longPressDuration: 0, onPress: () => {
12
+ props.onPress?.();
13
+ }, children: [slots.default?.(), props.label ? _jsx("text", { children: props.label }) : null] }));
14
+ };
15
+ });
16
+ export const Tabs = compound(_Tabs, {
17
+ Tab,
18
+ });
@@ -1,40 +1,66 @@
1
- import e from "tailwindcss/plugin";
2
- //#region src/preset/index.ts
3
- var t = {
4
- primary: "var(--color-primary)",
5
- "primary-content": "var(--color-primary-content)",
6
- secondary: "var(--color-secondary)",
7
- "secondary-content": "var(--color-secondary-content)",
8
- accent: "var(--color-accent)",
9
- "accent-content": "var(--color-accent-content)",
10
- neutral: "var(--color-neutral)",
11
- "neutral-content": "var(--color-neutral-content)",
12
- "base-100": "var(--color-base-100)",
13
- "base-200": "var(--color-base-200)",
14
- "base-300": "var(--color-base-300)",
15
- "base-content": "var(--color-base-content)",
16
- info: "var(--color-info)",
17
- "info-content": "var(--color-info-content)",
18
- success: "var(--color-success)",
19
- "success-content": "var(--color-success-content)",
20
- warning: "var(--color-warning)",
21
- "warning-content": "var(--color-warning-content)",
22
- error: "var(--color-error)",
23
- "error-content": "var(--color-error-content)"
24
- }, n = e(({ addUtilities: e }) => {
25
- e({ ".flex-fill": {
26
- flexGrow: "1",
27
- flexShrink: "1",
28
- flexBasis: "0",
29
- minHeight: "0",
30
- display: "flex",
31
- flexDirection: "column"
32
- } });
33
- }), r = {
34
- theme: { extend: { colors: t } },
35
- plugins: [n]
36
- }, i = r;
37
- //#endregion
38
- export { r as DaisyLynxPreset, i as daisyuiPreset };
39
-
40
- //# sourceMappingURL=index.js.map
1
+ import plugin from 'tailwindcss/plugin';
2
+ /**
3
+ * DaisyUI Lynx Tailwind Preset
4
+ *
5
+ * Maps DaisyUI semantic color tokens to CSS custom properties
6
+ * defined in @sigx/lynx-daisyui/styles. Consumers add this
7
+ * preset to their tailwind.config.ts so utilities like
8
+ * `bg-primary` and `text-base-content` resolve to our tokens.
9
+ *
10
+ * Also ships a `flex-fill` utility — the Lynx-correct "fill remaining
11
+ * space" class. Why this is in our preset rather than baked into Lynx's
12
+ * own tailwind preset: in Lynx (like React Native) `flex: 1` shorthand
13
+ * expands to `flex: 1 1 auto`, where `flexBasis: 'auto'` sizes the box
14
+ * to its content first, collapsing the layout chain. The browser-CSS
15
+ * intuition that `flex-1` = "fill remaining space" is wrong here, and
16
+ * Tailwind's own `flex-1` class expands to the same broken shorthand.
17
+ * `flex-fill` writes the long-form properties directly so the result
18
+ * actually fills.
19
+ */
20
+ const daisyColors = {
21
+ 'primary': 'var(--color-primary)',
22
+ 'primary-content': 'var(--color-primary-content)',
23
+ 'secondary': 'var(--color-secondary)',
24
+ 'secondary-content': 'var(--color-secondary-content)',
25
+ 'accent': 'var(--color-accent)',
26
+ 'accent-content': 'var(--color-accent-content)',
27
+ 'neutral': 'var(--color-neutral)',
28
+ 'neutral-content': 'var(--color-neutral-content)',
29
+ 'base-100': 'var(--color-base-100)',
30
+ 'base-200': 'var(--color-base-200)',
31
+ 'base-300': 'var(--color-base-300)',
32
+ 'base-content': 'var(--color-base-content)',
33
+ 'info': 'var(--color-info)',
34
+ 'info-content': 'var(--color-info-content)',
35
+ 'success': 'var(--color-success)',
36
+ 'success-content': 'var(--color-success-content)',
37
+ 'warning': 'var(--color-warning)',
38
+ 'warning-content': 'var(--color-warning-content)',
39
+ 'error': 'var(--color-error)',
40
+ 'error-content': 'var(--color-error-content)',
41
+ };
42
+ const lynxLayoutPlugin = plugin(({ addUtilities }) => {
43
+ addUtilities({
44
+ // Long-form flex-fill — the Lynx-correct "take remaining space along
45
+ // the main axis" utility. Default flex direction column; consumers
46
+ // who want a horizontal fill compose with `flex-row` on the parent.
47
+ '.flex-fill': {
48
+ flexGrow: '1',
49
+ flexShrink: '1',
50
+ flexBasis: '0',
51
+ minHeight: '0',
52
+ display: 'flex',
53
+ flexDirection: 'column',
54
+ },
55
+ });
56
+ });
57
+ export const DaisyLynxPreset = {
58
+ theme: {
59
+ extend: {
60
+ colors: daisyColors,
61
+ },
62
+ },
63
+ plugins: [lynxLayoutPlugin],
64
+ };
65
+ /** Alias — preferred consumer name. */
66
+ export const daisyuiPreset = DaisyLynxPreset;
@@ -0,0 +1,2 @@
1
+ export declare const PRESSED_SCALE = 0.97;
2
+ export declare const PRESSED_OPACITY = 0.85;
@@ -0,0 +1,6 @@
1
+ // Shared press-feedback defaults applied across all interactive daisyui
2
+ // components. Lynx has no CSS `:active` support, so press feedback comes from
3
+ // `<Pressable>` (in `@sigx/lynx-gestures`) flipping inline opacity/transform
4
+ // on the main thread.
5
+ export const PRESSED_SCALE = 0.97;
6
+ export const PRESSED_OPACITY = 0.85;
@@ -1,3 +1,25 @@
1
+ /**
2
+ * DaisyUI color tokens — the set of semantic colors exposed by the
3
+ * built-in themes (`daisy-light` / `daisy-dark`) as `--color-<token>`
4
+ * CSS custom properties.
5
+ *
6
+ * Used by layout components' `background` prop so consumers can write
7
+ * `<Col background="base-100">` instead of `<Col class="bg-base-100">`
8
+ * and still get autocomplete + type safety.
9
+ *
10
+ * Single source of truth: both the `DaisyColor` union and the runtime
11
+ * `DAISY_COLOR_TOKENS` Set are derived from this tuple, so adding /
12
+ * removing a token in one place is impossible.
13
+ */
14
+ declare const DAISY_COLOR_TOKEN_LIST: readonly ['primary', 'primary-content', 'secondary', 'secondary-content', 'accent', 'accent-content', 'neutral', 'neutral-content', 'base-100', 'base-200', 'base-300', 'base-content', 'info', 'info-content', 'success', 'success-content', 'warning', 'warning-content', 'error', 'error-content'];
15
+ export type DaisyColor = typeof DAISY_COLOR_TOKEN_LIST[number];
16
+ /**
17
+ * Resolve a `background` prop value to a CSS color string.
18
+ *
19
+ * - Known daisyUI tokens (e.g. `'base-100'`) → `var(--color-base-100)`.
20
+ * - Anything else (`'#ffaa00'`, `'rgb(…)'`, `'var(--my-custom)'`) is passed through unchanged.
21
+ */
22
+ export declare function resolveDaisyColor(value: string): string;
1
23
  export type SpacingValue = number | {
2
24
  x?: number;
3
25
  y?: number;
@@ -6,14 +28,20 @@ export type SpacingValue = number | {
6
28
  bottom?: number;
7
29
  left?: number;
8
30
  };
31
+ /**
32
+ * Accepts a daisyUI color token (autocompleted) OR any raw CSS color
33
+ * string (`'#fff'`, `'rgb(…)'`, `'var(--foo)'`).
34
+ */
35
+ export type BackgroundValue = DaisyColor | (string & {});
9
36
  export interface BoxProps {
10
37
  width?: number | string;
11
38
  height?: number | string;
12
39
  flex?: number;
13
- background?: string;
40
+ background?: BackgroundValue;
14
41
  borderRadius?: number;
15
42
  padding?: SpacingValue;
16
43
  margin?: SpacingValue;
17
44
  }
18
45
  export declare function resolveSpacing(value: SpacingValue | undefined, prefix: 'padding' | 'margin'): Record<string, number>;
19
46
  export declare function resolveBoxStyle(props: BoxProps): Record<string, unknown>;
47
+ export {};
@@ -0,0 +1,90 @@
1
+ /**
2
+ * DaisyUI color tokens — the set of semantic colors exposed by the
3
+ * built-in themes (`daisy-light` / `daisy-dark`) as `--color-<token>`
4
+ * CSS custom properties.
5
+ *
6
+ * Used by layout components' `background` prop so consumers can write
7
+ * `<Col background="base-100">` instead of `<Col class="bg-base-100">`
8
+ * and still get autocomplete + type safety.
9
+ *
10
+ * Single source of truth: both the `DaisyColor` union and the runtime
11
+ * `DAISY_COLOR_TOKENS` Set are derived from this tuple, so adding /
12
+ * removing a token in one place is impossible.
13
+ */
14
+ const DAISY_COLOR_TOKEN_LIST = [
15
+ 'primary', 'primary-content',
16
+ 'secondary', 'secondary-content',
17
+ 'accent', 'accent-content',
18
+ 'neutral', 'neutral-content',
19
+ 'base-100', 'base-200', 'base-300', 'base-content',
20
+ 'info', 'info-content',
21
+ 'success', 'success-content',
22
+ 'warning', 'warning-content',
23
+ 'error', 'error-content',
24
+ ];
25
+ const DAISY_COLOR_TOKENS = new Set(DAISY_COLOR_TOKEN_LIST);
26
+ /**
27
+ * Resolve a `background` prop value to a CSS color string.
28
+ *
29
+ * - Known daisyUI tokens (e.g. `'base-100'`) → `var(--color-base-100)`.
30
+ * - Anything else (`'#ffaa00'`, `'rgb(…)'`, `'var(--my-custom)'`) is passed through unchanged.
31
+ */
32
+ export function resolveDaisyColor(value) {
33
+ return DAISY_COLOR_TOKENS.has(value)
34
+ ? `var(--color-${value})`
35
+ : value;
36
+ }
37
+ export function resolveSpacing(value, prefix) {
38
+ if (value === undefined)
39
+ return {};
40
+ if (typeof value === 'number') {
41
+ return {
42
+ [`${prefix}Top`]: value,
43
+ [`${prefix}Right`]: value,
44
+ [`${prefix}Bottom`]: value,
45
+ [`${prefix}Left`]: value,
46
+ };
47
+ }
48
+ const style = {};
49
+ if (value.top !== undefined)
50
+ style[`${prefix}Top`] = value.top;
51
+ else if (value.y !== undefined)
52
+ style[`${prefix}Top`] = value.y;
53
+ if (value.bottom !== undefined)
54
+ style[`${prefix}Bottom`] = value.bottom;
55
+ else if (value.y !== undefined)
56
+ style[`${prefix}Bottom`] = value.y;
57
+ if (value.right !== undefined)
58
+ style[`${prefix}Right`] = value.right;
59
+ else if (value.x !== undefined)
60
+ style[`${prefix}Right`] = value.x;
61
+ if (value.left !== undefined)
62
+ style[`${prefix}Left`] = value.left;
63
+ else if (value.x !== undefined)
64
+ style[`${prefix}Left`] = value.x;
65
+ return style;
66
+ }
67
+ export function resolveBoxStyle(props) {
68
+ const style = {};
69
+ if (props.width !== undefined)
70
+ style.width = props.width;
71
+ if (props.height !== undefined)
72
+ style.height = props.height;
73
+ if (props.flex !== undefined) {
74
+ // Lynx (like React Native) expands `flex: n` shorthand to
75
+ // `flex: n n auto`, where `flexBasis: 'auto'` means "size to content
76
+ // first" — which collapses the layout chain. Write the long-form so
77
+ // `<Center flex={1}>` etc. actually fill remaining space.
78
+ style.flexGrow = props.flex;
79
+ style.flexShrink = 1;
80
+ style.flexBasis = 0;
81
+ style.minHeight = 0;
82
+ }
83
+ if (props.background !== undefined)
84
+ style.backgroundColor = resolveDaisyColor(props.background);
85
+ if (props.borderRadius !== undefined)
86
+ style.borderRadius = props.borderRadius;
87
+ Object.assign(style, resolveSpacing(props.padding, 'padding'));
88
+ Object.assign(style, resolveSpacing(props.margin, 'margin'));
89
+ return style;
90
+ }