@homebound/form-state
Advanced tools
Comparing version 2.20.1 to 2.21.0
import { Fragment, ObjectState } from "./fields/objectField"; | ||
import { Rule } from "./rules"; | ||
import { Builtin } from "./utils"; | ||
import { Builtin, OmitIf } from "./utils"; | ||
/** | ||
@@ -11,7 +11,4 @@ * Config rules for each field in `T` that we're editing in a form. | ||
export type ObjectConfig<T> = { | ||
[P in keyof OmitIf<T, Function>]: T[P] extends Fragment<infer V> ? FragmentFieldConfig : T[P] extends Array<infer U> | null | undefined ? U extends Builtin ? ValueFieldConfig<T, T[P]> : ListFieldConfig<T, U> : ValueFieldConfig<T, T[P]> | ObjectFieldConfig<T[P]>; | ||
[P in keyof OmitIf<T, Function>]: T[P] extends Fragment<infer V> ? FragmentFieldConfig : T[P] extends Array<infer U> | null | undefined ? U extends Builtin ? ValueFieldConfig<T[P]> : ListFieldConfig<U> : ValueFieldConfig<T[P]> | ObjectFieldConfig<T[P]>; | ||
}; | ||
type OmitIf<Base, Condition> = Pick<Base, { | ||
[Key in keyof Base]: Base[Key] extends Condition ? never : Key; | ||
}[keyof Base]>; | ||
/** Field configuration for an opaque value that we don't actually want to include. */ | ||
@@ -22,3 +19,3 @@ export type FragmentFieldConfig = { | ||
/** Field configuration for primitive values, i.e. strings/numbers/Dates/user-defined types. */ | ||
export type ValueFieldConfig<T, V> = { | ||
export type ValueFieldConfig<V> = { | ||
type: "value"; | ||
@@ -50,3 +47,3 @@ rules?: Rule<V | null | undefined>[]; | ||
/** Field configuration for list values, i.e. `U` is `Book` in a form with `books: Book[]`. */ | ||
export type ListFieldConfig<T, U> = { | ||
export type ListFieldConfig<U> = { | ||
type: "list"; | ||
@@ -77,2 +74,1 @@ /** Rules that can run on the full list of children. */ | ||
}; | ||
export {}; |
@@ -11,2 +11,2 @@ import { ListFieldConfig, ObjectConfig } from "../config"; | ||
} | ||
export declare function newListFieldState<T, K extends keyof T, U>(parentInstance: T, parentState: () => ObjectState<T>, key: K, rules: Rule<readonly ObjectState<U>[]>[], listConfig: ListFieldConfig<T, U>, config: ObjectConfig<U>, strictOrder: boolean, maybeAutoSave: () => void): ListFieldState<U>; | ||
export declare function newListFieldState<T, K extends keyof T, U>(parentInstance: T, parentState: () => ObjectState<T>, key: K, rules: Rule<readonly ObjectState<U>[]>[], listConfig: ListFieldConfig<U>, config: ObjectConfig<U>, strictOrder: boolean, maybeAutoSave: () => void): ListFieldState<U>; |
@@ -14,3 +14,5 @@ import { ObjectState } from "./objectField"; | ||
export interface FieldState<V> { | ||
/** The key in the parent object, i.e. `firstName` in `author: { firstName: string }`. */ | ||
readonly key: string; | ||
/** The current value of the field. */ | ||
value: V; | ||
@@ -17,0 +19,0 @@ readonly originalValue: V; |
@@ -1,1 +0,11 @@ | ||
export {}; | ||
export declare class ObservableObject { | ||
firstName: string | undefined; | ||
lastName: string | undefined; | ||
age?: number | undefined; | ||
constructor(); | ||
get fullName(): string; | ||
set fullName(fullName: string); | ||
toInput(): { | ||
firstName: string | undefined; | ||
}; | ||
} |
@@ -36,13 +36,11 @@ "use strict"; | ||
// Configure the fields/behavior for AuthorInput's fields | ||
const formConfig = { | ||
firstName: { type: "value", rules: [index_1.required] }, | ||
lastName: { type: "value", rules: [index_1.required], readOnly: true }, | ||
books: { | ||
type: "list", | ||
rules: [({ value: list }) => ((list || []).length === 0 ? "Empty" : undefined)], | ||
config: { | ||
title: { type: "value", rules: [index_1.required] }, | ||
}, | ||
}, | ||
}; | ||
const formConfig = index_1.f.config({ | ||
firstName: index_1.f.value().req(), | ||
lastName: index_1.f.value().readOnly(), | ||
books: index_1.f | ||
.list({ | ||
title: index_1.f.value().req(), | ||
}) | ||
.rule(({ value }) => (value.length === 0 ? "Empty" : undefined)), | ||
}); | ||
function TextField(props) { | ||
@@ -49,0 +47,0 @@ const { field } = props; |
export { ObjectConfig } from "./config"; | ||
export { f } from "./configBuilders"; | ||
export { ListFieldState } from "./fields/listField"; | ||
@@ -3,0 +4,0 @@ export { ObjectState, createObjectState } from "./fields/objectField"; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.useFormStates = exports.useFormState = exports.required = exports.createObjectState = void 0; | ||
exports.useFormStates = exports.useFormState = exports.required = exports.createObjectState = exports.f = void 0; | ||
var configBuilders_1 = require("./configBuilders"); | ||
Object.defineProperty(exports, "f", { enumerable: true, get: function () { return configBuilders_1.f; } }); | ||
var objectField_1 = require("./fields/objectField"); | ||
@@ -5,0 +7,0 @@ Object.defineProperty(exports, "createObjectState", { enumerable: true, get: function () { return objectField_1.createObjectState; } }); |
@@ -11,2 +11,5 @@ import { ObjectConfig } from "./config"; | ||
}; | ||
export type OmitIf<Base, Condition> = Pick<Base, { | ||
[Key in keyof Base]: Base[Key] extends Condition ? never : Key; | ||
}[keyof Base]>; | ||
export declare function fail(message?: string): never; | ||
@@ -13,0 +16,0 @@ export declare function assertNever(x: never): never; |
{ | ||
"name": "@homebound/form-state", | ||
"version": "2.20.1", | ||
"version": "2.21.0", | ||
"author": "Homebound", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
@@ -8,3 +8,3 @@ ![npm](https://img.shields.io/npm/v/@homebound/form-state) | ||
It acts as a buffer between the canonical data (i.e. the server-side data, or your app's GraphQL/Redux/etc. global store) and the user's WIP data that is being actively mutated in form fields (which is "too chatty"/WIP to store in the global store). | ||
It acts as a buffer between the canonical data (i.e. the server-side data, or your app's GraphQL/Redux/etc. global store) and the user's WIP data that is being actively mutated in form fields (which is "too chatty"/WIP to push back into global stores). | ||
@@ -25,3 +25,3 @@ It also keeps track of low-level form UX details like: | ||
- Supports collections of children, i.e. a `author: { books: [...} }` will include only changed books if necessary | ||
- Child collections can be either exhausive (if any child changes, submit them all) or incremental (only include changed children), to match the backend endpoint's semantics | ||
- Child collections can be either exhaustive (if any child changes, submit them all) or incremental (only include changed children), to match the backend endpoint's semantics | ||
@@ -42,6 +42,7 @@ # Main Features | ||
interface FieldState { | ||
value: V | ||
errors: string[] | ||
valid: boolean | ||
touched: boolean | ||
// Can be used to read & write the value bound into the form field | ||
value: V; | ||
errors: string[]; | ||
valid: boolean; | ||
touched: boolean; | ||
} | ||
@@ -52,3 +53,3 @@ ``` | ||
This facilitates the "gold standard" of form libraries, which is "one line per form field", i.e: | ||
This facilitates the "gold standard" of form DX, which is "one line per form field", i.e: | ||
@@ -131,3 +132,3 @@ ```tsx | ||
// The `Fragment` type tells form-state this is not a regular form field | ||
data: Fragment<AuthorFragment > | ||
data: Fragment<AuthorFragment>; | ||
}; | ||
@@ -139,3 +140,3 @@ | ||
data: { type: "fragment" }, | ||
} | ||
}; | ||
@@ -156,3 +157,2 @@ // Now in the component... | ||
# Internal Implementation Notes | ||
@@ -159,0 +159,0 @@ |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
209499
49
4320