@mindees/atlas 0.7.0 → 0.9.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/components.d.ts +71 -1
- package/dist/components.d.ts.map +1 -1
- package/dist/components.js +205 -2
- package/dist/components.js.map +1 -1
- package/dist/form.d.ts +53 -0
- package/dist/form.d.ts.map +1 -0
- package/dist/form.js +91 -0
- package/dist/form.js.map +1 -0
- package/dist/hooks.d.ts +63 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +102 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/components.d.ts
CHANGED
|
@@ -92,6 +92,76 @@ interface ActivityIndicatorProps extends BaseProps {
|
|
|
92
92
|
readonly animating?: boolean;
|
|
93
93
|
}
|
|
94
94
|
declare const ActivityIndicator: Component<ActivityIndicatorProps>;
|
|
95
|
+
/** Props for {@link Checkbox}. (`label` is omitted from a11y to repurpose it as the visible label.) */
|
|
96
|
+
interface CheckboxProps extends Omit<BaseProps, 'style' | 'label'> {
|
|
97
|
+
/** Controlled checked state (static or reactive). */
|
|
98
|
+
readonly value: Reactive<boolean>;
|
|
99
|
+
readonly onValueChange?: (value: boolean) => void;
|
|
100
|
+
readonly disabled?: boolean;
|
|
101
|
+
/** Optional visible label rendered beside the box (a string becomes a `Text`). */
|
|
102
|
+
readonly label?: MindeesNode;
|
|
103
|
+
readonly style?: Reactive<StyleInput>;
|
|
104
|
+
}
|
|
105
|
+
/** An accessible checkbox (RN ships none built-in). Toggles `value`; `aria-checked` tracks it. */
|
|
106
|
+
declare const Checkbox: Component<CheckboxProps>;
|
|
107
|
+
/** One option in a {@link RadioGroup}. */
|
|
108
|
+
interface RadioOption {
|
|
109
|
+
readonly value: string;
|
|
110
|
+
readonly label: MindeesNode;
|
|
111
|
+
}
|
|
112
|
+
/** Props for {@link RadioGroup}. */
|
|
113
|
+
interface RadioGroupProps extends Omit<BaseProps, 'style'> {
|
|
114
|
+
/** Controlled selected value (static or reactive). */
|
|
115
|
+
readonly value: Reactive<string>;
|
|
116
|
+
readonly options: readonly RadioOption[];
|
|
117
|
+
readonly onValueChange?: (value: string) => void;
|
|
118
|
+
readonly disabled?: boolean;
|
|
119
|
+
readonly style?: Reactive<StyleInput>;
|
|
120
|
+
}
|
|
121
|
+
/** A single-select radio group (RN ships none built-in). Each row is `role="radio"`. */
|
|
122
|
+
declare const RadioGroup: Component<RadioGroupProps>;
|
|
123
|
+
/** Props for {@link Skeleton}. */
|
|
124
|
+
interface SkeletonProps extends Omit<BaseProps, 'style'> {
|
|
125
|
+
readonly width?: number | string;
|
|
126
|
+
readonly height?: number | string;
|
|
127
|
+
readonly radius?: number;
|
|
128
|
+
readonly style?: Reactive<StyleInput>;
|
|
129
|
+
}
|
|
130
|
+
/** A muted loading placeholder block (RN ships none built-in). */
|
|
131
|
+
declare const Skeleton: Component<SkeletonProps>;
|
|
132
|
+
/** One tab: a value, a header label, and its panel content. */
|
|
133
|
+
interface TabItem {
|
|
134
|
+
readonly value: string;
|
|
135
|
+
readonly label: MindeesNode;
|
|
136
|
+
readonly content: MindeesNode;
|
|
137
|
+
}
|
|
138
|
+
/** Props for {@link Tabs}. */
|
|
139
|
+
interface TabsProps extends Omit<BaseProps, 'style'> {
|
|
140
|
+
/** Controlled selected tab value (static or reactive). */
|
|
141
|
+
readonly value: Reactive<string>;
|
|
142
|
+
readonly tabs: readonly TabItem[];
|
|
143
|
+
readonly onValueChange?: (value: string) => void;
|
|
144
|
+
readonly style?: Reactive<StyleInput>;
|
|
145
|
+
}
|
|
146
|
+
/** An accessible tab strip + panel (RN ships none built-in). Only the active panel region re-renders. */
|
|
147
|
+
declare const Tabs: Component<TabsProps>;
|
|
148
|
+
/** One collapsible accordion section. */
|
|
149
|
+
interface AccordionSection {
|
|
150
|
+
readonly id: string;
|
|
151
|
+
readonly header: MindeesNode;
|
|
152
|
+
readonly content: MindeesNode;
|
|
153
|
+
}
|
|
154
|
+
/** Props for {@link Accordion}. */
|
|
155
|
+
interface AccordionProps extends Omit<BaseProps, 'style'> {
|
|
156
|
+
readonly sections: readonly AccordionSection[];
|
|
157
|
+
/** Allow more than one section open at once (default: single-open). */
|
|
158
|
+
readonly multiple?: boolean;
|
|
159
|
+
/** Section ids open on first render. */
|
|
160
|
+
readonly defaultOpen?: readonly string[];
|
|
161
|
+
readonly style?: Reactive<StyleInput>;
|
|
162
|
+
}
|
|
163
|
+
/** Collapsible sections (RN ships none built-in). Each header is `aria-expanded`; panels mount lazily. */
|
|
164
|
+
declare const Accordion: Component<AccordionProps>;
|
|
95
165
|
//#endregion
|
|
96
|
-
export { ActivityIndicator, ActivityIndicatorProps, Avatar, AvatarProps, Badge, BadgeProps, Card, CardProps, Chip, ChipProps, Divider, DividerProps, KeyboardAvoidingView, KeyboardAvoidingViewProps, ProgressBar, ProgressBarProps, SafeAreaView, SafeAreaViewProps, Switch, SwitchProps };
|
|
166
|
+
export { Accordion, AccordionProps, AccordionSection, ActivityIndicator, ActivityIndicatorProps, Avatar, AvatarProps, Badge, BadgeProps, Card, CardProps, Checkbox, CheckboxProps, Chip, ChipProps, Divider, DividerProps, KeyboardAvoidingView, KeyboardAvoidingViewProps, ProgressBar, ProgressBarProps, RadioGroup, RadioGroupProps, RadioOption, SafeAreaView, SafeAreaViewProps, Skeleton, SkeletonProps, Switch, SwitchProps, TabItem, Tabs, TabsProps };
|
|
97
167
|
//# sourceMappingURL=components.d.ts.map
|
package/dist/components.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"components.d.ts","names":[],"sources":["../src/components.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"components.d.ts","names":[],"sources":["../src/components.ts"],"mappings":";;;;;;UAqDiB,SAAA,SAAkB,SAAS;EAAA,SACjC,QAAA,GAAW,WAAA;EAQT;EAAA,SANF,OAAA;;WAEA,OAAA;EAI2B;EAAA,SAF3B,MAAA;AAAA;AAAA,cAEE,IAAA,EAAM,SAAS,CAAC,SAAA;;UA4BZ,YAAA,SAAqB,SAAS;EAAA,SACpC,WAAA;EAAA,SACA,SAAA;EAEA;EAAA,SAAA,KAAA;AAAA;AAAA,cAEE,OAAA,EAAS,SAAS,CAAC,YAAA;AAAA,KAoBpB,SAAA;;UAUK,UAAA,SAAmB,SAAA;EAAA,SACzB,QAAA,GAAW,WAAA;EAAA,SACX,IAAA,GAAO,SAAA;AAAA;AAAA,cAEL,KAAA,EAAO,SAAS,CAAC,UAAA;;UAwCb,WAAA,SAAoB,SAAS;EAAA,SACnC,GAAA;EAAA,SACA,IAAA;;WAEA,IAAA;AAAA;AAAA,cAEE,MAAA,EAAQ,SAAS,CAAC,WAAA;;UA2Cd,SAAA,SAAkB,IAAA,CAAK,SAAA;EAAA,SAC7B,KAAA;EAAA,SACA,QAAA,GAAW,QAAA;EAAA,SACX,QAAA;EAAA,SACA,OAAA;EAAA,SACA,OAAA,GAAU,WAAA;EAAA,SACV,QAAA,GAAW,WAAA;EAAA,SACX,KAAA,GAAQ,QAAA,CAAS,UAAA;AAAA;AAAA,cAEf,IAAA,EAAM,SAAS,CAAC,SAAA;;UA4DZ,WAAA,SAAoB,IAAA,CAAK,SAAA;EA9JF;EAAA,SAgK7B,KAAA,EAAO,QAAA;EAAA,SACP,aAAA,IAAiB,KAAA;EAAA,SACjB,QAAA;EAAA,SACA,KAAA,GAAQ,QAAA,CAAS,UAAA;AAAA;AAAA,cAEf,MAAA,EAAQ,SAAS,CAAC,WAAA;;UAgDd,iBAAA,SAA0B,SAAA;EAAA,SAChC,QAAA,GAAW,WAAA;EA1KP;EAAA,SA4KJ,KAAA,GAAQ,aAAA;AAAA;AAAA,cAEN,YAAA,EAAc,SAAS,CAAC,iBAAA;;UAsBpB,yBAAA,SAAkC,SAAS;EAAA,SACjD,QAAA,GAAW,WAAA;AAAA;AAAA,cAET,oBAAA,EAAsB,SAAS,CAAC,yBAAA;;UAY5B,gBAAA,SAAyB,SAAS;EAjK9B;EAAA,SAmKV,KAAA,GAAQ,QAAA;EAAA,SACR,UAAA;EAAA,SACA,KAAA;EAAA,SACA,MAAA;AAAA;AAAA,cAEE,WAAA,EAAa,SAAS,CAAC,gBAAA;;;;;;UAyCnB,sBAAA,SAA+B,SAAS;EAlN9C;EAAA,SAoNA,IAAA;EAnNU;EAAA,SAqNV,KAAA;EApNW;EAAA,SAsNX,SAAA;AAAA;AAAA,cAEE,iBAAA,EAAmB,SAAS,CAAC,sBAAA;;UAoBzB,aAAA,SAAsB,IAAA,CAAK,SAAA;EAzO/B;EAAA,SA2OF,KAAA,EAAO,QAAA;EAAA,SACP,aAAA,IAAiB,KAAA;EAAA,SACjB,QAAA;EA7O2B;EAAA,SA+O3B,KAAA,GAAQ,WAAA;EAAA,SACR,KAAA,GAAQ,QAAA,CAAS,UAAA;AAAA;;cAIf,QAAA,EAAU,SAAS,CAAC,aAAA;;UAqDhB,WAAA;EAAA,SACN,KAAA;EAAA,SACA,KAAA,EAAO,WAAW;AAAA;;UAIZ,eAAA,SAAwB,IAAA,CAAK,SAAA;EAjPnC;EAAA,SAmPA,KAAA,EAAO,QAAA;EAAA,SACP,OAAA,WAAkB,WAAA;EAAA,SAClB,aAAA,IAAiB,KAAA;EAAA,SACjB,QAAA;EAAA,SACA,KAAA,GAAQ,QAAA,CAAS,UAAA;AAAA;;cAIf,UAAA,EAAY,SAAS,CAAC,eAAA;AAxPG;AAAA,UAoTrB,aAAA,SAAsB,IAAA,CAAK,SAAA;EAAA,SACjC,KAAA;EAAA,SACA,MAAA;EAAA,SACA,MAAA;EAAA,SACA,KAAA,GAAQ,QAAA,CAAS,UAAA;AAAA;;cAIf,QAAA,EAAU,SAAS,CAAC,aAAA;;UAoBhB,OAAA;EAAA,SACN,KAAA;EAAA,SACA,KAAA,EAAO,WAAA;EAAA,SACP,OAAA,EAAS,WAAW;AAAA;;UAId,SAAA,SAAkB,IAAA,CAAK,SAAA;EAlS7B;EAAA,SAoSA,KAAA,EAAO,QAAA;EAAA,SACP,IAAA,WAAe,OAAA;EAAA,SACf,aAAA,IAAiB,KAAA;EAAA,SACjB,KAAA,GAAQ,QAAA,CAAS,UAAA;AAAA;;cAIf,IAAA,EAAM,SAAS,CAAC,SAAA;AAzSyB;AAAA,UA8WrC,gBAAA;EAAA,SACN,EAAA;EAAA,SACA,MAAA,EAAQ,WAAA;EAAA,SACR,OAAA,EAAS,WAAW;AAAA;;UAId,cAAA,SAAuB,IAAA,CAAK,SAAA;EAAA,SAClC,QAAA,WAAmB,gBAAA;EA/VG;EAAA,SAiWtB,QAAA;EA1VV;EAAA,SA4VU,WAAA;EAAA,SACA,KAAA,GAAQ,QAAA,CAAS,UAAA;AAAA;AAtV5B;AAAA,cA0Va,SAAA,EAAW,SAAS,CAAC,cAAA"}
|
package/dist/components.js
CHANGED
|
@@ -3,7 +3,7 @@ import { flattenStyle } from "./style.js";
|
|
|
3
3
|
import { toHostProps } from "./host.js";
|
|
4
4
|
import { Image, Pressable, Text, View } from "./primitives.js";
|
|
5
5
|
import { fontWeight, radius, space, useTheme } from "./tokens.js";
|
|
6
|
-
import { createElement } from "@mindees/core";
|
|
6
|
+
import { createElement, signal, untrack } from "@mindees/core";
|
|
7
7
|
//#region src/components.ts
|
|
8
8
|
/**
|
|
9
9
|
* Atlas components — higher-level building blocks composed purely from the primitives
|
|
@@ -302,7 +302,210 @@ const ActivityIndicator = (props) => {
|
|
|
302
302
|
host["aria-busy"] = "true";
|
|
303
303
|
return createElement("activityindicator", host);
|
|
304
304
|
};
|
|
305
|
+
/** An accessible checkbox (RN ships none built-in). Toggles `value`; `aria-checked` tracks it. */
|
|
306
|
+
const Checkbox = (props) => {
|
|
307
|
+
const theme = useTheme();
|
|
308
|
+
const { value, onValueChange, disabled, label, style, ...rest } = props;
|
|
309
|
+
const isOn = toAccessor(value, false);
|
|
310
|
+
const box = () => ({
|
|
311
|
+
width: 22,
|
|
312
|
+
height: 22,
|
|
313
|
+
borderRadius: radius.sm,
|
|
314
|
+
display: "flex",
|
|
315
|
+
alignItems: "center",
|
|
316
|
+
justifyContent: "center",
|
|
317
|
+
borderWidth: 2,
|
|
318
|
+
borderColor: isOn() ? theme().color.primary : theme().color.textMuted,
|
|
319
|
+
backgroundColor: isOn() ? theme().color.primary : "transparent",
|
|
320
|
+
opacity: disabled ? .5 : 1
|
|
321
|
+
});
|
|
322
|
+
const check = createElement(Text, { style: () => ({
|
|
323
|
+
color: theme().color.onPrimary,
|
|
324
|
+
fontSize: 14,
|
|
325
|
+
fontWeight: fontWeight.bold
|
|
326
|
+
}) }, () => isOn() ? "✓" : "");
|
|
327
|
+
const handlePress = onValueChange && !disabled ? () => onValueChange(!isOn()) : void 0;
|
|
328
|
+
const boxEl = createElement(Pressable, {
|
|
329
|
+
...rest,
|
|
330
|
+
role: rest.role ?? "checkbox",
|
|
331
|
+
state: () => ({
|
|
332
|
+
...typeof rest.state === "function" ? rest.state() : rest.state ?? {},
|
|
333
|
+
checked: isOn()
|
|
334
|
+
}),
|
|
335
|
+
...disabled ? { disabled: true } : {},
|
|
336
|
+
...handlePress ? { onPress: handlePress } : {},
|
|
337
|
+
style: mergeStyle(box, style)
|
|
338
|
+
}, check);
|
|
339
|
+
if (label === void 0) return boxEl;
|
|
340
|
+
return createElement(View, { style: {
|
|
341
|
+
display: "flex",
|
|
342
|
+
flexDirection: "row",
|
|
343
|
+
alignItems: "center",
|
|
344
|
+
gap: space.sm
|
|
345
|
+
} }, boxEl, typeof label === "string" ? createElement(Text, {}, label) : label);
|
|
346
|
+
};
|
|
347
|
+
/** A single-select radio group (RN ships none built-in). Each row is `role="radio"`. */
|
|
348
|
+
const RadioGroup = (props) => {
|
|
349
|
+
const theme = useTheme();
|
|
350
|
+
const { value, options, onValueChange, disabled, style, ...rest } = props;
|
|
351
|
+
const selected = toAccessor(value, "");
|
|
352
|
+
const rows = options.map((option) => {
|
|
353
|
+
const checked = () => selected() === option.value;
|
|
354
|
+
const ring = createElement(View, { style: () => ({
|
|
355
|
+
width: 22,
|
|
356
|
+
height: 22,
|
|
357
|
+
borderRadius: radius.full,
|
|
358
|
+
display: "flex",
|
|
359
|
+
alignItems: "center",
|
|
360
|
+
justifyContent: "center",
|
|
361
|
+
borderWidth: 2,
|
|
362
|
+
borderColor: checked() ? theme().color.primary : theme().color.textMuted
|
|
363
|
+
}) }, createElement(View, { style: () => ({
|
|
364
|
+
width: 12,
|
|
365
|
+
height: 12,
|
|
366
|
+
borderRadius: radius.full,
|
|
367
|
+
backgroundColor: checked() ? theme().color.primary : "transparent"
|
|
368
|
+
}) }));
|
|
369
|
+
const handlePress = onValueChange && !disabled ? () => onValueChange(option.value) : void 0;
|
|
370
|
+
return createElement(Pressable, {
|
|
371
|
+
role: "radio",
|
|
372
|
+
state: () => ({ checked: checked() }),
|
|
373
|
+
...disabled ? { disabled: true } : {},
|
|
374
|
+
...handlePress ? { onPress: handlePress } : {},
|
|
375
|
+
style: {
|
|
376
|
+
display: "flex",
|
|
377
|
+
flexDirection: "row",
|
|
378
|
+
alignItems: "center",
|
|
379
|
+
gap: space.sm
|
|
380
|
+
}
|
|
381
|
+
}, ring, typeof option.label === "string" ? createElement(Text, {}, option.label) : option.label);
|
|
382
|
+
});
|
|
383
|
+
return createElement(View, {
|
|
384
|
+
...toHostProps({
|
|
385
|
+
...rest,
|
|
386
|
+
style
|
|
387
|
+
}),
|
|
388
|
+
role: rest.role ?? "radiogroup",
|
|
389
|
+
style: mergeStyle({
|
|
390
|
+
display: "flex",
|
|
391
|
+
flexDirection: "column",
|
|
392
|
+
gap: space.sm
|
|
393
|
+
}, style)
|
|
394
|
+
}, ...rows);
|
|
395
|
+
};
|
|
396
|
+
/** A muted loading placeholder block (RN ships none built-in). */
|
|
397
|
+
const Skeleton = (props) => {
|
|
398
|
+
const theme = useTheme();
|
|
399
|
+
const { width = "100%", height = 16, radius: radius$2 = radius.sm, style, ...rest } = props;
|
|
400
|
+
const base = () => ({
|
|
401
|
+
width,
|
|
402
|
+
height,
|
|
403
|
+
borderRadius: radius$2,
|
|
404
|
+
backgroundColor: theme().color.surfaceVariant
|
|
405
|
+
});
|
|
406
|
+
const host = toHostProps({
|
|
407
|
+
...rest,
|
|
408
|
+
style: mergeStyle(base, style)
|
|
409
|
+
});
|
|
410
|
+
if (!host.role) host.role = "status";
|
|
411
|
+
host["aria-busy"] = "true";
|
|
412
|
+
return createElement("view", host);
|
|
413
|
+
};
|
|
414
|
+
/** An accessible tab strip + panel (RN ships none built-in). Only the active panel region re-renders. */
|
|
415
|
+
const Tabs = (props) => {
|
|
416
|
+
const theme = useTheme();
|
|
417
|
+
const { value, tabs, onValueChange, style, ...rest } = props;
|
|
418
|
+
const selected = toAccessor(value, tabs[0]?.value ?? "");
|
|
419
|
+
const tabBar = createElement(View, {
|
|
420
|
+
role: "tablist",
|
|
421
|
+
style: () => ({
|
|
422
|
+
display: "flex",
|
|
423
|
+
flexDirection: "row",
|
|
424
|
+
gap: space.xs,
|
|
425
|
+
borderBottomWidth: 1,
|
|
426
|
+
borderColor: theme().color.border
|
|
427
|
+
})
|
|
428
|
+
}, ...tabs.map((t) => {
|
|
429
|
+
const active = () => selected() === t.value;
|
|
430
|
+
return createElement(Pressable, {
|
|
431
|
+
role: "tab",
|
|
432
|
+
state: () => ({ selected: active() }),
|
|
433
|
+
...onValueChange ? { onPress: () => onValueChange(t.value) } : {},
|
|
434
|
+
style: () => ({
|
|
435
|
+
paddingTop: space.xs,
|
|
436
|
+
paddingBottom: space.xs,
|
|
437
|
+
paddingLeft: space.sm,
|
|
438
|
+
paddingRight: space.sm,
|
|
439
|
+
borderBottomWidth: 2,
|
|
440
|
+
borderColor: active() ? theme().color.primary : "transparent"
|
|
441
|
+
})
|
|
442
|
+
}, typeof t.label === "string" ? createElement(Text, { style: () => ({
|
|
443
|
+
color: active() ? theme().color.primary : theme().color.text,
|
|
444
|
+
...active() ? { fontWeight: fontWeight.semibold } : {}
|
|
445
|
+
}) }, t.label) : t.label);
|
|
446
|
+
}));
|
|
447
|
+
const panel = createElement(View, {
|
|
448
|
+
role: "tabpanel",
|
|
449
|
+
style: { padding: space.md }
|
|
450
|
+
}, () => {
|
|
451
|
+
const active = tabs.find((t) => t.value === selected());
|
|
452
|
+
return active ? active.content : null;
|
|
453
|
+
});
|
|
454
|
+
return createElement(View, {
|
|
455
|
+
...toHostProps({
|
|
456
|
+
...rest,
|
|
457
|
+
style
|
|
458
|
+
}),
|
|
459
|
+
style: mergeStyle({
|
|
460
|
+
display: "flex",
|
|
461
|
+
flexDirection: "column"
|
|
462
|
+
}, style)
|
|
463
|
+
}, tabBar, panel);
|
|
464
|
+
};
|
|
465
|
+
/** Collapsible sections (RN ships none built-in). Each header is `aria-expanded`; panels mount lazily. */
|
|
466
|
+
const Accordion = (props) => {
|
|
467
|
+
const theme = useTheme();
|
|
468
|
+
const { sections, multiple, defaultOpen = [], style, ...rest } = props;
|
|
469
|
+
const open = signal(new Set(defaultOpen));
|
|
470
|
+
const isOpen = (id) => open().has(id);
|
|
471
|
+
const toggle = (id) => {
|
|
472
|
+
const next = new Set(untrack(open));
|
|
473
|
+
if (next.has(id)) next.delete(id);
|
|
474
|
+
else {
|
|
475
|
+
if (!multiple) next.clear();
|
|
476
|
+
next.add(id);
|
|
477
|
+
}
|
|
478
|
+
open.set(next);
|
|
479
|
+
};
|
|
480
|
+
const rows = sections.map((section) => {
|
|
481
|
+
return createElement(View, {}, createElement(Pressable, {
|
|
482
|
+
role: "button",
|
|
483
|
+
state: () => ({ expanded: isOpen(section.id) }),
|
|
484
|
+
onPress: () => toggle(section.id),
|
|
485
|
+
style: () => ({
|
|
486
|
+
display: "flex",
|
|
487
|
+
flexDirection: "row",
|
|
488
|
+
alignItems: "center",
|
|
489
|
+
justifyContent: "space-between",
|
|
490
|
+
paddingTop: space.sm,
|
|
491
|
+
paddingBottom: space.sm,
|
|
492
|
+
borderBottomWidth: 1,
|
|
493
|
+
borderColor: theme().color.border
|
|
494
|
+
})
|
|
495
|
+
}, typeof section.header === "string" ? createElement(Text, {}, section.header) : section.header, createElement(Text, { style: () => ({ color: theme().color.textMuted }) }, () => isOpen(section.id) ? "▾" : "▸")), createElement(View, { style: { padding: space.sm } }, () => isOpen(section.id) ? section.content : null));
|
|
496
|
+
});
|
|
497
|
+
return createElement(View, {
|
|
498
|
+
...toHostProps({
|
|
499
|
+
...rest,
|
|
500
|
+
style
|
|
501
|
+
}),
|
|
502
|
+
style: mergeStyle({
|
|
503
|
+
display: "flex",
|
|
504
|
+
flexDirection: "column"
|
|
505
|
+
}, style)
|
|
506
|
+
}, ...rows);
|
|
507
|
+
};
|
|
305
508
|
//#endregion
|
|
306
|
-
export { ActivityIndicator, Avatar, Badge, Card, Chip, Divider, KeyboardAvoidingView, ProgressBar, SafeAreaView, Switch };
|
|
509
|
+
export { Accordion, ActivityIndicator, Avatar, Badge, Card, Checkbox, Chip, Divider, KeyboardAvoidingView, ProgressBar, RadioGroup, SafeAreaView, Skeleton, Switch, Tabs };
|
|
307
510
|
|
|
308
511
|
//# sourceMappingURL=components.js.map
|
package/dist/components.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"components.js","names":["radiusScale","radius"],"sources":["../src/components.ts"],"sourcesContent":["/**\n * Atlas components — higher-level building blocks composed purely from the primitives\n * (View/Text/Pressable/Image) + the device hooks. No new host concepts, so every one\n * renders on web *and* native today, and stays fine-grained: reactive bits are accessor\n * styles, so only the changed node re-runs (no component re-render).\n *\n * Colors come from the design tokens via {@link useTheme}, so components re-theme\n * automatically light↔dark (handbook §23/§31). Spacing/radius/type use the token scales.\n *\n * @module\n */\n\nimport { type Accessor, type Component, createElement, type MindeesNode } from '@mindees/core'\nimport { useKeyboard, useSafeAreaInsets } from './environment'\nimport { type BaseProps, type Reactive, toHostProps } from './host'\nimport { Image, Pressable, Text, View } from './primitives'\nimport { flattenStyle, type StyleInput } from './style'\nimport { fontWeight, radius as radiusScale, space, type Theme, useTheme } from './tokens'\n\n/** Merge a base style with a caller's (possibly reactive) style, staying reactive if either is. */\nfunction mergeStyle(\n base: StyleInput | Accessor<StyleInput>,\n style: Reactive<StyleInput> | undefined,\n): Reactive<StyleInput> {\n const baseFn = typeof base === 'function' ? (base as Accessor<StyleInput>) : null\n const styleFn = typeof style === 'function' ? (style as Accessor<StyleInput>) : null\n if (baseFn || styleFn) {\n // In each branch the non-fn side isn't a function, so the StyleInput cast is sound.\n const baseVal = base as StyleInput\n const styleVal = style as StyleInput\n return () => flattenStyle([baseFn ? baseFn() : baseVal, styleFn ? styleFn() : styleVal])\n }\n return flattenStyle([base as StyleInput, style as StyleInput])\n}\n\n/** Normalize a `Reactive<T>` to an accessor. */\nfunction toAccessor<T>(value: Reactive<T>, fallback: T): Accessor<T> {\n if (typeof value === 'function') return value as Accessor<T>\n return () => (value === undefined ? fallback : value)\n}\n\n// ---------------------------------------------------------------------------\n// Card\n// ---------------------------------------------------------------------------\n\n/** A surface that groups one coherent unit of content. */\nexport interface CardProps extends BaseProps {\n readonly children?: MindeesNode\n /** Visual emphasis. `elevated` (default) lifts off the bg; `filled` is a soft tint; `outlined` is a hairline. */\n readonly variant?: 'elevated' | 'filled' | 'outlined'\n /** Internal padding (handbook default 16). */\n readonly padding?: number | string\n /** Corner radius (handbook 12–16 for app cards). */\n readonly radius?: number\n}\nexport const Card: Component<CardProps> = (props) => {\n const theme = useTheme()\n const {\n variant = 'elevated',\n padding = space.md,\n radius = radiusScale.lg,\n style,\n children,\n ...rest\n } = props\n const base: Accessor<StyleInput> = () => {\n const c = theme().color\n const surface: StyleInput =\n variant === 'outlined'\n ? { borderWidth: 1, borderColor: c.border }\n : variant === 'filled'\n ? { backgroundColor: c.surfaceVariant }\n : { backgroundColor: c.surface, borderWidth: 1, borderColor: c.border }\n return { padding, borderRadius: radius, ...surface }\n }\n return createElement(View, { ...rest, style: mergeStyle(base, style) }, children)\n}\n\n// ---------------------------------------------------------------------------\n// Divider\n// ---------------------------------------------------------------------------\n\n/** A thin rule separating content. */\nexport interface DividerProps extends BaseProps {\n readonly orientation?: 'horizontal' | 'vertical'\n readonly thickness?: number\n /** Override color (defaults to the theme border). */\n readonly color?: string\n}\nexport const Divider: Component<DividerProps> = (props) => {\n const theme = useTheme()\n const { orientation = 'horizontal', thickness = 1, color, style, ...rest } = props\n const base: Accessor<StyleInput> = () => {\n const bg = color ?? theme().color.border\n return orientation === 'horizontal'\n ? { height: thickness, alignSelf: 'stretch', backgroundColor: bg }\n : { width: thickness, alignSelf: 'stretch', backgroundColor: bg }\n }\n return createElement(View, {\n ...rest,\n role: rest.role ?? 'separator',\n style: mergeStyle(base, style),\n })\n}\n\n// ---------------------------------------------------------------------------\n// Badge\n// ---------------------------------------------------------------------------\n\nexport type BadgeTone = 'neutral' | 'info' | 'success' | 'warning' | 'danger'\n\n/** Resolve a tone to its {bg, fg} in the active theme. */\nfunction toneColors(tone: BadgeTone, theme: Theme): { bg: string; fg: string } {\n const c = theme.color\n if (tone === 'neutral') return { bg: c.surfaceVariant, fg: c.text }\n return { bg: c[tone], fg: c.onTone }\n}\n\n/** A compact status/count pill. */\nexport interface BadgeProps extends BaseProps {\n readonly children?: MindeesNode\n readonly tone?: BadgeTone\n}\nexport const Badge: Component<BadgeProps> = (props) => {\n const theme = useTheme()\n const { tone = 'neutral', style, children, ...rest } = props\n const base: Accessor<StyleInput> = () => ({\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n paddingTop: space['3xs'],\n paddingBottom: space['3xs'],\n paddingLeft: space.xs,\n paddingRight: space.xs,\n borderRadius: radiusScale.full,\n backgroundColor: toneColors(tone, theme()).bg,\n })\n const textStyle: Accessor<StyleInput> = () => ({\n fontSize: 12,\n fontWeight: fontWeight.semibold,\n color: toneColors(tone, theme()).fg,\n })\n return createElement(\n View,\n { ...rest, role: rest.role ?? 'status', style: mergeStyle(base, style) },\n createElement(Text, { style: textStyle }, children),\n )\n}\n\n// ---------------------------------------------------------------------------\n// Avatar\n// ---------------------------------------------------------------------------\n\n/** Up-to-two-letter initials from a name. */\nfunction initialsOf(name: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean)\n if (parts.length === 0) return '?'\n const first = parts[0]?.[0] ?? ''\n const last = parts.length > 1 ? (parts[parts.length - 1]?.[0] ?? '') : ''\n return (first + last).toUpperCase()\n}\n\n/** A circular user image, falling back to initials. */\nexport interface AvatarProps extends BaseProps {\n readonly src?: string\n readonly name?: string\n /** Diameter in px (default 40). */\n readonly size?: number\n}\nexport const Avatar: Component<AvatarProps> = (props) => {\n const theme = useTheme()\n const { src, name, size = 40, style, ...rest } = props\n const base: Accessor<StyleInput> = () => ({\n width: size,\n height: size,\n borderRadius: size / 2,\n overflow: 'hidden',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: theme().color.surfaceVariant,\n })\n const content = src\n ? createElement(Image, {\n src,\n label: name ?? '',\n ...(name ? {} : { decorative: true }),\n style: { width: size, height: size },\n })\n : createElement(\n Text,\n {\n style: () => ({\n fontSize: Math.round(size * 0.4),\n fontWeight: fontWeight.semibold,\n color: theme().color.text,\n }),\n },\n name ? initialsOf(name) : '?',\n )\n return createElement(\n View,\n { ...rest, label: rest.label ?? name, style: mergeStyle(base, style) },\n content,\n )\n}\n\n// ---------------------------------------------------------------------------\n// Chip\n// ---------------------------------------------------------------------------\n\n/** A compact, optionally-selectable token (filter/choice/input). */\nexport interface ChipProps extends Omit<BaseProps, 'style'> {\n readonly label: string\n readonly selected?: Reactive<boolean>\n readonly disabled?: boolean\n readonly onPress?: () => void\n readonly leading?: MindeesNode\n readonly trailing?: MindeesNode\n readonly style?: Reactive<StyleInput>\n}\nexport const Chip: Component<ChipProps> = (props) => {\n const theme = useTheme()\n const { label, selected = false, disabled, onPress, leading, trailing, style, ...rest } = props\n const isSelected = toAccessor(selected, false)\n const base: Accessor<StyleInput> = () => {\n const c = theme().color\n const on = isSelected()\n return {\n display: 'flex',\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'center',\n gap: space['2xs'],\n minHeight: 32,\n paddingTop: space['2xs'],\n paddingBottom: space['2xs'],\n paddingLeft: space.sm,\n paddingRight: space.sm,\n borderRadius: radiusScale.full,\n borderWidth: 1,\n borderColor: on ? c.primary : c.border,\n backgroundColor: on ? c.primary : 'transparent',\n opacity: disabled ? 0.5 : 1,\n }\n }\n const text = createElement(\n Text,\n {\n style: () => ({\n fontSize: 14,\n fontWeight: fontWeight.medium,\n color: isSelected() ? theme().color.onPrimary : theme().color.text,\n }),\n },\n label,\n )\n const inner: MindeesNode = [leading, text, trailing].filter((n) => n != null) as MindeesNode\n return createElement(\n Pressable,\n {\n ...rest,\n role: rest.role ?? 'button',\n // Reactive `aria-pressed` so a toggle chip announces its selected state as it changes.\n state: () => ({\n ...(typeof rest.state === 'function' ? rest.state() : (rest.state ?? {})),\n pressed: isSelected(),\n }),\n ...(onPress ? { onPress } : {}),\n ...(disabled ? { disabled: true } : {}),\n style: mergeStyle(base, style),\n },\n inner,\n )\n}\n\n// ---------------------------------------------------------------------------\n// Switch\n// ---------------------------------------------------------------------------\n\n/** A binary on/off toggle (composed track + knob; flips instantly on press). */\nexport interface SwitchProps extends Omit<BaseProps, 'style'> {\n /** Controlled state (static or reactive). */\n readonly value: Reactive<boolean>\n readonly onValueChange?: (value: boolean) => void\n readonly disabled?: boolean\n readonly style?: Reactive<StyleInput>\n}\nexport const Switch: Component<SwitchProps> = (props) => {\n const theme = useTheme()\n const { value, onValueChange, disabled, style, ...rest } = props\n const isOn = toAccessor(value, false)\n const track: Accessor<StyleInput> = () => ({\n display: 'flex',\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: isOn() ? 'flex-end' : 'flex-start',\n width: 52,\n height: 32,\n borderRadius: radiusScale.full,\n padding: 3,\n backgroundColor: isOn() ? theme().color.primary : theme().color.textMuted,\n opacity: disabled ? 0.5 : 1,\n })\n const knob = createElement(View, {\n style: () => ({\n width: 26,\n height: 26,\n borderRadius: 13,\n backgroundColor: theme().color.onPrimary,\n }),\n })\n const handlePress = onValueChange && !disabled ? () => onValueChange(!isOn()) : undefined\n return createElement(\n Pressable,\n {\n ...rest,\n role: rest.role ?? 'switch',\n // Reactive state → `aria-checked` tracks the toggle (a static object would bake it once).\n state: () => ({\n ...(typeof rest.state === 'function' ? rest.state() : (rest.state ?? {})),\n checked: isOn(),\n }),\n ...(disabled ? { disabled: true } : {}),\n ...(handlePress ? { onPress: handlePress } : {}),\n style: mergeStyle(track, style),\n },\n knob,\n )\n}\n\n// ---------------------------------------------------------------------------\n// SafeAreaView\n// ---------------------------------------------------------------------------\n\n/** A container that pads itself by the live safe-area insets (notch, home indicator, …). */\nexport interface SafeAreaViewProps extends BaseProps {\n readonly children?: MindeesNode\n /** Which edges to inset (default: all four). */\n readonly edges?: ReadonlyArray<'top' | 'right' | 'bottom' | 'left'>\n}\nexport const SafeAreaView: Component<SafeAreaViewProps> = (props) => {\n const insets = useSafeAreaInsets()\n const { edges, style, children, ...rest } = props\n const wants = (edge: 'top' | 'right' | 'bottom' | 'left'): boolean =>\n !edges || edges.includes(edge)\n const base: Accessor<StyleInput> = () => {\n const i = insets()\n return {\n paddingTop: wants('top') ? i.top : 0,\n paddingRight: wants('right') ? i.right : 0,\n paddingBottom: wants('bottom') ? i.bottom : 0,\n paddingLeft: wants('left') ? i.left : 0,\n }\n }\n return createElement(View, { ...rest, style: mergeStyle(base, style) }, children)\n}\n\n// ---------------------------------------------------------------------------\n// KeyboardAvoidingView\n// ---------------------------------------------------------------------------\n\n/** A container that pads its bottom by the live keyboard height so content stays visible. */\nexport interface KeyboardAvoidingViewProps extends BaseProps {\n readonly children?: MindeesNode\n}\nexport const KeyboardAvoidingView: Component<KeyboardAvoidingViewProps> = (props) => {\n const keyboard = useKeyboard()\n const { style, children, ...rest } = props\n const base: Accessor<StyleInput> = () => ({ paddingBottom: keyboard().height })\n return createElement(View, { ...rest, style: mergeStyle(base, style) }, children)\n}\n\n// ---------------------------------------------------------------------------\n// ProgressBar (determinate)\n// ---------------------------------------------------------------------------\n\n/** A determinate progress bar (track + reactive fill). */\nexport interface ProgressBarProps extends BaseProps {\n /** Progress 0..1 (static or reactive). Values outside the range are clamped. */\n readonly value?: Reactive<number>\n readonly trackColor?: string\n readonly color?: string\n readonly height?: number\n}\nexport const ProgressBar: Component<ProgressBarProps> = (props) => {\n const theme = useTheme()\n const { value = 0, trackColor, color, height = 6, style, ...rest } = props\n const progress = toAccessor(value, 0)\n const track: Accessor<StyleInput> = () => ({\n width: '100%',\n height,\n borderRadius: height / 2,\n overflow: 'hidden',\n backgroundColor: trackColor ?? theme().color.surfaceVariant,\n })\n const fill: Accessor<StyleInput> = () => ({\n height,\n borderRadius: height / 2,\n backgroundColor: color ?? theme().color.primary,\n width: `${Math.max(0, Math.min(1, progress())) * 100}%`,\n })\n return createElement(\n View,\n {\n ...rest,\n role: rest.role ?? 'progressbar',\n // A progressbar with no value is inaccessible — emit a reactive aria-valuenow (0..1 range).\n valueMin: 0,\n valueMax: 1,\n valueNow: () => Math.max(0, Math.min(1, progress())),\n style: mergeStyle(track, style),\n },\n createElement(View, { style: fill }),\n )\n}\n\n// ---------------------------------------------------------------------------\n// ActivityIndicator\n// ---------------------------------------------------------------------------\n\n/**\n * A spinning loading indicator. Emits the `activityindicator` host tag, which each backend\n * renders natively: web → a CSS keyframe spinner (size from `width`/`height`, arc from `color`),\n * Android → an indeterminate `ProgressBar`. Size/color flow through ordinary style keys.\n */\nexport interface ActivityIndicatorProps extends BaseProps {\n /** Diameter in px (default 24). */\n readonly size?: number\n /** Spinner color (defaults to the theme primary). */\n readonly color?: string\n /** When false, renders nothing (so callers can gate it without a conditional). */\n readonly animating?: boolean\n}\nexport const ActivityIndicator: Component<ActivityIndicatorProps> = (props) => {\n const theme = useTheme()\n const { size = 24, color, animating = true, style, ...rest } = props\n if (animating === false) return null\n const base: Accessor<StyleInput> = () => ({\n width: size,\n height: size,\n color: color ?? theme().color.primary,\n })\n const host = toHostProps({ ...rest, style: mergeStyle(base, style) })\n if (!host.role) host.role = 'status'\n host['aria-busy'] = 'true'\n return createElement('activityindicator', host)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"components.js","names":["radiusScale","radius"],"sources":["../src/components.ts"],"sourcesContent":["/**\n * Atlas components — higher-level building blocks composed purely from the primitives\n * (View/Text/Pressable/Image) + the device hooks. No new host concepts, so every one\n * renders on web *and* native today, and stays fine-grained: reactive bits are accessor\n * styles, so only the changed node re-runs (no component re-render).\n *\n * Colors come from the design tokens via {@link useTheme}, so components re-theme\n * automatically light↔dark (handbook §23/§31). Spacing/radius/type use the token scales.\n *\n * @module\n */\n\nimport {\n type Accessor,\n type Component,\n createElement,\n type MindeesNode,\n signal,\n untrack,\n} from '@mindees/core'\nimport { useKeyboard, useSafeAreaInsets } from './environment'\nimport { type BaseProps, type Reactive, toHostProps } from './host'\nimport { Image, Pressable, Text, View } from './primitives'\nimport { flattenStyle, type StyleInput } from './style'\nimport { fontWeight, radius as radiusScale, space, type Theme, useTheme } from './tokens'\n\n/** Merge a base style with a caller's (possibly reactive) style, staying reactive if either is. */\nfunction mergeStyle(\n base: StyleInput | Accessor<StyleInput>,\n style: Reactive<StyleInput> | undefined,\n): Reactive<StyleInput> {\n const baseFn = typeof base === 'function' ? (base as Accessor<StyleInput>) : null\n const styleFn = typeof style === 'function' ? (style as Accessor<StyleInput>) : null\n if (baseFn || styleFn) {\n // In each branch the non-fn side isn't a function, so the StyleInput cast is sound.\n const baseVal = base as StyleInput\n const styleVal = style as StyleInput\n return () => flattenStyle([baseFn ? baseFn() : baseVal, styleFn ? styleFn() : styleVal])\n }\n return flattenStyle([base as StyleInput, style as StyleInput])\n}\n\n/** Normalize a `Reactive<T>` to an accessor. */\nfunction toAccessor<T>(value: Reactive<T>, fallback: T): Accessor<T> {\n if (typeof value === 'function') return value as Accessor<T>\n return () => (value === undefined ? fallback : value)\n}\n\n// ---------------------------------------------------------------------------\n// Card\n// ---------------------------------------------------------------------------\n\n/** A surface that groups one coherent unit of content. */\nexport interface CardProps extends BaseProps {\n readonly children?: MindeesNode\n /** Visual emphasis. `elevated` (default) lifts off the bg; `filled` is a soft tint; `outlined` is a hairline. */\n readonly variant?: 'elevated' | 'filled' | 'outlined'\n /** Internal padding (handbook default 16). */\n readonly padding?: number | string\n /** Corner radius (handbook 12–16 for app cards). */\n readonly radius?: number\n}\nexport const Card: Component<CardProps> = (props) => {\n const theme = useTheme()\n const {\n variant = 'elevated',\n padding = space.md,\n radius = radiusScale.lg,\n style,\n children,\n ...rest\n } = props\n const base: Accessor<StyleInput> = () => {\n const c = theme().color\n const surface: StyleInput =\n variant === 'outlined'\n ? { borderWidth: 1, borderColor: c.border }\n : variant === 'filled'\n ? { backgroundColor: c.surfaceVariant }\n : { backgroundColor: c.surface, borderWidth: 1, borderColor: c.border }\n return { padding, borderRadius: radius, ...surface }\n }\n return createElement(View, { ...rest, style: mergeStyle(base, style) }, children)\n}\n\n// ---------------------------------------------------------------------------\n// Divider\n// ---------------------------------------------------------------------------\n\n/** A thin rule separating content. */\nexport interface DividerProps extends BaseProps {\n readonly orientation?: 'horizontal' | 'vertical'\n readonly thickness?: number\n /** Override color (defaults to the theme border). */\n readonly color?: string\n}\nexport const Divider: Component<DividerProps> = (props) => {\n const theme = useTheme()\n const { orientation = 'horizontal', thickness = 1, color, style, ...rest } = props\n const base: Accessor<StyleInput> = () => {\n const bg = color ?? theme().color.border\n return orientation === 'horizontal'\n ? { height: thickness, alignSelf: 'stretch', backgroundColor: bg }\n : { width: thickness, alignSelf: 'stretch', backgroundColor: bg }\n }\n return createElement(View, {\n ...rest,\n role: rest.role ?? 'separator',\n style: mergeStyle(base, style),\n })\n}\n\n// ---------------------------------------------------------------------------\n// Badge\n// ---------------------------------------------------------------------------\n\nexport type BadgeTone = 'neutral' | 'info' | 'success' | 'warning' | 'danger'\n\n/** Resolve a tone to its {bg, fg} in the active theme. */\nfunction toneColors(tone: BadgeTone, theme: Theme): { bg: string; fg: string } {\n const c = theme.color\n if (tone === 'neutral') return { bg: c.surfaceVariant, fg: c.text }\n return { bg: c[tone], fg: c.onTone }\n}\n\n/** A compact status/count pill. */\nexport interface BadgeProps extends BaseProps {\n readonly children?: MindeesNode\n readonly tone?: BadgeTone\n}\nexport const Badge: Component<BadgeProps> = (props) => {\n const theme = useTheme()\n const { tone = 'neutral', style, children, ...rest } = props\n const base: Accessor<StyleInput> = () => ({\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n paddingTop: space['3xs'],\n paddingBottom: space['3xs'],\n paddingLeft: space.xs,\n paddingRight: space.xs,\n borderRadius: radiusScale.full,\n backgroundColor: toneColors(tone, theme()).bg,\n })\n const textStyle: Accessor<StyleInput> = () => ({\n fontSize: 12,\n fontWeight: fontWeight.semibold,\n color: toneColors(tone, theme()).fg,\n })\n return createElement(\n View,\n { ...rest, role: rest.role ?? 'status', style: mergeStyle(base, style) },\n createElement(Text, { style: textStyle }, children),\n )\n}\n\n// ---------------------------------------------------------------------------\n// Avatar\n// ---------------------------------------------------------------------------\n\n/** Up-to-two-letter initials from a name. */\nfunction initialsOf(name: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean)\n if (parts.length === 0) return '?'\n const first = parts[0]?.[0] ?? ''\n const last = parts.length > 1 ? (parts[parts.length - 1]?.[0] ?? '') : ''\n return (first + last).toUpperCase()\n}\n\n/** A circular user image, falling back to initials. */\nexport interface AvatarProps extends BaseProps {\n readonly src?: string\n readonly name?: string\n /** Diameter in px (default 40). */\n readonly size?: number\n}\nexport const Avatar: Component<AvatarProps> = (props) => {\n const theme = useTheme()\n const { src, name, size = 40, style, ...rest } = props\n const base: Accessor<StyleInput> = () => ({\n width: size,\n height: size,\n borderRadius: size / 2,\n overflow: 'hidden',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: theme().color.surfaceVariant,\n })\n const content = src\n ? createElement(Image, {\n src,\n label: name ?? '',\n ...(name ? {} : { decorative: true }),\n style: { width: size, height: size },\n })\n : createElement(\n Text,\n {\n style: () => ({\n fontSize: Math.round(size * 0.4),\n fontWeight: fontWeight.semibold,\n color: theme().color.text,\n }),\n },\n name ? initialsOf(name) : '?',\n )\n return createElement(\n View,\n { ...rest, label: rest.label ?? name, style: mergeStyle(base, style) },\n content,\n )\n}\n\n// ---------------------------------------------------------------------------\n// Chip\n// ---------------------------------------------------------------------------\n\n/** A compact, optionally-selectable token (filter/choice/input). */\nexport interface ChipProps extends Omit<BaseProps, 'style'> {\n readonly label: string\n readonly selected?: Reactive<boolean>\n readonly disabled?: boolean\n readonly onPress?: () => void\n readonly leading?: MindeesNode\n readonly trailing?: MindeesNode\n readonly style?: Reactive<StyleInput>\n}\nexport const Chip: Component<ChipProps> = (props) => {\n const theme = useTheme()\n const { label, selected = false, disabled, onPress, leading, trailing, style, ...rest } = props\n const isSelected = toAccessor(selected, false)\n const base: Accessor<StyleInput> = () => {\n const c = theme().color\n const on = isSelected()\n return {\n display: 'flex',\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'center',\n gap: space['2xs'],\n minHeight: 32,\n paddingTop: space['2xs'],\n paddingBottom: space['2xs'],\n paddingLeft: space.sm,\n paddingRight: space.sm,\n borderRadius: radiusScale.full,\n borderWidth: 1,\n borderColor: on ? c.primary : c.border,\n backgroundColor: on ? c.primary : 'transparent',\n opacity: disabled ? 0.5 : 1,\n }\n }\n const text = createElement(\n Text,\n {\n style: () => ({\n fontSize: 14,\n fontWeight: fontWeight.medium,\n color: isSelected() ? theme().color.onPrimary : theme().color.text,\n }),\n },\n label,\n )\n const inner: MindeesNode = [leading, text, trailing].filter((n) => n != null) as MindeesNode\n return createElement(\n Pressable,\n {\n ...rest,\n role: rest.role ?? 'button',\n // Reactive `aria-pressed` so a toggle chip announces its selected state as it changes.\n state: () => ({\n ...(typeof rest.state === 'function' ? rest.state() : (rest.state ?? {})),\n pressed: isSelected(),\n }),\n ...(onPress ? { onPress } : {}),\n ...(disabled ? { disabled: true } : {}),\n style: mergeStyle(base, style),\n },\n inner,\n )\n}\n\n// ---------------------------------------------------------------------------\n// Switch\n// ---------------------------------------------------------------------------\n\n/** A binary on/off toggle (composed track + knob; flips instantly on press). */\nexport interface SwitchProps extends Omit<BaseProps, 'style'> {\n /** Controlled state (static or reactive). */\n readonly value: Reactive<boolean>\n readonly onValueChange?: (value: boolean) => void\n readonly disabled?: boolean\n readonly style?: Reactive<StyleInput>\n}\nexport const Switch: Component<SwitchProps> = (props) => {\n const theme = useTheme()\n const { value, onValueChange, disabled, style, ...rest } = props\n const isOn = toAccessor(value, false)\n const track: Accessor<StyleInput> = () => ({\n display: 'flex',\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: isOn() ? 'flex-end' : 'flex-start',\n width: 52,\n height: 32,\n borderRadius: radiusScale.full,\n padding: 3,\n backgroundColor: isOn() ? theme().color.primary : theme().color.textMuted,\n opacity: disabled ? 0.5 : 1,\n })\n const knob = createElement(View, {\n style: () => ({\n width: 26,\n height: 26,\n borderRadius: 13,\n backgroundColor: theme().color.onPrimary,\n }),\n })\n const handlePress = onValueChange && !disabled ? () => onValueChange(!isOn()) : undefined\n return createElement(\n Pressable,\n {\n ...rest,\n role: rest.role ?? 'switch',\n // Reactive state → `aria-checked` tracks the toggle (a static object would bake it once).\n state: () => ({\n ...(typeof rest.state === 'function' ? rest.state() : (rest.state ?? {})),\n checked: isOn(),\n }),\n ...(disabled ? { disabled: true } : {}),\n ...(handlePress ? { onPress: handlePress } : {}),\n style: mergeStyle(track, style),\n },\n knob,\n )\n}\n\n// ---------------------------------------------------------------------------\n// SafeAreaView\n// ---------------------------------------------------------------------------\n\n/** A container that pads itself by the live safe-area insets (notch, home indicator, …). */\nexport interface SafeAreaViewProps extends BaseProps {\n readonly children?: MindeesNode\n /** Which edges to inset (default: all four). */\n readonly edges?: ReadonlyArray<'top' | 'right' | 'bottom' | 'left'>\n}\nexport const SafeAreaView: Component<SafeAreaViewProps> = (props) => {\n const insets = useSafeAreaInsets()\n const { edges, style, children, ...rest } = props\n const wants = (edge: 'top' | 'right' | 'bottom' | 'left'): boolean =>\n !edges || edges.includes(edge)\n const base: Accessor<StyleInput> = () => {\n const i = insets()\n return {\n paddingTop: wants('top') ? i.top : 0,\n paddingRight: wants('right') ? i.right : 0,\n paddingBottom: wants('bottom') ? i.bottom : 0,\n paddingLeft: wants('left') ? i.left : 0,\n }\n }\n return createElement(View, { ...rest, style: mergeStyle(base, style) }, children)\n}\n\n// ---------------------------------------------------------------------------\n// KeyboardAvoidingView\n// ---------------------------------------------------------------------------\n\n/** A container that pads its bottom by the live keyboard height so content stays visible. */\nexport interface KeyboardAvoidingViewProps extends BaseProps {\n readonly children?: MindeesNode\n}\nexport const KeyboardAvoidingView: Component<KeyboardAvoidingViewProps> = (props) => {\n const keyboard = useKeyboard()\n const { style, children, ...rest } = props\n const base: Accessor<StyleInput> = () => ({ paddingBottom: keyboard().height })\n return createElement(View, { ...rest, style: mergeStyle(base, style) }, children)\n}\n\n// ---------------------------------------------------------------------------\n// ProgressBar (determinate)\n// ---------------------------------------------------------------------------\n\n/** A determinate progress bar (track + reactive fill). */\nexport interface ProgressBarProps extends BaseProps {\n /** Progress 0..1 (static or reactive). Values outside the range are clamped. */\n readonly value?: Reactive<number>\n readonly trackColor?: string\n readonly color?: string\n readonly height?: number\n}\nexport const ProgressBar: Component<ProgressBarProps> = (props) => {\n const theme = useTheme()\n const { value = 0, trackColor, color, height = 6, style, ...rest } = props\n const progress = toAccessor(value, 0)\n const track: Accessor<StyleInput> = () => ({\n width: '100%',\n height,\n borderRadius: height / 2,\n overflow: 'hidden',\n backgroundColor: trackColor ?? theme().color.surfaceVariant,\n })\n const fill: Accessor<StyleInput> = () => ({\n height,\n borderRadius: height / 2,\n backgroundColor: color ?? theme().color.primary,\n width: `${Math.max(0, Math.min(1, progress())) * 100}%`,\n })\n return createElement(\n View,\n {\n ...rest,\n role: rest.role ?? 'progressbar',\n // A progressbar with no value is inaccessible — emit a reactive aria-valuenow (0..1 range).\n valueMin: 0,\n valueMax: 1,\n valueNow: () => Math.max(0, Math.min(1, progress())),\n style: mergeStyle(track, style),\n },\n createElement(View, { style: fill }),\n )\n}\n\n// ---------------------------------------------------------------------------\n// ActivityIndicator\n// ---------------------------------------------------------------------------\n\n/**\n * A spinning loading indicator. Emits the `activityindicator` host tag, which each backend\n * renders natively: web → a CSS keyframe spinner (size from `width`/`height`, arc from `color`),\n * Android → an indeterminate `ProgressBar`. Size/color flow through ordinary style keys.\n */\nexport interface ActivityIndicatorProps extends BaseProps {\n /** Diameter in px (default 24). */\n readonly size?: number\n /** Spinner color (defaults to the theme primary). */\n readonly color?: string\n /** When false, renders nothing (so callers can gate it without a conditional). */\n readonly animating?: boolean\n}\nexport const ActivityIndicator: Component<ActivityIndicatorProps> = (props) => {\n const theme = useTheme()\n const { size = 24, color, animating = true, style, ...rest } = props\n if (animating === false) return null\n const base: Accessor<StyleInput> = () => ({\n width: size,\n height: size,\n color: color ?? theme().color.primary,\n })\n const host = toHostProps({ ...rest, style: mergeStyle(base, style) })\n if (!host.role) host.role = 'status'\n host['aria-busy'] = 'true'\n return createElement('activityindicator', host)\n}\n\n// ---------------------------------------------------------------------------\n// Checkbox\n// ---------------------------------------------------------------------------\n\n/** Props for {@link Checkbox}. (`label` is omitted from a11y to repurpose it as the visible label.) */\nexport interface CheckboxProps extends Omit<BaseProps, 'style' | 'label'> {\n /** Controlled checked state (static or reactive). */\n readonly value: Reactive<boolean>\n readonly onValueChange?: (value: boolean) => void\n readonly disabled?: boolean\n /** Optional visible label rendered beside the box (a string becomes a `Text`). */\n readonly label?: MindeesNode\n readonly style?: Reactive<StyleInput>\n}\n\n/** An accessible checkbox (RN ships none built-in). Toggles `value`; `aria-checked` tracks it. */\nexport const Checkbox: Component<CheckboxProps> = (props) => {\n const theme = useTheme()\n const { value, onValueChange, disabled, label, style, ...rest } = props\n const isOn = toAccessor(value, false)\n const box: Accessor<StyleInput> = () => ({\n width: 22,\n height: 22,\n borderRadius: radiusScale.sm,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderWidth: 2,\n borderColor: isOn() ? theme().color.primary : theme().color.textMuted,\n backgroundColor: isOn() ? theme().color.primary : 'transparent',\n opacity: disabled ? 0.5 : 1,\n })\n const check = createElement(\n Text,\n {\n style: () => ({ color: theme().color.onPrimary, fontSize: 14, fontWeight: fontWeight.bold }),\n },\n () => (isOn() ? '✓' : ''),\n )\n const handlePress = onValueChange && !disabled ? () => onValueChange(!isOn()) : undefined\n const boxEl = createElement(\n Pressable,\n {\n ...rest,\n role: rest.role ?? 'checkbox',\n state: () => ({\n ...(typeof rest.state === 'function' ? rest.state() : (rest.state ?? {})),\n checked: isOn(),\n }),\n ...(disabled ? { disabled: true } : {}),\n ...(handlePress ? { onPress: handlePress } : {}),\n style: mergeStyle(box, style),\n },\n check,\n )\n if (label === undefined) return boxEl\n return createElement(\n View,\n { style: { display: 'flex', flexDirection: 'row', alignItems: 'center', gap: space.sm } },\n boxEl,\n typeof label === 'string' ? createElement(Text, {}, label) : label,\n )\n}\n\n// ---------------------------------------------------------------------------\n// RadioGroup\n// ---------------------------------------------------------------------------\n\n/** One option in a {@link RadioGroup}. */\nexport interface RadioOption {\n readonly value: string\n readonly label: MindeesNode\n}\n\n/** Props for {@link RadioGroup}. */\nexport interface RadioGroupProps extends Omit<BaseProps, 'style'> {\n /** Controlled selected value (static or reactive). */\n readonly value: Reactive<string>\n readonly options: readonly RadioOption[]\n readonly onValueChange?: (value: string) => void\n readonly disabled?: boolean\n readonly style?: Reactive<StyleInput>\n}\n\n/** A single-select radio group (RN ships none built-in). Each row is `role=\"radio\"`. */\nexport const RadioGroup: Component<RadioGroupProps> = (props) => {\n const theme = useTheme()\n const { value, options, onValueChange, disabled, style, ...rest } = props\n const selected = toAccessor(value, '')\n const rows = options.map((option) => {\n const checked = (): boolean => selected() === option.value\n const dot = createElement(View, {\n style: () => ({\n width: 12,\n height: 12,\n borderRadius: radiusScale.full,\n backgroundColor: checked() ? theme().color.primary : 'transparent',\n }),\n })\n const ring = createElement(\n View,\n {\n style: () => ({\n width: 22,\n height: 22,\n borderRadius: radiusScale.full,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderWidth: 2,\n borderColor: checked() ? theme().color.primary : theme().color.textMuted,\n }),\n },\n dot,\n )\n const handlePress = onValueChange && !disabled ? () => onValueChange(option.value) : undefined\n return createElement(\n Pressable,\n {\n role: 'radio',\n state: () => ({ checked: checked() }),\n ...(disabled ? { disabled: true } : {}),\n ...(handlePress ? { onPress: handlePress } : {}),\n style: { display: 'flex', flexDirection: 'row', alignItems: 'center', gap: space.sm },\n },\n ring,\n typeof option.label === 'string' ? createElement(Text, {}, option.label) : option.label,\n )\n })\n return createElement(\n View,\n {\n ...toHostProps({ ...rest, style }),\n role: rest.role ?? 'radiogroup',\n style: mergeStyle({ display: 'flex', flexDirection: 'column', gap: space.sm }, style),\n },\n ...rows,\n )\n}\n\n// ---------------------------------------------------------------------------\n// Skeleton\n// ---------------------------------------------------------------------------\n\n/** Props for {@link Skeleton}. */\nexport interface SkeletonProps extends Omit<BaseProps, 'style'> {\n readonly width?: number | string\n readonly height?: number | string\n readonly radius?: number\n readonly style?: Reactive<StyleInput>\n}\n\n/** A muted loading placeholder block (RN ships none built-in). */\nexport const Skeleton: Component<SkeletonProps> = (props) => {\n const theme = useTheme()\n const { width = '100%', height = 16, radius = radiusScale.sm, style, ...rest } = props\n const base: Accessor<StyleInput> = () => ({\n width,\n height,\n borderRadius: radius,\n backgroundColor: theme().color.surfaceVariant,\n })\n const host = toHostProps({ ...rest, style: mergeStyle(base, style) })\n if (!host.role) host.role = 'status'\n host['aria-busy'] = 'true'\n return createElement('view', host)\n}\n\n// ---------------------------------------------------------------------------\n// Tabs\n// ---------------------------------------------------------------------------\n\n/** One tab: a value, a header label, and its panel content. */\nexport interface TabItem {\n readonly value: string\n readonly label: MindeesNode\n readonly content: MindeesNode\n}\n\n/** Props for {@link Tabs}. */\nexport interface TabsProps extends Omit<BaseProps, 'style'> {\n /** Controlled selected tab value (static or reactive). */\n readonly value: Reactive<string>\n readonly tabs: readonly TabItem[]\n readonly onValueChange?: (value: string) => void\n readonly style?: Reactive<StyleInput>\n}\n\n/** An accessible tab strip + panel (RN ships none built-in). Only the active panel region re-renders. */\nexport const Tabs: Component<TabsProps> = (props) => {\n const theme = useTheme()\n const { value, tabs, onValueChange, style, ...rest } = props\n const selected = toAccessor(value, tabs[0]?.value ?? '')\n const tabBar = createElement(\n View,\n {\n role: 'tablist',\n style: () => ({\n display: 'flex',\n flexDirection: 'row',\n gap: space.xs,\n borderBottomWidth: 1,\n borderColor: theme().color.border,\n }),\n },\n ...tabs.map((t) => {\n const active = (): boolean => selected() === t.value\n return createElement(\n Pressable,\n {\n role: 'tab',\n state: () => ({ selected: active() }),\n ...(onValueChange ? { onPress: () => onValueChange(t.value) } : {}),\n style: () => ({\n paddingTop: space.xs,\n paddingBottom: space.xs,\n paddingLeft: space.sm,\n paddingRight: space.sm,\n borderBottomWidth: 2,\n borderColor: active() ? theme().color.primary : 'transparent',\n }),\n },\n typeof t.label === 'string'\n ? createElement(\n Text,\n {\n style: () => ({\n color: active() ? theme().color.primary : theme().color.text,\n ...(active() ? { fontWeight: fontWeight.semibold } : {}),\n }),\n },\n t.label,\n )\n : t.label,\n )\n }),\n )\n // The active panel is a reactive region: switching tabs re-runs ONLY this region.\n const panel = createElement(View, { role: 'tabpanel', style: { padding: space.md } }, () => {\n const active = tabs.find((t) => t.value === selected())\n return active ? active.content : null\n })\n return createElement(\n View,\n {\n ...toHostProps({ ...rest, style }),\n style: mergeStyle({ display: 'flex', flexDirection: 'column' }, style),\n },\n tabBar,\n panel,\n )\n}\n\n// ---------------------------------------------------------------------------\n// Accordion\n// ---------------------------------------------------------------------------\n\n/** One collapsible accordion section. */\nexport interface AccordionSection {\n readonly id: string\n readonly header: MindeesNode\n readonly content: MindeesNode\n}\n\n/** Props for {@link Accordion}. */\nexport interface AccordionProps extends Omit<BaseProps, 'style'> {\n readonly sections: readonly AccordionSection[]\n /** Allow more than one section open at once (default: single-open). */\n readonly multiple?: boolean\n /** Section ids open on first render. */\n readonly defaultOpen?: readonly string[]\n readonly style?: Reactive<StyleInput>\n}\n\n/** Collapsible sections (RN ships none built-in). Each header is `aria-expanded`; panels mount lazily. */\nexport const Accordion: Component<AccordionProps> = (props) => {\n const theme = useTheme()\n const { sections, multiple, defaultOpen = [], style, ...rest } = props\n const open = signal<ReadonlySet<string>>(new Set(defaultOpen))\n const isOpen = (id: string): boolean => open().has(id)\n const toggle = (id: string): void => {\n const next = new Set(untrack(open))\n if (next.has(id)) next.delete(id)\n else {\n if (!multiple) next.clear()\n next.add(id)\n }\n open.set(next)\n }\n const rows = sections.map((section) => {\n const header = createElement(\n Pressable,\n {\n role: 'button',\n state: () => ({ expanded: isOpen(section.id) }),\n onPress: () => toggle(section.id),\n style: () => ({\n display: 'flex',\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'space-between',\n paddingTop: space.sm,\n paddingBottom: space.sm,\n borderBottomWidth: 1,\n borderColor: theme().color.border,\n }),\n },\n typeof section.header === 'string' ? createElement(Text, {}, section.header) : section.header,\n createElement(Text, { style: () => ({ color: theme().color.textMuted }) }, () =>\n isOpen(section.id) ? '▾' : '▸',\n ),\n )\n // The panel region re-runs only when THIS section toggles.\n const panel = createElement(View, { style: { padding: space.sm } }, () =>\n isOpen(section.id) ? section.content : null,\n )\n return createElement(View, {}, header, panel)\n })\n return createElement(\n View,\n {\n ...toHostProps({ ...rest, style }),\n style: mergeStyle({ display: 'flex', flexDirection: 'column' }, style),\n },\n ...rows,\n )\n}\n"],"mappings":"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"}
|
package/dist/form.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Accessor } from "@mindees/core";
|
|
2
|
+
import { StandardSchemaV1 } from "@mindees/router";
|
|
3
|
+
|
|
4
|
+
//#region src/form.d.ts
|
|
5
|
+
/** A bound field: reactive value/error/touched plus setters. */
|
|
6
|
+
interface Field<V> {
|
|
7
|
+
/** Reactive current value. */
|
|
8
|
+
readonly value: Accessor<V>;
|
|
9
|
+
/** Set the value (re-validates when `validateOnChange`). */
|
|
10
|
+
set(value: V): void;
|
|
11
|
+
/** Reactive validation error for this field, or `undefined`. */
|
|
12
|
+
readonly error: Accessor<string | undefined>;
|
|
13
|
+
/** Reactive touched flag (set on blur or submit). */
|
|
14
|
+
readonly touched: Accessor<boolean>;
|
|
15
|
+
/** Mark the field touched (re-validates when `validateOnChange`). */
|
|
16
|
+
onBlur(): void;
|
|
17
|
+
}
|
|
18
|
+
/** The form controller returned by {@link useForm}. */
|
|
19
|
+
interface FormApi<T extends object> {
|
|
20
|
+
/** Reactive current values. */
|
|
21
|
+
readonly values: Accessor<T>;
|
|
22
|
+
/** Reactive per-field error messages. */
|
|
23
|
+
readonly errors: Accessor<Partial<Record<keyof T, string>>>;
|
|
24
|
+
/** Reactive: no current errors. */
|
|
25
|
+
readonly isValid: Accessor<boolean>;
|
|
26
|
+
/** Reactive: a submit is in flight. */
|
|
27
|
+
readonly isSubmitting: Accessor<boolean>;
|
|
28
|
+
/** Bind a field by name. */
|
|
29
|
+
field<K extends keyof T>(name: K): Field<T[K]>;
|
|
30
|
+
/** Set one field's value (re-validates when `validateOnChange`). */
|
|
31
|
+
setValue<K extends keyof T>(name: K, value: T[K]): void;
|
|
32
|
+
/** Run validation now; returns whether the form is valid. */
|
|
33
|
+
validate(): boolean;
|
|
34
|
+
/** Validate, mark all fields touched, and call `onSubmit` with the values if valid. */
|
|
35
|
+
handleSubmit(): Promise<void>;
|
|
36
|
+
/** Reset values/errors/touched to the initial state. */
|
|
37
|
+
reset(): void;
|
|
38
|
+
}
|
|
39
|
+
/** Options for {@link useForm}. */
|
|
40
|
+
interface UseFormOptions<T extends object> {
|
|
41
|
+
readonly initialValues: T;
|
|
42
|
+
/** A Standard Schema (Zod/Valibot/ArkType/…) validating the whole values object. */
|
|
43
|
+
readonly schema?: StandardSchemaV1<unknown, T>;
|
|
44
|
+
/** Called with the validated values on a valid submit. */
|
|
45
|
+
readonly onSubmit: (values: T) => void | Promise<void>;
|
|
46
|
+
/** Re-validate on every change/blur (default: validate on submit only). */
|
|
47
|
+
readonly validateOnChange?: boolean;
|
|
48
|
+
}
|
|
49
|
+
/** Create a form controller with signal-backed state + Standard Schema validation. */
|
|
50
|
+
declare function useForm<T extends object>(options: UseFormOptions<T>): FormApi<T>;
|
|
51
|
+
//#endregion
|
|
52
|
+
export { Field, FormApi, UseFormOptions, useForm };
|
|
53
|
+
//# sourceMappingURL=form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form.d.ts","names":[],"sources":["../src/form.ts"],"mappings":";;;;;UAciB,KAAA;EAQW;EAAA,SANjB,KAAA,EAAO,QAAA,CAAS,CAAA;EAAhB;EAET,GAAA,CAAI,KAAA,EAAO,CAAA;EAFc;EAAA,SAIhB,KAAA,EAAO,QAAA;EAFL;EAAA,SAIF,OAAA,EAAS,QAAA;EAFT;EAIT,MAAA;AAAA;;UAIe,OAAA;EAJT;EAAA,SAMG,MAAA,EAAQ,QAAA,CAAS,CAAA;EAFX;EAAA,SAIN,MAAA,EAAQ,QAAA,CAAS,OAAA,CAAQ,MAAA,OAAa,CAAA;EAJzB;EAAA,SAMb,OAAA,EAAS,QAAA;EAJD;EAAA,SAMR,YAAA,EAAc,QAAA;EAJW;EAMlC,KAAA,iBAAsB,CAAA,EAAG,IAAA,EAAM,CAAA,GAAI,KAAA,CAAM,CAAA,CAAE,CAAA;EAN1B;EAQjB,QAAA,iBAAyB,CAAA,EAAG,IAAA,EAAM,CAAA,EAAG,KAAA,EAAO,CAAA,CAAE,CAAA;EAJvB;EAMvB,QAAA;EAJ+B;EAM/B,YAAA,IAAgB,OAAA;EAN2B;EAQ3C,KAAA;AAAA;;UAIe,cAAA;EAAA,SACN,aAAA,EAAe,CAAA;EAPR;EAAA,SASP,MAAA,GAAS,gBAAA,UAA0B,CAAA;EATrB;EAAA,SAWd,QAAA,GAAW,MAAA,EAAQ,CAAA,YAAa,OAAA;EAzBhC;EAAA,SA2BA,gBAAA;AAAA;;iBAIK,OAAA,mBAA0B,OAAA,EAAS,cAAA,CAAe,CAAA,IAAK,OAAA,CAAQ,CAAA"}
|
package/dist/form.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { batch, signal, untrack } from "@mindees/core";
|
|
2
|
+
//#region src/form.ts
|
|
3
|
+
/**
|
|
4
|
+
* `useForm` — a built-in form-state hook with **Standard Schema** validation (Zod/Valibot/ArkType/…),
|
|
5
|
+
* the thing RN and Flutter make you reach for react-hook-form / formik / a custom solution to get.
|
|
6
|
+
* Field values, errors, and touched state are signals, so a field binding re-renders only itself.
|
|
7
|
+
* Validation is synchronous (a Standard Schema that returns a Promise is rejected, mirroring the
|
|
8
|
+
* router) and maps each issue to its field via the issue path.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
/** Create a form controller with signal-backed state + Standard Schema validation. */
|
|
13
|
+
function useForm(options) {
|
|
14
|
+
const values = signal({ ...options.initialValues });
|
|
15
|
+
const errors = signal({});
|
|
16
|
+
const touched = signal({});
|
|
17
|
+
const submitting = signal(false);
|
|
18
|
+
/** Validate `vals` against the schema; return a per-field error map (empty = valid). */
|
|
19
|
+
const computeErrors = (vals) => {
|
|
20
|
+
if (!options.schema) return {};
|
|
21
|
+
const result = options.schema["~standard"].validate(vals);
|
|
22
|
+
if (result instanceof Promise) throw new TypeError("useForm: async Standard Schema validation is not supported (use a sync schema)");
|
|
23
|
+
if (!result.issues) return {};
|
|
24
|
+
const map = {};
|
|
25
|
+
for (const issue of result.issues) {
|
|
26
|
+
const seg = issue.path?.[0];
|
|
27
|
+
const key = typeof seg === "object" && seg !== null ? seg.key : seg;
|
|
28
|
+
if (key !== void 0 && map[key] === void 0) map[key] = issue.message;
|
|
29
|
+
}
|
|
30
|
+
return map;
|
|
31
|
+
};
|
|
32
|
+
const validate = () => {
|
|
33
|
+
const errs = computeErrors(untrack(values));
|
|
34
|
+
errors.set(errs);
|
|
35
|
+
return Object.keys(errs).length === 0;
|
|
36
|
+
};
|
|
37
|
+
const setValue = (name, value) => {
|
|
38
|
+
values.set({
|
|
39
|
+
...untrack(values),
|
|
40
|
+
[name]: value
|
|
41
|
+
});
|
|
42
|
+
if (options.validateOnChange) validate();
|
|
43
|
+
};
|
|
44
|
+
const field = (name) => ({
|
|
45
|
+
value: () => values()[name],
|
|
46
|
+
set: (value) => setValue(name, value),
|
|
47
|
+
error: () => errors()[name],
|
|
48
|
+
touched: () => touched()[name] === true,
|
|
49
|
+
onBlur: () => {
|
|
50
|
+
touched.set({
|
|
51
|
+
...untrack(touched),
|
|
52
|
+
[name]: true
|
|
53
|
+
});
|
|
54
|
+
if (options.validateOnChange) validate();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
const handleSubmit = async () => {
|
|
58
|
+
const allTouched = {};
|
|
59
|
+
for (const k of Object.keys(untrack(values))) allTouched[k] = true;
|
|
60
|
+
touched.set(allTouched);
|
|
61
|
+
if (!validate()) return;
|
|
62
|
+
batch(() => submitting.set(true));
|
|
63
|
+
try {
|
|
64
|
+
await options.onSubmit(untrack(values));
|
|
65
|
+
} finally {
|
|
66
|
+
submitting.set(false);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const reset = () => {
|
|
70
|
+
batch(() => {
|
|
71
|
+
values.set({ ...options.initialValues });
|
|
72
|
+
errors.set({});
|
|
73
|
+
touched.set({});
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
return {
|
|
77
|
+
values: () => values(),
|
|
78
|
+
errors: () => errors(),
|
|
79
|
+
isValid: () => Object.keys(errors()).length === 0,
|
|
80
|
+
isSubmitting: () => submitting(),
|
|
81
|
+
field,
|
|
82
|
+
setValue,
|
|
83
|
+
validate,
|
|
84
|
+
handleSubmit,
|
|
85
|
+
reset
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
export { useForm };
|
|
90
|
+
|
|
91
|
+
//# sourceMappingURL=form.js.map
|
package/dist/form.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form.js","names":[],"sources":["../src/form.ts"],"sourcesContent":["/**\n * `useForm` — a built-in form-state hook with **Standard Schema** validation (Zod/Valibot/ArkType/…),\n * the thing RN and Flutter make you reach for react-hook-form / formik / a custom solution to get.\n * Field values, errors, and touched state are signals, so a field binding re-renders only itself.\n * Validation is synchronous (a Standard Schema that returns a Promise is rejected, mirroring the\n * router) and maps each issue to its field via the issue path.\n *\n * @module\n */\n\nimport { type Accessor, batch, signal, untrack } from '@mindees/core'\nimport type { StandardSchemaV1 } from '@mindees/router'\n\n/** A bound field: reactive value/error/touched plus setters. */\nexport interface Field<V> {\n /** Reactive current value. */\n readonly value: Accessor<V>\n /** Set the value (re-validates when `validateOnChange`). */\n set(value: V): void\n /** Reactive validation error for this field, or `undefined`. */\n readonly error: Accessor<string | undefined>\n /** Reactive touched flag (set on blur or submit). */\n readonly touched: Accessor<boolean>\n /** Mark the field touched (re-validates when `validateOnChange`). */\n onBlur(): void\n}\n\n/** The form controller returned by {@link useForm}. */\nexport interface FormApi<T extends object> {\n /** Reactive current values. */\n readonly values: Accessor<T>\n /** Reactive per-field error messages. */\n readonly errors: Accessor<Partial<Record<keyof T, string>>>\n /** Reactive: no current errors. */\n readonly isValid: Accessor<boolean>\n /** Reactive: a submit is in flight. */\n readonly isSubmitting: Accessor<boolean>\n /** Bind a field by name. */\n field<K extends keyof T>(name: K): Field<T[K]>\n /** Set one field's value (re-validates when `validateOnChange`). */\n setValue<K extends keyof T>(name: K, value: T[K]): void\n /** Run validation now; returns whether the form is valid. */\n validate(): boolean\n /** Validate, mark all fields touched, and call `onSubmit` with the values if valid. */\n handleSubmit(): Promise<void>\n /** Reset values/errors/touched to the initial state. */\n reset(): void\n}\n\n/** Options for {@link useForm}. */\nexport interface UseFormOptions<T extends object> {\n readonly initialValues: T\n /** A Standard Schema (Zod/Valibot/ArkType/…) validating the whole values object. */\n readonly schema?: StandardSchemaV1<unknown, T>\n /** Called with the validated values on a valid submit. */\n readonly onSubmit: (values: T) => void | Promise<void>\n /** Re-validate on every change/blur (default: validate on submit only). */\n readonly validateOnChange?: boolean\n}\n\n/** Create a form controller with signal-backed state + Standard Schema validation. */\nexport function useForm<T extends object>(options: UseFormOptions<T>): FormApi<T> {\n const values = signal<T>({ ...options.initialValues })\n const errors = signal<Partial<Record<keyof T, string>>>({})\n const touched = signal<Partial<Record<keyof T, boolean>>>({})\n const submitting = signal(false)\n\n /** Validate `vals` against the schema; return a per-field error map (empty = valid). */\n const computeErrors = (vals: T): Partial<Record<keyof T, string>> => {\n if (!options.schema) return {}\n const result = options.schema['~standard'].validate(vals)\n if (result instanceof Promise) {\n throw new TypeError(\n 'useForm: async Standard Schema validation is not supported (use a sync schema)',\n )\n }\n if (!result.issues) return {}\n const map: Partial<Record<keyof T, string>> = {}\n for (const issue of result.issues) {\n const seg = issue.path?.[0]\n const key = (typeof seg === 'object' && seg !== null ? seg.key : seg) as keyof T | undefined\n if (key !== undefined && map[key] === undefined) map[key] = issue.message // first issue per field\n }\n return map\n }\n\n const validate = (): boolean => {\n const errs = computeErrors(untrack(values))\n errors.set(errs)\n return Object.keys(errs).length === 0\n }\n\n const setValue = <K extends keyof T>(name: K, value: T[K]): void => {\n values.set({ ...untrack(values), [name]: value } as T)\n if (options.validateOnChange) validate()\n }\n\n const field = <K extends keyof T>(name: K): Field<T[K]> => ({\n value: () => values()[name],\n set: (value) => setValue(name, value),\n error: () => errors()[name],\n touched: () => touched()[name] === true,\n onBlur: () => {\n touched.set({ ...untrack(touched), [name]: true })\n if (options.validateOnChange) validate()\n },\n })\n\n const handleSubmit = async (): Promise<void> => {\n // mark every field touched so all errors show\n const allTouched: Partial<Record<keyof T, boolean>> = {}\n for (const k of Object.keys(untrack(values)) as (keyof T)[]) allTouched[k] = true\n touched.set(allTouched)\n if (!validate()) return\n batch(() => submitting.set(true))\n try {\n await options.onSubmit(untrack(values))\n } finally {\n submitting.set(false)\n }\n }\n\n const reset = (): void => {\n batch(() => {\n values.set({ ...options.initialValues })\n errors.set({})\n touched.set({})\n })\n }\n\n return {\n values: () => values(),\n errors: () => errors(),\n isValid: () => Object.keys(errors()).length === 0,\n isSubmitting: () => submitting(),\n field,\n setValue,\n validate,\n handleSubmit,\n reset,\n }\n}\n"],"mappings":";;;;;;;;;;;;AA6DA,SAAgB,QAA0B,SAAwC;CAChF,MAAM,SAAS,OAAU,EAAE,GAAG,QAAQ,cAAc,CAAC;CACrD,MAAM,SAAS,OAAyC,CAAC,CAAC;CAC1D,MAAM,UAAU,OAA0C,CAAC,CAAC;CAC5D,MAAM,aAAa,OAAO,KAAK;;CAG/B,MAAM,iBAAiB,SAA8C;EACnE,IAAI,CAAC,QAAQ,QAAQ,OAAO,CAAC;EAC7B,MAAM,SAAS,QAAQ,OAAO,aAAa,SAAS,IAAI;EACxD,IAAI,kBAAkB,SACpB,MAAM,IAAI,UACR,gFACF;EAEF,IAAI,CAAC,OAAO,QAAQ,OAAO,CAAC;EAC5B,MAAM,MAAwC,CAAC;EAC/C,KAAK,MAAM,SAAS,OAAO,QAAQ;GACjC,MAAM,MAAM,MAAM,OAAO;GACzB,MAAM,MAAO,OAAO,QAAQ,YAAY,QAAQ,OAAO,IAAI,MAAM;GACjE,IAAI,QAAQ,KAAA,KAAa,IAAI,SAAS,KAAA,GAAW,IAAI,OAAO,MAAM;EACpE;EACA,OAAO;CACT;CAEA,MAAM,iBAA0B;EAC9B,MAAM,OAAO,cAAc,QAAQ,MAAM,CAAC;EAC1C,OAAO,IAAI,IAAI;EACf,OAAO,OAAO,KAAK,IAAI,EAAE,WAAW;CACtC;CAEA,MAAM,YAA+B,MAAS,UAAsB;EAClE,OAAO,IAAI;GAAE,GAAG,QAAQ,MAAM;IAAI,OAAO;EAAM,CAAM;EACrD,IAAI,QAAQ,kBAAkB,SAAS;CACzC;CAEA,MAAM,SAA4B,UAA0B;EAC1D,aAAa,OAAO,EAAE;EACtB,MAAM,UAAU,SAAS,MAAM,KAAK;EACpC,aAAa,OAAO,EAAE;EACtB,eAAe,QAAQ,EAAE,UAAU;EACnC,cAAc;GACZ,QAAQ,IAAI;IAAE,GAAG,QAAQ,OAAO;KAAI,OAAO;GAAK,CAAC;GACjD,IAAI,QAAQ,kBAAkB,SAAS;EACzC;CACF;CAEA,MAAM,eAAe,YAA2B;EAE9C,MAAM,aAAgD,CAAC;EACvD,KAAK,MAAM,KAAK,OAAO,KAAK,QAAQ,MAAM,CAAC,GAAkB,WAAW,KAAK;EAC7E,QAAQ,IAAI,UAAU;EACtB,IAAI,CAAC,SAAS,GAAG;EACjB,YAAY,WAAW,IAAI,IAAI,CAAC;EAChC,IAAI;GACF,MAAM,QAAQ,SAAS,QAAQ,MAAM,CAAC;EACxC,UAAU;GACR,WAAW,IAAI,KAAK;EACtB;CACF;CAEA,MAAM,cAAoB;EACxB,YAAY;GACV,OAAO,IAAI,EAAE,GAAG,QAAQ,cAAc,CAAC;GACvC,OAAO,IAAI,CAAC,CAAC;GACb,QAAQ,IAAI,CAAC,CAAC;EAChB,CAAC;CACH;CAEA,OAAO;EACL,cAAc,OAAO;EACrB,cAAc,OAAO;EACrB,eAAe,OAAO,KAAK,OAAO,CAAC,EAAE,WAAW;EAChD,oBAAoB,WAAW;EAC/B;EACA;EACA;EACA;EACA;CACF;AACF"}
|
package/dist/hooks.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Accessor } from "@mindees/core";
|
|
2
|
+
|
|
3
|
+
//#region src/hooks.d.ts
|
|
4
|
+
/** A boolean toggle (RN/React: bring-your-own). */
|
|
5
|
+
interface Toggle {
|
|
6
|
+
/** Reactive current value. */
|
|
7
|
+
readonly value: Accessor<boolean>;
|
|
8
|
+
/** Flip the value. */
|
|
9
|
+
toggle(): void;
|
|
10
|
+
/** Set `true`. */
|
|
11
|
+
on(): void;
|
|
12
|
+
/** Set `false`. */
|
|
13
|
+
off(): void;
|
|
14
|
+
/** Set explicitly. */
|
|
15
|
+
set(value: boolean): void;
|
|
16
|
+
}
|
|
17
|
+
/** A reactive boolean with toggle/on/off helpers. */
|
|
18
|
+
declare function useToggle(initial?: boolean): Toggle;
|
|
19
|
+
/** A bounded counter. */
|
|
20
|
+
interface Counter {
|
|
21
|
+
/** Reactive current count (always within `[min, max]`). */
|
|
22
|
+
readonly count: Accessor<number>;
|
|
23
|
+
/** Increment by `by` (default `step`). */
|
|
24
|
+
inc(by?: number): void;
|
|
25
|
+
/** Decrement by `by` (default `step`). */
|
|
26
|
+
dec(by?: number): void;
|
|
27
|
+
/** Set explicitly (clamped). */
|
|
28
|
+
set(value: number): void;
|
|
29
|
+
/** Reset to the initial value (clamped). */
|
|
30
|
+
reset(): void;
|
|
31
|
+
}
|
|
32
|
+
/** A reactive number with inc/dec/reset + optional min/max/step clamping. */
|
|
33
|
+
declare function useCounter(initial?: number, options?: {
|
|
34
|
+
min?: number;
|
|
35
|
+
max?: number;
|
|
36
|
+
step?: number;
|
|
37
|
+
}): Counter;
|
|
38
|
+
/** Track the PREVIOUS value of a reactive source (`undefined` until it changes once). */
|
|
39
|
+
declare function usePrevious<T>(source: Accessor<T>): Accessor<T | undefined>;
|
|
40
|
+
/** A reducer over reactive state (React's `useReducer`, signal-backed). */
|
|
41
|
+
declare function useReducer<S, A>(reducer: (state: S, action: A) => S, initial: S): [Accessor<S>, (action: A) => void];
|
|
42
|
+
/** The reactive state of an async resource. */
|
|
43
|
+
interface AsyncState<T> {
|
|
44
|
+
/** The latest resolved value, or `undefined`. */
|
|
45
|
+
readonly data: Accessor<T | undefined>;
|
|
46
|
+
/** The latest rejection, or `undefined`. */
|
|
47
|
+
readonly error: Accessor<unknown>;
|
|
48
|
+
/** Whether a run is in flight. */
|
|
49
|
+
readonly loading: Accessor<boolean>;
|
|
50
|
+
/** (Re)run the fetcher; supersedes any in-flight run (last-write-wins). */
|
|
51
|
+
run(): void;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Run an async fetcher into reactive `data`/`error`/`loading`. The newest `run()` wins — a stale
|
|
55
|
+
* in-flight promise can never clobber a newer result — and a run in flight when the owner disposes
|
|
56
|
+
* is ignored. Runs immediately unless `immediate: false`.
|
|
57
|
+
*/
|
|
58
|
+
declare function useAsync<T>(fetcher: () => Promise<T>, options?: {
|
|
59
|
+
immediate?: boolean;
|
|
60
|
+
}): AsyncState<T>;
|
|
61
|
+
//#endregion
|
|
62
|
+
export { AsyncState, Counter, Toggle, useAsync, useCounter, usePrevious, useReducer, useToggle };
|
|
63
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","names":[],"sources":["../src/hooks.ts"],"mappings":";;;;UAaiB,MAAA;EAUf;EAAA,SARS,KAAA,EAAO,QAAQ;EAQN;EANlB,MAAA;EAUc;EARd,EAAA;;EAEA,GAAA;EAMgD;EAJhD,GAAA,CAAI,KAAA;AAAA;;iBAIU,SAAA,CAAU,OAAA,aAAkB,MAAM;;UAYjC,OAAA;EAIf;EAAA,SAFS,KAAA,EAAO,QAAQ;EAIxB;EAFA,GAAA,CAAI,EAAA;EAIJ;EAFA,GAAA,CAAI,EAAA;EAIJ;EAFA,GAAA,CAAI,KAAA;EAEC;EAAL,KAAA;AAAA;;iBAIc,UAAA,CACd,OAAA,WACA,OAAA;EAAW,GAAA;EAAc,GAAA;EAAc,IAAA;AAAA,IACtC,OAAO;;iBAgBM,WAAA,IAAe,MAAA,EAAQ,QAAA,CAAS,CAAA,IAAK,QAAA,CAAS,CAAA;;iBAgB9C,UAAA,OACd,OAAA,GAAU,KAAA,EAAO,CAAA,EAAG,MAAA,EAAQ,CAAA,KAAM,CAAA,EAClC,OAAA,EAAS,CAAA,IACP,QAAA,CAAS,CAAA,IAAK,MAAA,EAAQ,CAAA;AAnB1B;AAAA,UAyBiB,UAAA;EAzBU;EAAA,SA2BhB,IAAA,EAAM,QAAA,CAAS,CAAA;EA3Ba;EAAA,SA6B5B,KAAA,EAAO,QAAA;EA7BmC;EAAA,SA+B1C,OAAA,EAAS,QAAA;EA/ByC;EAiC3D,GAAA;AAAA;;;;;;iBAQc,QAAA,IACd,OAAA,QAAe,OAAA,CAAQ,CAAA,GACvB,OAAA;EAAW,SAAA;AAAA,IACV,UAAA,CAAW,CAAA"}
|
package/dist/hooks.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { batch, effect, onCleanup, signal, untrack } from "@mindees/core";
|
|
2
|
+
//#region src/hooks.ts
|
|
3
|
+
/**
|
|
4
|
+
* Standard utility hooks — the batteries RN and Flutter make you reach for a library to get. Each is
|
|
5
|
+
* a thin, allocation-light wrapper over the reactive core (signals + effect), so they're
|
|
6
|
+
* renderer-agnostic (web + native), tracked where it matters, and untracked where a write shouldn't
|
|
7
|
+
* self-subscribe. Device/UI hooks (useWindowDimensions, useColorScheme, …) live in `environment.ts`;
|
|
8
|
+
* these are the stateful logic hooks.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
/** A reactive boolean with toggle/on/off helpers. */
|
|
13
|
+
function useToggle(initial = false) {
|
|
14
|
+
const s = signal(initial);
|
|
15
|
+
return {
|
|
16
|
+
value: () => s(),
|
|
17
|
+
toggle: () => s.set(!untrack(s)),
|
|
18
|
+
on: () => s.set(true),
|
|
19
|
+
off: () => s.set(false),
|
|
20
|
+
set: (value) => s.set(value)
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** A reactive number with inc/dec/reset + optional min/max/step clamping. */
|
|
24
|
+
function useCounter(initial = 0, options = {}) {
|
|
25
|
+
const step = options.step ?? 1;
|
|
26
|
+
const min = options.min ?? Number.NEGATIVE_INFINITY;
|
|
27
|
+
const max = options.max ?? Number.POSITIVE_INFINITY;
|
|
28
|
+
const clamp = (v) => Math.min(max, Math.max(min, v));
|
|
29
|
+
const s = signal(clamp(initial));
|
|
30
|
+
return {
|
|
31
|
+
count: () => s(),
|
|
32
|
+
inc: (by = step) => s.set(clamp(untrack(s) + by)),
|
|
33
|
+
dec: (by = step) => s.set(clamp(untrack(s) - by)),
|
|
34
|
+
set: (value) => s.set(clamp(value)),
|
|
35
|
+
reset: () => s.set(clamp(initial))
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** Track the PREVIOUS value of a reactive source (`undefined` until it changes once). */
|
|
39
|
+
function usePrevious(source) {
|
|
40
|
+
const prev = signal(void 0);
|
|
41
|
+
let last;
|
|
42
|
+
let first = true;
|
|
43
|
+
effect(() => {
|
|
44
|
+
const next = source();
|
|
45
|
+
untrack(() => {
|
|
46
|
+
if (!first) prev.set(last);
|
|
47
|
+
last = next;
|
|
48
|
+
first = false;
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
return () => prev();
|
|
52
|
+
}
|
|
53
|
+
/** A reducer over reactive state (React's `useReducer`, signal-backed). */
|
|
54
|
+
function useReducer(reducer, initial) {
|
|
55
|
+
const s = signal(initial);
|
|
56
|
+
return [() => s(), (action) => s.set(reducer(untrack(s), action))];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Run an async fetcher into reactive `data`/`error`/`loading`. The newest `run()` wins — a stale
|
|
60
|
+
* in-flight promise can never clobber a newer result — and a run in flight when the owner disposes
|
|
61
|
+
* is ignored. Runs immediately unless `immediate: false`.
|
|
62
|
+
*/
|
|
63
|
+
function useAsync(fetcher, options = {}) {
|
|
64
|
+
const data = signal(void 0);
|
|
65
|
+
const error = signal(void 0);
|
|
66
|
+
const loading = signal(false);
|
|
67
|
+
let token = 0;
|
|
68
|
+
const run = () => {
|
|
69
|
+
const mine = ++token;
|
|
70
|
+
batch(() => {
|
|
71
|
+
loading.set(true);
|
|
72
|
+
error.set(void 0);
|
|
73
|
+
});
|
|
74
|
+
fetcher().then((value) => {
|
|
75
|
+
if (mine !== token) return;
|
|
76
|
+
batch(() => {
|
|
77
|
+
data.set(value);
|
|
78
|
+
loading.set(false);
|
|
79
|
+
});
|
|
80
|
+
}, (err) => {
|
|
81
|
+
if (mine !== token) return;
|
|
82
|
+
batch(() => {
|
|
83
|
+
error.set(err);
|
|
84
|
+
loading.set(false);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
onCleanup(() => {
|
|
89
|
+
token++;
|
|
90
|
+
});
|
|
91
|
+
if (options.immediate !== false) run();
|
|
92
|
+
return {
|
|
93
|
+
data: () => data(),
|
|
94
|
+
error: () => error(),
|
|
95
|
+
loading: () => loading(),
|
|
96
|
+
run
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
export { useAsync, useCounter, usePrevious, useReducer, useToggle };
|
|
101
|
+
|
|
102
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","names":[],"sources":["../src/hooks.ts"],"sourcesContent":["/**\n * Standard utility hooks — the batteries RN and Flutter make you reach for a library to get. Each is\n * a thin, allocation-light wrapper over the reactive core (signals + effect), so they're\n * renderer-agnostic (web + native), tracked where it matters, and untracked where a write shouldn't\n * self-subscribe. Device/UI hooks (useWindowDimensions, useColorScheme, …) live in `environment.ts`;\n * these are the stateful logic hooks.\n *\n * @module\n */\n\nimport { type Accessor, batch, effect, onCleanup, signal, untrack } from '@mindees/core'\n\n/** A boolean toggle (RN/React: bring-your-own). */\nexport interface Toggle {\n /** Reactive current value. */\n readonly value: Accessor<boolean>\n /** Flip the value. */\n toggle(): void\n /** Set `true`. */\n on(): void\n /** Set `false`. */\n off(): void\n /** Set explicitly. */\n set(value: boolean): void\n}\n\n/** A reactive boolean with toggle/on/off helpers. */\nexport function useToggle(initial = false): Toggle {\n const s = signal(initial)\n return {\n value: () => s(),\n toggle: () => s.set(!untrack(s)),\n on: () => s.set(true),\n off: () => s.set(false),\n set: (value) => s.set(value),\n }\n}\n\n/** A bounded counter. */\nexport interface Counter {\n /** Reactive current count (always within `[min, max]`). */\n readonly count: Accessor<number>\n /** Increment by `by` (default `step`). */\n inc(by?: number): void\n /** Decrement by `by` (default `step`). */\n dec(by?: number): void\n /** Set explicitly (clamped). */\n set(value: number): void\n /** Reset to the initial value (clamped). */\n reset(): void\n}\n\n/** A reactive number with inc/dec/reset + optional min/max/step clamping. */\nexport function useCounter(\n initial = 0,\n options: { min?: number; max?: number; step?: number } = {},\n): Counter {\n const step = options.step ?? 1\n const min = options.min ?? Number.NEGATIVE_INFINITY\n const max = options.max ?? Number.POSITIVE_INFINITY\n const clamp = (v: number): number => Math.min(max, Math.max(min, v))\n const s = signal(clamp(initial))\n return {\n count: () => s(),\n inc: (by = step) => s.set(clamp(untrack(s) + by)),\n dec: (by = step) => s.set(clamp(untrack(s) - by)),\n set: (value) => s.set(clamp(value)),\n reset: () => s.set(clamp(initial)),\n }\n}\n\n/** Track the PREVIOUS value of a reactive source (`undefined` until it changes once). */\nexport function usePrevious<T>(source: Accessor<T>): Accessor<T | undefined> {\n const prev = signal<T | undefined>(undefined)\n let last: T | undefined\n let first = true\n effect(() => {\n const next = source() // track the source\n untrack(() => {\n if (!first) prev.set(last)\n last = next\n first = false\n })\n })\n return () => prev()\n}\n\n/** A reducer over reactive state (React's `useReducer`, signal-backed). */\nexport function useReducer<S, A>(\n reducer: (state: S, action: A) => S,\n initial: S,\n): [Accessor<S>, (action: A) => void] {\n const s = signal(initial)\n return [() => s(), (action) => s.set(reducer(untrack(s), action))]\n}\n\n/** The reactive state of an async resource. */\nexport interface AsyncState<T> {\n /** The latest resolved value, or `undefined`. */\n readonly data: Accessor<T | undefined>\n /** The latest rejection, or `undefined`. */\n readonly error: Accessor<unknown>\n /** Whether a run is in flight. */\n readonly loading: Accessor<boolean>\n /** (Re)run the fetcher; supersedes any in-flight run (last-write-wins). */\n run(): void\n}\n\n/**\n * Run an async fetcher into reactive `data`/`error`/`loading`. The newest `run()` wins — a stale\n * in-flight promise can never clobber a newer result — and a run in flight when the owner disposes\n * is ignored. Runs immediately unless `immediate: false`.\n */\nexport function useAsync<T>(\n fetcher: () => Promise<T>,\n options: { immediate?: boolean } = {},\n): AsyncState<T> {\n const data = signal<T | undefined>(undefined)\n const error = signal<unknown>(undefined)\n const loading = signal(false)\n let token = 0\n const run = (): void => {\n const mine = ++token\n batch(() => {\n loading.set(true)\n error.set(undefined)\n })\n fetcher().then(\n (value) => {\n if (mine !== token) return // superseded / disposed\n batch(() => {\n data.set(value)\n loading.set(false)\n })\n },\n (err) => {\n if (mine !== token) return\n batch(() => {\n error.set(err)\n loading.set(false)\n })\n },\n )\n }\n onCleanup(() => {\n token++ // invalidate any in-flight run on dispose\n })\n if (options.immediate !== false) run()\n return { data: () => data(), error: () => error(), loading: () => loading(), run }\n}\n"],"mappings":";;;;;;;;;;;;AA2BA,SAAgB,UAAU,UAAU,OAAe;CACjD,MAAM,IAAI,OAAO,OAAO;CACxB,OAAO;EACL,aAAa,EAAE;EACf,cAAc,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;EAC/B,UAAU,EAAE,IAAI,IAAI;EACpB,WAAW,EAAE,IAAI,KAAK;EACtB,MAAM,UAAU,EAAE,IAAI,KAAK;CAC7B;AACF;;AAiBA,SAAgB,WACd,UAAU,GACV,UAAyD,CAAC,GACjD;CACT,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,MAAM,QAAQ,OAAO,OAAO;CAClC,MAAM,MAAM,QAAQ,OAAO,OAAO;CAClC,MAAM,SAAS,MAAsB,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,CAAC,CAAC;CACnE,MAAM,IAAI,OAAO,MAAM,OAAO,CAAC;CAC/B,OAAO;EACL,aAAa,EAAE;EACf,MAAM,KAAK,SAAS,EAAE,IAAI,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;EAChD,MAAM,KAAK,SAAS,EAAE,IAAI,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;EAChD,MAAM,UAAU,EAAE,IAAI,MAAM,KAAK,CAAC;EAClC,aAAa,EAAE,IAAI,MAAM,OAAO,CAAC;CACnC;AACF;;AAGA,SAAgB,YAAe,QAA8C;CAC3E,MAAM,OAAO,OAAsB,KAAA,CAAS;CAC5C,IAAI;CACJ,IAAI,QAAQ;CACZ,aAAa;EACX,MAAM,OAAO,OAAO;EACpB,cAAc;GACZ,IAAI,CAAC,OAAO,KAAK,IAAI,IAAI;GACzB,OAAO;GACP,QAAQ;EACV,CAAC;CACH,CAAC;CACD,aAAa,KAAK;AACpB;;AAGA,SAAgB,WACd,SACA,SACoC;CACpC,MAAM,IAAI,OAAO,OAAO;CACxB,OAAO,OAAO,EAAE,IAAI,WAAW,EAAE,IAAI,QAAQ,QAAQ,CAAC,GAAG,MAAM,CAAC,CAAC;AACnE;;;;;;AAmBA,SAAgB,SACd,SACA,UAAmC,CAAC,GACrB;CACf,MAAM,OAAO,OAAsB,KAAA,CAAS;CAC5C,MAAM,QAAQ,OAAgB,KAAA,CAAS;CACvC,MAAM,UAAU,OAAO,KAAK;CAC5B,IAAI,QAAQ;CACZ,MAAM,YAAkB;EACtB,MAAM,OAAO,EAAE;EACf,YAAY;GACV,QAAQ,IAAI,IAAI;GAChB,MAAM,IAAI,KAAA,CAAS;EACrB,CAAC;EACD,QAAQ,EAAE,MACP,UAAU;GACT,IAAI,SAAS,OAAO;GACpB,YAAY;IACV,KAAK,IAAI,KAAK;IACd,QAAQ,IAAI,KAAK;GACnB,CAAC;EACH,IACC,QAAQ;GACP,IAAI,SAAS,OAAO;GACpB,YAAY;IACV,MAAM,IAAI,GAAG;IACb,QAAQ,IAAI,KAAK;GACnB,CAAC;EACH,CACF;CACF;CACA,gBAAgB;EACd;CACF,CAAC;CACD,IAAI,QAAQ,cAAc,OAAO,IAAI;CACrC,OAAO;EAAE,YAAY,KAAK;EAAG,aAAa,MAAM;EAAG,eAAe,QAAQ;EAAG;CAAI;AACnF"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { A11yProps, A11yState, Role, toA11yProps } from "./a11y.js";
|
|
2
2
|
import { StyleInput, StyleObject, StyleValue, flattenStyle } from "./style.js";
|
|
3
3
|
import { BaseProps, Reactive, resolveStyle, toHostProps } from "./host.js";
|
|
4
|
-
import { ActivityIndicator, ActivityIndicatorProps, Avatar, AvatarProps, Badge, BadgeProps, Card, CardProps, Chip, ChipProps, Divider, DividerProps, KeyboardAvoidingView, KeyboardAvoidingViewProps, ProgressBar, ProgressBarProps, SafeAreaView, SafeAreaViewProps, Switch, SwitchProps } from "./components.js";
|
|
4
|
+
import { Accordion, AccordionProps, AccordionSection, ActivityIndicator, ActivityIndicatorProps, Avatar, AvatarProps, Badge, BadgeProps, Card, CardProps, Checkbox, CheckboxProps, Chip, ChipProps, Divider, DividerProps, KeyboardAvoidingView, KeyboardAvoidingViewProps, ProgressBar, ProgressBarProps, RadioGroup, RadioGroupProps, RadioOption, SafeAreaView, SafeAreaViewProps, Skeleton, SkeletonProps, Switch, SwitchProps, TabItem, Tabs, TabsProps } from "./components.js";
|
|
5
5
|
import { ColorScheme, KeyboardState, PlatformEnvironment, SafeAreaInsets, WindowDimensions, getEnvironment, setEnvironment, useColorScheme, useKeyboard, useSafeAreaInsets, useWindowDimensions } from "./environment.js";
|
|
6
|
+
import { Field, FormApi, UseFormOptions, useForm } from "./form.js";
|
|
6
7
|
import { AttachableGesture, GestureView, GestureViewProps } from "./gesture.js";
|
|
8
|
+
import { AsyncState, Counter, Toggle, useAsync, useCounter, usePrevious, useReducer, useToggle } from "./hooks.js";
|
|
7
9
|
import { Theme, ThemeColors, duration, easing, fontSize, fontWeight, getTheme, lineHeight, palette, radius, space, tokens, useTheme } from "./tokens.js";
|
|
8
10
|
import { animateTo, motion } from "./motion.js";
|
|
9
11
|
import { FocusScope, FocusScopeProps, Modal, ModalProps } from "./overlay.js";
|
|
@@ -14,7 +16,7 @@ import { Maturity, NotImplementedError, PackageInfo, notImplemented } from "@min
|
|
|
14
16
|
/** The npm package name. */
|
|
15
17
|
declare const name = "@mindees/atlas";
|
|
16
18
|
/** The package version. All `@mindees/*` packages share one locked version line. */
|
|
17
|
-
declare const VERSION = "0.
|
|
19
|
+
declare const VERSION = "0.9.0";
|
|
18
20
|
/** Current maturity of this package. See the repository `STATUS.md`. */
|
|
19
21
|
declare const maturity: Maturity;
|
|
20
22
|
/**
|
|
@@ -24,5 +26,5 @@ declare const maturity: Maturity;
|
|
|
24
26
|
*/
|
|
25
27
|
declare const info: PackageInfo;
|
|
26
28
|
//#endregion
|
|
27
|
-
export { type A11yProps, type A11yState, ActivityIndicator, type ActivityIndicatorProps, type AttachableGesture, Avatar, type AvatarProps, Badge, type BadgeProps, type BaseProps, Button, type ButtonProps, Card, type CardProps, Chip, type ChipProps, type ColorScheme, Column, Divider, type DividerProps, FocusScope, type FocusScopeProps, GestureView, type GestureViewProps, Image, type ImageProps, type InteractionState, KeyboardAvoidingView, type KeyboardAvoidingViewProps, type KeyboardState, type Maturity, Modal, type ModalProps, NotImplementedError, type PackageInfo, type PlatformEnvironment, Pressable, type PressableProps, ProgressBar, type ProgressBarProps, type Reactive, type Role, Row, type SafeAreaInsets, SafeAreaView, type SafeAreaViewProps, ScrollView, type ScrollViewProps, Spacer, type SpacerProps, Stack, type StackProps, type StyleInput, type StyleObject, type StyleValue, Switch, type SwitchProps, Text, TextInput, type TextInputProps, type TextProps, type Theme, type ThemeColors, VERSION, View, type ViewProps, type WindowDimensions, animateTo, duration, easing, flattenStyle, fontSize, fontWeight, getEnvironment, getTheme, info, lineHeight, maturity, motion, name, notImplemented, palette, radius, resolveStyle, setEnvironment, space, toA11yProps, toHostProps, tokens, useColorScheme, useKeyboard, usePressable, useSafeAreaInsets, useTheme, useWindowDimensions };
|
|
29
|
+
export { type A11yProps, type A11yState, Accordion, type AccordionProps, type AccordionSection, ActivityIndicator, type ActivityIndicatorProps, type AsyncState, type AttachableGesture, Avatar, type AvatarProps, Badge, type BadgeProps, type BaseProps, Button, type ButtonProps, Card, type CardProps, Checkbox, type CheckboxProps, Chip, type ChipProps, type ColorScheme, Column, type Counter, Divider, type DividerProps, type Field, FocusScope, type FocusScopeProps, type FormApi, GestureView, type GestureViewProps, Image, type ImageProps, type InteractionState, KeyboardAvoidingView, type KeyboardAvoidingViewProps, type KeyboardState, type Maturity, Modal, type ModalProps, NotImplementedError, type PackageInfo, type PlatformEnvironment, Pressable, type PressableProps, ProgressBar, type ProgressBarProps, RadioGroup, type RadioGroupProps, type RadioOption, type Reactive, type Role, Row, type SafeAreaInsets, SafeAreaView, type SafeAreaViewProps, ScrollView, type ScrollViewProps, Skeleton, type SkeletonProps, Spacer, type SpacerProps, Stack, type StackProps, type StyleInput, type StyleObject, type StyleValue, Switch, type SwitchProps, type TabItem, Tabs, type TabsProps, Text, TextInput, type TextInputProps, type TextProps, type Theme, type ThemeColors, type Toggle, type UseFormOptions, VERSION, View, type ViewProps, type WindowDimensions, animateTo, duration, easing, flattenStyle, fontSize, fontWeight, getEnvironment, getTheme, info, lineHeight, maturity, motion, name, notImplemented, palette, radius, resolveStyle, setEnvironment, space, toA11yProps, toHostProps, tokens, useAsync, useColorScheme, useCounter, useForm, useKeyboard, usePressable, usePrevious, useReducer, useSafeAreaInsets, useTheme, useToggle, useWindowDimensions };
|
|
28
30
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;;;;;;;;;;cAea,IAAA;;cAGA,OAAA;;cAGA,QAAA,EAAU,QAAyB;;AAN/B;AAGjB;;;cAUa,IAAA,EAAM,WAAiE"}
|
package/dist/index.js
CHANGED
|
@@ -4,8 +4,10 @@ import { flattenStyle } from "./style.js";
|
|
|
4
4
|
import { resolveStyle, toHostProps } from "./host.js";
|
|
5
5
|
import { Button, Column, Image, Pressable, Row, ScrollView, Spacer, Stack, Text, TextInput, View, usePressable } from "./primitives.js";
|
|
6
6
|
import { duration, easing, fontSize, fontWeight, getTheme, lineHeight, palette, radius, space, tokens, useTheme } from "./tokens.js";
|
|
7
|
-
import { ActivityIndicator, Avatar, Badge, Card, Chip, Divider, KeyboardAvoidingView, ProgressBar, SafeAreaView, Switch } from "./components.js";
|
|
7
|
+
import { Accordion, ActivityIndicator, Avatar, Badge, Card, Checkbox, Chip, Divider, KeyboardAvoidingView, ProgressBar, RadioGroup, SafeAreaView, Skeleton, Switch, Tabs } from "./components.js";
|
|
8
|
+
import { useForm } from "./form.js";
|
|
8
9
|
import { GestureView } from "./gesture.js";
|
|
10
|
+
import { useAsync, useCounter, usePrevious, useReducer, useToggle } from "./hooks.js";
|
|
9
11
|
import { animateTo, motion } from "./motion.js";
|
|
10
12
|
import { FocusScope, Modal } from "./overlay.js";
|
|
11
13
|
import { NotImplementedError, notImplemented } from "@mindees/core";
|
|
@@ -13,7 +15,7 @@ import { NotImplementedError, notImplemented } from "@mindees/core";
|
|
|
13
15
|
/** The npm package name. */
|
|
14
16
|
const name = "@mindees/atlas";
|
|
15
17
|
/** The package version. All `@mindees/*` packages share one locked version line. */
|
|
16
|
-
const VERSION = "0.
|
|
18
|
+
const VERSION = "0.9.0";
|
|
17
19
|
/** Current maturity of this package. See the repository `STATUS.md`. */
|
|
18
20
|
const maturity = "experimental";
|
|
19
21
|
/**
|
|
@@ -27,6 +29,6 @@ const info = Object.freeze({
|
|
|
27
29
|
maturity
|
|
28
30
|
});
|
|
29
31
|
//#endregion
|
|
30
|
-
export { ActivityIndicator, Avatar, Badge, Button, Card, Chip, Column, Divider, FocusScope, GestureView, Image, KeyboardAvoidingView, Modal, NotImplementedError, Pressable, ProgressBar, Row, SafeAreaView, ScrollView, Spacer, Stack, Switch, Text, TextInput, VERSION, View, animateTo, duration, easing, flattenStyle, fontSize, fontWeight, getEnvironment, getTheme, info, lineHeight, maturity, motion, name, notImplemented, palette, radius, resolveStyle, setEnvironment, space, toA11yProps, toHostProps, tokens, useColorScheme, useKeyboard, usePressable, useSafeAreaInsets, useTheme, useWindowDimensions };
|
|
32
|
+
export { Accordion, ActivityIndicator, Avatar, Badge, Button, Card, Checkbox, Chip, Column, Divider, FocusScope, GestureView, Image, KeyboardAvoidingView, Modal, NotImplementedError, Pressable, ProgressBar, RadioGroup, Row, SafeAreaView, ScrollView, Skeleton, Spacer, Stack, Switch, Tabs, Text, TextInput, VERSION, View, animateTo, duration, easing, flattenStyle, fontSize, fontWeight, getEnvironment, getTheme, info, lineHeight, maturity, motion, name, notImplemented, palette, radius, resolveStyle, setEnvironment, space, toA11yProps, toHostProps, tokens, useAsync, useColorScheme, useCounter, useForm, useKeyboard, usePressable, usePrevious, useReducer, useSafeAreaInsets, useTheme, useToggle, useWindowDimensions };
|
|
31
33
|
|
|
32
34
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@mindees/atlas` (Atlas) — accessible, signals-native UI primitives. Function components\n * over `@mindees/core`'s `createElement` that return renderer-agnostic `MindeesNode` trees:\n * web rendering is real via the Helix DOM backend; native is a labeled 🔬 research track (the\n * same serializable tree, interpreted by a native host later). A curated cross-platform\n * `StyleObject`, typed accessibility, and a structural theme (on the `@mindees/atlas/theme`\n * subpath). The virtualized recycling `List` is on the `@mindees/atlas/list` subpath.\n *\n * @module\n */\n\nimport type { Maturity, PackageInfo } from '@mindees/core'\nimport { NotImplementedError, notImplemented } from '@mindees/core'\n\n/** The npm package name. */\nexport const name = '@mindees/atlas'\n\n/** The package version. All `@mindees/*` packages share one locked version line. */\nexport const VERSION = '0.
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@mindees/atlas` (Atlas) — accessible, signals-native UI primitives. Function components\n * over `@mindees/core`'s `createElement` that return renderer-agnostic `MindeesNode` trees:\n * web rendering is real via the Helix DOM backend; native is a labeled 🔬 research track (the\n * same serializable tree, interpreted by a native host later). A curated cross-platform\n * `StyleObject`, typed accessibility, and a structural theme (on the `@mindees/atlas/theme`\n * subpath). The virtualized recycling `List` is on the `@mindees/atlas/list` subpath.\n *\n * @module\n */\n\nimport type { Maturity, PackageInfo } from '@mindees/core'\nimport { NotImplementedError, notImplemented } from '@mindees/core'\n\n/** The npm package name. */\nexport const name = '@mindees/atlas'\n\n/** The package version. All `@mindees/*` packages share one locked version line. */\nexport const VERSION = '0.9.0'\n\n/** Current maturity of this package. See the repository `STATUS.md`. */\nexport const maturity: Maturity = 'experimental'\n\n/**\n * Static identity + maturity metadata for this package. Frozen so the\n * self-reported identity tooling introspects cannot be mutated at runtime,\n * matching the `readonly` fields of {@link PackageInfo}.\n */\nexport const info: PackageInfo = Object.freeze({ name, version: VERSION, maturity })\n\nexport { type A11yProps, type A11yState, type Role, toA11yProps } from './a11y'\nexport {\n Accordion,\n type AccordionProps,\n type AccordionSection,\n ActivityIndicator,\n type ActivityIndicatorProps,\n Avatar,\n type AvatarProps,\n Badge,\n type BadgeProps,\n Card,\n type CardProps,\n Checkbox,\n type CheckboxProps,\n Chip,\n type ChipProps,\n Divider,\n type DividerProps,\n KeyboardAvoidingView,\n type KeyboardAvoidingViewProps,\n ProgressBar,\n type ProgressBarProps,\n RadioGroup,\n type RadioGroupProps,\n type RadioOption,\n SafeAreaView,\n type SafeAreaViewProps,\n Skeleton,\n type SkeletonProps,\n Switch,\n type SwitchProps,\n type TabItem,\n Tabs,\n type TabsProps,\n} from './components'\nexport {\n type ColorScheme,\n getEnvironment,\n type KeyboardState,\n type PlatformEnvironment,\n type SafeAreaInsets,\n setEnvironment,\n useColorScheme,\n useKeyboard,\n useSafeAreaInsets,\n useWindowDimensions,\n type WindowDimensions,\n} from './environment'\nexport { type Field, type FormApi, type UseFormOptions, useForm } from './form'\nexport { type AttachableGesture, GestureView, type GestureViewProps } from './gesture'\nexport {\n type AsyncState,\n type Counter,\n type Toggle,\n useAsync,\n useCounter,\n usePrevious,\n useReducer,\n useToggle,\n} from './hooks'\nexport { type BaseProps, type Reactive, resolveStyle, toHostProps } from './host'\nexport { animateTo, motion } from './motion'\nexport {\n FocusScope,\n type FocusScopeProps,\n Modal,\n type ModalProps,\n} from './overlay'\nexport {\n Button,\n type ButtonProps,\n Column,\n Image,\n type ImageProps,\n type InteractionState,\n Pressable,\n type PressableProps,\n Row,\n ScrollView,\n type ScrollViewProps,\n Spacer,\n type SpacerProps,\n Stack,\n type StackProps,\n Text,\n TextInput,\n type TextInputProps,\n type TextProps,\n usePressable,\n View,\n type ViewProps,\n} from './primitives'\nexport { flattenStyle, type StyleInput, type StyleObject, type StyleValue } from './style'\nexport {\n duration,\n easing,\n fontSize,\n fontWeight,\n getTheme,\n lineHeight,\n palette,\n radius,\n space,\n type Theme,\n type ThemeColors,\n tokens,\n useTheme,\n} from './tokens'\nexport type { Maturity, PackageInfo }\nexport { NotImplementedError, notImplemented }\n"],"mappings":";;;;;;;;;;;;;;;AAeA,MAAa,OAAO;;AAGpB,MAAa,UAAU;;AAGvB,MAAa,WAAqB;;;;;;AAOlC,MAAa,OAAoB,OAAO,OAAO;CAAE;CAAM,SAAS;CAAS;AAAS,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mindees/atlas",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "MindeesNative Atlas - accessible, signals-native UI primitives + a virtualized recycling list. Renderer-agnostic (web real, native research track).",
|
|
5
5
|
"license": "MIT OR Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -39,12 +39,12 @@
|
|
|
39
39
|
"directory": "packages/atlas"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@mindees/core": "0.
|
|
43
|
-
"@mindees/router": "0.
|
|
42
|
+
"@mindees/core": "0.9.0",
|
|
43
|
+
"@mindees/router": "0.9.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"happy-dom": "20.9.0",
|
|
47
|
-
"@mindees/renderer": "0.
|
|
47
|
+
"@mindees/renderer": "0.9.0"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "tsdown",
|