nuxt-ui-tools 1.9.0 → 1.10.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/module.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "configKey": "nuxtUiTools",
6
6
  "docs": "https://ui.nuxt.com/docs/getting-started/installation/nuxt",
7
7
  "name": "nuxt-ui-tools",
8
- "version": "1.9.0",
8
+ "version": "1.10.1",
9
9
  "builder": {
10
10
  "@nuxt/module-builder": "1.0.3",
11
11
  "unbuild": "unknown"
@@ -30,7 +30,7 @@ export declare function useFieldControl<TField extends FormField>(field: () => T
30
30
  activate: () => void;
31
31
  add: (option: FormValue) => import("../utils/options.js").ResolvedFormOption;
32
32
  creatable: import("vue").ComputedRef<boolean>;
33
- create: (label: string) => Promise<import("../utils/options.js").ResolvedFormOption | null>;
33
+ create: (label?: string) => Promise<import("../utils/options.js").ResolvedFormOption | null>;
34
34
  createLabel: import("vue").ComputedRef<string | undefined>;
35
35
  creating: import("vue").ComputedRef<boolean>;
36
36
  disableOnLoading: import("vue").ComputedRef<boolean>;
@@ -54,7 +54,7 @@ export declare function useFieldControl<TField extends FormField>(field: () => T
54
54
  selectedItems: import("vue").ComputedRef<readonly import("../utils/options.js").ResolvedFormOption[]>;
55
55
  setSearch: (term: string) => void;
56
56
  };
57
- params: import("vue").ComputedRef<import("../types/index.js").FormFieldCallbackParams<{}, Readonly<import("../../shared/index.js").GenericObject>, unknown, unknown>>;
57
+ params: import("vue").ComputedRef<import("../types/index.js").FormFieldCallbackParams<{}, import("../types/index.js").FormDependencies, unknown, unknown>>;
58
58
  placeholder: import("vue").ComputedRef<string>;
59
59
  validationPending: import("vue").ComputedRef<boolean>;
60
60
  };
@@ -1,6 +1,7 @@
1
1
  import { debounceFilter, throttleFilter, watchWithFilter } from "@vueuse/core";
2
2
  import { computed, onMounted } from "vue";
3
3
  import { isEqualFormValue } from "../utils/compare.js";
4
+ import { readFormDependencyValues } from "../utils/dependencies.js";
4
5
  import { cloneFormValue } from "../utils/path.js";
5
6
  import { isFunction, isNumber, isObject } from "../utils/predicate.js";
6
7
  import { useFormRuntimeContext } from "./use-form-runtime.js";
@@ -29,7 +30,7 @@ export function useFieldEffects(field, path) {
29
30
  }
30
31
  );
