@timeax/form-palette 0.0.25 → 0.0.27

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/Readme.md CHANGED
@@ -1,428 +1,435 @@
1
- ### Form Palette: Developer Documentation (Core, Adapters, Variants)
1
+ ### Form Palette: Build Forms with InputField (Consumer Guide)
2
2
 
3
- This guide documents how to use and extend the `packages/form-palette` package from a developer’s perspective. It covers the core runtime, the adapter system, and the variant layer (including the built-in Shadcn-based preset).
3
+ This guide shows how to build forms with @timeax/form-palette. It focuses on InputField usage, variants, and practical recipes taken from the playground’s App.tsx.
4
4
 
5
- Key source locations:
6
- - Core runtime and hooks: `src/core/*`
7
- - Adapters schema and registry: `src/schema/adapter.ts`, `src/core/adapter-registry.ts`, `src/adapters/*`
8
- - Field and input composition: `src/schema/field.ts`, `src/input/input-field.tsx`
9
- - Variant typing and registry: `src/schema/variant.ts`, `src/variants/*`, `src/presets/shadcn-variants/*`
5
+ If you just want to build forms, start here. Internals and source file locations are intentionally de‑emphasized.
10
6
 
11
7
  ---
12
8
 
13
- ### 1) Core
9
+ ### Quick start
14
10
 
15
- #### 1.1 What the Core Provides
16
- The core coordinates form state, validation, submission, and UI control state. It exposes a `CoreContext` you access through hooks, and it manages fields and buttons that register into the form lifecycle.
17
-
18
- Primary entry points:
19
- - `CoreProvider` — wraps a form subtree with a live core instance.
20
- - `useCore()` / `useCoreContext()` — access the `CoreContext` API.
21
- - `useField()` — register a field with the core and get a typed, ergonomic API for value, errors, disabled/readOnly, and validation.
22
- - `input/input-field.tsx` — high-level field UI composition that binds a variant (visual control) to core state with field layout.
23
-
24
- Important types:
25
- - `CoreContext` in `src/schema/core.ts` — defines the public form API.
26
- - `Field` in `src/schema/field.ts` — runtime representation of a single form field.
11
+ Install (from npm):
27
12
 
13
+ ```bash
14
+ npm install @timeax/form-palette
15
+ ```
28
16
 
29
- #### 1.2 Core Provider and Context
30
- - React context is declared in `src/core/context.ts` as `CoreContextReact`.
31
- - You must wrap your form with `CoreProvider` (see `src/core/core-provider.tsx`). Consumers then call `useCore()` or `useCoreContext()`.
17
+ Minimal form:
32
18
 
33
- Minimal setup:
34
19
  ```tsx
35
- import { CoreProvider } from "@/core/core-provider";
36
- import { useCore } from "@/core";
20
+ import { Form, InputField } from "@timeax/form-palette";
21
+
22
+ export default function Example() {
23
+ function onSubmit(e: any) {
24
+ // e.form gives you programmatic access
25
+ // e.formData is the values snapshot
26
+ console.log("Submitted", e.formData);
27
+ }
37
28
 
38
- export function MyForm() {
39
29
  return (
40
- <CoreProvider>
41
- <Inner />
42
- </CoreProvider>
30
+ <Form wrapped gap={12} onSubmit={onSubmit}>
31
+ <InputField name="email" label="Email" variant="text" required />
32
+ <InputField name="password" label="Password" variant="password" required />
33
+ <button type="submit">Submit</button>
34
+ </Form>
43
35
  );
44
36
  }
45
-
46
- function Inner() {
47
- const core = useCore();
48
- // core.values(), core.submit(), etc.
49
- return null;
50
- }
51
37
  ```
52
38
 
39
+ ---
53
40
 
