nuxt-ui-tools 1.17.1 → 1.18.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 +1 -0
- package/dist/runtime/form/components/page/form-page-navigation.d.vue.ts +5 -3
- package/dist/runtime/form/components/page/form-page-navigation.vue +6 -3
- package/dist/runtime/form/components/page/form-page-navigation.vue.d.ts +5 -3
- package/dist/runtime/form/components/page/form-page-section.vue +6 -5
- package/dist/runtime/form/components/page/form-page-sections.d.vue.ts +4 -3
- package/dist/runtime/form/components/page/form-page-sections.vue +7 -2
- package/dist/runtime/form/components/page/form-page-sections.vue.d.ts +4 -3
- package/dist/runtime/form/components/page/form-page.d.vue.ts +2 -0
- package/dist/runtime/form/components/page/form-page.vue +10 -2
- package/dist/runtime/form/components/page/form-page.vue.d.ts +2 -0
- package/dist/runtime/form/components/renderer/form-field-renderer.vue +6 -4
- package/dist/runtime/form/components/renderer/form-field-shell.d.vue.ts +4 -4
- package/dist/runtime/form/components/renderer/form-field-shell.vue +28 -4
- package/dist/runtime/form/components/renderer/form-field-shell.vue.d.ts +4 -4
- package/dist/runtime/form/components/root/form.vue +2 -2
- package/dist/runtime/form/composables/use-field-control.d.ts +1 -1
- package/dist/runtime/form/composables/use-field-control.js +9 -21
- package/dist/runtime/form/composables/use-field-options.js +8 -4
- package/dist/runtime/form/composables/use-form-page.d.ts +11 -1
- package/dist/runtime/form/composables/use-form-page.js +12 -6
- package/dist/runtime/form/composables/use-form-runtime.js +1 -1
- package/dist/runtime/form/fields/array-primitive/types.d.ts +1 -1
- package/dist/runtime/form/fields/checkbox-card/component.vue +54 -28
- package/dist/runtime/form/fields/checkbox-card/config.d.ts +1 -0
- package/dist/runtime/form/fields/checkbox-card/config.js +1 -1
- package/dist/runtime/form/fields/checkbox-card/types.d.ts +3 -3
- package/dist/runtime/form/fields/choice-card/choice-card-label.d.vue.ts +7 -0
- package/dist/runtime/form/fields/choice-card/choice-card-label.vue +27 -1
- package/dist/runtime/form/fields/choice-card/choice-card-label.vue.d.ts +7 -0
- package/dist/runtime/form/fields/choice-card/types.d.ts +54 -1
- package/dist/runtime/form/fields/choice-card/use-choice-card.d.ts +7 -3
- package/dist/runtime/form/fields/choice-card/use-choice-card.js +37 -15
- package/dist/runtime/form/fields/choice-card/utils.d.ts +27 -0
- package/dist/runtime/form/fields/choice-card/utils.js +17 -0
- package/dist/runtime/form/fields/hierarchy/component.vue +3 -2
- package/dist/runtime/form/fields/index.d.ts +2 -0
- package/dist/runtime/form/fields/radio-card/component.vue +58 -28
- package/dist/runtime/form/fields/radio-card/config.d.ts +1 -0
- package/dist/runtime/form/fields/radio-card/config.js +1 -1
- package/dist/runtime/form/fields/radio-card/types.d.ts +3 -3
- package/dist/runtime/form/types/field-base.d.ts +6 -0
- package/dist/runtime/form/types/field-kind.d.ts +3 -1
- package/dist/runtime/form/types/field.d.ts +1 -0
- package/dist/runtime/form/types/page.d.ts +20 -8
- package/dist/runtime/form/types/ui.d.ts +24 -4
- package/dist/runtime/form/utils/disabled.d.ts +11 -0
- package/dist/runtime/form/utils/disabled.js +11 -0
- package/dist/runtime/form/utils/field-instance.js +3 -0
- package/dist/runtime/form/utils/options.d.ts +22 -1
- package/dist/runtime/form/utils/options.js +15 -0
- package/dist/runtime/form/utils/page-surface.d.ts +21 -0
- package/dist/runtime/form/utils/page-surface.js +27 -0
- package/dist/runtime/form/utils/page.d.ts +21 -1
- package/dist/runtime/form/utils/page.js +24 -2
- package/dist/runtime/i18n/locales/en.js +28 -8
- package/dist/runtime/i18n/locales/fr.js +28 -8
- package/dist/runtime/i18n/types.d.ts +46 -7
- package/dist/runtime/table/components/actions/row-actions.d.vue.ts +2 -0
- package/dist/runtime/table/components/actions/row-actions.vue +8 -3
- package/dist/runtime/table/components/actions/row-actions.vue.d.ts +2 -0
- package/dist/runtime/table/components/data-list/data-list-content.vue +7 -1
- package/dist/runtime/table/components/data-list/data-list-default.d.vue.ts +16 -16
- package/dist/runtime/table/components/data-list/data-list-default.vue +3 -0
- package/dist/runtime/table/components/data-list/data-list-default.vue.d.ts +16 -16
- package/dist/runtime/table/components/data-list/data-list-layout-switch.vue +7 -9
- package/dist/runtime/table/components/data-list/data-list-root.vue +5 -6
- package/dist/runtime/table/components/data-list/data-list-sort-menu.vue +3 -1
- package/dist/runtime/table/components/data-list/data-list-table.d.vue.ts +8 -1
- package/dist/runtime/table/components/data-list/data-list-table.vue +18 -1
- package/dist/runtime/table/components/data-list/data-list-table.vue.d.ts +8 -1
- package/dist/runtime/table/components/data-list/data-list-view-toggle.d.vue.ts +25 -0
- package/dist/runtime/table/components/data-list/data-list-view-toggle.vue +57 -0
- package/dist/runtime/table/components/data-list/data-list-view-toggle.vue.d.ts +25 -0
- package/dist/runtime/table/components/data-list/sort-menu-panel.vue +1 -1
- package/dist/runtime/table/components/layout/table-footer.d.vue.ts +6 -4
- package/dist/runtime/table/components/layout/table-footer.vue +80 -3
- package/dist/runtime/table/components/layout/table-footer.vue.d.ts +6 -4
- package/dist/runtime/table/components/summary/summary-content.d.ts +13 -0
- package/dist/runtime/table/components/summary/summary-content.js +5 -0
- package/dist/runtime/table/components/summary/summary-renderer.d.vue.ts +31 -0
- package/dist/runtime/table/components/summary/summary-renderer.vue +282 -0
- package/dist/runtime/table/components/summary/summary-renderer.vue.d.ts +31 -0
- package/dist/runtime/table/components/summary/summary-row.d.vue.ts +19 -0
- package/dist/runtime/table/components/summary/summary-row.vue +102 -0
- package/dist/runtime/table/components/summary/summary-row.vue.d.ts +19 -0
- package/dist/runtime/table/components/table/table-empty-state.vue +39 -9
- package/dist/runtime/table/components/table/table-renderer.vue +1 -1
- package/dist/runtime/table/components/table/table-row-actions-control.vue +10 -6
- package/dist/runtime/table/components/table/table-selection-control.d.vue.ts +5 -1
- package/dist/runtime/table/components/table/table-selection-control.vue +17 -4
- package/dist/runtime/table/components/table/table-selection-control.vue.d.ts +5 -1
- package/dist/runtime/table/components/table/table-tree-control.vue +2 -1
- package/dist/runtime/table/components/table/table-tree-header.vue +0 -1
- package/dist/runtime/table/composables/use-table-api.d.ts +2 -0
- package/dist/runtime/table/composables/use-table-api.js +8 -0
- package/dist/runtime/table/composables/use-table-columns.d.ts +2 -0
- package/dist/runtime/table/composables/use-table-controls.d.ts +6 -0
- package/dist/runtime/table/composables/use-table-controls.js +6 -3
- package/dist/runtime/table/composables/use-table-internals.d.ts +33 -0
- package/dist/runtime/table/composables/use-table-internals.js +9 -0
- package/dist/runtime/table/composables/use-table-row-labels.d.ts +16 -0
- package/dist/runtime/table/composables/use-table-row-labels.js +22 -0
- package/dist/runtime/table/composables/use-table-row-summary.d.ts +18 -0
- package/dist/runtime/table/composables/use-table-row-summary.js +41 -0
- package/dist/runtime/table/constants/mobile.d.ts +79 -0
- package/dist/runtime/table/constants/mobile.js +17 -0
- package/dist/runtime/table/constants/summary.d.ts +67 -0
- package/dist/runtime/table/constants/summary.js +31 -0
- package/dist/runtime/table/types/data-list.d.ts +58 -8
- package/dist/runtime/table/types/layout.d.ts +73 -0
- package/dist/runtime/table/types/schema.d.ts +9 -1
- package/dist/runtime/table/types/table-api.d.ts +12 -0
- package/dist/runtime/table/utils/columns/defs.js +3 -1
- package/dist/runtime/table/utils/columns/render.d.ts +2 -0
- package/dist/runtime/table/utils/columns/render.js +2 -2
- package/dist/runtime/table/utils/data-list-ui.js +3 -1
- package/dist/runtime/table/utils/mobile-defaults.d.ts +7 -0
- package/dist/runtime/table/utils/mobile-defaults.js +25 -0
- package/package.json +1 -1
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { FormPageSectionState } from '../../types/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* Navigation of a form page: one entry per visible section, following the section in view.
|
|
4
|
-
* An entry shows whether its section is complete,
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* An entry shows whether its section is complete, to fix, to complete, or optional with nothing
|
|
5
|
+
* entered (a dashed circle and an "Optional" tag, never "to complete"), and, with
|
|
6
|
+
* `controls.dirtyCheck`, a dot on modified sections. The footer sums up the sections left to
|
|
7
|
+
* complete, which leaves out untouched optional ones, or the modified ones with dirty checking.
|
|
7
8
|
*
|
|
8
9
|
* From 768px of page width it is a pinned column; below, a row of chips that scrolls sideways.
|
|
10
|
+
* It renders nothing while the page has fewer visible sections than `navigation.minSections`.
|
|
9
11
|
*/
|
|
10
12
|
type __VLS_Slots = {
|
|
11
13
|
/** Replaces the heading, which comes from the schema `navigation.title`. */
|
|
@@ -73,6 +73,7 @@ function select(event, key) {
|
|
|
73
73
|
|
|
74
74
|
<template>
|
|
75
75
|
<nav
|
|
76
|
+
v-if="page.navigationVisible.value"
|
|
76
77
|
:aria-labelledby="page.navigationTitle.value || $slots.title ? titleId : void 0"
|
|
77
78
|
:aria-label="page.navigationTitle.value || $slots.title ? void 0 : t('form.page.navigation')"
|
|
78
79
|
:style="{ '--nut-form-page-header': `${page.stickyOffset.value}px` }"
|
|
@@ -138,7 +139,7 @@ function select(event, key) {
|
|
|
138
139
|
aria-hidden="true"
|
|
139
140
|
:class="
|
|
140
141
|
mergeFormUiClass(
|
|
141
|
-
'grid size-4 shrink-0 place-items-center rounded-full border-[1.5px] border-accented text-[10px] leading-none font-bold group-data-[active]:border-primary data-[state=complete]:border-(--ui-text-highlighted) data-[state=complete]:bg-(--ui-text-highlighted) data-[state=complete]:text-(--ui-bg) data-[state=invalid]:border-error data-[state=invalid]:bg-error data-[state=invalid]:text-inverted group-data-[active]:data-[state=complete]:border-primary group-data-[active]:data-[state=complete]:bg-primary group-data-[active]:data-[state=complete]:text-inverted',
|
|
142
|
+
'grid size-4 shrink-0 place-items-center rounded-full border-[1.5px] border-accented text-[10px] leading-none font-bold group-data-[active]:border-primary data-[state=optional]:border-dashed data-[state=complete]:border-(--ui-text-highlighted) data-[state=complete]:bg-(--ui-text-highlighted) data-[state=complete]:text-(--ui-bg) data-[state=invalid]:border-error data-[state=invalid]:bg-error data-[state=invalid]:text-inverted group-data-[active]:data-[state=complete]:border-primary group-data-[active]:data-[state=complete]:bg-primary group-data-[active]:data-[state=complete]:text-inverted',
|
|
142
143
|
pageUi?.navigationIndicator
|
|
143
144
|
)
|
|
144
145
|
"
|
|
@@ -182,15 +183,17 @@ function select(event, key) {
|
|
|
182
183
|
data-form-page-dirty
|
|
183
184
|
/>
|
|
184
185
|
<span
|
|
185
|
-
v-else-if="section.
|
|
186
|
+
v-else-if="section.status === 'optional'"
|
|
187
|
+
aria-hidden="true"
|
|
186
188
|
:class="
|
|
187
189
|
mergeFormUiClass(
|
|
188
190
|
'hidden shrink-0 text-[11px] font-normal text-dimmed @3xl/form-page:inline',
|
|
189
191
|
pageUi?.navigationOptional
|
|
190
192
|
)
|
|
191
193
|
"
|
|
194
|
+
data-form-page-optional
|
|
192
195
|
>
|
|
193
|
-
{{ t("form.page.optional") }}
|
|
196
|
+
{{ t("form.page.status.optional") }}
|
|
194
197
|
</span>
|
|
195
198
|
</a>
|
|
196
199
|
</slot>
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { FormPageSectionState } from '../../types/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* Navigation of a form page: one entry per visible section, following the section in view.
|
|
4
|
-
* An entry shows whether its section is complete,
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* An entry shows whether its section is complete, to fix, to complete, or optional with nothing
|
|
5
|
+
* entered (a dashed circle and an "Optional" tag, never "to complete"), and, with
|
|
6
|
+
* `controls.dirtyCheck`, a dot on modified sections. The footer sums up the sections left to
|
|
7
|
+
* complete, which leaves out untouched optional ones, or the modified ones with dirty checking.
|
|
7
8
|
*
|
|
8
9
|
* From 768px of page width it is a pinned column; below, a row of chips that scrolls sideways.
|
|
10
|
+
* It renders nothing while the page has fewer visible sections than `navigation.minSections`.
|
|
9
11
|
*/
|
|
10
12
|
type __VLS_Slots = {
|
|
11
13
|
/** Replaces the heading, which comes from the schema `navigation.title`. */
|
|
@@ -50,7 +50,7 @@ onBeforeUnmount(() => unregister?.());
|
|
|
50
50
|
}"
|
|
51
51
|
:class="
|
|
52
52
|
mergeFormUiClass(
|
|
53
|
-
|
|
53
|
+
`flex min-w-0 flex-col ${page.surfaceClasses.value.section}`,
|
|
54
54
|
pageUi?.section
|
|
55
55
|
)
|
|
56
56
|
"
|
|
@@ -77,7 +77,7 @@ onBeforeUnmount(() => unregister?.());
|
|
|
77
77
|
v-if="section.description"
|
|
78
78
|
:class="
|
|
79
79
|
mergeFormUiClass(
|
|
80
|
-
|
|
80
|
+
`m-0 text-[13px] text-muted ${page.surfaceClasses.value.sectionDescription}`,
|
|
81
81
|
pageUi?.sectionDescription
|
|
82
82
|
)
|
|
83
83
|
"
|
|
@@ -85,11 +85,12 @@ onBeforeUnmount(() => unregister?.());
|
|
|
85
85
|
{{ section.description }}
|
|
86
86
|
</p>
|
|
87
87
|
<UBadge
|
|
88
|
-
v-if="section.optional"
|
|
88
|
+
v-if="section.status === 'optional'"
|
|
89
89
|
color="neutral"
|
|
90
90
|
variant="soft"
|
|
91
|
-
:label="t('form.page.optional')"
|
|
92
|
-
:class="
|
|
91
|
+
:label="t('form.page.status.optional')"
|
|
92
|
+
:class="pageUi?.sectionOptional"
|
|
93
|
+
data-form-page-optional
|
|
93
94
|
/>
|
|
94
95
|
<div
|
|
95
96
|
v-if="page.dirtyCheck.value || $slots.actions"
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { FormPageSectionState } from '../../types/index.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* with `controls.dirtyCheck`, a
|
|
5
|
-
* stand in while the schema context
|
|
3
|
+
* Sections of a form page, one per visible section, each with its title, description, fields, and,
|
|
4
|
+
* with `controls.dirtyCheck`, a mark and a reset button while it is modified. They are cards, or
|
|
5
|
+
* plain blocks with the `page.surface` UI option. Skeletons stand in while the schema context
|
|
6
|
+
* loads.
|
|
6
7
|
*/
|
|
7
8
|
type __VLS_Slots = {
|
|
8
9
|
/** Extra buttons in a section header, before its reset button. */
|
|
@@ -18,7 +18,12 @@ const { contextError, contextLoading, contextPending, refreshContext } = page.ro
|
|
|
18
18
|
|
|
19
19
|
<template>
|
|
20
20
|
<div
|
|
21
|
-
:class="
|
|
21
|
+
:class="
|
|
22
|
+
mergeFormUiClass(
|
|
23
|
+
`flex min-w-0 flex-col ${page.surfaceClasses.value.sections}`,
|
|
24
|
+
pageUi?.sections
|
|
25
|
+
)
|
|
26
|
+
"
|
|
22
27
|
data-form-page-sections
|
|
23
28
|
>
|
|
24
29
|
<UAlert
|
|
@@ -48,7 +53,7 @@ const { contextError, contextLoading, contextPending, refreshContext } = page.ro
|
|
|
48
53
|
:key="section.key"
|
|
49
54
|
:class="
|
|
50
55
|
mergeFormUiClass(
|
|
51
|
-
|
|
56
|
+
`grid gap-4 ${page.surfaceClasses.value.sectionSkeleton}`,
|
|
52
57
|
pageUi?.sectionSkeleton
|
|
53
58
|
)
|
|
54
59
|
"
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { FormPageSectionState } from '../../types/index.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* with `controls.dirtyCheck`, a
|
|
5
|
-
* stand in while the schema context
|
|
3
|
+
* Sections of a form page, one per visible section, each with its title, description, fields, and,
|
|
4
|
+
* with `controls.dirtyCheck`, a mark and a reset button while it is modified. They are cards, or
|
|
5
|
+
* plain blocks with the `page.surface` UI option. Skeletons stand in while the schema context
|
|
6
|
+
* loads.
|
|
6
7
|
*/
|
|
7
8
|
type __VLS_Slots = {
|
|
8
9
|
/** Extra buttons in a section header, before its reset button. */
|
|
@@ -2,6 +2,8 @@ import type { FormPageSectionState, FormRendererController, FormUiConfig, FormVa
|
|
|
2
2
|
/**
|
|
3
3
|
* A form laid out as a page: a header with the title and the actions, a navigation that follows
|
|
4
4
|
* the section in view, and one card per section. Render a schema from `defineFormPageSchema`.
|
|
5
|
+
* The `page.surface` UI option draws the sections as `plain` blocks instead of cards, and the
|
|
6
|
+
* schema `navigation.minSections` leaves the navigation out of a page with few sections.
|
|
5
7
|
*
|
|
6
8
|
* The page scrolls on its own, so give it a height (`h-full` in a layout that sizes its main
|
|
7
9
|
* area). Put parts in the default slot to compose another layout: `FormPageHeader`,
|
|
@@ -30,6 +30,10 @@ const root = useFormRoot({
|
|
|
30
30
|
});
|
|
31
31
|
const page = provideFormPage(useFormPage({ element: () => element.value, hash: () => hash, root }));
|
|
32
32
|
const pageUi = computed(() => root.formUi.ui.value.page?.ui);
|
|
33
|
+
const BODY_UI = {
|
|
34
|
+
beside: "grid gap-4 px-4 pt-4 pb-16 @3xl/form-page:grid-cols-[200px_minmax(0,1fr)] @3xl/form-page:items-start @3xl/form-page:gap-7 @3xl/form-page:px-7 @3xl/form-page:pt-6 @3xl/form-page:pb-20 @5xl/form-page:grid-cols-[230px_minmax(0,760px)]",
|
|
35
|
+
single: "mx-auto grid w-full max-w-[816px] gap-4 px-4 pt-4 pb-16 @3xl/form-page:gap-7 @3xl/form-page:px-7 @3xl/form-page:pt-6 @3xl/form-page:pb-20"
|
|
36
|
+
};
|
|
33
37
|
</script>
|
|
34
38
|
|
|
35
39
|
<template>
|
|
@@ -37,8 +41,12 @@ const pageUi = computed(() => root.formUi.ui.value.page?.ui);
|
|
|
37
41
|
ref="element"
|
|
38
42
|
novalidate
|
|
39
43
|
:class="
|
|
40
|
-
mergeFormUiClass(
|
|
44
|
+
mergeFormUiClass(
|
|
45
|
+
`@container/form-page h-full min-w-0 overflow-y-auto ${page.surfaceClasses.value.root}`,
|
|
46
|
+
pageUi?.root
|
|
47
|
+
)
|
|
41
48
|
"
|
|
49
|
+
:data-surface="page.surface.value"
|
|
42
50
|
data-form-page
|
|
43
51
|
@submit.prevent="root.submit"
|
|
44
52
|
>
|
|
@@ -63,7 +71,7 @@ const pageUi = computed(() => root.formUi.ui.value.page?.ui);
|
|
|
63
71
|
<div
|
|
64
72
|
:class="
|
|
65
73
|
mergeFormUiClass(
|
|
66
|
-
|
|
74
|
+
page.navigationVisible.value ? BODY_UI.beside : BODY_UI.single,
|
|
67
75
|
pageUi?.body
|
|
68
76
|
)
|
|
69
77
|
"
|
|
@@ -2,6 +2,8 @@ import type { FormPageSectionState, FormRendererController, FormUiConfig, FormVa
|
|
|
2
2
|
/**
|
|
3
3
|
* A form laid out as a page: a header with the title and the actions, a navigation that follows
|
|
4
4
|
* the section in view, and one card per section. Render a schema from `defineFormPageSchema`.
|
|
5
|
+
* The `page.surface` UI option draws the sections as `plain` blocks instead of cards, and the
|
|
6
|
+
* schema `navigation.minSections` leaves the navigation out of a page with few sections.
|
|
5
7
|
*
|
|
6
8
|
* The page scrolls on its own, so give it a height (`h-full` in a layout that sizes its main
|
|
7
9
|
* area). Put parts in the default slot to compose another layout: `FormPageHeader`,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import UAlert from "@nuxt/ui/components/Alert.vue";
|
|
3
3
|
import { computed, ref, useId, watchEffect } from "vue";
|
|
4
|
+
import { useUiToolsLocale } from "../../../i18n/use-locale";
|
|
4
5
|
import { useFieldEffects } from "../../composables/use-field-effects";
|
|
5
6
|
import {
|
|
6
7
|
provideFormFieldBare,
|
|
@@ -67,6 +68,7 @@ const props = defineProps({
|
|
|
67
68
|
controlAttrs: { type: null, required: false }
|
|
68
69
|
});
|
|
69
70
|
const form = useFormRuntimeContext();
|
|
71
|
+
const { t } = useUiToolsLocale();
|
|
70
72
|
const element = ref(null);
|
|
71
73
|
const bare = computed(() => props.bare === true);
|
|
72
74
|
const controlId = useId();
|
|
@@ -208,8 +210,8 @@ function resolveControlLabel(controlField) {
|
|
|
208
210
|
color="neutral"
|
|
209
211
|
variant="soft"
|
|
210
212
|
icon="i-lucide-construction"
|
|
211
|
-
title="
|
|
212
|
-
:description="
|
|
213
|
+
:title="t('form.fields.unsupported.title')"
|
|
214
|
+
:description="t('form.fields.unsupported.description', { type: field.type.value })"
|
|
213
215
|
/>
|
|
214
216
|
</template>
|
|
215
217
|
|
|
@@ -225,8 +227,8 @@ function resolveControlLabel(controlField) {
|
|
|
225
227
|
color="neutral"
|
|
226
228
|
variant="soft"
|
|
227
229
|
icon="i-lucide-construction"
|
|
228
|
-
title="
|
|
229
|
-
:description="
|
|
230
|
+
:title="t('form.fields.unsupported.title')"
|
|
231
|
+
:description="t('form.fields.unsupported.description', { type: field.type.value })"
|
|
230
232
|
/>
|
|
231
233
|
</div>
|
|
232
234
|
</template>
|
|
@@ -9,12 +9,12 @@ declare var __VLS_1: {
|
|
|
9
9
|
description: string | undefined;
|
|
10
10
|
hint: string | undefined;
|
|
11
11
|
required: any;
|
|
12
|
-
},
|
|
12
|
+
}, __VLS_67: {
|
|
13
13
|
label: string | undefined;
|
|
14
14
|
description: string | undefined;
|
|
15
15
|
hint: string | undefined;
|
|
16
16
|
required: any;
|
|
17
|
-
},
|
|
17
|
+
}, __VLS_81: {
|
|
18
18
|
label: string | undefined;
|
|
19
19
|
description: string | undefined;
|
|
20
20
|
hint: string | undefined;
|
|
@@ -23,9 +23,9 @@ declare var __VLS_1: {
|
|
|
23
23
|
type __VLS_Slots = {} & {
|
|
24
24
|
default?: (props: typeof __VLS_1) => any;
|
|
25
25
|
} & {
|
|
26
|
-
default?: (props: typeof
|
|
26
|
+
default?: (props: typeof __VLS_67) => any;
|
|
27
27
|
} & {
|
|
28
|
-
default?: (props: typeof
|
|
28
|
+
default?: (props: typeof __VLS_81) => any;
|
|
29
29
|
};
|
|
30
30
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
31
31
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -11,6 +11,7 @@ import { useFormFieldBare } from "../../composables/use-form-field-chrome";
|
|
|
11
11
|
import { useFormRuntimeContext } from "../../composables/use-form-runtime";
|
|
12
12
|
import { useFormUi } from "../../composables/use-form-ui";
|
|
13
13
|
import { getSchemaDirtyCheck } from "../../utils/controls";
|
|
14
|
+
import { resolveFieldDisabledReason } from "../../utils/disabled";
|
|
14
15
|
import { createFormFieldInstance } from "../../utils/field-instance";
|
|
15
16
|
import { isFunction, isNumber, isString } from "../../utils/predicate";
|
|
16
17
|
import { resolveRequired } from "../../utils/state";
|
|
@@ -85,6 +86,9 @@ const help = computed(
|
|
|
85
86
|
() => field.value.capability.has("hint") && "help" in props.field ? resolveFieldText(props.field.help, params.value) : void 0
|
|
86
87
|
);
|
|
87
88
|
const shellHelp = computed(() => props.inlineLabel ? void 0 : help.value);
|
|
89
|
+
const disabledReason = computed(
|
|
90
|
+
() => field.value.capability.has("hint") ? resolveFieldDisabledReason(props.field, params.value) : void 0
|
|
91
|
+
);
|
|
88
92
|
const dirty = computed(
|
|
89
93
|
() => "dirtyCheck" in props.field && (props.field.dirtyCheck === true || getSchemaDirtyCheck(form.schema.value)) && form.dirtyPaths.value.includes(props.path.join("."))
|
|
90
94
|
);
|
|
@@ -130,7 +134,7 @@ function renderLabelExtra() {
|
|
|
130
134
|
:description="shellDescription"
|
|
131
135
|
:hint="shellLabelExtra === void 0 || shellLabelExtra === null ? shellHint : void 0"
|
|
132
136
|
:error="error"
|
|
133
|
-
:help="shellHelp"
|
|
137
|
+
:help="shellHelp ?? disabledReason"
|
|
134
138
|
:required="required"
|
|
135
139
|
:size="formUi.controlSize.value"
|
|
136
140
|
:orientation="orientation"
|
|
@@ -183,6 +187,26 @@ function renderLabelExtra() {
|
|
|
183
187
|
<component :is="renderLabelExtra" />
|
|
184
188
|
</span>
|
|
185
189
|
</template>
|
|
190
|
+
<template v-if="disabledReason" #help>
|
|
191
|
+
<span v-if="shellHelp" class="block">{{ shellHelp }}</span>
|
|
192
|
+
<span
|
|
193
|
+
:class="
|
|
194
|
+
mergeFormUiClass(
|
|
195
|
+
'flex items-start gap-1.5',
|
|
196
|
+
shellHelp ? 'mt-1' : void 0,
|
|
197
|
+
fieldUi?.disabledReason
|
|
198
|
+
)
|
|
199
|
+
"
|
|
200
|
+
data-form-disabled-reason=""
|
|
201
|
+
>
|
|
202
|
+
<UIcon
|
|
203
|
+
name="i-lucide-lock"
|
|
204
|
+
aria-hidden="true"
|
|
205
|
+
:class="mergeFormUiClass('mt-0.5 size-3.5 shrink-0', fieldUi?.disabledReasonIcon)"
|
|
206
|
+
/>
|
|
207
|
+
<span>{{ disabledReason }}</span>
|
|
208
|
+
</span>
|
|
209
|
+
</template>
|
|
186
210
|
<UCollapsible
|
|
187
211
|
v-if="collapsible"
|
|
188
212
|
v-model:open="open"
|
|
@@ -195,7 +219,7 @@ function renderLabelExtra() {
|
|
|
195
219
|
:icon="open ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'"
|
|
196
220
|
:class="mergeFormUiClass('justify-self-start', fieldUi?.collapseTrigger)"
|
|
197
221
|
>
|
|
198
|
-
{{ open ? "
|
|
222
|
+
{{ open ? t("form.fields.shell.collapse") : t("form.fields.shell.expand") }}
|
|
199
223
|
</UButton>
|
|
200
224
|
<template #content>
|
|
201
225
|
<div :class="mergeFormUiClass('flex items-start gap-2', fieldUi?.body)">
|
|
@@ -216,7 +240,7 @@ function renderLabelExtra() {
|
|
|
216
240
|
color="neutral"
|
|
217
241
|
variant="ghost"
|
|
218
242
|
size="xs"
|
|
219
|
-
aria-label="
|
|
243
|
+
:aria-label="t('form.fields.shell.reset')"
|
|
220
244
|
:class="fieldUi?.reset"
|
|
221
245
|
@click="resetField"
|
|
222
246
|
/>
|
|
@@ -239,7 +263,7 @@ function renderLabelExtra() {
|
|
|
239
263
|
color="neutral"
|
|
240
264
|
variant="ghost"
|
|
241
265
|
size="xs"
|
|
242
|
-
aria-label="
|
|
266
|
+
:aria-label="t('form.fields.shell.reset')"
|
|
243
267
|
:class="fieldUi?.reset"
|
|
244
268
|
@click="resetField"
|
|
245
269
|
/>
|
|
@@ -9,12 +9,12 @@ declare var __VLS_1: {
|
|
|
9
9
|
description: string | undefined;
|
|
10
10
|
hint: string | undefined;
|
|
11
11
|
required: any;
|
|
12
|
-
},
|
|
12
|
+
}, __VLS_67: {
|
|
13
13
|
label: string | undefined;
|
|
14
14
|
description: string | undefined;
|
|
15
15
|
hint: string | undefined;
|
|
16
16
|
required: any;
|
|
17
|
-
},
|
|
17
|
+
}, __VLS_81: {
|
|
18
18
|
label: string | undefined;
|
|
19
19
|
description: string | undefined;
|
|
20
20
|
hint: string | undefined;
|
|
@@ -23,9 +23,9 @@ declare var __VLS_1: {
|
|
|
23
23
|
type __VLS_Slots = {} & {
|
|
24
24
|
default?: (props: typeof __VLS_1) => any;
|
|
25
25
|
} & {
|
|
26
|
-
default?: (props: typeof
|
|
26
|
+
default?: (props: typeof __VLS_67) => any;
|
|
27
27
|
} & {
|
|
28
|
-
default?: (props: typeof
|
|
28
|
+
default?: (props: typeof __VLS_81) => any;
|
|
29
29
|
};
|
|
30
30
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
31
31
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -143,7 +143,7 @@ defineExpose({
|
|
|
143
143
|
validate: runtime.validate
|
|
144
144
|
});
|
|
145
145
|
function stepLabel(index) {
|
|
146
|
-
return runtime.steps.value[index]?.label ??
|
|
146
|
+
return runtime.steps.value[index]?.label ?? t("form.steps.fallback", { number: index + 1 });
|
|
147
147
|
}
|
|
148
148
|
function getHeaderText(headerConfig, key) {
|
|
149
149
|
if (!isRecord(headerConfig)) {
|
|
@@ -245,7 +245,7 @@ function getSchemaShowStepper(schema) {
|
|
|
245
245
|
:size="formUi.controlSize.value"
|
|
246
246
|
icon="i-lucide-x"
|
|
247
247
|
:disabled="runtime.actionPending.value !== null"
|
|
248
|
-
aria-label="
|
|
248
|
+
:aria-label="t('form.actions.closeButton')"
|
|
249
249
|
:class="formUi.ui.value.root?.ui?.close"
|
|
250
250
|
@click="cancel"
|
|
251
251
|
/>
|
|
@@ -55,7 +55,7 @@ export declare function useFieldControl<TField extends FormField>(field: () => T
|
|
|
55
55
|
setSearch: (term: string) => void;
|
|
56
56
|
};
|
|
57
57
|
params: import("vue").ComputedRef<import("../types/index.js").FormFieldCallbackParams<{}, import("../types/index.js").FormDependencies, unknown, unknown>>;
|
|
58
|
-
placeholder: import("vue").ComputedRef<string>;
|
|
58
|
+
placeholder: import("vue").ComputedRef<string | undefined>;
|
|
59
59
|
validationPending: import("vue").ComputedRef<boolean>;
|
|
60
60
|
};
|
|
61
61
|
export {};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { watchWithFilter } from "@vueuse/core";
|
|
2
2
|
import { computed, onScopeDispose } from "vue";
|
|
3
|
-
import { useUiToolsLocale } from "../../i18n/use-locale.js";
|
|
4
3
|
import { isEqualFormValue } from "../utils/compare.js";
|
|
4
|
+
import { isFieldDisabledByCallback } from "../utils/disabled.js";
|
|
5
5
|
import { createFormFieldInstance } from "../utils/field-instance.js";
|
|
6
6
|
import { cloneFormValue } from "../utils/path.js";
|
|
7
|
-
import { isFunction,
|
|
7
|
+
import { isFunction, isObject, isString } from "../utils/predicate.js";
|
|
8
|
+
import { resolveFieldText } from "../utils/text.js";
|
|
8
9
|
import { mergeFormUiClass } from "../utils/ui.js";
|
|
9
10
|
import { useFieldOptions } from "./use-field-options.js";
|
|
10
11
|
import { useFormFieldControlAttrs } from "./use-form-field-chrome.js";
|
|
@@ -24,7 +25,6 @@ export function useResolvedFieldProps(field, path) {
|
|
|
24
25
|
export function useFieldControl(field, path, controlOptions = {}) {
|
|
25
26
|
const form = useFormRuntimeContext();
|
|
26
27
|
const formUi = useFormUi();
|
|
27
|
-
const { t } = useUiToolsLocale();
|
|
28
28
|
const fieldControlAttrs = useFormFieldControlAttrs();
|
|
29
29
|
const api = computed(() => form.getFieldApi(path(), field()));
|
|
30
30
|
const params = computed(() => form.getFieldCallbackParams(path(), field()));
|
|
@@ -73,24 +73,12 @@ export function useFieldControl(field, path, controlOptions = {}) {
|
|
|
73
73
|
const value = controlProps.value.size;
|
|
74
74
|
return isFormControlSize(value) ? value : formUi.controlSize.value;
|
|
75
75
|
});
|
|
76
|
-
const disabled = computed(
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const placeholder = computed(() => {
|
|
83
|
-
const current = field();
|
|
84
|
-
const value = Object.getOwnPropertyDescriptor(current, "placeholder")?.value;
|
|
85
|
-
if (isFunction(value)) {
|
|
86
|
-
const resolved = value(params.value);
|
|
87
|
-
return isString(resolved) || isNumber(resolved) ? String(resolved) : t("form.fields.text.defaultPlaceholder");
|
|
88
|
-
}
|
|
89
|
-
if (isString(value) || isNumber(value)) {
|
|
90
|
-
return String(value);
|
|
91
|
-
}
|
|
92
|
-
return t("form.fields.text.defaultPlaceholder");
|
|
93
|
-
});
|
|
76
|
+
const disabled = computed(
|
|
77
|
+
() => isFieldDisabledByCallback(field(), params.value) || form.actionPending.value !== null || options.disableOnLoading.value && options.loading.value
|
|
78
|
+
);
|
|
79
|
+
const placeholder = computed(
|
|
80
|
+
() => resolveFieldText(Object.getOwnPropertyDescriptor(field(), "placeholder")?.value, params.value)
|
|
81
|
+
);
|
|
94
82
|
let lastValue = cloneFormValue(form.getValue(path()));
|
|
95
83
|
watchWithFilter(
|
|
96
84
|
() => form.getValue(path()),
|
|
@@ -6,7 +6,9 @@ import {
|
|
|
6
6
|
mergeResolvedOptions,
|
|
7
7
|
normalizeOptionItem,
|
|
8
8
|
normalizeOptionItems,
|
|
9
|
-
normalizeOptionSelection
|
|
9
|
+
normalizeOptionSelection,
|
|
10
|
+
readOptionSource,
|
|
11
|
+
resolveOptionGroupKey
|
|
10
12
|
} from "../utils/options.js";
|
|
11
13
|
import { isRecord, relativePathSegments } from "../utils/path.js";
|
|
12
14
|
import {
|
|
@@ -31,7 +33,10 @@ export function useFieldOptions(params) {
|
|
|
31
33
|
const promiseResolution = shallowRef(null);
|
|
32
34
|
const creating = ref(false);
|
|
33
35
|
const remoteConfig = computed(() => resolveRemoteOptionConfig(params.field()));
|
|
34
|
-
const optionKeys = computed(() =>
|
|
36
|
+
const optionKeys = computed(() => ({
|
|
37
|
+
...resolveOptionKeys(params.field()),
|
|
38
|
+
group: resolveOptionGroupKey(params.field())
|
|
39
|
+
}));
|
|
35
40
|
const remote = useRemoteFieldOptions({
|
|
36
41
|
api: params.api,
|
|
37
42
|
callbackParams: params.callbackParams,
|
|
@@ -389,8 +394,7 @@ function resolveOptionSource(field, params) {
|
|
|
389
394
|
if (!createFormFieldInstance(field).capability.has("options")) {
|
|
390
395
|
return [];
|
|
391
396
|
}
|
|
392
|
-
const
|
|
393
|
-
const source = isRecord(options) && "source" in options ? options.source : options;
|
|
397
|
+
const source = readOptionSource(field);
|
|
394
398
|
return isFunction(source) ? source(params) : source;
|
|
395
399
|
}
|
|
396
400
|
function emptyTrackedOptionSource() {
|
|
@@ -2,7 +2,8 @@ import type { FormPageSectionState } from '../types/index.js';
|
|
|
2
2
|
import type { useFormRoot } from './use-form-root.js';
|
|
3
3
|
/**
|
|
4
4
|
* State shared by a form page and its parts: the visible sections and where each stands, the
|
|
5
|
-
* section in view, the navigation summary,
|
|
5
|
+
* section in view, the navigation summary, whether the navigation shows, the surface the
|
|
6
|
+
* sections are drawn with, and the actions a part can take on a section.
|
|
6
7
|
*/
|
|
7
8
|
export declare function useFormPage(params: {
|
|
8
9
|
root: ReturnType<typeof useFormRoot>;
|
|
@@ -19,6 +20,7 @@ export declare function useFormPage(params: {
|
|
|
19
20
|
}[]>;
|
|
20
21
|
modified: import("vue").ComputedRef<number>;
|
|
21
22
|
navigationTitle: import("vue").ComputedRef<string | undefined>;
|
|
23
|
+
navigationVisible: import("vue").ComputedRef<boolean>;
|
|
22
24
|
register: (key: string, element: HTMLElement) => () => void;
|
|
23
25
|
remaining: import("vue").ComputedRef<number>;
|
|
24
26
|
resetSection: (key: string) => Promise<void>;
|
|
@@ -42,6 +44,8 @@ export declare function useFormPage(params: {
|
|
|
42
44
|
scrollTo: (key: string, options?: import("../types/index.js").FormPageScrollOptions) => boolean;
|
|
43
45
|
sections: import("vue").ComputedRef<readonly FormPageSectionState[]>;
|
|
44
46
|
stickyOffset: import("vue").Ref<number, number>;
|
|
47
|
+
surface: import("vue").ComputedRef<import("../types/index.js").FormPageSurface>;
|
|
48
|
+
surfaceClasses: import("vue").ComputedRef<import("../utils/page-surface.js").FormPageSurfaceClasses>;
|
|
45
49
|
};
|
|
46
50
|
export declare function provideFormPage(page: ReturnType<typeof useFormPage>): {
|
|
47
51
|
active: import("vue").Ref<string | undefined, string | undefined>;
|
|
@@ -53,6 +57,7 @@ export declare function provideFormPage(page: ReturnType<typeof useFormPage>): {
|
|
|
53
57
|
}[]>;
|
|
54
58
|
modified: import("vue").ComputedRef<number>;
|
|
55
59
|
navigationTitle: import("vue").ComputedRef<string | undefined>;
|
|
60
|
+
navigationVisible: import("vue").ComputedRef<boolean>;
|
|
56
61
|
register: (key: string, element: HTMLElement) => () => void;
|
|
57
62
|
remaining: import("vue").ComputedRef<number>;
|
|
58
63
|
resetSection: (key: string) => Promise<void>;
|
|
@@ -76,6 +81,8 @@ export declare function provideFormPage(page: ReturnType<typeof useFormPage>): {
|
|
|
76
81
|
scrollTo: (key: string, options?: import("../types/index.js").FormPageScrollOptions) => boolean;
|
|
77
82
|
sections: import("vue").ComputedRef<readonly FormPageSectionState[]>;
|
|
78
83
|
stickyOffset: import("vue").Ref<number, number>;
|
|
84
|
+
surface: import("vue").ComputedRef<import("../types/index.js").FormPageSurface>;
|
|
85
|
+
surfaceClasses: import("vue").ComputedRef<import("../utils/page-surface.js").FormPageSurfaceClasses>;
|
|
79
86
|
};
|
|
80
87
|
/** The page a part renders in. Parts only work inside `FormPage`. */
|
|
81
88
|
export declare function useFormPageContext(): {
|
|
@@ -88,6 +95,7 @@ export declare function useFormPageContext(): {
|
|
|
88
95
|
}[]>;
|
|
89
96
|
modified: import("vue").ComputedRef<number>;
|
|
90
97
|
navigationTitle: import("vue").ComputedRef<string | undefined>;
|
|
98
|
+
navigationVisible: import("vue").ComputedRef<boolean>;
|
|
91
99
|
register: (key: string, element: HTMLElement) => () => void;
|
|
92
100
|
remaining: import("vue").ComputedRef<number>;
|
|
93
101
|
resetSection: (key: string) => Promise<void>;
|
|
@@ -111,4 +119,6 @@ export declare function useFormPageContext(): {
|
|
|
111
119
|
scrollTo: (key: string, options?: import("../types/index.js").FormPageScrollOptions) => boolean;
|
|
112
120
|
sections: import("vue").ComputedRef<readonly FormPageSectionState[]>;
|
|
113
121
|
stickyOffset: import("vue").Ref<number, number>;
|
|
122
|
+
surface: import("vue").ComputedRef<import("../types/index.js").FormPageSurface>;
|
|
123
|
+
surfaceClasses: import("vue").ComputedRef<import("../utils/page-surface.js").FormPageSurfaceClasses>;
|
|
114
124
|
};
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { computed, inject, provide, ref } from "vue";
|
|
2
2
|
import { getSchemaDirtyCheck } from "../utils/controls.js";
|
|
3
3
|
import {
|
|
4
|
+
getFormPageNavigationMinSections,
|
|
4
5
|
getFormPageNavigationTitle,
|
|
5
6
|
getFormPageSections,
|
|
7
|
+
isFormPageSectionBlocking,
|
|
6
8
|
isFormPageSectionVisible,
|
|
7
9
|
resolveFormPageSectionState
|
|
8
10
|
} from "../utils/page.js";
|
|
11
|
+
import { getFormPageSurfaceClasses, resolveFormPageSurface } from "../utils/page-surface.js";
|
|
9
12
|
import { cloneFormValue } from "../utils/path.js";
|
|
10
13
|
import { useFormPageScroll } from "./use-form-page-scroll.js";
|
|
11
14
|
export function useFormPage(params) {
|
|
@@ -36,11 +39,11 @@ export function useFormPage(params) {
|
|
|
36
39
|
);
|
|
37
40
|
const dirtyCheck = computed(() => getSchemaDirtyCheck(root.schema.value));
|
|
38
41
|
const navigationTitle = computed(() => getFormPageNavigationTitle(root.schema.value));
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
);
|
|
42
|
+
const navigationMinSections = computed(() => getFormPageNavigationMinSections(root.schema.value));
|
|
43
|
+
const navigationVisible = computed(() => sections.value.length >= navigationMinSections.value);
|
|
44
|
+
const surface = computed(() => resolveFormPageSurface(root.formUi.ui.value.page?.surface));
|
|
45
|
+
const surfaceClasses = computed(() => getFormPageSurfaceClasses(surface.value));
|
|
46
|
+
const remaining = computed(() => sections.value.filter(isFormPageSectionBlocking).length);
|
|
44
47
|
const modified = computed(() => sections.value.filter((section) => section.dirty).length);
|
|
45
48
|
const stickyOffset = ref(0);
|
|
46
49
|
const scroll = useFormPageScroll({ hash: params.hash, root: params.element, sections });
|
|
@@ -62,13 +65,16 @@ export function useFormPage(params) {
|
|
|
62
65
|
items,
|
|
63
66
|
modified,
|
|
64
67
|
navigationTitle,
|
|
68
|
+
navigationVisible,
|
|
65
69
|
register: scroll.register,
|
|
66
70
|
remaining,
|
|
67
71
|
resetSection,
|
|
68
72
|
root,
|
|
69
73
|
scrollTo: scroll.scrollTo,
|
|
70
74
|
sections,
|
|
71
|
-
stickyOffset
|
|
75
|
+
stickyOffset,
|
|
76
|
+
surface,
|
|
77
|
+
surfaceClasses
|
|
72
78
|
};
|
|
73
79
|
}
|
|
74
80
|
const formPageKey = Symbol("nuxt-ui-tools-form-page");
|
|
@@ -221,7 +221,7 @@ export function useFormRuntime(params) {
|
|
|
221
221
|
active: currentStepIndex.value === index,
|
|
222
222
|
index,
|
|
223
223
|
key: step.key ?? String(index + 1),
|
|
224
|
-
label: isFunction(step.title) ? String(step.title()) : String(step.title ?? step.key ??
|
|
224
|
+
label: isFunction(step.title) ? String(step.title()) : String(step.title ?? step.key ?? t("form.steps.fallback", { number: index + 1 })),
|
|
225
225
|
root: step.root
|
|
226
226
|
}))
|
|
227
227
|
);
|