@mindees/atlas 0.22.0 → 0.22.1
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/form.js +2 -1
- package/dist/form.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/form.js
CHANGED
|
@@ -55,6 +55,7 @@ function useForm(options) {
|
|
|
55
55
|
}
|
|
56
56
|
});
|
|
57
57
|
const handleSubmit = async () => {
|
|
58
|
+
if (untrack(submitting)) return;
|
|
58
59
|
const allTouched = {};
|
|
59
60
|
for (const k of Object.keys(untrack(values))) allTouched[k] = true;
|
|
60
61
|
touched.set(allTouched);
|
|
@@ -76,7 +77,7 @@ function useForm(options) {
|
|
|
76
77
|
return {
|
|
77
78
|
values: () => values(),
|
|
78
79
|
errors: () => errors(),
|
|
79
|
-
isValid: () => Object.keys(
|
|
80
|
+
isValid: () => Object.keys(computeErrors(values())).length === 0,
|
|
80
81
|
isSubmitting: () => submitting(),
|
|
81
82
|
field,
|
|
82
83
|
setValue,
|
package/dist/form.js.map
CHANGED
|
@@ -1 +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(
|
|
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 // Guard against double submission (double-tap, or a re-entrant call before a slow onSubmit\n // settles) — the documented `isSubmitting` state now actually prevents a second concurrent submit.\n if (untrack(submitting)) return\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 // Derive validity from the schema against CURRENT values (reactive), not the last-written `errors`\n // signal — so a fresh form with invalid initialValues reports invalid before any validate() runs.\n isValid: () => Object.keys(computeErrors(values())).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;EAG9C,IAAI,QAAQ,UAAU,GAAG;EAEzB,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;EAGrB,eAAe,OAAO,KAAK,cAAc,OAAO,CAAC,CAAC,EAAE,WAAW;EAC/D,oBAAoB,WAAW;EAC/B;EACA;EACA;EACA;EACA;CACF;AACF"}
|
package/dist/index.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ import { Maturity, NotImplementedError, PackageInfo, notImplemented } from "@min
|
|
|
18
18
|
/** The npm package name. */
|
|
19
19
|
declare const name = "@mindees/atlas";
|
|
20
20
|
/** The package version. All `@mindees/*` packages share one locked version line. */
|
|
21
|
-
declare const VERSION = "0.22.
|
|
21
|
+
declare const VERSION = "0.22.1";
|
|
22
22
|
/** Current maturity of this package. See the repository `STATUS.md`. */
|
|
23
23
|
declare const maturity: Maturity;
|
|
24
24
|
/**
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,7 @@ import { NotImplementedError, notImplemented } from "@mindees/core";
|
|
|
17
17
|
/** The npm package name. */
|
|
18
18
|
const name = "@mindees/atlas";
|
|
19
19
|
/** The package version. All `@mindees/*` packages share one locked version line. */
|
|
20
|
-
const VERSION = "0.22.
|
|
20
|
+
const VERSION = "0.22.1";
|
|
21
21
|
/** Current maturity of this package. See the repository `STATUS.md`. */
|
|
22
22
|
const maturity = "experimental";
|
|
23
23
|
/**
|
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.22.
|
|
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.22.1'\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 type Segment,\n SegmentedControl,\n type SegmentedControlProps,\n Skeleton,\n type SkeletonProps,\n Stepper,\n type StepperProps,\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 useReducedMotion,\n useSafeAreaInsets,\n useWindowDimensions,\n type WindowDimensions,\n} from './environment'\nexport { ErrorBoundary, type ErrorBoundaryProps } from './error-boundary'\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 PersistentSignalOptions,\n type SignalStorage,\n type Toggle,\n useAsync,\n useCounter,\n useDebounce,\n useInterval,\n usePersistentSignal,\n usePrevious,\n useReducer,\n useTimeout,\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 Toast,\n type ToastProps,\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 { Show, type ShowProps } from './show'\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.22.
|
|
3
|
+
"version": "0.22.1",
|
|
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.22.
|
|
43
|
-
"@mindees/router": "0.22.
|
|
42
|
+
"@mindees/core": "0.22.1",
|
|
43
|
+
"@mindees/router": "0.22.1"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"happy-dom": "20.9.0",
|
|
47
|
-
"@mindees/renderer": "0.22.
|
|
47
|
+
"@mindees/renderer": "0.22.1"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "tsdown",
|