54
- #### 1.3 CoreContext API highlights (from `src/schema/core.ts`)
55
- Commonly used members:
56
- - `values(): ValuesResult<V>` take a snapshot of values (respects `shared`, `alias`, and ignore rules).
57
- - `submit()` / `forceSubmit()` — trigger submit flow.
58
- - `validate(report?: boolean)` run validation across registered fields.
59
- - `addField(field: Field)` internal hook integration (handled by `useField`).
60
- - `error(name, message | bag)` set error(s) at runtime.
61
- - `controlButton()` core adjusts active submit button’s loading/disabled state during async flows.
62
- - `prepare(type?, route?, extra?, ignoreForm?, autoErr?)` / `go(data, ignoreForm?)` lower-level orchestration used by adapters and submission.
63
- - `setValue(name, value)` / `reset(inputs?)` programmatic state control.
64
-
65
- There is also a typed submit event (`SubmitEvent`) and callback props on providers (`BaseProps`, `CoreProps`) that let hosts hook into lifecycle events such as `onChange`, `onFinish`, `onSubmitted<K extends AdapterKey>(form, payload, resolve)` etc.
66
-
67
-
68
- #### 1.4 Fields and `useField`
69
- `useField` in `src/core/hooks/use-field.ts` is the main way a visual control binds to the core. It:
70
- - Registers a `Field` with the core and returns a live object with:
71
- - `value`, `setValue(next, variant?)`
72
- - `error`, `setError(message)`
73
- - `required`, `disabled`, `readOnly` and their setters
74
- - `loading`, `setLoading(loading)`
75
- - identity and binding metadata (`name`, `bindId`, `bind`, `shared`, `alias`, `groupId`, `main`, `ignore`)
76
- - `validate(report?)` delegating to field-level validation when provided
77
- - Coordinates with core-level `onChange` and per-field `onChange` based on core props such as `changeBefore` (see around lines 431–452 of `use-field.ts`).
78
- - Ensures the core can toggle the active submit button’s disabled state (`form.controlButton()`).
79
-
80
- Validation at field-level:
81
- - `UseFieldOptions<T>['validate']` returns `true | false | string` (or array in some higher-level paths).
82
- - Any returned message(s) are normalized for display.
83
-
84
- Identity and binding (`UseFieldOptions`):
85
- - `name?: string` — primary key in values and error bags.
86
- - `bindId?: string` / `bind?: string` — internal/external binding identifiers; used for advanced grouping and data flows.
87
- - `shared?: string` — nest values under a shared namespace (e.g., `profile.first_name`).
88
- - `groupId?: string`, `main?: boolean` — group controls (e.g., radios) and indicate a primary member.
89
- - `alias?: string` — map errors/names to a different label when desired.
90
- - `ignore?: boolean` — participate in registry without contributing to snapshots/validation when ignored.
91
-
92
-
93
- #### 1.5 Input composition: `input/input-field.tsx`
94
- `InputField` is a rich compositor that:
95
- - Looks up a variant by `variant` key (via `getVariant` from `src/variants`).
96
- - Builds the field layout using `FieldLayoutConfig` and optional variant-level `resolveLayout`.
97
- - Wires helper slots (label, sublabel, description, error placement) via primitives from `src/presets/ui/field`.
98
- - Normalizes validation results (`normalizeValidateResult`).
99
-
100
- Typical usage:
101
- ```tsx
102
- import { InputField } from "@/input/input-field";
41
+ ### InputField basics
42
+
43
+ Use one component for all input types by switching the variant key. InputField wires the value, validation, and a consistent label/description/error layout for you.
44
+
45
+ Common props (apply to most variants):
46
+ - name: unique field key
47
+ - variant: which control to render (see Variant reference below)
48
+ - label, sublabel: title and a small inline hint
49
+ - description/helpText: helper copy under the control
50
+ - errorText: force an error message (or rely on validation)
51
+ - required, disabled, readOnly
52
+ - icon, prefix, suffix, leadingControl, trailingControl: decorate the input content
53
+ - contain: force the input and label to share a tile-like container
54
+ - validate(value, report): return true | false | string for simple inline validation
55
+ - onChange(detail): detail.value holds the new value; prevent default if you need to override
56
+
57
+ Example with decorations and validation:
103
58
 
59
+ ```tsx
104
60
  <InputField
105
- name="email"
61
+ name="username"
106
62
  variant="text"
107
- label="Email"
108
- description="We’ll never share your email"
109
- required
110
- // variant-specific props are forwarded
63
+ label="Username"
64
+ sublabel="public handle"
65
+ prefix="@"
66
+ validate={(value, report) => {
67
+ if (!value) return report ? "Required" : false;
68
+ if (value.length < 3) return report ? "Min 3 characters" : false;
69
+ return true;
70
+ }}
71
+ onChange={(e) => console.log("username:", e.value)}
111
72
  />
