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
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { useAppConfig } from "nuxt/app";
|
|
2
|
+
import { computed, nextTick, onBeforeUnmount, onMounted, watch } from "vue";
|
|
3
|
+
import { useRouter } from "vue-router";
|
|
4
|
+
import { useUiToolsLocale } from "../../i18n/use-locale.js";
|
|
5
|
+
import {
|
|
6
|
+
getSchemaAutoFocus,
|
|
7
|
+
getSchemaDirtyNavigation,
|
|
8
|
+
getSchemaSyncInput,
|
|
9
|
+
getSchemaUi,
|
|
10
|
+
getSchemaValidationMode
|
|
11
|
+
} from "../utils/controls.js";
|
|
12
|
+
import { isRecord } from "../utils/path.js";
|
|
13
|
+
import { isString, isUndefined, stringArray } from "../utils/predicate.js";
|
|
14
|
+
import { collectFormFieldsPaths } from "../utils/state.js";
|
|
15
|
+
import { resolveFormText } from "../utils/text.js";
|
|
16
|
+
import { mergeFormUi, resolveAppFormUi } from "../utils/ui.js";
|
|
17
|
+
import { provideFormRuntime, useFormRuntime } from "./use-form-runtime.js";
|
|
18
|
+
import { provideFormUi } from "./use-form-ui.js";
|
|
19
|
+
const DEFAULT_DIRTY_NAVIGATION_MESSAGE = "You have unsaved changes. Close this form?";
|
|
20
|
+
export function useFormRoot(params) {
|
|
21
|
+
const router = useRouter();
|
|
22
|
+
const { t } = useUiToolsLocale();
|
|
23
|
+
const appConfig = useAppConfig();
|
|
24
|
+
const schema = computed(() => params.form()?.schema.value ?? params.schema() ?? {});
|
|
25
|
+
const formUi = provideFormUi(
|
|
26
|
+
computed(
|
|
27
|
+
() => mergeFormUi(resolveAppFormUi(appConfig), getSchemaUi(schema.value), params.ui())
|
|
28
|
+
)
|
|
29
|
+
);
|
|
30
|
+
const input = computed(() => params.form()?.input.value ?? params.input());
|
|
31
|
+
const syncInput = computed(
|
|
32
|
+
() => params.form()?.syncInput.value ?? params.syncInput() ?? getSchemaSyncInput(schema.value)
|
|
33
|
+
);
|
|
34
|
+
const validationMode = computed(
|
|
35
|
+
() => params.form()?.validationMode.value ?? params.validate() ?? getSchemaValidationMode(schema.value)
|
|
36
|
+
);
|
|
37
|
+
const runtime = useFormRuntime({ input, schema, syncInput, validationMode });
|
|
38
|
+
provideFormRuntime(runtime);
|
|
39
|
+
watch(
|
|
40
|
+
params.form,
|
|
41
|
+
(controller, previousController) => {
|
|
42
|
+
previousController?.unbind(runtime);
|
|
43
|
+
controller?.bind(runtime);
|
|
44
|
+
},
|
|
45
|
+
{ immediate: true }
|
|
46
|
+
);
|
|
47
|
+
onBeforeUnmount(() => params.form()?.unbind(runtime));
|
|
48
|
+
onMounted(async () => {
|
|
49
|
+
await nextTick();
|
|
50
|
+
const target = getSchemaAutoFocus(schema.value);
|
|
51
|
+
if (isString(target)) {
|
|
52
|
+
await runtime.focusField(target);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (target === true) {
|
|
56
|
+
await focusFirstField();
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
if (import.meta.client) {
|
|
60
|
+
let handleBeforeUnload = function(event) {
|
|
61
|
+
if (!shouldConfirmDirtyNavigation()) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
event.preventDefault();
|
|
65
|
+
event.returnValue = "";
|
|
66
|
+
};
|
|
67
|
+
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
68
|
+
onBeforeUnmount(() => window.removeEventListener("beforeunload", handleBeforeUnload));
|
|
69
|
+
}
|
|
70
|
+
const removeRouteGuard = router.beforeEach((to, from) => {
|
|
71
|
+
if (runtime.actionPending.value === "submit" || isSameDocument(to, from)) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
if (!shouldConfirmDirtyNavigation()) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
return window.confirm(getDirtyNavigationMessage());
|
|
78
|
+
});
|
|
79
|
+
onBeforeUnmount(removeRouteGuard);
|
|
80
|
+
const contextPending = computed(
|
|
81
|
+
() => Object.values(runtime.context).some(
|
|
82
|
+
(resource) => "pending" in resource && resource.pending && isUndefined(resource.value)
|
|
83
|
+
)
|
|
84
|
+
);
|
|
85
|
+
const contextLoading = computed(
|
|
86
|
+
() => Object.values(runtime.context).some((resource) => "loading" in resource && resource.loading)
|
|
87
|
+
);
|
|
88
|
+
const contextError = computed(() => {
|
|
89
|
+
for (const resource of Object.values(runtime.context)) {
|
|
90
|
+
if (!("error" in resource) || !resource.error) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (resource.error instanceof Error) {
|
|
94
|
+
return resource.error.message;
|
|
95
|
+
}
|
|
96
|
+
if (isString(resource.error)) {
|
|
97
|
+
return resource.error;
|
|
98
|
+
}
|
|
99
|
+
return t("form.states.contextError.description");
|
|
100
|
+
}
|
|
101
|
+
return void 0;
|
|
102
|
+
});
|
|
103
|
+
async function refreshContext() {
|
|
104
|
+
await Promise.all(
|
|
105
|
+
Object.values(runtime.context).flatMap(
|
|
106
|
+
(resource) => "refresh" in resource ? [resource.refresh()] : []
|
|
107
|
+
)
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
async function submit() {
|
|
111
|
+
const controller = params.form();
|
|
112
|
+
const result = controller ? await controller.submitHandler() : await runtime.submitHandler();
|
|
113
|
+
if (result.success) {
|
|
114
|
+
params.onSubmitted(runtime.output.value, result);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function cancel() {
|
|
118
|
+
if (shouldConfirmDirtyNavigation() && !window.confirm(getDirtyNavigationMessage())) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
params.onCancelled(runtime.output.value);
|
|
122
|
+
}
|
|
123
|
+
function shouldConfirmDirtyNavigation() {
|
|
124
|
+
if (!runtime.isDirty.value) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
const config = getSchemaDirtyNavigation(schema.value);
|
|
128
|
+
if (!config) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
if (!isRecord(config)) {
|
|
132
|
+
return config === true;
|
|
133
|
+
}
|
|
134
|
+
const ignored = stringArray(config.ignorePaths);
|
|
135
|
+
return runtime.dirtyPaths.value.some(
|
|
136
|
+
(path) => !ignored.some((ignoredPath) => path === ignoredPath || path.startsWith(`${ignoredPath}.`))
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
function getDirtyNavigationMessage() {
|
|
140
|
+
const config = getSchemaDirtyNavigation(schema.value);
|
|
141
|
+
if (!isRecord(config)) {
|
|
142
|
+
return DEFAULT_DIRTY_NAVIGATION_MESSAGE;
|
|
143
|
+
}
|
|
144
|
+
const message = Object.getOwnPropertyDescriptor(config, "message")?.value;
|
|
145
|
+
return resolveFormText(message) ?? DEFAULT_DIRTY_NAVIGATION_MESSAGE;
|
|
146
|
+
}
|
|
147
|
+
async function focusFirstField() {
|
|
148
|
+
const parentPath = runtime.currentStepRoot.value ? [runtime.currentStepRoot.value] : [];
|
|
149
|
+
for (const path of collectFormFieldsPaths(runtime.currentFields.value, parentPath)) {
|
|
150
|
+
if (await runtime.focusField(path)) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
cancel,
|
|
157
|
+
contextError,
|
|
158
|
+
contextLoading,
|
|
159
|
+
contextPending,
|
|
160
|
+
formUi,
|
|
161
|
+
input,
|
|
162
|
+
refreshContext,
|
|
163
|
+
runtime,
|
|
164
|
+
schema,
|
|
165
|
+
submit,
|
|
166
|
+
validationMode
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function isSameDocument(to, from) {
|
|
170
|
+
return to.fullPath.split("#")[0] === from.fullPath.split("#")[0];
|
|
171
|
+
}
|
|
@@ -137,7 +137,7 @@ export function useFormRuntime(params) {
|
|
|
137
137
|
}
|
|
138
138
|
return valid;
|
|
139
139
|
}
|
|
140
|
-
const
|
|
140
|
+
const submission = useFormSubmission({
|
|
141
141
|
beforeNext: () => isStepped.value ? runBeforeNext() : Promise.resolve(true),
|
|
142
142
|
focusFirstInvalid: focus.focusFirstInvalid,
|
|
143
143
|
getApi: () => createPublicFormApi(runtime),
|
|
@@ -146,6 +146,13 @@ export function useFormRuntime(params) {
|
|
|
146
146
|
getSchema: () => params.schema.value,
|
|
147
147
|
validate
|
|
148
148
|
});
|
|
149
|
+
async function submitHandler(externalSubmitHandler) {
|
|
150
|
+
const result = await submission.submitHandler(externalSubmitHandler);
|
|
151
|
+
if (result.success) {
|
|
152
|
+
state.rebaseline();
|
|
153
|
+
}
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
149
156
|
let hasInitializedInput = !isUndefined(params.input?.value);
|
|
150
157
|
state.initialize(params.input?.value);
|
|
151
158
|
let pendingInput;
|
|
@@ -221,7 +228,9 @@ export function useFormRuntime(params) {
|
|
|
221
228
|
const isLastStep = computed(() => currentStepIndex.value >= steps.value.length - 1);
|
|
222
229
|
const canGoPrevious = computed(() => isStepped.value && !isFirstStep.value);
|
|
223
230
|
const canGoNext = computed(() => isStepped.value && !isLastStep.value);
|
|
224
|
-
const actionPending = computed(
|
|
231
|
+
const actionPending = computed(
|
|
232
|
+
() => navigationActionPending.value ?? submission.actionPending.value
|
|
233
|
+
);
|
|
225
234
|
async function runBeforeNext() {
|
|
226
235
|
const handler = getSchemaLifecycleHandler(params.schema.value, "onBeforeNext");
|
|
227
236
|
if (!handler) {
|
|
@@ -433,10 +442,10 @@ export function useFormRuntime(params) {
|
|
|
433
442
|
state: state.state,
|
|
434
443
|
steps,
|
|
435
444
|
submit: async () => {
|
|
436
|
-
const result = await
|
|
445
|
+
const result = await submitHandler();
|
|
437
446
|
return result.success;
|
|
438
447
|
},
|
|
439
|
-
submitHandler
|
|
448
|
+
submitHandler,
|
|
440
449
|
trackEffect: effects.track,
|
|
441
450
|
validate,
|
|
442
451
|
validateCurrentStep
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { computed, shallowRef, toValue } from "vue";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { getSchemaSyncInput, getSchemaValidationMode } from "../utils/controls.js";
|
|
3
|
+
import { isUndefined } from "../utils/predicate.js";
|
|
4
4
|
export function useForm(params) {
|
|
5
5
|
const runtime = shallowRef(null);
|
|
6
6
|
const schema = computed(() => toValue(params.schema));
|
|
@@ -151,23 +151,3 @@ export function useForm(params) {
|
|
|
151
151
|
validationMode
|
|
152
152
|
};
|
|
153
153
|
}
|
|
154
|
-
function getSchemaSyncInput(schema) {
|
|
155
|
-
const controls = getSchemaControls(schema);
|
|
156
|
-
const value = controls ? Object.getOwnPropertyDescriptor(controls, "syncInput")?.value : void 0;
|
|
157
|
-
if (isBoolean(value)) {
|
|
158
|
-
return value;
|
|
159
|
-
}
|
|
160
|
-
return stringArray(value);
|
|
161
|
-
}
|
|
162
|
-
function getSchemaValidationMode(schema) {
|
|
163
|
-
const controls = getSchemaControls(schema);
|
|
164
|
-
const value = controls ? Object.getOwnPropertyDescriptor(controls, "validate")?.value : void 0;
|
|
165
|
-
return value === false || value === "required" || value === "validators" ? value : true;
|
|
166
|
-
}
|
|
167
|
-
function getSchemaControls(schema) {
|
|
168
|
-
if (!isRecord(schema)) {
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
const controls = Object.getOwnPropertyDescriptor(schema, "controls")?.value;
|
|
172
|
-
return isRecord(controls) ? controls : void 0;
|
|
173
|
-
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const CARD_SELECTED_RING = "has-data-[state=checked]:ring-4 has-data-[state=checked]:ring-primary/20";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const CARD_SELECTED_RING = "has-data-[state=checked]:ring-4 has-data-[state=checked]:ring-primary/20";
|
|
@@ -5,17 +5,20 @@ import FormFieldShell from "../../components/renderer/form-field-shell.vue";
|
|
|
5
5
|
import { useFieldControl } from "../../composables/use-field-control";
|
|
6
6
|
import { formOptionKey } from "../../utils/options";
|
|
7
7
|
import { isBoolean, isNumber, isString, isUndefined } from "../../utils/predicate";
|
|
8
|
-
import
|
|
8
|
+
import ChoiceCardLabel from "../choice-card/choice-card-label.vue";
|
|
9
|
+
import { CHOICE_CARD_PROPS, useChoiceCard } from "../choice-card/use-choice-card";
|
|
9
10
|
const props = defineProps({
|
|
10
11
|
field: { type: Object, required: true },
|
|
11
12
|
path: { type: Array, required: true }
|
|
12
13
|
});
|
|
13
14
|
const { fieldProps, form, controlProps, disabled, handleBlur, options } = useFieldControl(
|
|
14
15
|
() => props.field,
|
|
15
|
-
() => props.path
|
|
16
|
+
() => props.path,
|
|
17
|
+
{ omit: CHOICE_CARD_PROPS }
|
|
16
18
|
);
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
+
const cards = useChoiceCard({ props: () => fieldProps.value, ui: () => controlProps.value.ui });
|
|
20
|
+
const optionByKey = computed(
|
|
21
|
+
() => new Map(options.items.value.map((item) => [formOptionKey(item.value), item]))
|
|
19
22
|
);
|
|
20
23
|
const model = computed({
|
|
21
24
|
get: () => {
|
|
@@ -25,21 +28,17 @@ const model = computed({
|
|
|
25
28
|
set: (value) => form.setValue(
|
|
26
29
|
props.path,
|
|
27
30
|
value.flatMap((key) => {
|
|
28
|
-
const optionValue =
|
|
31
|
+
const optionValue = optionByKey.value.get(key)?.value;
|
|
29
32
|
return isUndefined(optionValue) ? [] : [optionValue];
|
|
30
33
|
})
|
|
31
34
|
)
|
|
32
35
|
});
|
|
33
36
|
const items = computed(
|
|
34
|
-
() => options.items.value.map((
|
|
37
|
+
() => options.items.value.map(({ icon: _icon, ...item }) => ({
|
|
38
|
+
...item,
|
|
39
|
+
value: formOptionKey(item.value)
|
|
40
|
+
}))
|
|
35
41
|
);
|
|
36
|
-
const groupUi = computed(() => ({
|
|
37
|
-
...controlProps.value.ui,
|
|
38
|
-
fieldset: mergeFormUiClass(
|
|
39
|
-
controlProps.value.ui?.fieldset,
|
|
40
|
-
fieldProps.value.orientation === "horizontal" ? "flex-wrap" : void 0
|
|
41
|
-
)
|
|
42
|
-
}));
|
|
43
42
|
function isOptionValue(value) {
|
|
44
43
|
return isString(value) || isNumber(value) || isBoolean(value);
|
|
45
44
|
}
|
|
@@ -54,12 +53,24 @@ function isOptionValue(value) {
|
|
|
54
53
|
value-key="value"
|
|
55
54
|
label-key="label"
|
|
56
55
|
description-key="description"
|
|
57
|
-
:orientation="fieldProps.orientation"
|
|
58
|
-
:ui="groupUi"
|
|
59
56
|
variant="card"
|
|
60
|
-
:
|
|
57
|
+
:orientation="cards.orientation.value"
|
|
58
|
+
:indicator="cards.indicator.value"
|
|
59
|
+
:ui="cards.ui.value"
|
|
60
|
+
:style="cards.style.value"
|
|
61
61
|
:disabled="disabled"
|
|
62
62
|
@blur="handleBlur"
|
|
63
|
-
|
|
63
|
+
>
|
|
64
|
+
<template #label="{ item }">
|
|
65
|
+
<ChoiceCardLabel
|
|
66
|
+
:label="optionByKey.get(item.value)?.label ?? item.label"
|
|
67
|
+
:icon="optionByKey.get(item.value)?.icon"
|
|
68
|
+
:tile="cards.tile.value"
|
|
69
|
+
:corner="cards.corner.value"
|
|
70
|
+
:selected="model.includes(item.value)"
|
|
71
|
+
:ui="cards.ui.value"
|
|
72
|
+
/>
|
|
73
|
+
</template>
|
|
74
|
+
</UCheckboxGroup>
|
|
64
75
|
</FormFieldShell>
|
|
65
76
|
</template>
|
|
@@ -1,11 +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 { FormOptionConfig, FormOptionItem, FormOptionValue, FormOptionsSource } from '../../types/options.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
indicator?: 'start' | 'end' | 'hidden';
|
|
7
|
-
}
|
|
8
|
-
export interface FormCheckboxCardField<TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>, TValue extends FormOptionValue = FormOptionValue, TOption extends FormOptionItem<TValue> = FormOptionItem<TValue>> extends FormStatefulFieldBase<'checkbox-card', readonly TValue[] | null, TContext, TDeps, FormCheckboxCardProps> {
|
|
4
|
+
import type { FormChoiceCardProps } from '../choice-card/types.js';
|
|
5
|
+
export interface FormCheckboxCardField<TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>, TValue extends FormOptionValue = FormOptionValue, TOption extends FormOptionItem<TValue> = FormOptionItem<TValue>> extends FormStatefulFieldBase<'checkbox-card', readonly TValue[] | null, TContext, TDeps, FormChoiceCardProps> {
|
|
9
6
|
options: FormOptionConfig<TOption, TContext, TDeps, readonly TValue[] | null> | FormOptionsSource<TOption, TContext, TDeps, readonly TValue[] | null>;
|
|
10
7
|
}
|
|
11
8
|
export type CheckboxCardFieldOutput<TField> = readonly FieldOptionValue<TField>[] | NullableValue;
|
|
@@ -6,6 +6,7 @@ import { useFieldControl } from "../../composables/use-field-control";
|
|
|
6
6
|
import { formOptionKey } from "../../utils/options";
|
|
7
7
|
import { isBoolean, isNumber, isString, isUndefined } from "../../utils/predicate";
|
|
8
8
|
import { mergeFormUiClass } from "../../utils/ui";
|
|
9
|
+
import { CARD_SELECTED_RING } from "../card-selection";
|
|
9
10
|
const props = defineProps({
|
|
10
11
|
field: { type: Object, required: true },
|
|
11
12
|
path: { type: Array, required: true }
|
|
@@ -38,6 +39,10 @@ const groupUi = computed(() => ({
|
|
|
38
39
|
fieldset: mergeFormUiClass(
|
|
39
40
|
controlProps.value.ui?.fieldset,
|
|
40
41
|
fieldProps.value.orientation === "horizontal" ? "flex-wrap" : void 0
|
|
42
|
+
),
|
|
43
|
+
item: mergeFormUiClass(
|
|
44
|
+
fieldProps.value.variant === "card" ? CARD_SELECTED_RING : void 0,
|
|
45
|
+
controlProps.value.ui?.item
|
|
41
46
|
)
|
|
42
47
|
}));
|
|
43
48
|
function isOptionValue(value) {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { FormControlUi } from '../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Content of a card's label: the option icon, inline or in a tile above the label, and the check
|
|
4
|
+
* a selected card shows in its corner. Styled through `ui.tile`, `ui.tileIcon`, `ui.optionIcon`,
|
|
5
|
+
* `ui.check`, and `ui.checkIcon`.
|
|
6
|
+
*/
|
|
7
|
+
type __VLS_Props = {
|
|
8
|
+
label?: string;
|
|
9
|
+
icon?: string;
|
|
10
|
+
tile: boolean;
|
|
11
|
+
corner: boolean;
|
|
12
|
+
selected: boolean;
|
|
13
|
+
ui?: FormControlUi;
|
|
14
|
+
};
|
|
15
|
+
declare const __VLS_export: 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>;
|
|
16
|
+
declare const _default: typeof __VLS_export;
|
|
17
|
+
export default _default;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import UIcon from "@nuxt/ui/components/Icon.vue";
|
|
3
|
+
import { mergeFormUiClass } from "../../utils/ui";
|
|
4
|
+
defineProps({
|
|
5
|
+
label: { type: String, required: false },
|
|
6
|
+
icon: { type: String, required: false },
|
|
7
|
+
tile: { type: Boolean, required: true },
|
|
8
|
+
corner: { type: Boolean, required: true },
|
|
9
|
+
selected: { type: Boolean, required: true },
|
|
10
|
+
ui: { type: Object, required: false }
|
|
11
|
+
});
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<span
|
|
16
|
+
v-if="tile && icon"
|
|
17
|
+
:data-selected="selected || void 0"
|
|
18
|
+
:class="
|
|
19
|
+
mergeFormUiClass(
|
|
20
|
+
'grid size-8 place-items-center rounded-lg bg-elevated text-muted transition-colors data-[selected]:bg-primary/10 data-[selected]:text-primary',
|
|
21
|
+
ui?.tile
|
|
22
|
+
)
|
|
23
|
+
"
|
|
24
|
+
data-choice-tile
|
|
25
|
+
>
|
|
26
|
+
<UIcon :name="icon" :class="mergeFormUiClass('size-4', ui?.tileIcon)" aria-hidden="true" />
|
|
27
|
+
</span>
|
|
28
|
+
<span v-if="!tile && icon" class="inline-flex items-center gap-2">
|
|
29
|
+
<UIcon
|
|
30
|
+
:name="icon"
|
|
31
|
+
:class="mergeFormUiClass('size-4 shrink-0', ui?.optionIcon)"
|
|
32
|
+
aria-hidden="true"
|
|
33
|
+
/>
|
|
34
|
+
<span>{{ label }}</span>
|
|
35
|
+
</span>
|
|
36
|
+
<span v-else class="block">{{ label }}</span>
|
|
37
|
+
<span
|
|
38
|
+
v-if="corner && selected"
|
|
39
|
+
aria-hidden="true"
|
|
40
|
+
:class="
|
|
41
|
+
mergeFormUiClass(
|
|
42
|
+
'absolute end-3 top-3 grid size-4 place-items-center rounded-[4px] bg-primary text-inverted',
|
|
43
|
+
ui?.check
|
|
44
|
+
)
|
|
45
|
+
"
|
|
46
|
+
data-choice-check
|
|
47
|
+
>
|
|
48
|
+
<UIcon name="i-lucide-check" :class="mergeFormUiClass('size-2.5', ui?.checkIcon)" />
|
|
49
|
+
</span>
|
|
50
|
+
</template>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { FormControlUi } from '../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Content of a card's label: the option icon, inline or in a tile above the label, and the check
|
|
4
|
+
* a selected card shows in its corner. Styled through `ui.tile`, `ui.tileIcon`, `ui.optionIcon`,
|
|
5
|
+
* `ui.check`, and `ui.checkIcon`.
|
|
6
|
+
*/
|
|
7
|
+
type __VLS_Props = {
|
|
8
|
+
label?: string;
|
|
9
|
+
icon?: string;
|
|
10
|
+
tile: boolean;
|
|
11
|
+
corner: boolean;
|
|
12
|
+
selected: boolean;
|
|
13
|
+
ui?: FormControlUi;
|
|
14
|
+
};
|
|
15
|
+
declare const __VLS_export: 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>;
|
|
16
|
+
declare const _default: typeof __VLS_export;
|
|
17
|
+
export default _default;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presentation of the radio and checkbox cards.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* // A fixed grid of cards, each with its icon in a tile above the label and a check in the
|
|
7
|
+
* // corner once selected.
|
|
8
|
+
* props: { columns: '1 md:2 lg:3', icon: 'tile', indicator: 'corner' }
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export interface FormChoiceCardProps {
|
|
12
|
+
/**
|
|
13
|
+
* Lays the cards out in a fixed grid of this many equal columns. Accepts breakpoints:
|
|
14
|
+
* `3` or `'1 md:2 lg:3'`. Without it, cards flow along `orientation`.
|
|
15
|
+
*/
|
|
16
|
+
columns?: number | string;
|
|
17
|
+
/** Direction of the cards when they are not in a `columns` grid. */
|
|
18
|
+
orientation?: 'horizontal' | 'vertical';
|
|
19
|
+
/**
|
|
20
|
+
* Selection mark: the radio or checkbox before the content (`start`, the default), after it
|
|
21
|
+
* (`end`), none (`hidden`), or a check in the top corner of the selected cards (`corner`).
|
|
22
|
+
*/
|
|
23
|
+
indicator?: 'start' | 'end' | 'hidden' | 'corner';
|
|
24
|
+
/**
|
|
25
|
+
* Where an option's `icon` shows: before its label (`inline`, the default), or in a tile above
|
|
26
|
+
* it (`tile`) that takes the selection color. Style the parts the engine renders with the
|
|
27
|
+
* `ui` slots `tile`, `tileIcon`, `optionIcon`, `check`, and `checkIcon`, next to the Nuxt UI
|
|
28
|
+
* slots of the group.
|
|
29
|
+
*/
|
|
30
|
+
icon?: 'inline' | 'tile';
|
|
31
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { FormControlUi } from '../../types/index.js';
|
|
2
|
+
import type { FormChoiceCardProps } from './types.js';
|
|
3
|
+
/** Props the card fields render themselves, kept away from the Nuxt UI group. */
|
|
4
|
+
export declare const CHOICE_CARD_PROPS: string[];
|
|
5
|
+
/**
|
|
6
|
+
* Presentation of a radio or checkbox card group: the flowing row or fixed grid of cards, the
|
|
7
|
+
* selection mark, and where option icons go. Returns the props and `ui` slots for the Nuxt UI
|
|
8
|
+
* group, and what the card content renders (`tile`, `corner`).
|
|
9
|
+
*/
|
|
10
|
+
export declare function useChoiceCard(params: {
|
|
11
|
+
props: () => FormChoiceCardProps;
|
|
12
|
+
ui: () => FormControlUi | undefined;
|
|
13
|
+
}): {
|
|
14
|
+
corner: import("vue").ComputedRef<boolean>;
|
|
15
|
+
indicator: import("vue").ComputedRef<"end" | "start" | "hidden" | undefined>;
|
|
16
|
+
orientation: import("vue").ComputedRef<"horizontal" | "vertical" | undefined>;
|
|
17
|
+
style: import("vue").ComputedRef<{
|
|
18
|
+
'--nut-choice-columns': string;
|
|
19
|
+
} | undefined>;
|
|
20
|
+
tile: import("vue").ComputedRef<boolean>;
|
|
21
|
+
ui: import("vue").ComputedRef<FormControlUi>;
|
|
22
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { computed } from "vue";
|
|
2
|
+
import { useResponsiveValue } from "../../../shared/composables/use-responsive-value.js";
|
|
3
|
+
import { mergeFormUiClass } from "../../utils/ui.js";
|
|
4
|
+
import { CARD_SELECTED_RING } from "../card-selection.js";
|
|
5
|
+
export const CHOICE_CARD_PROPS = ["columns", "icon", "indicator", "orientation"];
|
|
6
|
+
export function useChoiceCard(params) {
|
|
7
|
+
const columnsValue = useResponsiveValue(() => String(params.props().columns ?? ""));
|
|
8
|
+
const columns = computed(() => {
|
|
9
|
+
const count = Math.trunc(Number(columnsValue.value));
|
|
10
|
+
return count > 0 ? count : void 0;
|
|
11
|
+
});
|
|
12
|
+
const corner = computed(() => params.props().indicator === "corner");
|
|
13
|
+
const tile = computed(() => params.props().icon === "tile");
|
|
14
|
+
const indicator = computed(() => {
|
|
15
|
+
const value = params.props().indicator;
|
|
16
|
+
return value === "corner" ? "hidden" : value;
|
|
17
|
+
});
|
|
18
|
+
const orientation = computed(() => params.props().orientation);
|
|
19
|
+
const style = computed(
|
|
20
|
+
() => columns.value ? { "--nut-choice-columns": String(columns.value) } : void 0
|
|
21
|
+
);
|
|
22
|
+
const ui = computed(() => {
|
|
23
|
+
const current = params.ui();
|
|
24
|
+
return {
|
|
25
|
+
...current,
|
|
26
|
+
fieldset: mergeFormUiClass(
|
|
27
|
+
columns.value ? "grid grid-cols-[repeat(var(--nut-choice-columns),minmax(0,1fr))] gap-2.5" : `gap-2.5${orientation.value === "horizontal" ? " flex-wrap" : ""}`,
|
|
28
|
+
current?.fieldset
|
|
29
|
+
),
|
|
30
|
+
item: mergeFormUiClass(
|
|
31
|
+
`border-default ${CARD_SELECTED_RING}`,
|
|
32
|
+
corner.value ? "relative" : void 0,
|
|
33
|
+
current?.item
|
|
34
|
+
),
|
|
35
|
+
// A tile stacks above the title, and the description below it, with gaps.
|
|
36
|
+
label: mergeFormUiClass(
|
|
37
|
+
tile.value ? "flex flex-col items-start gap-2.5 font-semibold" : void 0,
|
|
38
|
+
current?.label
|
|
39
|
+
),
|
|
40
|
+
// A hidden indicator centers the text: keep it aligned, and clear of an inline corner check.
|
|
41
|
+
wrapper: mergeFormUiClass(
|
|
42
|
+
tile.value ? "flex flex-col gap-1.5" : void 0,
|
|
43
|
+
[
|
|
44
|
+
indicator.value === "hidden" ? "ms-0 text-start" : "",
|
|
45
|
+
corner.value && !tile.value ? "pe-7" : ""
|
|
46
|
+
].join(" "),
|
|
47
|
+
current?.wrapper
|
|
48
|
+
)
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
return { corner, indicator, orientation, style, tile, ui };
|
|
52
|
+
}
|
|
@@ -4,6 +4,8 @@ import { computed } from "vue";
|
|
|
4
4
|
import FormFieldShell from "../../components/renderer/form-field-shell.vue";
|
|
5
5
|
import { useFieldControl } from "../../composables/use-field-control";
|
|
6
6
|
import { isBoolean, isNumber, isString } from "../../utils/predicate";
|
|
7
|
+
import { mergeFormUiClass } from "../../utils/ui";
|
|
8
|
+
import { CARD_SELECTED_RING } from "../card-selection";
|
|
7
9
|
const props = defineProps({
|
|
8
10
|
field: { type: Object, required: true },
|
|
9
11
|
path: { type: Array, required: true }
|
|
@@ -23,6 +25,13 @@ const model = computed({
|
|
|
23
25
|
set: (value) => form.setValue(props.path, value)
|
|
24
26
|
});
|
|
25
27
|
const items = computed(() => [...options.items.value]);
|
|
28
|
+
const groupUi = computed(() => ({
|
|
29
|
+
...controlProps.value.ui,
|
|
30
|
+
item: mergeFormUiClass(
|
|
31
|
+
fieldProps.value.variant === "card" ? CARD_SELECTED_RING : void 0,
|
|
32
|
+
controlProps.value.ui?.item
|
|
33
|
+
)
|
|
34
|
+
}));
|
|
26
35
|
</script>
|
|
27
36
|
|
|
28
37
|
<template>
|
|
@@ -34,6 +43,7 @@ const items = computed(() => [...options.items.value]);
|
|
|
34
43
|
label-key="label"
|
|
35
44
|
description-key="description"
|
|
36
45
|
:items="items"
|
|
46
|
+
:ui="groupUi"
|
|
37
47
|
:variant="fieldProps.variant === 'table' ? 'list' : fieldProps.variant ?? 'list'"
|
|
38
48
|
:orientation="fieldProps.orientation"
|
|
39
49
|
:indicator="fieldProps.indicator"
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import UIcon from "@nuxt/ui/components/Icon.vue";
|
|
3
2
|
import URadioGroup from "@nuxt/ui/components/RadioGroup.vue";
|
|
4
3
|
import { computed } from "vue";
|
|
5
4
|
import FormFieldShell from "../../components/renderer/form-field-shell.vue";
|
|
6
5
|
import { useFieldControl } from "../../composables/use-field-control";
|
|
7
6
|
import { isBoolean, isNumber, isString } from "../../utils/predicate";
|
|
8
|
-
import
|
|
7
|
+
import ChoiceCardLabel from "../choice-card/choice-card-label.vue";
|
|
8
|
+
import { CHOICE_CARD_PROPS, useChoiceCard } from "../choice-card/use-choice-card";
|
|
9
9
|
const props = defineProps({
|
|
10
10
|
field: { type: Object, required: true },
|
|
11
11
|
path: { type: Array, required: true }
|
|
12
12
|
});
|
|
13
13
|
const { fieldProps, form, controlProps, disabled, handleBlur, options } = useFieldControl(
|
|
14
14
|
() => props.field,
|
|
15
|
-
() => props.path
|
|
15
|
+
() => props.path,
|
|
16
|
+
{ omit: CHOICE_CARD_PROPS }
|
|
16
17
|
);
|
|
18
|
+
const cards = useChoiceCard({ props: () => fieldProps.value, ui: () => controlProps.value.ui });
|
|
17
19
|
const model = computed({
|
|
18
20
|
get: () => {
|
|
19
21
|
const value = form.getValue(props.path);
|
|
@@ -25,13 +27,6 @@ const model = computed({
|
|
|
25
27
|
set: (value) => form.setValue(props.path, value)
|
|
26
28
|
});
|
|
27
29
|
const items = computed(() => [...options.items.value]);
|
|
28
|
-
const groupUi = computed(() => ({
|
|
29
|
-
...controlProps.value.ui,
|
|
30
|
-
fieldset: mergeFormUiClass(
|
|
31
|
-
controlProps.value.ui?.fieldset,
|
|
32
|
-
fieldProps.value.orientation === "horizontal" ? "flex-wrap" : void 0
|
|
33
|
-
)
|
|
34
|
-
}));
|
|
35
30
|
</script>
|
|
36
31
|
|
|
37
32
|
<template>
|
|
@@ -43,16 +38,22 @@ const groupUi = computed(() => ({
|
|
|
43
38
|
label-key="label"
|
|
44
39
|
variant="card"
|
|
45
40
|
:items="items"
|
|
46
|
-
:orientation="
|
|
47
|
-
:
|
|
41
|
+
:orientation="cards.orientation.value"
|
|
42
|
+
:indicator="cards.indicator.value"
|
|
43
|
+
:ui="cards.ui.value"
|
|
44
|
+
:style="cards.style.value"
|
|
48
45
|
:disabled="disabled"
|
|
49
46
|
@blur="handleBlur"
|
|
50
47
|
>
|
|
51
48
|
<template #label="{ item }">
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
<ChoiceCardLabel
|
|
50
|
+
:label="item.label"
|
|
51
|
+
:icon="item.icon"
|
|
52
|
+
:tile="cards.tile.value"
|
|
53
|
+
:corner="cards.corner.value"
|
|
54
|
+
:selected="model === item.value"
|
|
55
|
+
:ui="cards.ui.value"
|
|
56
|
+
/>
|
|
56
57
|
</template>
|
|
57
58
|
</URadioGroup>
|
|
58
59
|
</FormFieldShell>
|