nuxt-ui-tools 1.2.0 → 1.4.0
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 +1 -1
- package/dist/module.mjs +18 -2
- package/dist/runtime/dashboard/components/block/dashboard-block-state.d.vue.ts +2 -0
- package/dist/runtime/dashboard/components/block/dashboard-block-state.vue +4 -1
- package/dist/runtime/dashboard/components/block/dashboard-block-state.vue.d.ts +2 -0
- package/dist/runtime/dashboard/components/block/dashboard-chips.d.vue.ts +20 -0
- package/dist/runtime/dashboard/components/block/dashboard-chips.vue +42 -0
- package/dist/runtime/dashboard/components/block/dashboard-chips.vue.d.ts +20 -0
- package/dist/runtime/dashboard/components/block/dashboard-series-picker.d.vue.ts +8 -0
- package/dist/runtime/dashboard/components/block/dashboard-series-picker.vue +29 -0
- package/dist/runtime/dashboard/components/block/dashboard-series-picker.vue.d.ts +8 -0
- package/dist/runtime/dashboard/components/block/dashboard-skeleton.d.vue.ts +1 -1
- package/dist/runtime/dashboard/components/block/dashboard-skeleton.vue.d.ts +1 -1
- package/dist/runtime/dashboard/components/block/dashboard-view-scope.d.vue.ts +21 -0
- package/dist/runtime/dashboard/components/block/dashboard-view-scope.vue +12 -0
- package/dist/runtime/dashboard/components/block/dashboard-view-scope.vue.d.ts +21 -0
- package/dist/runtime/dashboard/components/dashboard-alerts.vue +3 -3
- package/dist/runtime/dashboard/components/dashboard-bar-chart.d.vue.ts +14 -3
- package/dist/runtime/dashboard/components/dashboard-bar-chart.vue +30 -9
- package/dist/runtime/dashboard/components/dashboard-bar-chart.vue.d.ts +14 -3
- package/dist/runtime/dashboard/components/dashboard-bars.vue +4 -4
- package/dist/runtime/dashboard/components/dashboard-card.vue +28 -6
- package/dist/runtime/dashboard/components/dashboard-combo-chart.d.vue.ts +6 -1
- package/dist/runtime/dashboard/components/dashboard-combo-chart.vue +13 -3
- package/dist/runtime/dashboard/components/dashboard-combo-chart.vue.d.ts +6 -1
- package/dist/runtime/dashboard/components/dashboard-donut-chart.vue +5 -5
- package/dist/runtime/dashboard/components/dashboard-feed.vue +1 -1
- package/dist/runtime/dashboard/components/dashboard-filter.d.vue.ts +67 -0
- package/dist/runtime/dashboard/components/dashboard-filter.vue +134 -0
- package/dist/runtime/dashboard/components/dashboard-filter.vue.d.ts +67 -0
- package/dist/runtime/dashboard/components/dashboard-filters.d.vue.ts +49 -0
- package/dist/runtime/dashboard/components/dashboard-filters.vue +96 -0
- package/dist/runtime/dashboard/components/dashboard-filters.vue.d.ts +49 -0
- package/dist/runtime/dashboard/components/dashboard-funnel.vue +5 -5
- package/dist/runtime/dashboard/components/dashboard-gauge.vue +3 -3
- package/dist/runtime/dashboard/components/dashboard-grid.d.vue.ts +10 -3
- package/dist/runtime/dashboard/components/dashboard-grid.vue +24 -9
- package/dist/runtime/dashboard/components/dashboard-grid.vue.d.ts +10 -3
- package/dist/runtime/dashboard/components/dashboard-line-chart.d.vue.ts +14 -3
- package/dist/runtime/dashboard/components/dashboard-line-chart.vue +30 -9
- package/dist/runtime/dashboard/components/dashboard-line-chart.vue.d.ts +14 -3
- package/dist/runtime/dashboard/components/dashboard-list.d.vue.ts +3 -0
- package/dist/runtime/dashboard/components/dashboard-list.vue +18 -6
- package/dist/runtime/dashboard/components/dashboard-list.vue.d.ts +3 -0
- package/dist/runtime/dashboard/components/dashboard-page.d.vue.ts +53 -0
- package/dist/runtime/dashboard/components/dashboard-page.vue +156 -0
- package/dist/runtime/dashboard/components/dashboard-page.vue.d.ts +53 -0
- package/dist/runtime/dashboard/components/dashboard-paired-bars.vue +4 -4
- package/dist/runtime/dashboard/components/dashboard-refresh.d.vue.ts +1 -1
- package/dist/runtime/dashboard/components/dashboard-refresh.vue +1 -0
- package/dist/runtime/dashboard/components/dashboard-refresh.vue.d.ts +1 -1
- package/dist/runtime/dashboard/components/dashboard-stack-bar.vue +4 -4
- package/dist/runtime/dashboard/components/dashboard-stat.d.vue.ts +14 -2
- package/dist/runtime/dashboard/components/dashboard-stat.vue +28 -10
- package/dist/runtime/dashboard/components/dashboard-stat.vue.d.ts +14 -2
- package/dist/runtime/dashboard/components/dashboard-stats.vue +3 -3
- package/dist/runtime/dashboard/components/dashboard-table.d.vue.ts +1 -1
- package/dist/runtime/dashboard/components/dashboard-table.vue +3 -3
- package/dist/runtime/dashboard/components/dashboard-table.vue.d.ts +1 -1
- package/dist/runtime/dashboard/components/dashboard-tabs.d.vue.ts +1 -1
- package/dist/runtime/dashboard/components/dashboard-tabs.vue.d.ts +1 -1
- package/dist/runtime/dashboard/components/dashboard-total.d.vue.ts +7 -2
- package/dist/runtime/dashboard/components/dashboard-total.vue +10 -2
- package/dist/runtime/dashboard/components/dashboard-total.vue.d.ts +7 -2
- package/dist/runtime/dashboard/components/dashboard-view-tabs.d.vue.ts +33 -0
- package/dist/runtime/dashboard/components/dashboard-view-tabs.vue +66 -0
- package/dist/runtime/dashboard/components/dashboard-view-tabs.vue.d.ts +33 -0
- package/dist/runtime/dashboard/components/dashboard-widget.vue +1 -1
- package/dist/runtime/dashboard/components/filter/dashboard-filter-menu.d.vue.ts +44 -0
- package/dist/runtime/dashboard/components/filter/dashboard-filter-menu.vue +220 -0
- package/dist/runtime/dashboard/components/filter/dashboard-filter-menu.vue.d.ts +44 -0
- package/dist/runtime/dashboard/composables/use-dashboard-api.d.ts +6 -1
- package/dist/runtime/dashboard/composables/use-dashboard-api.js +29 -6
- package/dist/runtime/dashboard/composables/use-dashboard-block.d.ts +5 -4
- package/dist/runtime/dashboard/composables/use-dashboard-block.js +32 -15
- package/dist/runtime/dashboard/composables/use-dashboard-chart.d.ts +8 -1
- package/dist/runtime/dashboard/composables/use-dashboard-chart.js +16 -4
- package/dist/runtime/dashboard/composables/use-dashboard-context.d.ts +50 -0
- package/dist/runtime/dashboard/composables/use-dashboard-context.js +56 -0
- package/dist/runtime/dashboard/composables/use-dashboard-derived.js +6 -1
- package/dist/runtime/dashboard/composables/use-dashboard-filter-control.d.ts +16 -0
- package/dist/runtime/dashboard/composables/use-dashboard-filter-control.js +161 -0
- package/dist/runtime/dashboard/composables/use-dashboard-filter-scope.d.ts +37 -0
- package/dist/runtime/dashboard/composables/use-dashboard-filter-scope.js +107 -0
- package/dist/runtime/dashboard/composables/use-dashboard-format.d.ts +46 -6
- package/dist/runtime/dashboard/composables/use-dashboard-format.js +51 -8
- package/dist/runtime/dashboard/composables/use-dashboard-options.d.ts +11 -4
- package/dist/runtime/dashboard/composables/use-dashboard-options.js +33 -47
- package/dist/runtime/dashboard/composables/use-dashboard-remote-options.d.ts +8 -5
- package/dist/runtime/dashboard/composables/use-dashboard-remote-options.js +59 -70
- package/dist/runtime/dashboard/composables/use-dashboard-resource.d.ts +15 -4
- package/dist/runtime/dashboard/composables/use-dashboard-resource.js +52 -17
- package/dist/runtime/dashboard/composables/use-dashboard-scope.d.ts +10 -7
- package/dist/runtime/dashboard/composables/use-dashboard-scope.js +26 -16
- package/dist/runtime/dashboard/composables/use-dashboard-series-picker.d.ts +20 -0
- package/dist/runtime/dashboard/composables/use-dashboard-series-picker.js +30 -0
- package/dist/runtime/dashboard/composables/use-dashboard-time.d.ts +2 -0
- package/dist/runtime/dashboard/composables/use-dashboard-time.js +4 -1
- package/dist/runtime/dashboard/composables/use-dashboard-ui.d.ts +12 -2
- package/dist/runtime/dashboard/composables/use-dashboard-ui.js +3 -0
- package/dist/runtime/dashboard/composables/use-dashboard-views.d.ts +15 -3
- package/dist/runtime/dashboard/composables/use-dashboard-views.js +33 -10
- package/dist/runtime/dashboard/composables/use-dashboard.d.ts +18 -12
- package/dist/runtime/dashboard/composables/use-dashboard.js +98 -9
- package/dist/runtime/dashboard/index.d.ts +3 -1
- package/dist/runtime/dashboard/index.js +3 -1
- package/dist/runtime/dashboard/schema/index.d.ts +62 -30
- package/dist/runtime/dashboard/schema/index.js +3 -0
- package/dist/runtime/dashboard/types/blocks.d.ts +30 -4
- package/dist/runtime/dashboard/types/charts.d.ts +2 -2
- package/dist/runtime/dashboard/types/controls.d.ts +123 -0
- package/dist/runtime/dashboard/types/filters.d.ts +236 -0
- package/dist/runtime/dashboard/types/index.d.ts +3 -2
- package/dist/runtime/dashboard/types/page.d.ts +17 -0
- package/dist/runtime/dashboard/types/page.js +0 -0
- package/dist/runtime/dashboard/types/resource.d.ts +66 -28
- package/dist/runtime/dashboard/types/runtime.d.ts +39 -8
- package/dist/runtime/dashboard/types/schema.d.ts +94 -54
- package/dist/runtime/dashboard/types/ui.d.ts +78 -0
- package/dist/runtime/dashboard/utils/builders/dashboard-filters.d.ts +142 -0
- package/dist/runtime/dashboard/utils/builders/dashboard-filters.js +135 -0
- package/dist/runtime/dashboard/utils/charts.d.ts +4 -15
- package/dist/runtime/dashboard/utils/charts.js +26 -28
- package/dist/runtime/dashboard/utils/{params/codecs.d.ts → codecs.d.ts} +2 -2
- package/dist/runtime/dashboard/utils/{params/codecs.js → codecs.js} +1 -1
- package/dist/runtime/dashboard/utils/environment.d.ts +36 -0
- package/dist/runtime/dashboard/utils/environment.js +30 -0
- package/dist/runtime/dashboard/utils/filters.d.ts +44 -0
- package/dist/runtime/dashboard/utils/filters.js +62 -0
- package/dist/runtime/dashboard/utils/format.d.ts +25 -0
- package/dist/runtime/dashboard/utils/format.js +66 -0
- package/dist/runtime/dashboard/utils/grid.d.ts +17 -0
- package/dist/runtime/dashboard/utils/grid.js +14 -0
- package/dist/runtime/dashboard/utils/options.d.ts +3 -1
- package/dist/runtime/dashboard/utils/options.js +6 -0
- package/dist/runtime/dashboard/utils/remote.d.ts +7 -0
- package/dist/runtime/dashboard/utils/remote.js +14 -0
- package/dist/runtime/dashboard/utils/resource.d.ts +16 -7
- package/dist/runtime/dashboard/utils/resource.js +15 -9
- package/dist/runtime/dashboard/utils/schema.d.ts +8 -3
- package/dist/runtime/dashboard/utils/schema.js +9 -4
- package/dist/runtime/dashboard/utils/state.d.ts +11 -6
- package/dist/runtime/dashboard/utils/state.js +10 -5
- package/dist/runtime/dashboard/utils/time.d.ts +2 -0
- package/dist/runtime/dashboard/utils/time.js +11 -1
- package/dist/runtime/dashboard/utils/ui.d.ts +1 -1
- package/dist/runtime/dashboard/utils/ui.js +1 -1
- package/dist/runtime/form/components/actions/form-actions.vue +13 -127
- package/dist/runtime/form/components/page/form-page-actions.d.vue.ts +3 -0
- package/dist/runtime/form/components/page/form-page-actions.vue +40 -0
- package/dist/runtime/form/components/page/form-page-actions.vue.d.ts +3 -0
- package/dist/runtime/form/components/page/form-page-header.d.vue.ts +24 -0
- package/dist/runtime/form/components/page/form-page-header.vue +99 -0
- package/dist/runtime/form/components/page/form-page-header.vue.d.ts +24 -0
- package/dist/runtime/form/components/page/form-page-navigation.d.vue.ts +34 -0
- package/dist/runtime/form/components/page/form-page-navigation.vue +229 -0
- package/dist/runtime/form/components/page/form-page-navigation.vue.d.ts +34 -0
- package/dist/runtime/form/components/page/form-page-section.d.vue.ts +19 -0
- package/dist/runtime/form/components/page/form-page-section.vue +114 -0
- package/dist/runtime/form/components/page/form-page-section.vue.d.ts +19 -0
- package/dist/runtime/form/components/page/form-page-sections.d.vue.ts +21 -0
- package/dist/runtime/form/components/page/form-page-sections.vue +75 -0
- package/dist/runtime/form/components/page/form-page-sections.vue.d.ts +21 -0
- package/dist/runtime/form/components/page/form-page.d.vue.ts +80 -0
- package/dist/runtime/form/components/page/form-page.vue +91 -0
- package/dist/runtime/form/components/page/form-page.vue.d.ts +80 -0
- package/dist/runtime/form/components/renderer/form-field-shell.vue +7 -11
- package/dist/runtime/form/components/root/form.vue +26 -195
- package/dist/runtime/form/composables/use-form-action-buttons.d.ts +32 -0
- package/dist/runtime/form/composables/use-form-action-buttons.js +89 -0
- package/dist/runtime/form/composables/use-form-actions.d.ts +8 -5
- package/dist/runtime/form/composables/use-form-actions.js +1 -1
- package/dist/runtime/form/composables/use-form-page-scroll.d.ts +22 -0
- package/dist/runtime/form/composables/use-form-page-scroll.js +178 -0
- package/dist/runtime/form/composables/use-form-page.d.ts +114 -0
- package/dist/runtime/form/composables/use-form-page.js +83 -0
- package/dist/runtime/form/composables/use-form-root.d.ts +34 -0
- package/dist/runtime/form/composables/use-form-root.js +171 -0
- package/dist/runtime/form/composables/use-form-runtime.js +13 -4
- package/dist/runtime/form/composables/use-form.js +2 -22
- package/dist/runtime/form/fields/card-selection.d.ts +1 -0
- package/dist/runtime/form/fields/card-selection.js +1 -0
- package/dist/runtime/form/fields/checkbox-card/component.vue +28 -17
- package/dist/runtime/form/fields/checkbox-card/types.d.ts +2 -5
- package/dist/runtime/form/fields/checkbox-group/component.vue +5 -0
- package/dist/runtime/form/fields/choice-card/choice-card-label.d.vue.ts +17 -0
- package/dist/runtime/form/fields/choice-card/choice-card-label.vue +50 -0
- package/dist/runtime/form/fields/choice-card/choice-card-label.vue.d.ts +17 -0
- package/dist/runtime/form/fields/choice-card/types.d.ts +31 -0
- package/dist/runtime/form/fields/choice-card/types.js +0 -0
- package/dist/runtime/form/fields/choice-card/use-choice-card.d.ts +22 -0
- package/dist/runtime/form/fields/choice-card/use-choice-card.js +52 -0
- package/dist/runtime/form/fields/radio/component.vue +10 -0
- package/dist/runtime/form/fields/radio-card/component.vue +17 -16
- package/dist/runtime/form/fields/radio-card/types.d.ts +2 -4
- package/dist/runtime/form/index.d.ts +1 -1
- package/dist/runtime/form/index.js +7 -1
- package/dist/runtime/form/schema/index.d.ts +1 -1
- package/dist/runtime/form/schema/index.js +1 -0
- package/dist/runtime/form/schema/page.d.ts +50 -0
- package/dist/runtime/form/schema/page.js +25 -0
- package/dist/runtime/form/types/index.d.ts +1 -0
- package/dist/runtime/form/types/options.d.ts +1 -11
- package/dist/runtime/form/types/page.d.ts +172 -0
- package/dist/runtime/form/types/page.js +0 -0
- package/dist/runtime/form/types/ui.d.ts +60 -0
- package/dist/runtime/form/utils/controls.d.ts +15 -0
- package/dist/runtime/form/utils/controls.js +38 -0
- package/dist/runtime/form/utils/focus.js +1 -1
- package/dist/runtime/form/utils/layout.js +3 -1
- package/dist/runtime/form/utils/page.d.ts +20 -0
- package/dist/runtime/form/utils/page.js +107 -0
- package/dist/runtime/form/utils/state.d.ts +2 -0
- package/dist/runtime/form/utils/state.js +1 -1
- package/dist/runtime/form/utils/ui.js +1 -0
- package/dist/runtime/i18n/locales/en.js +47 -0
- package/dist/runtime/i18n/locales/fr.js +47 -0
- package/dist/runtime/i18n/types.d.ts +70 -0
- package/dist/runtime/shared/styles/tokens.css +1 -1
- package/dist/runtime/shared/types/query.d.ts +8 -0
- package/dist/runtime/table/types/index.d.ts +1 -0
- package/dist/runtime/table/types/remote-options.d.ts +64 -0
- package/dist/runtime/table/types/remote-options.js +0 -0
- package/dist/runtime/table/types/source.d.ts +10 -1
- package/dist/runtime/table/utils/builders/table-source.d.ts +2 -6
- package/dist/runtime/table/utils/index.d.ts +1 -0
- package/dist/runtime/table/utils/index.js +1 -0
- package/dist/runtime/table/utils/remote-table-options.d.ts +26 -0
- package/dist/runtime/table/utils/remote-table-options.js +88 -0
- package/package.json +4 -1
- package/dist/runtime/dashboard/composables/use-dashboard-param-scope.d.ts +0 -30
- package/dist/runtime/dashboard/composables/use-dashboard-param-scope.js +0 -71
- package/dist/runtime/dashboard/types/options.d.ts +0 -46
- package/dist/runtime/dashboard/types/params.d.ts +0 -115
- package/dist/runtime/dashboard/utils/builders/dashboard-params.d.ts +0 -85
- package/dist/runtime/dashboard/utils/builders/dashboard-params.js +0 -87
- /package/dist/runtime/dashboard/types/{options.js → controls.js} +0 -0
- /package/dist/runtime/dashboard/types/{params.js → filters.js} +0 -0
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import type { FormStatefulFieldBase } from '../../types/field-base.js';
|
|
2
2
|
import type { FieldOptionValue, NullableValue } from '../../types/field-output-utils.js';
|
|
3
3
|
import type { FormOptionItem, FormOptionValue, FormOptionsSource } from '../../types/options.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
export interface FormRadioCardField<TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>, TValue extends FormOptionValue = FormOptionValue, TOption extends FormOptionItem<TValue> = FormOptionItem<TValue>> extends FormStatefulFieldBase<'radio-card', TValue | null, TContext, TDeps, FormRadioCardProps> {
|
|
4
|
+
import type { FormChoiceCardProps } from '../choice-card/types.js';
|
|
5
|
+
export interface FormRadioCardField<TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>, TValue extends FormOptionValue = FormOptionValue, TOption extends FormOptionItem<TValue> = FormOptionItem<TValue>> extends FormStatefulFieldBase<'radio-card', TValue | null, TContext, TDeps, FormChoiceCardProps> {
|
|
8
6
|
options: FormOptionsSource<TOption, TContext, TDeps, TValue | null>;
|
|
9
7
|
}
|
|
10
8
|
export type RadioCardFieldOutput<TField> = FieldOptionValue<TField> | NullableValue;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { defineFormField, defineFormFields, defineFormSchema } from './schema/index.js';
|
|
1
|
+
export { defineFormField, defineFormFields, defineFormPageSchema, defineFormPageSection, defineFormSchema, } from './schema/index.js';
|
|
2
2
|
export { useForm } from './composables/use-form.js';
|
|
3
3
|
export { useFormUi } from './composables/use-form-ui.js';
|
|
4
4
|
export { provideFormApi, useFormApi } from './composables/use-form-api.js';
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {
|
|
2
|
+
defineFormField,
|
|
3
|
+
defineFormFields,
|
|
4
|
+
defineFormPageSchema,
|
|
5
|
+
defineFormPageSection,
|
|
6
|
+
defineFormSchema
|
|
7
|
+
} from "./schema/index.js";
|
|
2
8
|
export { useForm } from "./composables/use-form.js";
|
|
3
9
|
export { useFormUi } from "./composables/use-form-ui.js";
|
|
4
10
|
export { provideFormApi, useFormApi } from "./composables/use-form-api.js";
|
|
@@ -81,4 +81,4 @@ export declare function defineFormField<const TField extends FormField>(field: T
|
|
|
81
81
|
* Defines a reusable field group while preserving literal inference.
|
|
82
82
|
*/
|
|
83
83
|
export declare function defineFormFields<const TFields extends readonly FormField[]>(fields: TFields): TFields;
|
|
84
|
-
export {};
|
|
84
|
+
export { defineFormPageSchema, defineFormPageSection } from './page.js';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { FormContextData, FormContextDefinition, FormPageSchema, FormPageSchemaInput, FormPageSection } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Defines one section of a form page while preserving literal field inference, so each section
|
|
4
|
+
* can live in its own file as a plain typed function.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* export function billingSection({ hasActiveContract }: { hasActiveContract: boolean }) {
|
|
9
|
+
* return defineFormPageSection({
|
|
10
|
+
* key: 'billing',
|
|
11
|
+
* label: () => t('account.sections.billing'),
|
|
12
|
+
* fields: [
|
|
13
|
+
* {
|
|
14
|
+
* key: 'vtestId',
|
|
15
|
+
* type: 'text',
|
|
16
|
+
* label: 'VTEST ID',
|
|
17
|
+
* // `accountType` belongs to another section: a page is one form, one state.
|
|
18
|
+
* dependencies: ['accountType'],
|
|
19
|
+
* required: ({ deps }) => hasActiveContract || 'accountType' in deps,
|
|
20
|
+
* },
|
|
21
|
+
* ],
|
|
22
|
+
* })
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function defineFormPageSection<const TSection extends FormPageSection>(section: TSection): TSection;
|
|
27
|
+
/**
|
|
28
|
+
* Defines a form page: one form whose fields are grouped in `sections`. Each section becomes a
|
|
29
|
+
* card of the page and an entry of its navigation; render it with `<UiFormPage :form />`.
|
|
30
|
+
*
|
|
31
|
+
* The result is a normal form schema. Its `fields` are the cards generated from the sections,
|
|
32
|
+
* so `useForm`, validation, cross-section dependencies, and the typed `formData` work as for any
|
|
33
|
+
* schema, and the same schema opens in a modal or drawer as a stack of cards.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* export function accountFormSchema({ account }: { account?: Account } = {}) {
|
|
38
|
+
* return defineFormPageSchema({
|
|
39
|
+
* header: { title: () => t(account ? 'account.edit' : 'account.create') },
|
|
40
|
+
* controls: { dirtyCheck: Boolean(account) },
|
|
41
|
+
* navigation: { title: () => t('account.sections.title') },
|
|
42
|
+
* sections: [accountTypeSection(), identitySection(), billingSection({ hasActiveContract: false })],
|
|
43
|
+
* })
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare function defineFormPageSchema<const TContext extends FormContextDefinition, const TSections extends readonly FormPageSection<FormContextData<NoInfer<TContext>>>[]>(schema: FormPageSchemaInput<TContext, TSections> & {
|
|
48
|
+
context: TContext;
|
|
49
|
+
}): FormPageSchema<FormPageSchemaInput<TContext, TSections>>;
|
|
50
|
+
export declare function defineFormPageSchema<const TSections extends readonly FormPageSection<FormContextData<undefined>>[]>(schema: FormPageSchemaInput<undefined, TSections>): FormPageSchema<FormPageSchemaInput<undefined, TSections>>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { isRecord } from "../utils/path.js";
|
|
2
|
+
export function defineFormPageSection(section) {
|
|
3
|
+
return section;
|
|
4
|
+
}
|
|
5
|
+
export function defineFormPageSchema(schema) {
|
|
6
|
+
if (!isRecord(schema) || !Array.isArray(schema.sections)) {
|
|
7
|
+
return schema;
|
|
8
|
+
}
|
|
9
|
+
return { ...schema, fields: schema.sections.filter(isRecord).map(toSectionCard) };
|
|
10
|
+
}
|
|
11
|
+
const CARD_PROPERTIES = ["condition", "dependencies", "description"];
|
|
12
|
+
function toSectionCard(section) {
|
|
13
|
+
const card = {
|
|
14
|
+
fields: section.fields,
|
|
15
|
+
key: section.key,
|
|
16
|
+
label: section.label,
|
|
17
|
+
type: "card"
|
|
18
|
+
};
|
|
19
|
+
for (const property of CARD_PROPERTIES) {
|
|
20
|
+
if (section[property] !== void 0) {
|
|
21
|
+
card[property] = section[property];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return card;
|
|
25
|
+
}
|
|
@@ -15,6 +15,7 @@ export type * from './layout';
|
|
|
15
15
|
export type * from './options';
|
|
16
16
|
export type * from './options-runtime';
|
|
17
17
|
export type * from './overlay';
|
|
18
|
+
export type * from './page';
|
|
18
19
|
export type * from './output';
|
|
19
20
|
export type * from './runtime';
|
|
20
21
|
export type * from './schema';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RemoteOptionsPageRequest, RemoteOptionsPagination, RemoteOptionsResult, RemoteOptionsSearch } from '../../shared/types/remote-options.js';
|
|
2
2
|
import type { FormValue } from './/index.js';
|
|
3
3
|
import type { FormFieldCallback, FormFieldCallbackParams } from './callbacks.js';
|
|
4
4
|
import type { FormQueryOptions } from './context.js';
|
|
@@ -68,12 +68,6 @@ export interface FormOptionConfig<TOption, TContext = NonNullable<unknown>, TDep
|
|
|
68
68
|
/** Disable the field while its option source has no usable data yet. */
|
|
69
69
|
disableOnLoading?: boolean;
|
|
70
70
|
}
|
|
71
|
-
/** @deprecated Use `RemoteOptionsPage` from `#ui-tools/shared`. Kept for public type compatibility. */
|
|
72
|
-
export type FormRemoteOptionsPage<TOption> = RemoteOptionsPage<TOption>;
|
|
73
|
-
/** @deprecated Use `RemoteCursorOptionsPage` from `#ui-tools/shared`. Kept for public type compatibility. */
|
|
74
|
-
export type FormRemoteCursorOptionsPage<TOption> = RemoteCursorOptionsPage<TOption>;
|
|
75
|
-
/** @deprecated Use `RemoteOptionsResult` from `#ui-tools/shared`. Kept for public type compatibility. */
|
|
76
|
-
export type FormRemoteOptionsResult<TOption> = RemoteOptionsResult<TOption>;
|
|
77
71
|
export interface FormRemoteOptionsRequest<TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>, TValue = FormValue, TOption = FormValue> extends FormFieldCallbackParams<TContext, TDeps, TValue, TOption> {
|
|
78
72
|
/** Current debounced search term. Empty when the menu lists unfiltered options. */
|
|
79
73
|
search: string;
|
|
@@ -87,10 +81,6 @@ export interface FormRemoteSelectedRequest<TContext = NonNullable<unknown>, TDep
|
|
|
87
81
|
values: readonly FormOptionValue[];
|
|
88
82
|
}
|
|
89
83
|
export type FormRemoteSource<TResult> = FormQueryOptions<TResult> | Promise<TResult>;
|
|
90
|
-
/** @deprecated Use `RemoteOptionsPagination` from `#ui-tools/shared`. Kept for public type compatibility. */
|
|
91
|
-
export type FormRemotePagination = RemoteOptionsPagination;
|
|
92
|
-
/** @deprecated Use `RemoteOptionsSearch` from `#ui-tools/shared`. Kept for public type compatibility. */
|
|
93
|
-
export type FormRemoteSearch = RemoteOptionsSearch;
|
|
94
84
|
/**
|
|
95
85
|
* Remote option configuration: server-side search, pagination, and selected-value hydration.
|
|
96
86
|
*/
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import type { FormValue } from './/index.js';
|
|
2
|
+
import type { FormAction } from './actions.js';
|
|
3
|
+
import type { FormApi, FormSubmitHandler } from './api.js';
|
|
4
|
+
import type { FormFieldCallback } from './callbacks.js';
|
|
5
|
+
import type { FormContextData, FormContextDefinition } from './context.js';
|
|
6
|
+
import type { FormField } from './field.js';
|
|
7
|
+
import type { FormLayoutConfig } from './layout.js';
|
|
8
|
+
import type { FormControlsConfig, FormDrawerConfig, FormFullscreenConfig, FormHeaderConfig, FormModalConfig } from './schema.js';
|
|
9
|
+
import type { FormUiConfig } from './ui.js';
|
|
10
|
+
import type { FormText } from './utils.js';
|
|
11
|
+
/**
|
|
12
|
+
* One section of a form page: a card in the page and an entry in its navigation.
|
|
13
|
+
*
|
|
14
|
+
* Every text accepts a function, so it can be translated:
|
|
15
|
+
* `label: () => t('account.sections.identity')`.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* export function identitySection() {
|
|
20
|
+
* return defineFormPageSection({
|
|
21
|
+
* key: 'identity',
|
|
22
|
+
* label: 'Identity',
|
|
23
|
+
* description: 'display name, legal entity and official identifiers',
|
|
24
|
+
* fields: [
|
|
25
|
+
* { key: 'name', type: 'text', label: 'Name', required: true },
|
|
26
|
+
* { key: 'legalEntity', type: 'text', label: 'Legal entity', required: true },
|
|
27
|
+
* ],
|
|
28
|
+
* })
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export interface FormPageSection<TContext = NonNullable<unknown>, TFields extends readonly FormField<TContext>[] = readonly FormField<TContext>[]> {
|
|
33
|
+
/**
|
|
34
|
+
* Names the section: its navigation entry, its scroll target, and the URL hash that opens the
|
|
35
|
+
* page on it (`#identity`). Also used as the section element's `id`, so keep it unique on the
|
|
36
|
+
* page. It does not prefix the keys of the section's fields.
|
|
37
|
+
*/
|
|
38
|
+
key: string;
|
|
39
|
+
/** Title on the section card and in the navigation. */
|
|
40
|
+
label: FormText;
|
|
41
|
+
/** Supporting copy next to the title. */
|
|
42
|
+
description?: FormText;
|
|
43
|
+
/**
|
|
44
|
+
* Shows the section as optional and leaves it out of the sections left to complete. A section
|
|
45
|
+
* without a required field is optional already; set this when its required fields are filled
|
|
46
|
+
* by their defaults and the user has nothing to do there.
|
|
47
|
+
*/
|
|
48
|
+
optional?: boolean;
|
|
49
|
+
/** Grid of the section's fields, merged over the schema `layout`. */
|
|
50
|
+
layout?: FormLayoutConfig;
|
|
51
|
+
/** Raw dependency paths read before evaluating `condition`. */
|
|
52
|
+
dependencies?: readonly (string | readonly [string, string])[];
|
|
53
|
+
/** Hides the section, its navigation entry, and its fields while it returns `false`. */
|
|
54
|
+
condition?: FormFieldCallback<boolean, TContext>;
|
|
55
|
+
/** Fields of the section. They write at the form root, as if the section were not there. */
|
|
56
|
+
fields: TFields;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Side navigation of a form page. Its entries are the page sections.
|
|
60
|
+
*/
|
|
61
|
+
export interface FormPageNavigationConfig {
|
|
62
|
+
/** Heading above the entries, e.g. `'Création'`. Accepts a function to translate it. */
|
|
63
|
+
title?: FormText;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Schema accepted by `defineFormPageSchema`: a form schema whose `sections` replace `fields`.
|
|
67
|
+
*/
|
|
68
|
+
export interface FormPageSchemaInput<TContext extends FormContextDefinition | undefined, TSections extends readonly FormPageSection<FormContextData<TContext>>[]> {
|
|
69
|
+
/** Stable key used by persistence, diagnostics, and test selectors. */
|
|
70
|
+
formKey?: string;
|
|
71
|
+
/** Page heading: `title`, `description`, and the `eyebrow` above the title. */
|
|
72
|
+
header?: FormHeaderConfig;
|
|
73
|
+
/** Form-scoped data sources exposed to fields as `ctx`. */
|
|
74
|
+
context?: TContext;
|
|
75
|
+
/** Default grid of every section. */
|
|
76
|
+
layout?: FormLayoutConfig;
|
|
77
|
+
/** Form-scoped presentation overrides, merged after app defaults. */
|
|
78
|
+
ui?: FormUiConfig;
|
|
79
|
+
/** Runtime lifecycle and validation controls. `dirtyCheck` rings the modified sections. */
|
|
80
|
+
controls?: FormControlsConfig;
|
|
81
|
+
/** Modal-shell sizing, used when the same schema opens in a modal. */
|
|
82
|
+
modal?: FormModalConfig;
|
|
83
|
+
/** Drawer-shell sizing, used when the same schema opens in a drawer. */
|
|
84
|
+
drawer?: FormDrawerConfig;
|
|
85
|
+
/** Fullscreen-shell behavior, used when the same schema opens fullscreen. */
|
|
86
|
+
fullscreen?: FormFullscreenConfig;
|
|
87
|
+
/** Page actions. Omit to render the built-in submit action. */
|
|
88
|
+
actions?: readonly FormAction[];
|
|
89
|
+
/** Runs after validation and before the external submit handler. Return `false` to cancel submit. */
|
|
90
|
+
onBeforeSubmit?: FormSubmitHandler<FormValue, never>;
|
|
91
|
+
/** Submit lifecycle hook. */
|
|
92
|
+
submit?: (params: {
|
|
93
|
+
value: FormValue;
|
|
94
|
+
api: FormApi;
|
|
95
|
+
ctx: FormContextData<TContext>;
|
|
96
|
+
}) => Promise<void> | void;
|
|
97
|
+
/** Side navigation of the page. */
|
|
98
|
+
navigation?: FormPageNavigationConfig;
|
|
99
|
+
/** Sections of the page, in navigation order. */
|
|
100
|
+
sections: TSections;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Card field generated from a section. It renders the section when the schema opens in an
|
|
104
|
+
* overlay, and its fields keep their root paths because a card adds no path segment.
|
|
105
|
+
*/
|
|
106
|
+
export type FormPageSectionCard<TSection> = TSection extends {
|
|
107
|
+
readonly key: infer TKey extends string;
|
|
108
|
+
readonly fields: infer TFields;
|
|
109
|
+
} ? {
|
|
110
|
+
readonly type: 'card';
|
|
111
|
+
readonly key: TKey;
|
|
112
|
+
readonly fields: TFields;
|
|
113
|
+
} : never;
|
|
114
|
+
/** Card fields generated from the sections of a form page, in order. */
|
|
115
|
+
export type FormPageSectionCards<TSections extends readonly FormValue[]> = {
|
|
116
|
+
readonly [TIndex in keyof TSections]: FormPageSectionCard<TSections[TIndex]>;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Schema returned by `defineFormPageSchema`: a normal form schema whose `fields` are the cards
|
|
120
|
+
* generated from `sections`, plus the `sections` and `navigation` that `FormPage` reads.
|
|
121
|
+
*/
|
|
122
|
+
export type FormPageSchema<TInput extends {
|
|
123
|
+
readonly sections: readonly FormValue[];
|
|
124
|
+
}> = TInput & {
|
|
125
|
+
readonly fields: FormPageSectionCards<TInput['sections']>;
|
|
126
|
+
};
|
|
127
|
+
/** A section paired with the card generated for it, as the page runtime reads them. */
|
|
128
|
+
export interface FormPageSectionEntry {
|
|
129
|
+
section: FormPageSection;
|
|
130
|
+
card: FormField;
|
|
131
|
+
}
|
|
132
|
+
/** How a form page scrolls to a section. */
|
|
133
|
+
export interface FormPageScrollOptions {
|
|
134
|
+
/** Defaults to `smooth`, or `instant` when the user prefers reduced motion. */
|
|
135
|
+
behavior?: ScrollBehavior;
|
|
136
|
+
/** Records the section in the URL hash. Defaults to the page `hash` option. */
|
|
137
|
+
hash?: boolean;
|
|
138
|
+
/** Moves focus to the section title, for keyboard and screen reader users. */
|
|
139
|
+
focus?: boolean;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Where a section stands:
|
|
143
|
+
*
|
|
144
|
+
* - `invalid`: it shows at least one validation error
|
|
145
|
+
* - `complete`: nothing required is missing and it shows no error; an optional section also
|
|
146
|
+
* needs a value, entered by the user or provided by the form input
|
|
147
|
+
* - `pending`: anything else
|
|
148
|
+
*/
|
|
149
|
+
export type FormPageSectionStatus = 'invalid' | 'complete' | 'pending';
|
|
150
|
+
/**
|
|
151
|
+
* Live state of a visible section, as the page navigation and section slots receive it.
|
|
152
|
+
*/
|
|
153
|
+
export interface FormPageSectionState {
|
|
154
|
+
/** Section key. */
|
|
155
|
+
key: string;
|
|
156
|
+
/** Position among the visible sections. */
|
|
157
|
+
index: number;
|
|
158
|
+
/** Resolved title. */
|
|
159
|
+
label: string;
|
|
160
|
+
/** Resolved supporting copy. */
|
|
161
|
+
description?: string;
|
|
162
|
+
/** True when the section is declared optional or has no required field. */
|
|
163
|
+
optional: boolean;
|
|
164
|
+
/** Number of required fields without a value. */
|
|
165
|
+
missing: number;
|
|
166
|
+
/** Summary of the section's requirements, see `FormPageSectionStatus`. */
|
|
167
|
+
status: FormPageSectionStatus;
|
|
168
|
+
/** True when a value of the section differs from the form's baseline. */
|
|
169
|
+
dirty: boolean;
|
|
170
|
+
/** Dirty paths inside the section. */
|
|
171
|
+
dirtyPaths: readonly string[];
|
|
172
|
+
}
|
|
File without changes
|
|
@@ -183,6 +183,64 @@ export interface FormOverlayUi {
|
|
|
183
183
|
overlay?: FormUiClass;
|
|
184
184
|
content?: FormUiClass;
|
|
185
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* Slots of a form page and its parts. Data attributes carry the live state, so a slot can
|
|
188
|
+
* restyle one state: `data-active` on the current navigation entry, `data-state`
|
|
189
|
+
* (`complete`, `invalid`, `pending`) on entries and indicators, `data-dirty` on modified
|
|
190
|
+
* sections and entries.
|
|
191
|
+
*
|
|
192
|
+
* Two CSS variables place sections under the pinned header: `--nut-form-page-header` (its
|
|
193
|
+
* measured height, set by the page) and `--nut-form-page-gap` (the room below it, `24px` by
|
|
194
|
+
* default). Set them on an element around the navigation and sections, such as the root.
|
|
195
|
+
*/
|
|
196
|
+
export interface FormPageUi {
|
|
197
|
+
/** The `<form>` element, which scrolls on its own and is the container of the page queries. */
|
|
198
|
+
root?: FormUiClass;
|
|
199
|
+
/** Grid holding the navigation and the sections. */
|
|
200
|
+
body?: FormUiClass;
|
|
201
|
+
header?: FormUiClass;
|
|
202
|
+
headerContent?: FormUiClass;
|
|
203
|
+
heading?: FormUiClass;
|
|
204
|
+
eyebrow?: FormUiClass;
|
|
205
|
+
title?: FormUiClass;
|
|
206
|
+
/** Line under the title: the description and the unsaved-changes badge. */
|
|
207
|
+
meta?: FormUiClass;
|
|
208
|
+
unsaved?: FormUiClass;
|
|
209
|
+
actions?: FormUiClass;
|
|
210
|
+
navigation?: FormUiClass;
|
|
211
|
+
/** Holds the navigation title and the entries. */
|
|
212
|
+
navigationGroup?: FormUiClass;
|
|
213
|
+
navigationTitle?: FormUiClass;
|
|
214
|
+
navigationList?: FormUiClass;
|
|
215
|
+
/** List item around each entry. */
|
|
216
|
+
navigationEntry?: FormUiClass;
|
|
217
|
+
navigationItem?: FormUiClass;
|
|
218
|
+
navigationIndicator?: FormUiClass;
|
|
219
|
+
/** Check of a complete section in the indicator. */
|
|
220
|
+
navigationIndicatorIcon?: FormUiClass;
|
|
221
|
+
/** Dot in the indicator of the current section while it is pending. */
|
|
222
|
+
navigationIndicatorMarker?: FormUiClass;
|
|
223
|
+
navigationLabel?: FormUiClass;
|
|
224
|
+
navigationOptional?: FormUiClass;
|
|
225
|
+
navigationDirty?: FormUiClass;
|
|
226
|
+
/** Wrapper under the entries, whose padding insets the summary. */
|
|
227
|
+
navigationFooter?: FormUiClass;
|
|
228
|
+
/** The summary, with its top border: "3 sections left to complete". */
|
|
229
|
+
navigationSummary?: FormUiClass;
|
|
230
|
+
/** The bold "3 sections" of the summary. */
|
|
231
|
+
navigationSummaryCount?: FormUiClass;
|
|
232
|
+
sections?: FormUiClass;
|
|
233
|
+
/** Placeholder card shown while the schema context loads. */
|
|
234
|
+
sectionSkeleton?: FormUiClass;
|
|
235
|
+
section?: FormUiClass;
|
|
236
|
+
sectionHeader?: FormUiClass;
|
|
237
|
+
sectionTitle?: FormUiClass;
|
|
238
|
+
sectionDescription?: FormUiClass;
|
|
239
|
+
sectionOptional?: FormUiClass;
|
|
240
|
+
sectionActions?: FormUiClass;
|
|
241
|
+
sectionReset?: FormUiClass;
|
|
242
|
+
sectionBody?: FormUiClass;
|
|
243
|
+
}
|
|
186
244
|
export interface FormUiPartConfig<TUi> {
|
|
187
245
|
ui?: TUi;
|
|
188
246
|
}
|
|
@@ -219,4 +277,6 @@ export interface FormUiConfig {
|
|
|
219
277
|
modal?: FormUiPartConfig<FormOverlayUi>;
|
|
220
278
|
drawer?: FormUiPartConfig<FormOverlayUi>;
|
|
221
279
|
fullscreen?: FormUiPartConfig<FormOverlayUi>;
|
|
280
|
+
/** Form page rendered by `FormPage` and its parts. */
|
|
281
|
+
page?: FormUiPartConfig<FormPageUi>;
|
|
222
282
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { FormUiConfig, FormValidationMode, FormValue } from '../types/index.js';
|
|
2
|
+
/** Reads the schema `controls` object. */
|
|
3
|
+
export declare function getSchemaControls(schema: FormValue): any;
|
|
4
|
+
/** Resolves `controls.syncInput`: `true`, a path list, or no synchronization. */
|
|
5
|
+
export declare function getSchemaSyncInput(schema: FormValue): boolean | readonly string[];
|
|
6
|
+
/** Resolves `controls.validate`, validating everything by default. */
|
|
7
|
+
export declare function getSchemaValidationMode(schema: FormValue): FormValidationMode;
|
|
8
|
+
/** Resolves `controls.autoFocus`: a raw field path, `true` for the first field, or `false`. */
|
|
9
|
+
export declare function getSchemaAutoFocus(schema: FormValue): string | boolean;
|
|
10
|
+
/** True when `controls.dirtyCheck` enables dirty metadata and reset affordances. */
|
|
11
|
+
export declare function getSchemaDirtyCheck(schema: FormValue): boolean;
|
|
12
|
+
/** Reads `controls.confirmNavOnDirty` as authored. */
|
|
13
|
+
export declare function getSchemaDirtyNavigation(schema: FormValue): unknown;
|
|
14
|
+
/** Reads the schema-level `ui` overrides. */
|
|
15
|
+
export declare function getSchemaUi(schema: FormValue): FormUiConfig | undefined;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { isRecord } from "./path.js";
|
|
2
|
+
import { isBoolean, isString, stringArray } from "./predicate.js";
|
|
3
|
+
export function getSchemaControls(schema) {
|
|
4
|
+
if (!isRecord(schema)) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
const controls = Object.getOwnPropertyDescriptor(schema, "controls")?.value;
|
|
8
|
+
return isRecord(controls) ? controls : void 0;
|
|
9
|
+
}
|
|
10
|
+
function getSchemaControl(schema, name) {
|
|
11
|
+
const controls = getSchemaControls(schema);
|
|
12
|
+
return controls ? Object.getOwnPropertyDescriptor(controls, name)?.value : void 0;
|
|
13
|
+
}
|
|
14
|
+
export function getSchemaSyncInput(schema) {
|
|
15
|
+
const value = getSchemaControl(schema, "syncInput");
|
|
16
|
+
return isBoolean(value) ? value : stringArray(value);
|
|
17
|
+
}
|
|
18
|
+
export function getSchemaValidationMode(schema) {
|
|
19
|
+
const value = getSchemaControl(schema, "validate");
|
|
20
|
+
return value === false || value === "required" || value === "validators" ? value : true;
|
|
21
|
+
}
|
|
22
|
+
export function getSchemaAutoFocus(schema) {
|
|
23
|
+
const value = getSchemaControl(schema, "autoFocus");
|
|
24
|
+
return isString(value) || isBoolean(value) ? value : false;
|
|
25
|
+
}
|
|
26
|
+
export function getSchemaDirtyCheck(schema) {
|
|
27
|
+
return getSchemaControl(schema, "dirtyCheck") === true;
|
|
28
|
+
}
|
|
29
|
+
export function getSchemaDirtyNavigation(schema) {
|
|
30
|
+
return getSchemaControl(schema, "confirmNavOnDirty");
|
|
31
|
+
}
|
|
32
|
+
export function getSchemaUi(schema) {
|
|
33
|
+
if (!isRecord(schema)) {
|
|
34
|
+
return void 0;
|
|
35
|
+
}
|
|
36
|
+
const value = Object.getOwnPropertyDescriptor(schema, "ui")?.value;
|
|
37
|
+
return isRecord(value) ? value : void 0;
|
|
38
|
+
}
|
|
@@ -24,7 +24,7 @@ export async function focusFormFieldElement(fieldElement) {
|
|
|
24
24
|
focusableElement.focus({ preventScroll: true });
|
|
25
25
|
fieldElement.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
|
|
26
26
|
await waitForFocusSettle();
|
|
27
|
-
return
|
|
27
|
+
return focusableElement.ownerDocument.activeElement === focusableElement;
|
|
28
28
|
}
|
|
29
29
|
export async function focusFirstInvalidFormField(errors) {
|
|
30
30
|
for (const error of errors) {
|
|
@@ -8,7 +8,9 @@ export function resolveFormLayoutConfig(base, override) {
|
|
|
8
8
|
return {
|
|
9
9
|
columns: override?.columns ?? base?.columns ?? FORM_LAYOUT_DEFAULTS.columns,
|
|
10
10
|
fieldSpan: override?.fieldSpan ?? base?.fieldSpan ?? FORM_LAYOUT_DEFAULTS.fieldSpan,
|
|
11
|
-
gap: override?.gap ?? base?.gap ?? FORM_LAYOUT_DEFAULTS.gap
|
|
11
|
+
gap: override?.gap ?? base?.gap ?? FORM_LAYOUT_DEFAULTS.gap,
|
|
12
|
+
labelPosition: override?.labelPosition ?? base?.labelPosition,
|
|
13
|
+
labelWidth: override?.labelWidth ?? base?.labelWidth
|
|
12
14
|
};
|
|
13
15
|
}
|
|
14
16
|
export function normalizeFormLayoutGap(value) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { FormObject, FormPageSectionEntry, FormPageSectionState, FormRuntime, FormValue } from '../types/index.js';
|
|
2
|
+
/** Pairs each section of a page schema with the card `defineFormPageSchema` generated for it. */
|
|
3
|
+
export declare function getFormPageSections(schema: FormValue): readonly FormPageSectionEntry[];
|
|
4
|
+
/** Resolved heading of the page navigation. */
|
|
5
|
+
export declare function getFormPageNavigationTitle(schema: FormValue): string | undefined;
|
|
6
|
+
/** True while the section's `condition` lets it render. */
|
|
7
|
+
export declare function isFormPageSectionVisible(entry: FormPageSectionEntry, runtime: FormRuntime): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Live state of one visible section: what it still misses, whether it shows errors, and what
|
|
10
|
+
* changed since the baseline.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveFormPageSectionState(params: {
|
|
13
|
+
entry: FormPageSectionEntry;
|
|
14
|
+
index: number;
|
|
15
|
+
runtime: FormRuntime;
|
|
16
|
+
/** Input the form was opened with: a value it provides counts as filled in. */
|
|
17
|
+
input: FormObject | undefined;
|
|
18
|
+
/** False when the validation mode does not enforce required fields. */
|
|
19
|
+
requiredEnforced: boolean;
|
|
20
|
+
}): FormPageSectionState;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { createFormFieldInstance } from "./field-instance.js";
|
|
2
|
+
import { getPathValue, isRecord } from "./path.js";
|
|
3
|
+
import { isBoolean, isFunction, isNumber, isString } from "./predicate.js";
|
|
4
|
+
import {
|
|
5
|
+
fieldPath,
|
|
6
|
+
getChildFields,
|
|
7
|
+
getSchemaFields,
|
|
8
|
+
isEmptyValue,
|
|
9
|
+
isFlatPassthroughField,
|
|
10
|
+
isObjectContainerField,
|
|
11
|
+
resolveRequired
|
|
12
|
+
} from "./state.js";
|
|
13
|
+
import { resolveFormText } from "./text.js";
|
|
14
|
+
export function getFormPageSections(schema) {
|
|
15
|
+
if (!isRecord(schema) || !Array.isArray(schema.sections)) {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
const cards = getSchemaFields(schema);
|
|
19
|
+
return schema.sections.filter(isFormPageSection).flatMap((section) => {
|
|
20
|
+
const card = cards.find((field) => field.key === section.key);
|
|
21
|
+
return card ? [{ card, section }] : [];
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export function getFormPageNavigationTitle(schema) {
|
|
25
|
+
if (!isRecord(schema) || !isRecord(schema.navigation)) {
|
|
26
|
+
return void 0;
|
|
27
|
+
}
|
|
28
|
+
const { title } = schema.navigation;
|
|
29
|
+
return isFormText(title) ? resolveFormText(title) : void 0;
|
|
30
|
+
}
|
|
31
|
+
export function isFormPageSectionVisible(entry, runtime) {
|
|
32
|
+
return !hasCondition(entry.card) || runtime.shouldRender(entry.card, [entry.card.key]);
|
|
33
|
+
}
|
|
34
|
+
export function resolveFormPageSectionState(params) {
|
|
35
|
+
const { entry, runtime } = params;
|
|
36
|
+
const leaves = collectVisibleLeaves(entry.section.fields, [], runtime);
|
|
37
|
+
const paths = leaves.map((leaf) => leaf.path.join("."));
|
|
38
|
+
const contains = (candidate) => paths.some((path) => isPathWithin(candidate, path));
|
|
39
|
+
const dirtyPaths = runtime.dirtyPaths.value.filter(contains);
|
|
40
|
+
const invalid = runtime.errors.value.some((error) => contains(error.path));
|
|
41
|
+
const required = params.requiredEnforced ? leaves.filter((leaf) => isRequiredLeaf(leaf, runtime)) : [];
|
|
42
|
+
const missing = required.filter((leaf) => isEmptyValue(runtime.getValue(leaf.path))).length;
|
|
43
|
+
const optional = entry.section.optional === true || required.length === 0;
|
|
44
|
+
const filled = !optional || leaves.some((leaf) => isProvided(leaf, runtime, dirtyPaths, params.input));
|
|
45
|
+
return {
|
|
46
|
+
description: resolveFormText(entry.section.description),
|
|
47
|
+
dirty: dirtyPaths.length > 0,
|
|
48
|
+
dirtyPaths,
|
|
49
|
+
index: params.index,
|
|
50
|
+
key: entry.section.key,
|
|
51
|
+
label: resolveFormText(entry.section.label) ?? entry.section.key,
|
|
52
|
+
missing,
|
|
53
|
+
optional,
|
|
54
|
+
status: invalid ? "invalid" : missing === 0 && filled ? "complete" : "pending"
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function collectVisibleLeaves(fields, parentPath, runtime) {
|
|
58
|
+
return fields.flatMap((field) => {
|
|
59
|
+
if (field.ignore === true) {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
const path = fieldPath(parentPath, field);
|
|
63
|
+
if (hasCondition(field) && !runtime.shouldRender(field, path)) {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
if (createFormFieldInstance(field).state.is("stateless")) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
if (isFlatPassthroughField(field)) {
|
|
70
|
+
return collectVisibleLeaves(getChildFields(field), parentPath, runtime);
|
|
71
|
+
}
|
|
72
|
+
if (isObjectContainerField(field)) {
|
|
73
|
+
return collectVisibleLeaves(getChildFields(field), path, runtime);
|
|
74
|
+
}
|
|
75
|
+
return [{ field, path }];
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function isRequiredLeaf(leaf, runtime) {
|
|
79
|
+
const required = Object.getOwnPropertyDescriptor(leaf.field, "required")?.value;
|
|
80
|
+
if (isBoolean(required)) {
|
|
81
|
+
return required;
|
|
82
|
+
}
|
|
83
|
+
const validation = Object.getOwnPropertyDescriptor(leaf.field, "validation")?.value;
|
|
84
|
+
if (!isFunction(required) && !isRecord(validation)) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
return resolveRequired(leaf.field, runtime.getFieldCallbackParams(leaf.path, leaf.field)) === true;
|
|
88
|
+
}
|
|
89
|
+
function isProvided(leaf, runtime, dirtyPaths, input) {
|
|
90
|
+
if (isEmptyValue(runtime.getValue(leaf.path))) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
const path = leaf.path.join(".");
|
|
94
|
+
return dirtyPaths.some((dirtyPath) => isPathWithin(dirtyPath, path)) || !isEmptyValue(getPathValue(input ?? {}, leaf.path));
|
|
95
|
+
}
|
|
96
|
+
function hasCondition(field) {
|
|
97
|
+
return isFunction(Object.getOwnPropertyDescriptor(field, "condition")?.value);
|
|
98
|
+
}
|
|
99
|
+
function isPathWithin(path, scope) {
|
|
100
|
+
return path === scope || path.startsWith(`${scope}.`);
|
|
101
|
+
}
|
|
102
|
+
function isFormPageSection(value) {
|
|
103
|
+
return isRecord(value) && isString(value.key) && isFormText(value.label) && Array.isArray(value.fields);
|
|
104
|
+
}
|
|
105
|
+
function isFormText(value) {
|
|
106
|
+
return isString(value) || isNumber(value) || isFunction(value);
|
|
107
|
+
}
|
|
@@ -30,4 +30,6 @@ export declare function getMatrixRows(field: FormField): any[];
|
|
|
30
30
|
export declare function resolveRequired(field: FormField, params: FormFieldCallbackParams): any;
|
|
31
31
|
export declare function resolveRequiredMessage(field: FormField, fallback?: string): string;
|
|
32
32
|
export declare function isEmptyValue(value: FormValue): boolean;
|
|
33
|
+
/** Child fields of a container, with the fields of every tab for tabs. */
|
|
34
|
+
export declare function getChildFields(field: FormField): FormField[];
|
|
33
35
|
export declare function getArrayItemFields(field: FormField, item: FormObject): FormField[];
|
|
@@ -588,7 +588,7 @@ function isFormField(value) {
|
|
|
588
588
|
const type = Object.getOwnPropertyDescriptor(value, "type")?.value;
|
|
589
589
|
return isString(value.key) && isString(type) && isRegisteredFormFieldType(type);
|
|
590
590
|
}
|
|
591
|
-
function getChildFields(field) {
|
|
591
|
+
export function getChildFields(field) {
|
|
592
592
|
const tabs = Object.getOwnPropertyDescriptor(field, "tabs")?.value;
|
|
593
593
|
if (Array.isArray(tabs)) {
|
|
594
594
|
return tabs.flatMap((tab) => {
|
|
@@ -39,6 +39,7 @@ function mergeFormUiPair(current, next) {
|
|
|
39
39
|
group: mergePart(current.group, next.group),
|
|
40
40
|
matrix: mergePart(current.matrix, next.matrix),
|
|
41
41
|
modal: mergePart(current.modal, next.modal),
|
|
42
|
+
page: mergePart(current.page, next.page),
|
|
42
43
|
root: mergePart(current.root, next.root),
|
|
43
44
|
tree: mergePart(current.tree, next.tree),
|
|
44
45
|
treeSelect: mergePart(current.treeSelect, next.treeSelect)
|