112
73
  ```
113
74
 
75
+ Programmatic control during submit:
114
76
 
115
- #### 1.6 Buttons
116
- Submit buttons can be registered with the core via imperatives that conform to `ButtonRef` (`src/schema/field.ts`):
117
- - `name: string`
118
- - Optional `loading`, `disabled` and setters `setLoading(v)`, `setDisabled(v)`
119
-
120
- The core toggles the active button during submit flows (success/error/finish) via `controlButton()`.
77
+ ```tsx
78
+ function onSubmit(e: any) {
79
+ // Programmatically set a value
80
+ e.form.inputs.getByName("email").setValue("demo@example.com");
81
+ console.log(e.formData);
82
+ }
83
+ ```
121
84
 
85
+ ---
122
86
 
123
- #### 1.7 Errors and Validation
124
- - Field-level validation is provided via `useField` and variant-level validation can also be built in.
125
- - Form-level validation (`core.validate(report?)`) runs all participating fields and aggregates into an error bag.
126
- - Error rendering within `InputField` uses `FieldError` and layout slots.
87
+ ### Variant reference (what value they hold and unique props)
127
88
 
89
+ Below are the built‑in variants with the props you’ll use most often. Examples mirror the playground App.tsx.
128
90
 
129
- ---
91
+ Note on options: selection controls accept options as primitives ("US") or objects ({ label, value, ...extra }).
130
92
 
131
- ### 2) Adapters
132
-
133
- Adapters abstract how submissions are executed (local resolve, Axios, Inertia, etc.). They are fully typed and pluggable at runtime.
134
-
135
- Key files:
136
- - Types: `src/schema/adapter.ts`
137
- - Registry: `src/core/adapter-registry.ts`
138
- - Built-ins: `src/adapters/axios.ts`, `src/adapters/inertia.ts`, index helpers in `src/adapters/index.ts`
139
-
140
-
141
- #### 2.1 Adapter Types (`src/schema/adapter.ts`)
142
- Core type concepts:
143
- - `AdapterCallbacks` — lifecycle hooks the adapter can invoke:
144
- - `onSuccess(response)`
145
- - `onError(error, updateRef)` (may ‘edit’ error/response context)
146
- - `onFinish()`
147
- - `AdapterResult<Body>` — the runtime object an adapter factory returns with three operations:
148
- - `submit(options?)` — trigger a submission side-effect
149
- - `send(options?)` — return a promise resolved with an `ok`-typed payload
150
- - `run(options?)` — a flexible operation; many adapters map it to `submit` or a hybrid
151
- - `AdapterFactory<Body>` / `NamedAdapterFactory<K>` — functions producing an `AdapterResult`
152
- - `Adapters` interface — the type-level registry that hosts/presets can augment with declaration merging to add their own keys and ok/error/payload shapes.
153
-
154
- Convenience type utilities:
155
- - `AdapterKey` — union of all adapter keys (e.g., `'local' | 'axios' | 'inertia' | ...'`).
156
- - `AdapterOk<K>`, `AdapterError<K>` — map adapter key to success/error payload types.
157
- - `AdapterSubmit<K>` — the type of what `onSubmitted(form, payload, resolve)` receives for a given adapter key.
158
-
159
-
160
- #### 2.2 Adapter Registry (`src/core/adapter-registry.ts`)
161
- - Maintains a simple in-memory registry of adapter factories keyed by `AdapterKey`.
162
- - Ships with a built-in `'local'` adapter (`localAdapter`) that:
163
- - `send()` resolves `{ data: config.data }`
164
- - `submit()`/`run()` are no-ops (core decides how to flow).
165
- - Public functions:
166
- - `registerAdapter(key, factory)` — register/override an adapter.
167
- - `getAdapter(key)` — retrieve a factory (or `undefined`).
168
- - `hasAdapter(key)` — check presence.
169
-
170
-
171
- #### 2.3 Built-in Adapters and Helpers (`src/adapters/*`)
172
- - `axios` adapter factory: `createAxiosAdapter` (see `src/adapters/axios.ts`).
173
- - `inertia` adapter factory: `createInertiaAdapter` (see `src/adapters/inertia.ts`).
174
- - Helpers in `src/adapters/index.ts`:
175
- - `registerAxiosAdapter()` — runtime check for `axios` and registers under key `'axios'`.
176
- - `registerInertiaAdapter()` — dynamic import of `@inertiajs/react`, checks router `.visit`, registers under key `'inertia'`.
177
- - `registerKnownAdapter(key: AdapterKey)` — switch over known keys.
178
- - `registerAllAdapters()` — registers both axios and inertia.
179
-
180
- Registering at app bootstrap:
181
- ```ts
182
- import { registerAxiosAdapter, registerInertiaAdapter } from "@/adapters";
183
-
184
- registerAxiosAdapter();
185
- await registerInertiaAdapter();
93
+ 1) text
94
+ - Value: string | undefined
95
+ - Nice extras: mask, slotChar, unmask, autoClear (phone-like masking); icon/prefix/suffix; searchable isn’t applicable here
96
+ - Example:
97
+ ```tsx
98
+ <InputField name="email" label="Email" variant="text" />
186
99
  ```
187
100
 
188
- Using adapters in submit flow (high level):
189
- - The core’s submit logic (via `CoreProvider`) prepares a submission, locates the configured adapter by key, builds the adapter result with callbacks, and then calls `submit` or `send` depending on scenario.
190
- - On successful `send()`, the core will call your `onSubmitted<K>()` with the typed payload `AdapterSubmit<K>` (see `CoreProps` in `schema/core.ts`).
101
+ 2) number
102
+ - Value: number | undefined
103
+ - Props: min, max, step, showButtons
104
+ - Example:
105
+ ```tsx
106
+ <InputField name="age" label="Age" variant="number" min={0} max={120} step={1} showButtons />
107
+ ```
191
108
 
192
- Typing benefits:
193
- - If you extend `Adapters` via declaration merging, downstream code using `AdapterSubmit<'myAdapter'>` becomes strongly typed.
109
+ 3) password
110
+ - Value: string | undefined
111
+ - Props: showToggle; strengthMeter; meterStyle="rules" | "bar" (depending on preset)
112
+ - Example:
113
+ ```tsx
114
+ <InputField name="pwd" label="Password" variant="password" showToggle strengthMeter meterStyle="rules" />
115
+ ```
194
116
 
117
+ 4) color
118
+ - Value: string | undefined (hex or css color)
119
+ - Props: showPreview, previewButtonClassName
120
+ - Example:
121
+ ```tsx
122
+ <InputField name="color" label="Favorite colour" variant="color" showPreview />
123
+ ```
195
124
 
196
- ---
125
+ 5) phone
126
+ - Value: string | undefined
127
+ - Typical usage uses masking controls the same way as text: mask, slotChar, unmask, autoClear
128
+ - Example (from playground labeled "Phone"):
129
+ ```tsx
130
+ <InputField
131
+ name="phone"
132
+ label="Phone"
133
+ variant="text" // or a dedicated phone variant if enabled in your build
134
+ mask="+99 99 999 999? x999"
135
+ slotChar="_"
136
+ autoClear
137
+ />
138
+ ```
197
139
 
198
- ### 3) Variants
199
-
200
- Variants define the interactive control and its public props/value shape. The system separates:
201
- - Type-level registry describing value/props per variant key: `src/schema/variant.ts`
202
- - Runtime variant modules and preset implementations:
203
- - `src/variants/core/*` variant modules composing presets with defaults.
204
- - `src/presets/shadcn-variants/*` — concrete Shadcn-based components for each variant.
205
-
206
-
207
- #### 3.1 Variant Types (`src/schema/variant.ts`)
208
- - `interface Variants` maps variant keys to `{ value, props }` pairs. This registry is used by:
209
- - `InputFieldProps<K>` — to infer the correct `value` and `props` for a given variant key.
210
- - `VariantModule<K>` — to type the runtime module providing a `Variant` component and optional layout resolution.
211
- - Built-in keys include: `text`, `number`, `phone`, `color`, `password`, `date`, `chips`, `textarea`, `toggle`, `toggle-group`, `radio`, `checkbox`, `select`, `multi-select`, `slider`, `keyvalue`, `custom`, `treeselect`, `file`.
212
- - All Shadcn prop types are re-exported by their files in `src/presets/shadcn-variants/*` and then aggregated into the `Variants` interface.
213
-
214
-
215
- #### 3.2 Variant Modules and Presets
216
- - A variant module is declared per key in `src/variants/core/*`. Example: `checkbox.tsx` wires the Shadcn checkbox preset and supplies defaults/layout:
217
- ```ts
218
- export const checkboxModule: VariantModuleFor<"checkbox"> = {
219
- variant: "checkbox",
220
- Variant: ShadcnCheckboxVariant as unknown as React.ComponentType<
221
- VariantBaseProps<CheckboxVariantPublicValue> & ShadcnCheckboxVariantPublicProps
222
- >,
223
- resolveLayout({ props }) {
224
- if (props.single) return toggleLayoutDefaults;
225
- return {};
226
- },
227
- meta: { label: "Checkbox", description: "...", tags: ["checkbox", "group", "boolean", "tri-state"] },
228
- };
140
+ 6) textarea
141
+ - Value: string | undefined
142
+ - Usual textarea props like placeholder, rows, etc.
143
+ - Example:
144
+ ```tsx
145
+ <InputField name="bio" label="Bio" variant="textarea" description="Tell us about you" />
229
146
  ```
230
- - The `Variant` in a module is a React component receiving `VariantBaseProps<TValue>` plus the variant’s public props. The base props include:
231
- - `field` (result of `useField`) with `value`, `setValue`, etc.
232
- - `onChange(detail: ChangeDetail<TValue>)`
233
- - `disabled`, `readOnly`, `required`, error, etc. (according to `VariantBaseProps` in `src/variants/shared`)
234
- - Presets implement the actual control UX (e.g., Shadcn Checkbox, Radio, Multiselect, File, TreeSelect, etc.) and are placed under `src/presets/shadcn-variants/*`.
235
147
 
148
+ 7) toggle
149
+ - Value: boolean | undefined
150
+ - Example:
151
+ ```tsx
152
+ <InputField name="tos" variant="toggle" label="Accept Terms" required />
153
+ ```
236
154
 
237
- #### 3.3 Using Variants via InputField
238
- `InputField` takes `variant={key}` and forwards the key’s public props to the preset, wires `useField`, and renders a consistent Shadcn-based field chrome.
155
+ 8) toggle-group
156
+ - Value: string | number | undefined (selected item)
157
+ - Props: options (primitives or objects), layout/density sizing depending on preset
158
+
159
+ 9) radio
160
+ - Value: string | number | undefined
161
+ - Props:
162
+ - options: primitives or objects with { value, label, description?, disabled? }
163
+ - layout?: "list" | "grid" (default "list"); columns?: number (when layout="grid")
164
+ - size?: "sm" | "md" | "lg"; density?: "compact" | "comfortable" | "loose"
165
+ - You can also map custom item shapes via optionValue/optionLabel style mappers depending on preset
166
+ - Example:
167
+ ```tsx
168
+ <InputField
169
+ name="role"
170
+ label="Role"
171
+ variant="radio"
172
+ options={[
173
+ { value: "reader", label: "Reader" },
174
+ { value: "editor", label: "Editor" },
175
+ ]}
176
+ />
177
+ ```
239
178
 
240
- Example — toggle and select:
179
+ 10) checkbox
180
+ - Value: boolean | string[] | number[] depending on single vs group usage
181
+ - Single boolean checkbox:
241
182
  ```tsx
242
- <InputField name="tos" variant="toggle" label="Accept Terms" required />
183
+ <InputField variant="checkbox" label="Remember me" />
184
+ ```
185
+ - Group example:
186
+ ```tsx
187
+ <InputField
188
+ name="perms"
189
+ variant="checkbox"
190
+ label="Permissions"
191
+ options={[
192
+ { value: "read", label: "Read content" },
193
+ { value: "write", label: "Write content" },
194
+ { value: "delete", label: "Delete content" },
195
+ ]}
196
+ />
197
+ ```
243
198
 
199
+ - Extras:
200
+ - single?: boolean switches to single‑checkbox mode (value becomes boolean | undefined)
201
+ - tristate?: boolean enables an indeterminate state for single or per‑item
202
+ - layout?: "list" | "grid"; columns?: number (grid mode)
203
+ - size?: "sm" | "md" | "lg"; density?: "compact" | "comfortable" | "loose"
204
+
205
+ 11) select
206
+ - Value: string | number | undefined
207
+ - Props (high‑use):
208
+ - options: (string|number)[] | { label?, value?, description?, disabled?, icon?, ... }[]
209
+ - searchable?: boolean (inline search box)
210
+ - searchPlaceholder?: string (placeholder inside the search box)
211
+ - clearable?: boolean (show clear button)
212
+ - placeholder?: string
213
+ - autoCap?: boolean (capitalise label text)
214
+ - emptyLabel?: React.ReactNode (shown when there are no options)
215
+ - emptySearchText?: React.ReactNode (shown when search returns no results)
216
+ - optionLabel, optionValue, optionDescription, optionDisabled, optionIcon, optionKey: map/compute option pieces
217
+ - renderOption?: (ctx) => ReactNode (custom row rendering; per‑option render overrides this)
218
+ - Example:
219
+ ```tsx
244
220
  <InputField
245
221
  name="country"
246
222
  variant="select"
247
223
  label="Country"
248
224
  options={[{ label: "USA", value: "US" }, { label: "Canada", value: "CA" }]}
249
225
  placeholder="Select a country"
226
+ searchable
227
+ clearable
250
228
  />
251
229
  ```
252
230
 
253
-
254
- #### 3.4 Building a Custom Variant
255
- 1) Extend the `Variants` interface if you’re adding a brand new key (optional if replacing internals only):
256
- ```ts
257
- declare module "@/schema/variant" {
258
- interface Variants {
259
- mycontrol: VariantEntry<MyValue | undefined, MyControlPublicProps>;
260
- }
261
- }
262
- ```
263
- 2) Create a preset component (e.g., in your own folder or mimic Shadcn preset style):
231
+ 12) multi-select
232
+ - Value: (string|number)[] | undefined
233
+ - Props: same mapping props as select (optionLabel, optionValue, optionDescription, optionDisabled, optionIcon, optionKey)
234
+ - searchable?: boolean; searchPlaceholder?: string
235
+ - clearable?: boolean; placeholder?: React.ReactNode
236
+ - autoCap?: boolean
237
+ - emptyLabel?: React.ReactNode; emptySearchText?: React.ReactNode
238
+ - renderOption?: (ctx) => ReactNode (custom row rendering; per‑option render overrides this)
239
+ - Example:
264
240
  ```tsx
265
- function MyControlVariant(props: VariantBaseProps<MyValue> & MyControlPublicProps) {
266
- const { field } = props; // field.value, field.setValue
267
- // render your control
268
- return <input value={field.value ?? ""} onChange={e => field.setValue(e.target.value)} />;
269
- }
270
- ```
271
- 3) Provide a variant module under `src/variants/core/mycontrol.tsx`:
272
- ```ts
273
- export const mycontrolModule: VariantModuleFor<"mycontrol"> = {
274
- variant: "mycontrol",
275
- Variant: MyControlVariant,
276
- resolveLayout({ defaults, overrides, props }) {
277
- return { ...defaults, ...overrides };
278
- },
279
- meta: { label: "My Control", description: "Custom control.", tags: ["custom"] },
280
- };
241
+ <InputField
242
+ name="tags"
243
+ label="Tags"
244
+ variant="multi-select"
245
+ options={["red", "green", "blue"]}
246
+ />
281
247
  ```
282
- 4) Register the module with the variant registry (see how existing modules are exported/aggregated in `src/variants`). After registration, you can use `<InputField variant="mycontrol" ... />`.
283
248
 
249
+ 13) chips
250
+ - Value: string[] | number[] | undefined
251
+ - Free‑form or from options; often used to add/remove tokens
284
252
 
285
- ---
286
-
287
- ### 4) Submission Lifecycle (Core × Adapters)
288
- A typical submit flow involves these steps:
289
- 1. User triggers submit via a registered button or programmatically (`core.submit()`).
290
- 2. Core collects values via `core.values()` and runs validation (`core.validate(true)`).
291
- 3. Core prepares an adapter instance (key `local` by default or your configured key) using `getAdapter(key)`.
292
- 4. Core calls `adapter.submit()` or `adapter.send()`. Adapters notify the core through `AdapterCallbacks`:
293
- - `onSuccess(ok)` — core then calls your `onSubmitted<K>(form, payload, resolve)` from `CoreProps` with typed `AdapterSubmit<K>`.
294
- - `onError(err, update)` — transform/record error; the core can populate error bags via `core.error(...)`.
295
- - `onFinish()` — always runs; core toggles button states via `controlButton()`.
296
-
297
- Hosts can implement `onSubmitted<K extends AdapterKey>(form, payload, resolve)` to centralize success handling. The `resolve()` callback is provided to let you complete any external flows before the core finalizes.
253
+ 14) treeselect
254
+ - Value: (string|number)[] | string | number | undefined (single or multiple tree selection)
255
+ - Option type: TreeSelectOption = { label, value, icon?, description?, children?: TreeSelectOption[] }
256
+ - Example:
257
+ ```tsx
258
+ import { TreeSelectOption } from "@timeax/form-palette/presets/shadcn-variants/tree-select-types";
298
259
 
260
+ const regionOptions: TreeSelectOption[] = [
261
+ { label: "Africa", value: "africa", children: [{ label: "Nigeria", value: "ng" }] },
262
+ { label: "Europe", value: "europe" },
263
+ ];
299
264
 
300
- ---
265
+ <InputField
266
+ name="regions"
267
+ label="Regions"
268
+ variant="treeselect"
269
+ options={regionOptions}
270
+ />
271
+ ```
301
272
 
302
- ### 5) Recipes
273
+ - Props (high‑use):
274
+ - multiple?: boolean (default true). If false, single‑select behaviour.
275
+ - searchable?: boolean; searchPlaceholder?: string
276
+ - clearable?: boolean; placeholder?: React.ReactNode
277
+ - autoCap?: boolean
278
+ - optionLabel, optionValue, optionDescription, optionDisabled, optionIcon, optionKey
279
+ - emptyLabel?: React.ReactNode; emptySearchText?: React.ReactNode
280
+ - renderOption?: ({ item, selected, option, click }) => ReactNode
281
+ - renderValue?: ({ selectedItems, placeholder }) => ReactNode (custom trigger content)
282
+ - expandAll?: boolean; defaultExpandedValues?: (string|number)[]
283
+ - leafOnly?: boolean (only leaf nodes are selectable)
284
+ - mode?: "default" | "button"; when "button", you can provide a custom trigger and show a selected‑count badge
285
+
286
+ 15) slider
287
+ - Value: number | [number, number] depending on range mode
288
+ - Props: min, max, step; possibly range/multiple depending on preset
289
+
290
+ 16) file
291
+ - Value: File | File[] | Custom file shape depending on configuration
292
+ - Types: FileItem, CustomFileLoader, FileLike are exported from the preset if you need advanced control
293
+ - Example (simple):
294
+ ```tsx
295
+ <InputField name="avatar" label="Avatar" variant="file" />
296
+ ```
303
297
 
304
- #### 5.1 Basic form with a couple of fields
298
+ 17) keyvalue
299
+ - Value: Record<string, string> | undefined
300
+ - Use to capture arbitrary key/value pairs
301
+
302
+ 18) editor
303
+ - Value: string | undefined (HTML or Markdown)
304
+ - Requires host CSS import once in your app:
305
+ - import "@toast-ui/editor/dist/toastui-editor.css";
306
+ - Props (high‑use):
307
+ - format?: "html" | "markdown" (stored value format; default "html")
308
+ - toolbar?: "default" | "none" | ToastToolbarItem[][]
309
+ - height?: string (e.g., "400px"), placeholder?: string
310
+ - editType?: "wysiwyg" | "markdown"; previewStyle?: "vertical" | "tab"
311
+ - pastePlainText?: boolean (force plain text on paste)
312
+ - Example:
305
313
  ```tsx
306
- import { CoreProvider } from "@/core/core-provider";
307
- import { InputField } from "@/input/input-field";
308
- import { useCore } from "@/core";
314
+ <InputField
315
+ name="content"
316
+ label="Content"
317
+ variant="editor"
318
+ format="markdown"
319
+ toolbar="default"
320
+ height="400px"
321
+ />
322
+ ```
309
323
 
310
- export function SimpleForm() {
311
- return (
312
- <CoreProvider
313
- // Optional: onSubmitted<K>(form, payload, resolve) {}
314
- // Optional: onChange(form, values) {}
315
- >
316
- <Fields />
317
- </CoreProvider>
318
- );
319
- }
324
+ 19) custom
325
+ - Bring your own control but still benefit from InputField’s layout and validation chrome
320
326
 
321
- function Fields() {
322
- const core = useCore();
327
+ ---
323
328
 
324
- return (
325
- <form onSubmit={(e) => { e.preventDefault(); core.submit(); }}>
326
- <InputField name="email" variant="text" label="Email" required />
327
- <InputField name="password" variant="password" label="Password" required />
329
+ ### Recipes from the playground
328
330
 
329
- <button type="submit">Sign in</button>
330
- </form>
331
- );
332
- }
331
+ Masking a phone‑like input:
332
+ ```tsx
333
+ <InputField
334
+ name="phone"
335
+ label="Phone"
336
+ variant="text"
337
+ mask="+99 99 999 999? x999"
338
+ slotChar="_"
339
+ autoClear
340
+ leadingControl={<span>Leading control</span>}
341
+ prefix="number: "
342
+ />
333
343
  ```
334
344
 
335
- #### 5.2 Register and use Axios adapter
336
- ```ts
337
- // app bootstrap
338
- import { registerAxiosAdapter } from "@/adapters";
339
- registerAxiosAdapter();
340
- ```
345
+ Password with strength meter:
341
346
  ```tsx
342
- <CoreProvider
343
- onSubmitted={async (form, payload /* AdapterSubmit<'axios'> */, resolve) => {
344
- console.log("Axios payload", payload);
345
- resolve();
346
- }}
347
- >
348
- {/* fields */}
349
- </CoreProvider>
347
+ <InputField
348
+ name="password"
349
+ label="Password"
350
+ variant="password"
351
+ placeholder="Enter your password"
352
+ strengthMeter
353
+ meterStyle="rules"
354
+ />
350
355
  ```
351
- Adapter selection is typically part of the provider/config you pass when preparing a submit; see `CoreProps` and your submit orchestration where you choose the adapter key.
352
356
 
353
- #### 5.3 Custom field-level validation
357
+ Selects and multi‑selects:
354
358
  ```tsx