31
32
  watchWithFilter(
32
- () => params.value.deps,
33
+ () => readFormDependencyValues(params.value.deps),
33
34
  (deps) => {
34
35
  if (lastDeps !== UNSET && isEqualFormValue(deps, lastDeps)) {
35
36
  return;
@@ -12,7 +12,7 @@ export declare function useFieldOptions(params: {
12
12
  activate: () => void;
13
13
  add: (option: FormValue) => ResolvedFormOption;
14
14
  creatable: ComputedRef<boolean>;
15
- create: (label: string) => Promise<ResolvedFormOption | null>;
15
+ create: (label?: string) => Promise<ResolvedFormOption | null>;
16
16
  createLabel: ComputedRef<string | undefined>;
17
17
  creating: ComputedRef<boolean>;
18
18
  disableOnLoading: ComputedRef<boolean>;
@@ -46,7 +46,10 @@ export function useFieldOptions(params) {
46
46
  }
47
47
  return resolveTrackedOptionSource(params.field(), params.callbackParams.value);
48
48
  });
49
- const resolvedSource = computed(() => trackedOptionSource.value.source);
49
+ const resolvedSource = computed(() => {
50
+ const { source } = trackedOptionSource.value;
51
+ return isRuntimeQueryOptions(source) && isDisabledQueryOptions(source) ? [] : source;
52
+ });
50
53
  const contextResources = computed(() => {
51
54
  const { ctx } = params.callbackParams.value;
52
55
  if (!isRecord(ctx)) {
@@ -260,8 +263,8 @@ export function useFieldOptions(params) {
260
263
  return normalized;
261
264
  }
262
265
  async function create(label) {
263
- const normalizedLabel = label.trim();
264
- if (!normalizedLabel || creating.value) {
266
+ const normalizedLabel = label?.trim() ?? "";
267
+ if (!isUndefined(label) && !normalizedLabel || creating.value) {
265
268
  return null;
266
269
  }
267
270
  const createOption = optionConfig.value?.create;
@@ -359,7 +362,7 @@ function resolveRemoteOptionConfig(field) {
359
362
  resolveSelected: loader.resolveSelected,
360
363
  search: loader.search,
361
364
  selectedQueryKeyFor: loader.selectedQueryKeyFor,
362
- source: loader.load
365
+ load: loader.load
363
366
  };
364
367
  }
365
368
  return isRemoteOptionConfig(options) ? options : null;
@@ -372,7 +375,7 @@ function isRemoteLoaderOptionConfig(value) {
372
375
  return isFunction(loader.load) && isFunction(loader.resolveSelected) && isRecord(loader.pagination);
373
376
  }
374
377
  function isRemoteOptionConfig(value) {
375
- return isRecord(value) && value.mode === "remote" && isFunction(Object.getOwnPropertyDescriptor(value, "source")?.value) && isRecord(value.pagination);
378
+ return isRecord(value) && value.mode === "remote" && isFunction(Object.getOwnPropertyDescriptor(value, "load")?.value) && isRecord(value.pagination);
376
379
  }
377
380
  function resolveOptionConfig(field) {
378
381
  if (!createFormFieldInstance(field).capability.has("options")) {
@@ -435,6 +438,9 @@ function trackContextAccess(ctx, contextKeys) {
435
438
  }
436
439
  });
437
440
  }
441
+ function isDisabledQueryOptions(source) {
442
+ return Object.getOwnPropertyDescriptor(source, "enabled")?.value === false;
443
+ }
438
444
  function isRuntimeQueryOptions(value) {
439
445
  if (!isRecord(value)) {
440
446
  return false;
@@ -139,7 +139,7 @@ export function useRemoteFieldOptions(params) {
139
139
  sourceFetching.value = true;
140
140
  try {
141
141
  const result = await resolveRemoteResult(
142
- config.source({
142
+ config.load({
143
143
  ...params.callbackParams.value,
144
144
  page: { cursor: page.cursor, index: page.index, size: pageSize.value },
145
145
  search: search.value
@@ -310,7 +310,7 @@ export function useRemoteFieldOptions(params) {
310
310
  async function loadChildrenPage(config, option, key, domain) {
311
311
  try {
312
312
  const result = await resolveRemoteResult(
313
- config.source({
313
+ config.load({
314
314
  ...params.callbackParams.value,
315
315
  page: { cursor: null, index: 1, size: pageSize.value },
316
316
  parent: rawOptions.get(key) ?? option,
@@ -395,10 +395,10 @@ export function useRemoteFieldOptions(params) {
395
395
  () => {
396
396
  const config = params.config.value;
397
397
  const { deps } = params.callbackParams.value;
398
- if (!config?.refreshOn?.length || !isRecord(deps)) {
398
+ if (!config?.refreshOn?.length) {
399
399
  return [];
400
400
  }
401
- return config.refreshOn.map((alias) => deps[alias]);
401
+ return config.refreshOn.map((alias) => deps.get(alias));
402
402
  },
403
403
  (next, previous) => {
404
404
  if (isUndefined(previous) || sameValues(next, previous)) {
@@ -7,7 +7,7 @@ export declare function isArrayActionConfig(action: FormArrayAction | undefined)
7
7
  export declare function useFormArrayItems(field: () => FormArrayItemsField, path: () => readonly string[]): {
8
8
  actionParams: (index: number) => {
9
9
  ctx: {};
10
- deps: Readonly<import("../../../shared/index.js").GenericObject>;
10
+ deps: import("../../types/index.js").FormDependencies;
11
11
  getOptions: (key: string) => readonly unknown[];
12
12
  getValue: (key: string) => unknown;
13
13
  index: number;
@@ -77,7 +77,7 @@ async function handleCreate(label) {
77
77
  await options.create(label);
78
78
  }
79
79
  async function handleCreateAction() {
80
- await handleCreate(searchTerm.value);
80
+ await options.create(searchTerm.value.trim() || void 0);
81
81
  }
82
82
  async function handleNativeCreate(label) {
83
83
  const normalizedLabel = label.trim();
@@ -85,7 +85,7 @@ async function handleCreate(label) {
85
85
  await options.create(label);
86
86
  }
87
87
  async function handleCreateAction() {
88
- await handleCreate(searchTerm.value);
88
+ await options.create(searchTerm.value.trim() || void 0);
89
89
  }
90
90
  async function handleNativeCreate(label) {
91
91
  const normalizedLabel = label.trim();
@@ -16,7 +16,7 @@ import type { FormContextData, FormContextDefinition, FormPageSchema, FormPageSc
16
16
  * label: 'VTEST ID',
17
17
  * // `accountType` belongs to another section: a page is one form, one state.
18
18
  * dependencies: ['accountType'],
19
- * required: ({ deps }) => hasActiveContract || 'accountType' in deps,
19
+ * required: ({ deps }) => hasActiveContract || deps.get('accountType') !== null,
20
20
  * },
21
21
  * ],
22
22
  * })
@@ -86,8 +86,11 @@ export interface FormFieldOptionsApi<TOption = FormValue> {
86
86
  error: () => FormValue | null;
87
87
  /** Refreshes the option source when it is async or query-backed. */
88
88
  refresh: () => Promise<void>;
89
- /** Creates a new option when the field configured an option creation handler. */
90
- create: (label: string) => Promise<TOption | null>;
89
+ /**
90
+ * Creates a new option when the field configured an option creation handler. A blank typed label
91
+ * creates nothing; calling it without a label runs the handler with an empty one.
92
+ */
93
+ create: (label?: string) => Promise<TOption | null>;
91
94
  /** Returns hydrated options for the current selection, including values outside loaded pages. */
92
95
  selected: () => readonly TOption[];
93
96
  /** True when the field loads options remotely. */
@@ -1,18 +1,30 @@
1
1
  import type { FormValue } from './/index.js';
2
2
  import type { FormFieldApi } from './api.js';
3
- import type { FormObject } from './utils.js';
4
3
  /**
5
- * Dependency values a field callback reads: each declared dependency under its target name, typed
6
- * `FormValue` until the callback narrows it, since a reusable field can be mounted anywhere.
4
+ * The dependencies a field callback reads, each under the target name its `dependencies` entry
5
+ * declares.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * dependencies: ['year', ['profile.country', 'country']],
10
+ * options: ({ deps }) => ratesFor(deps.get<number | null>('year'), deps.get<string>('country')),
11
+ * ```
7
12
  */
8
- export type FormDependencyValues = Readonly<FormObject>;
13
+ export interface FormDependencies {
14
+ /**
15
+ * Reads the dependency declared under `key`, typed as the caller states. The engine does not
16
+ * check the type, since a reusable field can be mounted anywhere; a key the field did not declare
17
+ * reads as `undefined`.
18
+ */
19
+ get: <TValue = FormValue>(key: string) => TValue;
20
+ }
9
21
  /**
10
22
  * Parameters passed to field-level callbacks.
11
23
  */
12
- export interface FormFieldCallbackParams<TContext = NonNullable<unknown>, TDeps = FormDependencyValues, TValue = FormValue, TOption = FormValue> {
24
+ export interface FormFieldCallbackParams<TContext = NonNullable<unknown>, TDeps = FormDependencies, TValue = FormValue, TOption = FormValue> {
13
25
  /** Fully typed form-scoped context declared on the schema. */
14
26
  ctx: TContext;
15
- /** Values read from the field's dependency list. */
27
+ /** The field's declared dependencies, read with `deps.get(key)`. */
16
28
  deps: TDeps;
17
29
  /** Field-level API for values, options, upload work, and validation. */
18
30
  api: FormFieldApi<TValue, TOption, TContext>;
@@ -20,4 +32,4 @@ export interface FormFieldCallbackParams<TContext = NonNullable<unknown>, TDeps
20
32
  /**
21
33
  * Function receiving field callback parameters.
22
34
  */
23
- export type FormFieldCallback<TResult, TContext = NonNullable<unknown>, TDeps = FormDependencyValues, TValue = FormValue, TOption = FormValue> = (params: FormFieldCallbackParams<TContext, TDeps, TValue, TOption>) => TResult;
35
+ export type FormFieldCallback<TResult, TContext = NonNullable<unknown>, TDeps = FormDependencies, TValue = FormValue, TOption = FormValue> = (params: FormFieldCallbackParams<TContext, TDeps, TValue, TOption>) => TResult;
@@ -7,7 +7,8 @@ type PathValue<TSource, TPath extends string> = TPath extends '$root' ? TSource
7
7
  type DependencyObject<TDependency, TState> = DependencyTarget<TDependency> extends infer TTarget ? TTarget extends string ? PathToObject<TTarget, PathValue<TState, DependencySource<TDependency>>> : NonNullable<unknown> : NonNullable<unknown>;
8
8
  type DependenciesValue<TDependencies, TState> = TDependencies extends readonly FormValue[] ? UnionToIntersection<DependencyObject<TDependencies[number], TState>> : NonNullable<unknown>;
9
9
  /**
10
- * Extracts the `deps` object made available to callbacks for a single field.
10
+ * Extracts the values of a single field's declared dependencies, keyed by target name: the types a
11
+ * callback reads through `deps.get(key)`.
11
12
  *
12
13
  * Pass the form internal state as the second type argument to resolve dependency values from
13
14
  * absolute dotted paths. `$parent` dependencies intentionally resolve to `unknown` at this layer
@@ -20,6 +21,7 @@ type DependenciesValue<TDependencies, TState> = TDependencies extends readonly F
20
21
  * dependencies: ['password', ['profile.country', 'country']]
21
22
  * }, State>
22
23
  * // { password: string | null, country: 'FR' | 'BE' | null }
24
+ * deps.get<Deps['country']>('country')
23
25
  * ```
24
26
  */
25
27
  export type ExtractFormFieldDependencies<TField, TState = FormObject> = TField extends {
@@ -1,6 +1,6 @@
1
1
  import type { FormValue } from './/index.js';
2
2
  import type { FormFieldApi } from './api.js';
3
- import type { FormDependencyValues, FormFieldCallback } from './callbacks.js';
3
+ import type { FormDependencies, FormFieldCallback } from './callbacks.js';
4
4
  import type { FormField } from './field.js';
5
5
  import type { FormContainerLayout, FormItemLayout } from './layout.js';
6
6
  import type { FormTransformConfig } from './transform.js';
@@ -19,11 +19,11 @@ export interface FormFieldDescription {
19
19
  /**
20
20
  * Common stateful field properties.
21
21
  */
22
- export type FormFieldProps<TProps = FormObject, TContext = NonNullable<unknown>, TDeps = FormDependencyValues> = FormDynamic<TProps & FormObject, {
22
+ export type FormFieldProps<TProps = FormObject, TContext = NonNullable<unknown>, TDeps = FormDependencies> = FormDynamic<TProps & FormObject, {
23
23
  ctx: TContext;
24
24
  deps: TDeps;
25
25
  }>;
26
- export interface FormStatefulFieldBase<TType extends FormFieldType, TValue, TContext = NonNullable<unknown>, TDeps = FormDependencyValues, TProps = FormObject> {
26
+ export interface FormStatefulFieldBase<TType extends FormFieldType, TValue, TContext = NonNullable<unknown>, TDeps = FormDependencies, TProps = FormObject> {
27
27
  /** Raw path used to read/write the field value in form state. */
28
28
  key: string;
29
29
  /** Discriminant used by the field registry. */
@@ -101,7 +101,7 @@ export interface FormStatefulFieldBase<TType extends FormFieldType, TValue, TCon
101
101
  /**
102
102
  * Base properties for stateless visual fields.
103
103
  */
104
- export interface FormStatelessFieldBase<TType extends FormFieldType, TContext = NonNullable<unknown>, TDeps = FormDependencyValues, TProps = FormObject> {
104
+ export interface FormStatelessFieldBase<TType extends FormFieldType, TContext = NonNullable<unknown>, TDeps = FormDependencies, TProps = FormObject> {
105
105
  /** Stable key for renderer identity. Stateless fields do not write form state. */
106
106
  key: string;
107
107
  /** Discriminant used by the field registry. */
@@ -118,7 +118,7 @@ export interface FormStatelessFieldBase<TType extends FormFieldType, TContext =
118
118
  /**
119
119
  * Base properties for structural fields that render children.
120
120
  */
121
- export interface FormContainerFieldBase<TType extends FormFieldType, TContext = NonNullable<unknown>, TDeps = FormDependencyValues, TProps = FormObject> {
121
+ export interface FormContainerFieldBase<TType extends FormFieldType, TContext = NonNullable<unknown>, TDeps = FormDependencies, TProps = FormObject> {
122
122
  /** Stable renderer key. */
123
123
  key: string;
124
124
  /** Discriminant used by the field registry. */
@@ -42,7 +42,7 @@ import type { FormTextField } from '../fields/text/types.js';
42
42
  import type { FormTextareaField } from '../fields/textarea/types.js';
43
43
  import type { FormTimeField } from '../fields/time/types.js';
44
44
  import type { FormUploadField } from '../fields/upload/types.js';
45
- import type { FormDependencyValues } from './callbacks.js';
45
+ import type { FormDependencies } from './callbacks.js';
46
46
  export type { FormContainerFieldBase, FormFieldType, FormStatefulFieldBase, FormStatelessFieldBase, } from './field-base.js';
47
47
  export type { FormArrayCollapseField } from '../fields/array-collapse/types.js';
48
48
  export type { FormArrayListField } from '../fields/array-list/types.js';
@@ -94,4 +94,4 @@ export type { FormUploadCallbackParams, FormUploadDeleteParams, FormUploadField,
94
94
  * Individual field schema contracts live beside their field implementation in
95
95
  * `src/runtime/form/fields/<kind>/types.ts`; this file only assembles the public union.
96
96
  */
97
- export type FormField<TContext = NonNullable<unknown>, TDeps = FormDependencyValues> = FormTextField<TContext, TDeps> | FormPasswordField<TContext, TDeps> | FormTextareaField<TContext, TDeps> | FormNumberField<TContext, TDeps> | FormAutoCompleteField<TContext, TDeps> | FormCheckboxField<TContext, TDeps> | FormSwitchField<TContext, TDeps> | FormSwitchGroupField<TContext, TDeps> | FormRadioField<TContext, TDeps> | FormRadioCardField<TContext, TDeps> | FormCheckboxGroupField<TContext, TDeps> | FormCheckboxCardField<TContext, TDeps> | FormSelectField<TContext, TDeps> | FormDateField<TContext, TDeps> | FormDateTimeField<TContext, TDeps> | FormDateRangeField<TContext, TDeps> | FormMonthRangeField<TContext, TDeps> | FormDateTimeRangeField<TContext, TDeps> | FormMonthField<TContext, TDeps> | FormYearField<TContext, TDeps> | FormTimeField<TContext, TDeps> | FormTreeSelectField<TContext, TDeps> | FormCascaderField<TContext, TDeps> | FormTreeField<TContext, TDeps> | FormPhoneNumberField<TContext, TDeps> | FormHiddenField<TContext, TDeps> | FormInfoField<TContext, TDeps> | FormDividerField<TContext, TDeps> | FormSectionField<TContext, TDeps> | FormInputGroupField<TContext, TDeps> | FormTabsField<TContext, TDeps> | FormGroupField<TContext, TDeps> | FormObjectField<TContext, TDeps> | FormMatrixField<TContext, TDeps> | FormCustomComponentField<TContext, TDeps> | FormFileField<TContext, TDeps> | FormUploadField<TContext, TDeps> | FormArrayListField<TContext, TDeps> | FormArrayTableField<TContext, TDeps> | FormArrayTabsField<TContext, TDeps> | FormArrayVariantField<TContext, TDeps> | FormArrayCollapseField<TContext, TDeps> | FormArrayPrimitiveField<TContext, TDeps> | FormSliderField<TContext, TDeps> | FormColorPickerField<TContext, TDeps> | FormOneTimeCodeField<TContext, TDeps> | FormRatingField<TContext, TDeps> | FormTagField<TContext, TDeps> | FormButtonField<TContext, TDeps> | FormCardField<TContext, TDeps> | FormColumnField<TContext, TDeps>;
97
+ export type FormField<TContext = NonNullable<unknown>, TDeps = FormDependencies> = FormTextField<TContext, TDeps> | FormPasswordField<TContext, TDeps> | FormTextareaField<TContext, TDeps> | FormNumberField<TContext, TDeps> | FormAutoCompleteField<TContext, TDeps> | FormCheckboxField<TContext, TDeps> | FormSwitchField<TContext, TDeps> | FormSwitchGroupField<TContext, TDeps> | FormRadioField<TContext, TDeps> | FormRadioCardField<TContext, TDeps> | FormCheckboxGroupField<TContext, TDeps> | FormCheckboxCardField<TContext, TDeps> | FormSelectField<TContext, TDeps> | FormDateField<TContext, TDeps> | FormDateTimeField<TContext, TDeps> | FormDateRangeField<TContext, TDeps> | FormMonthRangeField<TContext, TDeps> | FormDateTimeRangeField<TContext, TDeps> | FormMonthField<TContext, TDeps> | FormYearField<TContext, TDeps> | FormTimeField<TContext, TDeps> | FormTreeSelectField<TContext, TDeps> | FormCascaderField<TContext, TDeps> | FormTreeField<TContext, TDeps> | FormPhoneNumberField<TContext, TDeps> | FormHiddenField<TContext, TDeps> | FormInfoField<TContext, TDeps> | FormDividerField<TContext, TDeps> | FormSectionField<TContext, TDeps> | FormInputGroupField<TContext, TDeps> | FormTabsField<TContext, TDeps> | FormGroupField<TContext, TDeps> | FormObjectField<TContext, TDeps> | FormMatrixField<TContext, TDeps> | FormCustomComponentField<TContext, TDeps> | FormFileField<TContext, TDeps> | FormUploadField<TContext, TDeps> | FormArrayListField<TContext, TDeps> | FormArrayTableField<TContext, TDeps> | FormArrayTabsField<TContext, TDeps> | FormArrayVariantField<TContext, TDeps> | FormArrayCollapseField<TContext, TDeps> | FormArrayPrimitiveField<TContext, TDeps> | FormSliderField<TContext, TDeps> | FormColorPickerField<TContext, TDeps> | FormOneTimeCodeField<TContext, TDeps> | FormRatingField<TContext, TDeps> | FormTagField<TContext, TDeps> | FormButtonField<TContext, TDeps> | FormCardField<TContext, TDeps> | FormColumnField<TContext, TDeps>;
@@ -49,5 +49,6 @@ export interface FormOptionRuntimeState {
49
49
  /** Loads the direct children of a remote tree option. */
50
50
  loadChildren: (option: ResolvedFormOption) => Promise<void>;
51
51
  add: (option: FormValue) => void;
52
- create: (label: string) => Promise<ResolvedFormOption | null>;
52
+ /** Creates an option; a blank typed label creates nothing, and no label runs the handler with an empty one. */
53
+ create: (label?: string) => Promise<ResolvedFormOption | null>;
53
54
  }
@@ -1,7 +1,7 @@
1
1
  import type { QueryKey } from '@tanstack/vue-query';
2
2
  import type { RemoteOptionsLoader, RemoteOptionsPageRequest, RemoteOptionsPagination, RemoteOptionsResult, RemoteOptionsSearch } from '../../shared/types/remote-options.js';
3
3
  import type { FormValue } from './/index.js';
4
- import type { FormDependencyValues, FormFieldCallback, FormFieldCallbackParams } from './callbacks.js';
4
+ import type { FormDependencies, FormFieldCallback, FormFieldCallbackParams } from './callbacks.js';
5
5
  import type { FormQueryOptions } from './context.js';
6
6
  import type { FormMaybePromise, FormText } from './utils.js';
7
7
  /**
@@ -28,15 +28,18 @@ export type FormOptionItem<TValue extends FormOptionValue = FormOptionValue> = T
28
28
  /**
29
29
  * A static, sync-derived, promise-backed, or TanStack Query-backed option source.
30
30
  */
31
- export type FormOptionsSource<TOption, TContext = NonNullable<unknown>, TDeps = FormDependencyValues, TValue = FormValue> = readonly TOption[] | FormQueryOptions<readonly TOption[]> | FormFieldCallback<readonly TOption[] | Promise<readonly TOption[]> | FormQueryOptions<readonly TOption[]>, TContext, TDeps, TValue, TOption>;
31
+ export type FormOptionsSource<TOption, TContext = NonNullable<unknown>, TDeps = FormDependencies, TValue = FormValue> = readonly TOption[] | FormQueryOptions<readonly TOption[]> | FormFieldCallback<readonly TOption[] | Promise<readonly TOption[]> | FormQueryOptions<readonly TOption[]>, TContext, TDeps, TValue, TOption>;
32
32
  /**
33
33
  * Option creation hook for fields that can create a missing option from user input.
34
34
  */
35
- export interface FormCreateOptionParams<TContext = NonNullable<unknown>, TDeps = FormDependencyValues, TValue = FormValue, TOption = FormValue> extends FormFieldCallbackParams<TContext, TDeps, TValue, TOption> {
36
- /** User-entered label that should be converted into a concrete option. */
35
+ export interface FormCreateOptionParams<TContext = NonNullable<unknown>, TDeps = FormDependencies, TValue = FormValue, TOption = FormValue> extends FormFieldCallbackParams<TContext, TDeps, TValue, TOption> {
36
+ /**
37
+ * User-entered label that should be converted into a concrete option. Empty when the explicit
38
+ * create action runs without a search term, such as a creator that opens its own form.
39
+ */
37
40
  label: string;
38
41
  }
39
- export interface FormCreateOption<TOption, TContext = NonNullable<unknown>, TDeps = FormDependencyValues, TValue = FormValue> {
42
+ export interface FormCreateOption<TOption, TContext = NonNullable<unknown>, TDeps = FormDependencies, TValue = FormValue> {
40
43
  /** Label shown by the create affordance. */
41
44
  label?: FormText;
42
45
  /** Selects the newly-created option immediately. Defaults to `true`. */
@@ -46,14 +49,14 @@ export interface FormCreateOption<TOption, TContext = NonNullable<unknown>, TDep
46
49
  /** Creates an option and returns it. Return `null` when creation was cancelled. */
47
50
  handler: (params: FormCreateOptionParams<TContext, TDeps, TValue, TOption>) => FormMaybePromise<TOption | null>;
48
51
  }
49
- export interface FormOptionsChangeParams<TContext = NonNullable<unknown>, TDeps = FormDependencyValues, TValue = FormValue, TOption = FormValue> extends FormFieldCallbackParams<TContext, TDeps, TValue, TOption> {
52
+ export interface FormOptionsChangeParams<TContext = NonNullable<unknown>, TDeps = FormDependencies, TValue = FormValue, TOption = FormValue> extends FormFieldCallbackParams<TContext, TDeps, TValue, TOption> {
50
53
  /** Previously resolved normalized options. */
51
54
  previousOptions: readonly TOption[];
52
55
  }
53
56
  /**
54
57
  * Shared option configuration for option-based fields.
55
58
  */
56
- export interface FormOptionConfig<TOption, TContext = NonNullable<unknown>, TDeps = FormDependencyValues, TValue = FormValue> {
59
+ export interface FormOptionConfig<TOption, TContext = NonNullable<unknown>, TDeps = FormDependencies, TValue = FormValue> {
57
60
  /** Local option configs never declare a mode; `mode: 'remote'` selects the remote config. */
58
61
  mode?: never;
59
62
  /** Static, sync-derived, promise-backed, or query-backed options. */
@@ -73,7 +76,7 @@ export interface FormOptionConfig<TOption, TContext = NonNullable<unknown>, TDep
73
76
  /** Disable the field while its option source has no usable data yet. */
74
77
  disableOnLoading?: boolean;
75
78
  }
76
- export interface FormRemoteOptionsRequest<TContext = NonNullable<unknown>, TDeps = FormDependencyValues, TValue = FormValue, TOption = FormValue> extends FormFieldCallbackParams<TContext, TDeps, TValue, TOption> {
79
+ export interface FormRemoteOptionsRequest<TContext = NonNullable<unknown>, TDeps = FormDependencies, TValue = FormValue, TOption = FormValue> extends FormFieldCallbackParams<TContext, TDeps, TValue, TOption> {
77
80
  /** Current debounced search term. Empty when the menu lists unfiltered options. */
78
81
  search: string;
79
82
  /** Requested page. `cursor` is set for cursor pagination and `index` for page pagination. */
@@ -81,7 +84,7 @@ export interface FormRemoteOptionsRequest<TContext = NonNullable<unknown>, TDeps
81
84
  /** Parent option whose direct children are requested. Undefined for root pages. */
82
85
  parent?: NoInfer<TOption>;
83
86
  }
84
- export interface FormRemoteSelectedRequest<TContext = NonNullable<unknown>, TDeps = FormDependencyValues, TValue = FormValue, TOption = FormValue> extends FormFieldCallbackParams<TContext, TDeps, TValue, TOption> {
87
+ export interface FormRemoteSelectedRequest<TContext = NonNullable<unknown>, TDeps = FormDependencies, TValue = FormValue, TOption = FormValue> extends FormFieldCallbackParams<TContext, TDeps, TValue, TOption> {
85
88
  /** Selected values that are not present in the loaded pages. */
86
89
  values: readonly FormOptionValue[];
87
90
  }
@@ -89,10 +92,13 @@ export type FormRemoteSource<TResult> = FormQueryOptions<TResult> | Promise<TRes
89
92
  /**
90
93
  * Remote option configuration: server-side search, pagination, and selected-value hydration.
91
94
  */
92
- export interface FormRemoteOptionConfig<TOption, TContext = NonNullable<unknown>, TDeps = FormDependencyValues, TValue = FormValue> {
95
+ export interface FormRemoteOptionConfig<TOption, TContext = NonNullable<unknown>, TDeps = FormDependencies, TValue = FormValue> {
93
96
  mode: 'remote';
94
- /** Loads one page of options for the current search, page, and optional parent. */
95
- source: (request: FormRemoteOptionsRequest<TContext, TDeps, TValue, TOption>) => FormRemoteSource<RemoteOptionsResult<TOption>>;
97
+ /**
98
+ * Loads one page of options for the current search, page, and optional parent. Named apart from
99
+ * a local config's `source` so an object-form local source keeps typed callback parameters.
100
+ */
101
+ load: (request: FormRemoteOptionsRequest<TContext, TDeps, TValue, TOption>) => FormRemoteSource<RemoteOptionsResult<TOption>>;
96
102
  /** Hydrates selected values that the loaded pages do not contain. */
97
103
  resolveSelected?: (request: FormRemoteSelectedRequest<TContext, TDeps, TValue, TOption>) => FormRemoteSource<readonly TOption[]>;
98
104
  pagination: RemoteOptionsPagination;
@@ -118,13 +124,13 @@ export interface FormRemoteOptionConfig<TOption, TContext = NonNullable<unknown>
118
124
  disableOnLoading?: boolean;
119
125
  }
120
126
  /** Reuses one remote loader while keeping field-specific refresh and creation behavior local. */
121
- export interface FormRemoteLoaderOptionConfig<TOption, TContext = NonNullable<unknown>, TDeps = FormDependencyValues, TValue = FormValue> extends Omit<FormRemoteOptionConfig<TOption, TContext, TDeps, TValue>, 'source' | 'resolveSelected' | 'pagination' | 'search' | 'queryKeyFor' | 'selectedQueryKeyFor'> {
127
+ export interface FormRemoteLoaderOptionConfig<TOption, TContext = NonNullable<unknown>, TDeps = FormDependencies, TValue = FormValue> extends Omit<FormRemoteOptionConfig<TOption, TContext, TDeps, TValue>, 'load' | 'resolveSelected' | 'pagination' | 'search' | 'queryKeyFor' | 'selectedQueryKeyFor'> {
122
128
  loader: RemoteOptionsLoader<TOption>;
123
- source?: never;
129
+ load?: never;
124
130
  resolveSelected?: never;
125
131
  pagination?: never;
126
132
  search?: never;
127
133
  queryKeyFor?: never;
128
134
  selectedQueryKeyFor?: never;
129
135
  }
130
- export type FormAnyOptionConfig<TOption, TContext = NonNullable<unknown>, TDeps = FormDependencyValues, TValue = FormValue> = FormOptionConfig<TOption, TContext, TDeps, TValue> | FormRemoteOptionConfig<TOption, TContext, TDeps, TValue> | FormRemoteLoaderOptionConfig<TOption, TContext, TDeps, TValue> | FormOptionsSource<TOption, TContext, TDeps, TValue>;
136
+ export type FormAnyOptionConfig<TOption, TContext = NonNullable<unknown>, TDeps = FormDependencies, TValue = FormValue> = FormOptionConfig<TOption, TContext, TDeps, TValue> | FormRemoteOptionConfig<TOption, TContext, TDeps, TValue> | FormRemoteLoaderOptionConfig<TOption, TContext, TDeps, TValue> | FormOptionsSource<TOption, TContext, TDeps, TValue>;
@@ -1,7 +1,7 @@
1
1
  import type { RegleRuleRaw } from '@regle/core';
2
2
  import type { FormValue } from './/index.js';
3
3
  import type { FormFieldCallback } from './callbacks.js';
4
- import type { FormObject, FormText } from './utils.js';
4
+ import type { FormText } from './utils.js';
5
5
  /**
6
6
  * Controls when a field starts running validation while the user edits it.
7
7
  *
@@ -52,4 +52,4 @@ export interface FormValidationConfig<TValue = FormValue, TContext = NonNullable
52
52
  /** Native Regle rules keyed by their validation name. */
53
53
  export type FormValidators = Record<string, RegleRuleRaw>;
54
54
  /** Static or dependency-aware native Regle rules for one field. */
55
- export type FormValidatorsConfig<TContext = NonNullable<unknown>> = FormValidators | FormFieldCallback<FormValidators, TContext, FormObject>;
55
+ export type FormValidatorsConfig<TContext = NonNullable<unknown>> = FormValidators | FormFieldCallback<FormValidators, TContext>;
@@ -1,6 +1,17 @@
1
- import type { FormField, FormObject } from '../types/index.js';
1
+ import type { FormDependencies, FormField, FormObject } from '../types/index.js';
2
+ /**
3
+ * Resolves a field's declared dependencies against the form state, each under its target name.
4
+ */
2
5
  export declare function resolveFieldDependencies(params: {
3
6
  field: FormField;
4
7
  state: FormObject;
5
8
  parentPath: readonly string[];
6
- }): import("../../shared/index.js").GenericObject;
9
+ }): FormDependencies;
10
+ /**
11
+ * Wraps resolved dependency values in the reader field callbacks receive.
12
+ */
13
+ export declare function createFormDependencies(values: FormObject): FormDependencies;
14
+ /**
15
+ * The plain values behind a dependency reader, for the engine's own change detection.
16
+ */
17
+ export declare function readFormDependencyValues(dependencies: FormDependencies): import("../../shared/index.js").GenericObject;
@@ -1,23 +1,35 @@
1
1
  import { getScopedPathValue } from "./path.js";
2
2
  import { isString } from "./predicate.js";
3
+ const dependencyValues = /* @__PURE__ */ new WeakMap();
3
4
  export function resolveFieldDependencies(params) {
4
- const output = {};
5
+ const values = {};
5
6
  const dependencies = Object.getOwnPropertyDescriptor(params.field, "dependencies")?.value;
6
- if (!Array.isArray(dependencies)) {
7
- return output;
8
- }
9
- for (const dependency of dependencies) {
10
- const normalized = normalizeFieldDependency(dependency);
11
- if (!normalized) {
12
- continue;
7
+ if (Array.isArray(dependencies)) {
8
+ for (const dependency of dependencies) {
9
+ const normalized = normalizeFieldDependency(dependency);
10
+ if (!normalized) {
11
+ continue;
12
+ }
13
+ values[normalized.target] = getScopedPathValue(
14
+ params.state,
15
+ normalized.source,
16
+ params.parentPath
17
+ );
13
18
  }
14
- output[normalized.target] = getScopedPathValue(
15
- params.state,
16
- normalized.source,
17
- params.parentPath
18
- );
19
19
  }
20
- return output;
20
+ return createFormDependencies(values);
21
+ }
22
+ export function createFormDependencies(values) {
23
+ const dependencies = {
24
+ // SAFETY: the callback states the type it expects from a dependency it declared; the engine
25
+ // holds untyped form values and does not validate them, as the `get` contract documents.
26
+ get: (key) => values[key]
27
+ };
28
+ dependencyValues.set(dependencies, values);
29
+ return dependencies;
30
+ }
31
+ export function readFormDependencyValues(dependencies) {
32
+ return dependencyValues.get(dependencies) ?? {};
21
33
  }
22
34
  function normalizeFieldDependency(value) {
23
35
  if (isString(value)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-ui-tools",
3
- "version": "1.9.0",
3
+ "version": "1.10.1",
4
4
  "description": "Nuxt module and runtime toolkit for UI-heavy Nuxt applications",
5
5
  "homepage": "https://github.com/ChronicStone/nuxt-ui-tools#readme",
6
6
  "bugs": "https://github.com/ChronicStone/nuxt-ui-tools/issues",