@timeax/form-palette 0.0.21 → 0.0.23

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 ADDED
@@ -0,0 +1,428 @@
1
+ ### Form Palette: Developer Documentation (Core, Adapters, Variants)
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).
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/*`
10
+
11
+ ---
12
+
13
+ ### 1) Core
14
+
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.
27
+
28
+
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()`.
32
+
33
+ Minimal setup:
34
+ ```tsx
35
+ import { CoreProvider } from "@/core/core-provider";
36
+ import { useCore } from "@/core";
37
+
38
+ export function MyForm() {
39
+ return (
40
+ <CoreProvider>
41
+ <Inner />
42
+ </CoreProvider>
43
+ );
44
+ }
45
+
46
+ function Inner() {
47
+ const core = useCore();
48
+ // core.values(), core.submit(), etc.
49
+ return null;
50
+ }
51
+ ```
52
+
53
+
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";
103
+
104
+ <InputField
105
+ name="email"
106
+ variant="text"
107
+ label="Email"
108
+ description="We’ll never share your email"
109
+ required
110
+ // variant-specific props are forwarded
111
+ />
112
+ ```
113
+
114
+
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()`.
121
+
122
+
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.
127
+
128
+
129
+ ---
130
+
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();
186
+ ```
187
+
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`).
191
+
192
+ Typing benefits:
193
+ - If you extend `Adapters` via declaration merging, downstream code using `AdapterSubmit<'myAdapter'>` becomes strongly typed.
194
+
195
+
196
+ ---
197
+
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
+ };
229
+ ```
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
+
236
+
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.
239
+
240
+ Example — toggle and select:
241
+ ```tsx
242
+ <InputField name="tos" variant="toggle" label="Accept Terms" required />
243
+
244
+ <InputField
245
+ name="country"
246
+ variant="select"
247
+ label="Country"
248
+ options={[{ label: "USA", value: "US" }, { label: "Canada", value: "CA" }]}
249
+ placeholder="Select a country"
250
+ />
251
+ ```
252
+
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):
264
+ ```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
+ };
281
+ ```
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
+
284
+
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.
298
+
299
+
300
+ ---
301
+
302
+ ### 5) Recipes
303
+
304
+ #### 5.1 Basic form with a couple of fields
305
+ ```tsx
306
+ import { CoreProvider } from "@/core/core-provider";
307
+ import { InputField } from "@/input/input-field";
308
+ import { useCore } from "@/core";
309
+
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
+ }
320
+
321
+ function Fields() {
322
+ const core = useCore();
323
+
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 />
328
+
329
+ <button type="submit">Sign in</button>
330
+ </form>
331
+ );
332
+ }
333
+ ```
334
+
335
+ #### 5.2 Register and use Axios adapter
336
+ ```ts
337
+ // app bootstrap
338
+ import { registerAxiosAdapter } from "@/adapters";
339
+ registerAxiosAdapter();
340
+ ```
341
+ ```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>
350
+ ```
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
+
353
+ #### 5.3 Custom field-level validation
354
+ ```tsx
355
+ <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
+ }}
364
+ />
365
+ ```
366
+
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");
372
+ ```
373
+
374
+
375
+ ---
376
+
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.
385
+
386
+
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`.
399
+
400
+
401
+ ---
402
+
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
+
420
+
421
+ ---
422
+
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.
427
+
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.
package/dist/index.d.mts CHANGED
@@ -1157,7 +1157,7 @@ interface InputNumberProps extends Omit<ShadcnTextVariantProps, 'min' | 'max' |
1157
1157
  size?: FieldSize;
1158
1158
  invalid?: boolean;
1159
1159
  }
1160
- declare const InputNumber: React.NamedExoticComponent<InputNumberProps & React.RefAttributes<HTMLInputElement>>;
1160
+ declare const InputNumber: React.MemoExoticComponent<React.ForwardRefExoticComponent<InputNumberProps & React.RefAttributes<HTMLInputElement>>>;
1161
1161
 
1162
1162
  type ShadcnNumberVariantProps = Omit<InputNumberProps, "onValueChange" | "onChange" | "leadingControl" | "trailingControl"> & {
1163
1163
  /**
@@ -2215,6 +2215,11 @@ interface ShadcnCheckboxUiProps<TItem, TValue> {
2215
2215
  * Extra classes for the option label text.
2216
2216
  */