355
359
  <InputField
356
- name="username"
357
- variant="text"
358
- required
359
- validate={(value, report) => {
360
- if (!value) return report ? "Username is required" : false;
361
- if (value.length < 3) return report ? "Minimum 3 characters" : false;
362
- return true;
363
- }}
360
+ name="country"
361
+ variant="select"
362
+ label="Country"
363
+ options={[{ label: "USA", value: "US" }, { label: "Canada", value: "CA" }]}
364
+ placeholder="Select a country"
365
+ searchable
366
+ />
367
+
368
+ <InputField
369
+ name="languages"
370
+ variant="multi-select"
371
+ label="Languages"
372
+ options={["English", "French", "German"]}
364
373
  />
365
374
  ```
366
375
 
367
- #### 5.4 Programmatically set values and errors
368
- ```ts
369
- const core = useCore();
370
- core.setValue("email", "demo@site.test");
371
- core.error("email", "Already taken");
376
+ Tree select with icons and descriptions:
377
+ ```tsx
378
+ <InputField name="regions" label="Regions" variant="treeselect" options={regionOptions} />
379
+ ```
380
+
381
+ Single checkbox:
382
+ ```tsx
383
+ <InputField variant="checkbox" label="Remember me" />
372
384
  ```
373
385
 
386
+ Number with buttons:
387
+ ```tsx
388
+ <InputField name="age" label="Age" variant="number" min={0} max={120} step={1} showButtons />
389
+ ```
374
390
 
375
391
  ---
376
392
 
377
- ### 6) Gotchas and Best Practices
378
- - Always wrap consumers inside `<CoreProvider>`; `useCoreContext()` throws otherwise.
379
- - When integrating new adapters, register them at bootstrap (`registerAdapter(...)`).
380
- - Use `shared` to nest values under a parent key (e.g., `shared="profile"` with `name="first_name"` → `values.profile.first_name`).
381
- - Prefer `InputField` for consistent Shadcn layout and error rendering. If you build your own control, use `useField` directly and replicate the field chrome from `src/presets/ui/field`.
382
- - For grouped controls (radios, segmented toggles), use `groupId` and set `main` on the primary member when needed.
383
- - For adapter flows, handle `onSubmitted` in the provider to centralize success navigation or notifications.
384
- - The built-in `local` adapter is a no-op side-effect adapter that simply echoes `data`; useful for local validation or preview flows.
393
+ ### Submitting the form
385
394
 
395
+ Form wraps your fields and provides a submit event that carries both the values and utilities.
386
396
 
387
- ---
388
-
389
- ### 7) Extensibility Checklist
390
- - New adapter:
391
- - Define `Adapters` augmentation with `ok/err/props` types.
392
- - Implement `NamedAdapterFactory<K>` and return `{ submit, send, run }`.
393
- - Call `registerAdapter<K>("key", factory)` at boot.
394
- - New variant:
395
- - Optionally augment `Variants` with your key’s `value/props` types.
396
- - Author a preset component with `VariantBaseProps<TValue>`.
397
- - Create a variant module (with `meta`, optional `resolveLayout`).
398
- - Export/register the module with the variant registry and use via `InputField`.
397
+ ```tsx
398
+ import { Form, InputField } from "@timeax/form-palette";
399
+
400
+ function Example() {
401
+ function handleSubmit(e: any) {
402
+ // values snapshot
403
+ console.log(e.formData);
404
+ // programmatic API
405
+ e.form.inputs.getByName("email").setValue("this is nice");
406
+ }
399
407
 
408
+ return (
409
+ <Form wrapped onSubmit={handleSubmit}>
410
+ <InputField name="email" label="Email" variant="text" />
411
+ <button type="submit">Submit</button>
412
+ </Form>
413
+ );
414
+ }
415
+ ```
400
416
 