2217
2217
  labelClassName?: string;
2218
+ /**
2219
+ * Extra classes for the option label text in group mode only.
2220
+ * This allows styling group item labels without affecting single mode labels.
2221
+ */
2222
+ optionLabelClassName?: string;
2218
2223
  /**
2219
2224
  * Extra classes for the option description text.
2220
2225
  */
@@ -3606,7 +3611,7 @@ declare function useCore<V extends Dict = Dict>(): CoreContext<V>;
3606
3611
  */
3607
3612
  declare function useCoreContext<V extends Dict = Dict>(): CoreContext<V>;
3608
3613
 
3609
- type UseFieldValidate<T> = (value: T, report: boolean) => boolean | string;
3614
+ type UseFieldValidate<T> = (value: T, field?: Field, form?: CoreContext<any>, report?: boolean) => boolean | string;
3610
3615
  interface UseFieldOptions<T = unknown> {
3611
3616
  /**
3612
3617
  * Primary field name.
@@ -4023,7 +4028,7 @@ interface InputMaskProps$1 extends Omit<React.InputHTMLAttributes<HTMLInputEleme
4023
4028
  onChange?: (e: InputMaskChangeEvent) => void;
4024
4029
  onComplete?: (e: InputMaskCompleteEvent) => void;
4025
4030
  }
4026
- declare const InputMask: React.NamedExoticComponent<InputMaskProps$1 & React.RefAttributes<InputMaskRef>>;
4031
+ declare const InputMask: React.MemoExoticComponent<React.ForwardRefExoticComponent<InputMaskProps$1 & React.RefAttributes<InputMaskRef>>>;
4027
4032
 
4028
4033
  type MaskMode = "raw" | "masked";
4029
4034
  interface InputMaskProps {
package/dist/index.d.ts CHANGED
@@ -1157,7 +1157,7 @@ interface InputNumberProps extends Omit<ShadcnTextVariantProps, 'min' | 'max' |
1157
1157
  size?: FieldSize;
1158
1158
  invalid?: boolean;
1159
1159
  }
1160
- declare const InputNumber: React.NamedExoticComponent<InputNumberProps & React.RefAttributes<HTMLInputElement>>;
1160
+ declare const InputNumber: React.MemoExoticComponent<React.ForwardRefExoticComponent<InputNumberProps & React.RefAttributes<HTMLInputElement>>>;
1161
1161
 
1162
1162
  type ShadcnNumberVariantProps = Omit<InputNumberProps, "onValueChange" | "onChange" | "leadingControl" | "trailingControl"> & {
1163
1163
  /**
@@ -2215,6 +2215,11 @@ interface ShadcnCheckboxUiProps<TItem, TValue> {
2215
2215
  * Extra classes for the option label text.
2216
2216
  */
2217
2217
  labelClassName?: string;
2218
+ /**
2219
+ * Extra classes for the option label text in group mode only.
2220
+ * This allows styling group item labels without affecting single mode labels.
2221
+ */
2222
+ optionLabelClassName?: string;
2218
2223
  /**
2219
2224
  * Extra classes for the option description text.
2220
2225
  */
@@ -3606,7 +3611,7 @@ declare function useCore<V extends Dict = Dict>(): CoreContext<V>;
3606
3611
  */
3607
3612
  declare function useCoreContext<V extends Dict = Dict>(): CoreContext<V>;
3608
3613
 
3609
- type UseFieldValidate<T> = (value: T, report: boolean) => boolean | string;
3614
+ type UseFieldValidate<T> = (value: T, field?: Field, form?: CoreContext<any>, report?: boolean) => boolean | string;
3610
3615
  interface UseFieldOptions<T = unknown> {
3611
3616
  /**
3612
3617
  * Primary field name.
@@ -4023,7 +4028,7 @@ interface InputMaskProps$1 extends Omit<React.InputHTMLAttributes<HTMLInputEleme
4023
4028
  onChange?: (e: InputMaskChangeEvent) => void;
4024
4029
  onComplete?: (e: InputMaskCompleteEvent) => void;
4025
4030
  }
4026
- declare const InputMask: React.NamedExoticComponent<InputMaskProps$1 & React.RefAttributes<InputMaskRef>>;
4031
+ declare const InputMask: React.MemoExoticComponent<React.ForwardRefExoticComponent<InputMaskProps$1 & React.RefAttributes<InputMaskRef>>>;
4027
4032
 
4028
4033
  type MaskMode = "raw" | "masked";
4029
4034
  interface InputMaskProps {
package/dist/index.js CHANGED
@@ -4446,7 +4446,12 @@ function useField(options) {
4446
4446
  ok = false;
4447
4447
  message2 = "This field is required.";
4448
4448
  } else if (validate) {
4449
- const result = validate(current, !!report);
4449
+ const result = validate(
4450
+ current,
4451
+ fieldRef.current,
4452
+ form,
4453
+ !!report
4454
+ );
4450
4455
  if (typeof result === "string") {
4451
4456
  ok = false;
4452
4457
  message2 = result;
@@ -4707,7 +4712,7 @@ function useOptionalField(options) {
4707
4712
  ok = false;
4708
4713
  message2 = "This field is required.";
4709
4714
  } else if (validate) {
4710
- const result = validate(current, !!report);
4715
+ const result = validate(current, void 0, void 0, !!report);
4711
4716
  if (typeof result === "string") {
4712
4717
  ok = false;
4713
4718
  message2 = result;
@@ -15774,6 +15779,7 @@ var InnerShadcnCheckboxVariant = (props, ref) => {
15774
15779
  groupClassName,
15775
15780
  optionClassName,
15776
15781
  labelClassName,
15782
+ optionLabelClassName,
15777
15783
  descriptionClassName,
15778
15784
  className,
15779
15785
  // alias for groupClassName
@@ -16081,7 +16087,7 @@ var InnerShadcnCheckboxVariant = (props, ref) => {
16081
16087
  children: [
16082
16088
  checkboxNode,
16083
16089
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-col", children: [
16084
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: labelClassesBase, children: displayItem.label }),
16090
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(labelClassesBase, optionLabelClassName), children: displayItem.label }),
16085
16091
  displayItem.description != null && /* @__PURE__ */ jsxRuntime.jsx(
16086
16092
  "span",
16087
16093
  {
@@ -21142,24 +21148,20 @@ function InputField(props) {
21142
21148
  const effectiveSize = (_c = size != null ? size : (_b = (_a = module.defaults) == null ? void 0 : _a.layout) == null ? void 0 : _b.defaultSize) != null ? _c : void 0;
21143
21149
  const effectiveDensity = (_f = density != null ? density : (_e = (_d = module.defaults) == null ? void 0 : _d.layout) == null ? void 0 : _e.defaultDensity) != null ? _f : void 0;
21144
21150
  const validate = React10__namespace.useCallback(
21145
- (value2, _report) => {
21151
+ (value2, field2, form, _report) => {
21146
21152
  var _a2;
21147
21153
  const messages = [];
21148
21154
  if (module.validate) {
21149
21155
  const res = module.validate(value2, {
21150
21156
  required: !!required,
21151
21157
  props,
21152
- field: void 0,
21153
- form: void 0
21158
+ field: field2,
21159
+ form
21154
21160
  });
21155
21161
  messages.push(...normalizeValidateResult(res));
21156
21162
  }
21157
21163
  if (onValidate) {
21158
- const res = onValidate(
21159
- value2,
21160
- void 0,
21161
- void 0
21162
- );
21164
+ const res = onValidate(value2, field2, form);
21163
21165
  messages.push(...normalizeValidateResult(res));
21164
21166
  }
21165
21167
  if (!messages.length) return true;
@@ -21383,7 +21385,11 @@ function InputField(props) {
21383
21385
  )
21384
21386
  )
21385
21387
  ] });
21386
- const inlineRowClassName = cn("flex items-start gap-2", classes == null ? void 0 : classes.inlineRow);
21388
+ const inlineRowClassName = cn(
21389
+ "flex gap-2",
21390
+ hasLabelAboveSlots || hasLabelBelowSlots ? "items-start" : "items-center",
21391
+ classes == null ? void 0 : classes.inlineRow
21392
+ );
21387
21393
  const hasStackedLabelBlock = lp !== "hidden" && hasAnyLabelBlockContent;
21388
21394
  const stackedGroupClassName = cn(
21389
21395
  hasStackedLabelBlock && hasLabelRowContent && "mt-0.5",