401
417
  ---
402
418
 
403
- ### 8) Where to Look in the Code
404
- - Core API and lifecycle:
405
- - `src/schema/core.ts` types for `CoreContext`, provider props, submit event.
406
- - `src/core/core-provider.tsx` the runtime engine orchestrating values/validation/submit.
407
- - `src/core/hooks/use-field.ts` field integration and state.
408
- - `src/input/input-field.tsx` UI composition and variant glue.
409
- - Adapters:
410
- - `src/schema/adapter.ts` — adapter types and registry contracts.
411
- - `src/core/adapter-registry.ts` — runtime registry + `localAdapter`.
412
- - `src/adapters/index.ts` — axios/inertia registration helpers.
413
- - `src/adapters/axios.ts`, `src/adapters/inertia.ts` — concrete factories.
414
- - Variants and presets:
415
- - `src/schema/variant.ts` — variant key registry and types.
416
- - `src/variants/core/*` — modules wiring preset → registry with defaults.
417
- - `src/presets/shadcn-variants/*` — UI implementations.
418
- - `src/presets/ui/*` — field chrome primitives (label, description, error, group, etc.).
419
-
419
+ ### Tips and best practices
420
+ - Prefer InputField over wiring controls by hand; it gives you consistent labels, descriptions, and error placement.
421
+ - Use options as primitives for quick setups, or objects when you need description/disabled/icon per item.
422
+ - Use validate for quick client checks; you can also set errorText manually.
423
+ - For grouped controls (radios/checkbox groups), pass options; for a single boolean, omit options.
424
+ - Keep labels short and place longer helper copy into description/helpText.
420
425
 
421
426
  ---
422
427
 
423
- ### 9) Summary
424
- - Core manages field registry, validation, values, and submit orchestration; hooks provide a clean API to build inputs and drive UI.
425
- - Adapters encapsulate submission backends (local/axios/inertia/custom), offering typed payloads and lifecycle callbacks.
426
- - Variants define the shape and UI of controls. The Shadcn preset offers a full suite of ready-to-use components, while variant modules wire them into the registry and default layouts.
428
+ ### FAQ
429
+ - Can I access values without submitting? Yes, via the programmatic API exposed in events like onChange at the form level, or by reading e.form.values() from handlers inside the form.
430
+ - Can I bring my own input component? Yes. Use the "custom" variant or build a dedicated preset and still place it inside InputField to reuse layout and validation.
431
+ - Do I need to register adapters? Not for basic local use. Adapters matter when you integrate with external routers or clients; see the package’s adapters folder if needed.
432
+
433
+ ---
427
434
 
428
- With these pieces you can quickly build consistent forms, customize how data is submitted, and introduce new input types with a strongly-typed, extensible foundation.
435
+ This document intentionally centers on how to use the package to build forms. For deeper internals and extension points, browse the source or the developer docs in the repository.