nuxt-ui-tools 1.17.1 → 1.19.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-header.d.vue.ts +3 -1
- package/dist/runtime/form/components/page/form-page-header.vue +44 -34
- package/dist/runtime/form/components/page/form-page-header.vue.d.ts +3 -1
- 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 +11 -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/components/utils/form-option-choice-label.d.vue.ts +16 -0
- package/dist/runtime/form/components/utils/form-option-choice-label.vue +37 -0
- package/dist/runtime/form/components/utils/form-option-choice-label.vue.d.ts +16 -0
- package/dist/runtime/form/components/utils/form-option-display-value.d.vue.ts +14 -0
- package/dist/runtime/form/components/utils/form-option-display-value.vue +20 -0
- package/dist/runtime/form/components/utils/form-option-display-value.vue.d.ts +14 -0
- package/dist/runtime/form/components/utils/form-option-item-label.d.vue.ts +9 -2
- package/dist/runtime/form/components/utils/form-option-item-label.vue +20 -13
- package/dist/runtime/form/components/utils/form-option-item-label.vue.d.ts +9 -2
- package/dist/runtime/form/components/utils/form-option-leading.d.vue.ts +23 -0
- package/dist/runtime/form/components/utils/form-option-leading.vue +41 -0
- package/dist/runtime/form/components/utils/form-option-leading.vue.d.ts +23 -0
- package/dist/runtime/form/components/utils/form-option-render.d.ts +11 -0
- package/dist/runtime/form/components/utils/form-option-render.js +5 -0
- package/dist/runtime/form/components/utils/form-option-suffix.d.vue.ts +8 -0
- package/dist/runtime/form/components/utils/form-option-suffix.vue +15 -0
- package/dist/runtime/form/components/utils/form-option-suffix.vue.d.ts +8 -0
- package/dist/runtime/form/components/utils/form-option-value-overlay.d.vue.ts +30 -0
- package/dist/runtime/form/components/utils/form-option-value-overlay.vue +44 -0
- package/dist/runtime/form/components/utils/form-option-value-overlay.vue.d.ts +30 -0
- package/dist/runtime/form/composables/use-choice-group-items.d.ts +27 -0
- package/dist/runtime/form/composables/use-choice-group-items.js +24 -0
- package/dist/runtime/form/composables/use-field-control.d.ts +6 -6
- 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-layout.js +4 -3
- 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/composables/use-option-menu.d.ts +20 -0
- package/dist/runtime/form/composables/use-option-menu.js +41 -0
- package/dist/runtime/form/fields/array-primitive/types.d.ts +1 -1
- package/dist/runtime/form/fields/auto-complete/component.vue +92 -51
- package/dist/runtime/form/fields/auto-complete/config.d.ts +1 -0
- package/dist/runtime/form/fields/auto-complete/config.js +1 -1
- package/dist/runtime/form/fields/auto-complete/types.d.ts +2 -2
- package/dist/runtime/form/fields/checkbox-card/component.vue +55 -29
- 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/checkbox-group/component.vue +18 -4
- package/dist/runtime/form/fields/checkbox-group/types.d.ts +2 -2
- 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 +35 -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 +10 -0
- package/dist/runtime/form/fields/choice-card/utils.js +6 -0
- package/dist/runtime/form/fields/hierarchy/component.vue +3 -2
- package/dist/runtime/form/fields/index.d.ts +4 -0
- package/dist/runtime/form/fields/radio/component.vue +19 -2
- package/dist/runtime/form/fields/radio/types.d.ts +2 -2
- 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/fields/select/component.vue +56 -58
- package/dist/runtime/form/fields/select/config.d.ts +1 -0
- package/dist/runtime/form/fields/select/config.js +1 -1
- package/dist/runtime/form/fields/select/types.d.ts +2 -2
- 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/layout.d.ts +6 -0
- package/dist/runtime/form/types/options.d.ts +137 -0
- package/dist/runtime/form/types/page.d.ts +20 -8
- package/dist/runtime/form/types/ui.d.ts +29 -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 +56 -11
- package/dist/runtime/form/utils/options.js +86 -0
- package/dist/runtime/form/utils/page-surface.d.ts +26 -0
- package/dist/runtime/form/utils/page-surface.js +28 -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/spreadsheet/components/internal/spreadsheet-import-column-row.d.vue.ts +2 -2
- package/dist/runtime/spreadsheet/components/internal/spreadsheet-import-column-row.vue.d.ts +2 -2
- 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/filters/tags/boolean-filter-tag.d.vue.ts +2 -2
- package/dist/runtime/table/components/filters/tags/boolean-filter-tag.vue.d.ts +2 -2
- package/dist/runtime/table/components/filters/tags/date-filter-tag.d.vue.ts +2 -2
- package/dist/runtime/table/components/filters/tags/date-filter-tag.vue.d.ts +2 -2
- package/dist/runtime/table/components/filters/tags/number-filter-tag.d.vue.ts +2 -2
- package/dist/runtime/table/components/filters/tags/number-filter-tag.vue.d.ts +2 -2
- package/dist/runtime/table/components/filters/tags/option-filter-tag.d.vue.ts +2 -2
- package/dist/runtime/table/components/filters/tags/option-filter-tag.vue.d.ts +2 -2
- package/dist/runtime/table/components/filters/tags/text-filter-tag.d.vue.ts +2 -2
- package/dist/runtime/table/components/filters/tags/text-filter-tag.vue.d.ts +2 -2
- 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/remote-options.d.ts +4 -1
- 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.d.ts +1 -1
- 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/dist/runtime/table/utils/remote-table-options.js +1 -1
- package/package.json +1 -1
|
@@ -7,12 +7,19 @@ import type { ChoiceCardMark } from './types.js';
|
|
|
7
7
|
* semantics. Styled through `ui.tile`, `ui.tileIcon`, `ui.optionIcon`, `ui.check`,
|
|
8
8
|
* `ui.checkIcon`, `ui.switch`, and `ui.switchThumb`; the tile and the switch carry the option
|
|
9
9
|
* value as `data-value` for per-option styling.
|
|
10
|
+
*
|
|
11
|
+
* A `compact` card lays itself out as one row inside the grid its label becomes: the icon (or a
|
|
12
|
+
* smaller tile) beside the title, over a one-line description. Both lines truncate and keep the
|
|
13
|
+
* full text in their tooltip, and the marks center on the row. The card passes the description
|
|
14
|
+
* here because the Nuxt UI group would draw it below the icon instead.
|
|
10
15
|
*/
|
|
11
16
|
type __VLS_Props = {
|
|
12
17
|
label?: string;
|
|
18
|
+
description?: string;
|
|
13
19
|
icon?: string;
|
|
14
20
|
value?: string | number | boolean;
|
|
15
21
|
tile: boolean;
|
|
22
|
+
compact?: boolean;
|
|
16
23
|
mark: ChoiceCardMark;
|
|
17
24
|
selected: boolean;
|
|
18
25
|
ui?: FormControlUi;
|
|
@@ -3,13 +3,17 @@ import UIcon from "@nuxt/ui/components/Icon.vue";
|
|
|
3
3
|
import { mergeFormUiClass } from "../../utils/ui";
|
|
4
4
|
defineProps({
|
|
5
5
|
label: { type: String, required: false },
|
|
6
|
+
description: { type: String, required: false },
|
|
6
7
|
icon: { type: String, required: false },
|
|
7
8
|
value: { type: [String, Number, Boolean], required: false },
|
|
8
9
|
tile: { type: Boolean, required: true },
|
|
10
|
+
compact: { type: Boolean, required: false },
|
|
9
11
|
mark: { type: String, required: true },
|
|
10
12
|
selected: { type: Boolean, required: true },
|
|
11
13
|
ui: { type: Object, required: false }
|
|
12
14
|
});
|
|
15
|
+
const COMPACT_MARK = "end-2.5 top-1/2 -translate-y-1/2";
|
|
16
|
+
const COMPACT_TILE = "col-start-1 row-span-2 me-2.5 size-7 rounded-md";
|
|
13
17
|
</script>
|
|
14
18
|
|
|
15
19
|
<template>
|
|
@@ -20,6 +24,7 @@ defineProps({
|
|
|
20
24
|
:class="
|
|
21
25
|
mergeFormUiClass(
|
|
22
26
|
'grid size-8 place-items-center rounded-lg bg-elevated text-muted transition-colors data-[selected]:bg-primary/10 data-[selected]:text-primary',
|
|
27
|
+
compact ? COMPACT_TILE : void 0,
|
|
23
28
|
ui?.tile
|
|
24
29
|
)
|
|
25
30
|
"
|
|
@@ -27,7 +32,26 @@ defineProps({
|
|
|
27
32
|
>
|
|
28
33
|
<UIcon :name="icon" :class="mergeFormUiClass('size-4', ui?.tileIcon)" aria-hidden="true" />
|
|
29
34
|
</span>
|
|
30
|
-
<
|
|
35
|
+
<template v-if="compact">
|
|
36
|
+
<UIcon
|
|
37
|
+
v-if="icon && !tile"
|
|
38
|
+
:name="icon"
|
|
39
|
+
:class="
|
|
40
|
+
mergeFormUiClass('col-start-1 row-span-2 me-2.5 size-4 shrink-0 text-muted', ui?.optionIcon)
|
|
41
|
+
"
|
|
42
|
+
aria-hidden="true"
|
|
43
|
+
/>
|
|
44
|
+
<span :title="label" class="col-start-2 block truncate" data-choice-title>{{ label }}</span>
|
|
45
|
+
<span
|
|
46
|
+
v-if="description"
|
|
47
|
+
:title="description"
|
|
48
|
+
class="col-start-2 block truncate text-xs font-normal text-muted"
|
|
49
|
+
data-choice-description
|
|
50
|
+
>
|
|
51
|
+
{{ description }}
|
|
52
|
+
</span>
|
|
53
|
+
</template>
|
|
54
|
+
<span v-else-if="!tile && icon" class="inline-flex items-center gap-2">
|
|
31
55
|
<UIcon
|
|
32
56
|
:name="icon"
|
|
33
57
|
:class="mergeFormUiClass('size-4 shrink-0', ui?.optionIcon)"
|
|
@@ -42,6 +66,7 @@ defineProps({
|
|
|
42
66
|
:class="
|
|
43
67
|
mergeFormUiClass(
|
|
44
68
|
'absolute end-3 top-3 grid size-4 place-items-center rounded-[4px] bg-primary text-inverted',
|
|
69
|
+
compact ? COMPACT_MARK : void 0,
|
|
45
70
|
ui?.check
|
|
46
71
|
)
|
|
47
72
|
"
|
|
@@ -57,6 +82,7 @@ defineProps({
|
|
|
57
82
|
:class="
|
|
58
83
|
mergeFormUiClass(
|
|
59
84
|
'group/switch absolute end-3 top-3.5 inline-flex h-[18px] w-8 shrink-0 items-center rounded-full bg-accented p-0.5 transition-colors duration-150 data-[state=checked]:bg-primary',
|
|
85
|
+
compact ? COMPACT_MARK : void 0,
|
|
60
86
|
ui?.switch
|
|
61
87
|
)
|
|
62
88
|
"
|
|
@@ -7,12 +7,19 @@ import type { ChoiceCardMark } from './types.js';
|
|
|
7
7
|
* semantics. Styled through `ui.tile`, `ui.tileIcon`, `ui.optionIcon`, `ui.check`,
|
|
8
8
|
* `ui.checkIcon`, `ui.switch`, and `ui.switchThumb`; the tile and the switch carry the option
|
|
9
9
|
* value as `data-value` for per-option styling.
|
|
10
|
+
*
|
|
11
|
+
* A `compact` card lays itself out as one row inside the grid its label becomes: the icon (or a
|
|
12
|
+
* smaller tile) beside the title, over a one-line description. Both lines truncate and keep the
|
|
13
|
+
* full text in their tooltip, and the marks center on the row. The card passes the description
|
|
14
|
+
* here because the Nuxt UI group would draw it below the icon instead.
|
|
10
15
|
*/
|
|
11
16
|
type __VLS_Props = {
|
|
12
17
|
label?: string;
|
|
18
|
+
description?: string;
|
|
13
19
|
icon?: string;
|
|
14
20
|
value?: string | number | boolean;
|
|
15
21
|
tile: boolean;
|
|
22
|
+
compact?: boolean;
|
|
16
23
|
mark: ChoiceCardMark;
|
|
17
24
|
selected: boolean;
|
|
18
25
|
ui?: FormControlUi;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import type { FormOption, FormOptionValue } from '../../types/options.js';
|
|
2
|
+
import type { FormText } from '../../types/utils.js';
|
|
3
|
+
/** Size of the cards: `compact` packs long option lists into smaller, single-row cards. */
|
|
4
|
+
export type FormChoiceCardDensity = 'default' | 'compact';
|
|
1
5
|
/**
|
|
2
6
|
* Presentation of the radio and checkbox cards.
|
|
3
7
|
*
|
|
@@ -6,6 +10,9 @@
|
|
|
6
10
|
* // A fixed grid of cards, each with its icon in a tile above the label and a check in the
|
|
7
11
|
* // corner once selected.
|
|
8
12
|
* props: { columns: '1 md:2 lg:3', icon: 'tile', indicator: 'corner' }
|
|
13
|
+
*
|
|
14
|
+
* // Eight options in four groups, as smaller cards with the icon beside a one-line label.
|
|
15
|
+
* props: { columns: '1 md:2', density: 'compact', icon: 'tile', indicator: 'corner' }
|
|
9
16
|
* ```
|
|
10
17
|
*/
|
|
11
18
|
export interface FormChoiceCardProps {
|
|
@@ -25,12 +32,39 @@ export interface FormChoiceCardProps {
|
|
|
25
32
|
indicator?: 'start' | 'end' | 'hidden' | 'corner' | 'switch';
|
|
26
33
|
/**
|
|
27
34
|
* Where an option's `icon` shows: before its label (`inline`, the default), or in a tile above
|
|
28
|
-
* it (`tile`) that takes the selection color. Style the parts the engine renders with the
|
|
35
|
+
* it (`tile`) that takes the selection color, beside the label in the `compact` density. Style the parts the engine renders with the
|
|
29
36
|
* `ui` slots `tile`, `tileIcon`, `optionIcon`, `check`, `checkIcon`, `switch`, and
|
|
30
37
|
* `switchThumb`, next to the Nuxt UI slots of the group. The tile and the switch carry the
|
|
31
38
|
* option value as `data-value`, so one option can take its own colors.
|
|
32
39
|
*/
|
|
33
40
|
icon?: 'inline' | 'tile';
|
|
41
|
+
/**
|
|
42
|
+
* Size of the cards. `compact` shrinks the padding and tile, puts the icon beside the label
|
|
43
|
+
* instead of above it, and keeps the title and the description on one line each, truncated with
|
|
44
|
+
* an ellipsis (the full description stays in the card's tooltip). Defaults to `default`.
|
|
45
|
+
*/
|
|
46
|
+
density?: FormChoiceCardDensity;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Option of a radio or checkbox card: the shared option shape plus the heading the cards can show.
|
|
50
|
+
* A card draws its `icon` only, so the avatar, chip and suffix of other option fields are left out.
|
|
51
|
+
*/
|
|
52
|
+
export interface FormChoiceCardOption<TValue extends FormOptionValue = FormOptionValue> extends Omit<FormOption<TValue>, 'avatar' | 'chip' | 'suffix'> {
|
|
53
|
+
/** Icon shown with the label, inline or in a tile (see `props.icon`). */
|
|
54
|
+
icon?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Heading this option is listed under. Options sharing a heading are listed together, and the
|
|
57
|
+
* headings follow the order in which they first appear in the options. Options without one
|
|
58
|
+
* stay together, without a heading. Read from this property for options written inline in the
|
|
59
|
+
* schema; options a function, a promise or a query returns need `groupKey`.
|
|
60
|
+
*/
|
|
61
|
+
group?: FormText;
|
|
34
62
|
}
|
|
63
|
+
/** Authored option item accepted by radio and checkbox cards. */
|
|
64
|
+
export type FormChoiceCardOptionItem<TValue extends FormOptionValue = FormOptionValue> = TValue | FormChoiceCardOption<TValue>;
|
|
65
|
+
/** The card layouts: icon before the label, icon tile above it, or one compact row. */
|
|
66
|
+
export type ChoiceCardLayout = 'inline' | 'stacked' | 'compact';
|
|
35
67
|
/** The mark a card draws itself, in place of the Nuxt UI indicator. */
|
|
36
68
|
export type ChoiceCardMark = 'corner' | 'switch' | 'none';
|
|
69
|
+
/** The Nuxt UI group a card field wraps. */
|
|
70
|
+
export type ChoiceCardKind = 'checkbox' | 'radio';
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import type { FormControlUi } from '../../types/index.js';
|
|
2
|
-
import type { ChoiceCardMark, FormChoiceCardProps } from './types.js';
|
|
2
|
+
import type { ChoiceCardKind, ChoiceCardMark, FormChoiceCardProps } from './types.js';
|
|
3
3
|
/** Props the card fields render themselves, kept away from the Nuxt UI group. */
|
|
4
4
|
export declare const CHOICE_CARD_PROPS: string[];
|
|
5
5
|
/**
|
|
6
6
|
* Presentation of a radio or checkbox card group: the flowing row or fixed grid of cards, the
|
|
7
|
-
* selection mark,
|
|
8
|
-
* group, and what the card content renders
|
|
7
|
+
* selection mark, where option icons go, the density, and the headings of grouped options.
|
|
8
|
+
* Returns the props and `ui` slots for the Nuxt UI group, and what the card content renders
|
|
9
|
+
* (`compact`, `tile`, `mark`).
|
|
9
10
|
*/
|
|
10
11
|
export declare function useChoiceCard(params: {
|
|
12
|
+
kind: ChoiceCardKind;
|
|
11
13
|
props: () => FormChoiceCardProps;
|
|
12
14
|
ui: () => FormControlUi | undefined;
|
|
13
15
|
}): {
|
|
16
|
+
compact: import("vue").ComputedRef<boolean>;
|
|
17
|
+
groupsClass: import("vue").ComputedRef<string>;
|
|
14
18
|
indicator: import("vue").ComputedRef<"end" | "start" | "hidden" | undefined>;
|
|
15
19
|
mark: import("vue").ComputedRef<ChoiceCardMark>;
|
|
16
20
|
orientation: import("vue").ComputedRef<"horizontal" | "vertical" | undefined>;
|
|
@@ -2,7 +2,25 @@ import { computed } from "vue";
|
|
|
2
2
|
import { useResponsiveValue } from "../../../shared/composables/use-responsive-value.js";
|
|
3
3
|
import { mergeFormUiClass } from "../../utils/ui.js";
|
|
4
4
|
import { CARD_SELECTED_RING } from "../card-selection.js";
|
|
5
|
-
export const CHOICE_CARD_PROPS = ["columns", "icon", "indicator", "orientation"];
|
|
5
|
+
export const CHOICE_CARD_PROPS = ["columns", "density", "icon", "indicator", "orientation"];
|
|
6
|
+
const LAYOUT_UI = {
|
|
7
|
+
compact: {
|
|
8
|
+
gap: "gap-2",
|
|
9
|
+
item: "items-center p-2.5",
|
|
10
|
+
label: "grid min-w-0 grid-cols-[auto_minmax(0,1fr)] items-center",
|
|
11
|
+
wrapper: "min-w-0 items-stretch"
|
|
12
|
+
},
|
|
13
|
+
inline: { gap: "gap-2.5" },
|
|
14
|
+
stacked: {
|
|
15
|
+
gap: "gap-2.5",
|
|
16
|
+
label: "flex flex-col items-start gap-2.5 font-semibold",
|
|
17
|
+
wrapper: "flex flex-col items-start gap-1.5"
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const ICON_UI = {
|
|
21
|
+
checkbox: void 0,
|
|
22
|
+
radio: "hidden"
|
|
23
|
+
};
|
|
6
24
|
export function useChoiceCard(params) {
|
|
7
25
|
const columnsValue = useResponsiveValue(() => String(params.props().columns ?? ""));
|
|
8
26
|
const columns = computed(() => {
|
|
@@ -13,7 +31,12 @@ export function useChoiceCard(params) {
|
|
|
13
31
|
const value = params.props().indicator;
|
|
14
32
|
return value === "corner" || value === "switch" ? value : "none";
|
|
15
33
|
});
|
|
34
|
+
const compact = computed(() => params.props().density === "compact");
|
|
16
35
|
const tile = computed(() => params.props().icon === "tile");
|
|
36
|
+
const layout = computed(() => {
|
|
37
|
+
if (compact.value) return "compact";
|
|
38
|
+
return tile.value ? "stacked" : "inline";
|
|
39
|
+
});
|
|
17
40
|
const indicator = computed(() => {
|
|
18
41
|
const value = params.props().indicator;
|
|
19
42
|
return value === "corner" || value === "switch" ? "hidden" : value;
|
|
@@ -24,36 +47,35 @@ export function useChoiceCard(params) {
|
|
|
24
47
|
);
|
|
25
48
|
const ui = computed(() => {
|
|
26
49
|
const current = params.ui();
|
|
50
|
+
const layoutUi = LAYOUT_UI[layout.value];
|
|
51
|
+
const stacked = layout.value === "stacked";
|
|
27
52
|
return {
|
|
28
53
|
...current,
|
|
29
54
|
fieldset: mergeFormUiClass(
|
|
30
|
-
columns.value ?
|
|
55
|
+
columns.value ? `grid grid-cols-[repeat(var(--nut-choice-columns),minmax(0,1fr))] ${layoutUi.gap}` : `${layoutUi.gap}${orientation.value === "horizontal" ? " flex-wrap" : ""}`,
|
|
31
56
|
current?.fieldset
|
|
32
57
|
),
|
|
33
58
|
item: mergeFormUiClass(
|
|
34
|
-
`border-default ${CARD_SELECTED_RING}`,
|
|
59
|
+
`border-default ${CARD_SELECTED_RING} ${layoutUi.item ?? ""}`,
|
|
35
60
|
mark.value === "none" ? void 0 : "relative",
|
|
36
61
|
current?.item
|
|
37
62
|
),
|
|
38
|
-
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
label: mergeFormUiClass(
|
|
43
|
-
tile.value ? "flex flex-col items-start gap-2.5 font-semibold" : void 0,
|
|
44
|
-
current?.label
|
|
45
|
-
),
|
|
63
|
+
icon: mergeFormUiClass(ICON_UI[params.kind], current?.icon),
|
|
64
|
+
// The heading of a group of options, set above its cards.
|
|
65
|
+
legend: mergeFormUiClass("mb-2 text-sm font-medium text-muted", current?.legend),
|
|
66
|
+
label: mergeFormUiClass(layoutUi.label, current?.label),
|
|
46
67
|
// A hidden indicator centers the text: keep it aligned, and clear of an inline corner check.
|
|
47
68
|
wrapper: mergeFormUiClass(
|
|
48
|
-
|
|
69
|
+
layoutUi.wrapper,
|
|
49
70
|
[
|
|
50
71
|
indicator.value === "hidden" ? "ms-0 text-start" : "",
|
|
51
|
-
mark.value === "corner" && !
|
|
52
|
-
mark.value === "switch" && !
|
|
72
|
+
mark.value === "corner" && !stacked ? "pe-7" : "",
|
|
73
|
+
mark.value === "switch" && !stacked ? "pe-12" : ""
|
|
53
74
|
].join(" "),
|
|
54
75
|
current?.wrapper
|
|
55
76
|
)
|
|
56
77
|
};
|
|
57
78
|
});
|
|
58
|
-
|
|
79
|
+
const groupsClass = computed(() => mergeFormUiClass("flex flex-col gap-5", params.ui()?.groups));
|
|
80
|
+
return { compact, groupsClass, indicator, mark, orientation, style, tile, ui };
|
|
59
81
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { FormObject } from '../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Attributes for the Nuxt UI group at `index` among the groups of one field. Every group carries
|
|
4
|
+
* the control attributes of the field, but an `id` names a single element: the first group keeps
|
|
5
|
+
* it, as the control the label points to, and each later group takes `<id>-group-<index>`. The other
|
|
6
|
+
* attributes (`data-form-field`, `aria-label`) stay on every group, so focus moving from one
|
|
7
|
+
* group to the next does not leave the field. Attributes without an `id` are returned as they
|
|
8
|
+
* are: each Nuxt UI group then generates its own.
|
|
9
|
+
*/
|
|
10
|
+
export declare function choiceCardGroupAttrs<TAttrs extends FormObject>(attrs: TAttrs, index: number): TAttrs;
|
|
@@ -133,15 +133,16 @@ const selectedTreeItems = computed(() => {
|
|
|
133
133
|
return Array.isArray(selected) ? selected : [selected];
|
|
134
134
|
});
|
|
135
135
|
const treeSelectLabel = computed(() => {
|
|
136
|
+
const empty = placeholder.value ?? "";
|
|
136
137
|
if (!selectedTreeItems.value.length) {
|
|
137
|
-
return
|
|
138
|
+
return empty;
|
|
138
139
|
}
|
|
139
140
|
if (treeMultiple.value) {
|
|
140
141
|
return selectedTreeItems.value.map((item) => item.pathLabel).join(", ");
|
|
141
142
|
}
|
|
142
143
|
const [selected] = selectedTreeItems.value;
|
|
143
144
|
if (!selected) {
|
|
144
|
-
return
|
|
145
|
+
return empty;
|
|
145
146
|
}
|
|
146
147
|
return props.field.type === "tree-select" && fieldProps.value.showPath ? selected.pathLabel : String(selected.label);
|
|
147
148
|
});
|
|
@@ -86,6 +86,7 @@ export declare const formFieldKinds: readonly [{
|
|
|
86
86
|
};
|
|
87
87
|
readonly options: {
|
|
88
88
|
readonly enabled: true;
|
|
89
|
+
readonly groups: true;
|
|
89
90
|
};
|
|
90
91
|
readonly state: "stateful";
|
|
91
92
|
readonly transform: true;
|
|
@@ -529,6 +530,7 @@ export declare const formFieldKinds: readonly [{
|
|
|
529
530
|
};
|
|
530
531
|
readonly options: {
|
|
531
532
|
readonly enabled: true;
|
|
533
|
+
readonly groups: true;
|
|
532
534
|
};
|
|
533
535
|
readonly state: "stateful";
|
|
534
536
|
readonly transform: true;
|
|
@@ -545,6 +547,7 @@ export declare const formFieldKinds: readonly [{
|
|
|
545
547
|
};
|
|
546
548
|
readonly options: {
|
|
547
549
|
readonly enabled: true;
|
|
550
|
+
readonly groups: true;
|
|
548
551
|
};
|
|
549
552
|
readonly state: "stateful";
|
|
550
553
|
readonly transform: true;
|
|
@@ -561,6 +564,7 @@ export declare const formFieldKinds: readonly [{
|
|
|
561
564
|
};
|
|
562
565
|
readonly options: {
|
|
563
566
|
readonly enabled: true;
|
|
567
|
+
readonly groups: true;
|
|
564
568
|
};
|
|
565
569
|
readonly state: "stateful";
|
|
566
570
|
readonly transform: true;
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
import URadioGroup from "@nuxt/ui/components/RadioGroup.vue";
|
|
3
3
|
import { computed } from "vue";
|
|
4
4
|
import FormFieldShell from "../../components/renderer/form-field-shell.vue";
|
|
5
|
+
import FormOptionChoiceLabel from "../../components/utils/form-option-choice-label.vue";
|
|
6
|
+
import { useChoiceGroupItems } from "../../composables/use-choice-group-items";
|
|
5
7
|
import { useFieldControl } from "../../composables/use-field-control";
|
|
8
|
+
import { formOptionKey } from "../../utils/options";
|
|
6
9
|
import { isBoolean, isNumber, isString } from "../../utils/predicate";
|
|
7
10
|
import { mergeFormUiClass } from "../../utils/ui";
|
|
8
11
|
import { CARD_SELECTED_RING } from "../card-selection";
|
|
@@ -24,7 +27,13 @@ const model = computed({
|
|
|
24
27
|
},
|
|
25
28
|
set: (value) => form.setValue(props.path, value)
|
|
26
29
|
});
|
|
27
|
-
const
|
|
30
|
+
const renderOption = computed(() => props.field.renderOption);
|
|
31
|
+
const { items, optionOf } = useChoiceGroupItems({
|
|
32
|
+
custom: () => renderOption.value !== void 0,
|
|
33
|
+
options,
|
|
34
|
+
ui: () => controlProps.value.ui,
|
|
35
|
+
valueOf: (option) => option.value
|
|
36
|
+
});
|
|
28
37
|
const groupUi = computed(() => ({
|
|
29
38
|
...controlProps.value.ui,
|
|
30
39
|
item: mergeFormUiClass(
|
|
@@ -49,6 +58,14 @@ const groupUi = computed(() => ({
|
|
|
49
58
|
:indicator="fieldProps.indicator"
|
|
50
59
|
:disabled="disabled"
|
|
51
60
|
@blur="handleBlur"
|
|
52
|
-
|
|
61
|
+
>
|
|
62
|
+
<template #label="{ item }">
|
|
63
|
+
<FormOptionChoiceLabel
|
|
64
|
+
:option="optionOf(formOptionKey(item.value))"
|
|
65
|
+
:selected="model === item.value"
|
|
66
|
+
:render="renderOption"
|
|
67
|
+
/>
|
|
68
|
+
</template>
|
|
69
|
+
</URadioGroup>
|
|
53
70
|
</FormFieldShell>
|
|
54
71
|
</template>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FormStatefulFieldBase } from '../../types/field-base.js';
|
|
2
2
|
import type { FieldOptionValue, NullableValue } from '../../types/field-output-utils.js';
|
|
3
|
-
import type { FormOptionItem, FormOptionValue, FormOptionsSource } from '../../types/options.js';
|
|
3
|
+
import type { FormOptionItem, FormOptionRenderers, FormOptionValue, FormOptionsSource } from '../../types/options.js';
|
|
4
4
|
export interface FormRadioProps {
|
|
5
5
|
/**
|
|
6
6
|
* `list` (the default) lines up bare radios, `card` frames each option on its own, and `table`
|
|
@@ -11,7 +11,7 @@ export interface FormRadioProps {
|
|
|
11
11
|
orientation?: 'horizontal' | 'vertical';
|
|
12
12
|
indicator?: 'start' | 'end' | 'hidden';
|
|
13
13
|
}
|
|
14
|
-
export interface FormRadioField<TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>, TValue extends FormOptionValue = FormOptionValue, TOption extends FormOptionItem<TValue> = FormOptionItem<TValue>> extends FormStatefulFieldBase<'radio', TValue | null, TContext, TDeps, FormRadioProps> {
|
|
14
|
+
export interface FormRadioField<TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>, TValue extends FormOptionValue = FormOptionValue, TOption extends FormOptionItem<TValue> = FormOptionItem<TValue>> extends FormStatefulFieldBase<'radio', TValue | null, TContext, TDeps, FormRadioProps>, FormOptionRenderers<TOption> {
|
|
15
15
|
options: FormOptionsSource<TOption, TContext, TDeps, TValue | null>;
|
|
16
16
|
}
|
|
17
17
|
export type RadioFieldOutput<TField> = FieldOptionValue<TField> | NullableValue;
|
|
@@ -3,9 +3,11 @@ import URadioGroup from "@nuxt/ui/components/RadioGroup.vue";
|
|
|
3
3
|
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
|
+
import { formOptionKey, groupOptions } from "../../utils/options";
|
|
6
7
|
import { isBoolean, isNumber, isString } from "../../utils/predicate";
|
|
7
8
|
import ChoiceCardLabel from "../choice-card/choice-card-label.vue";
|
|
8
9
|
import { CHOICE_CARD_PROPS, useChoiceCard } from "../choice-card/use-choice-card";
|
|
10
|
+
import { choiceCardGroupAttrs } from "../choice-card/utils";
|
|
9
11
|
const props = defineProps({
|
|
10
12
|
field: { type: Object, required: true },
|
|
11
13
|
path: { type: Array, required: true }
|
|
@@ -15,7 +17,11 @@ const { fieldProps, form, controlProps, disabled, handleBlur, options } = useFie
|
|
|
15
17
|
() => props.path,
|
|
16
18
|
{ omit: CHOICE_CARD_PROPS }
|
|
17
19
|
);
|
|
18
|
-
const cards = useChoiceCard({
|
|
20
|
+
const cards = useChoiceCard({
|
|
21
|
+
kind: "radio",
|
|
22
|
+
props: () => fieldProps.value,
|
|
23
|
+
ui: () => controlProps.value.ui
|
|
24
|
+
});
|
|
19
25
|
const model = computed({
|
|
20
26
|
get: () => {
|
|
21
27
|
const value = form.getValue(props.path);
|
|
@@ -26,36 +32,60 @@ const model = computed({
|
|
|
26
32
|
},
|
|
27
33
|
set: (value) => form.setValue(props.path, value)
|
|
28
34
|
});
|
|
29
|
-
const
|
|
35
|
+
const descriptionByKey = computed(
|
|
36
|
+
() => new Map(options.items.value.map((item) => [formOptionKey(item.value), item.description]))
|
|
37
|
+
);
|
|
38
|
+
const items = computed(
|
|
39
|
+
() => options.items.value.map(
|
|
40
|
+
(item) => cards.compact.value ? { ...item, description: void 0 } : item
|
|
41
|
+
)
|
|
42
|
+
);
|
|
43
|
+
const groups = computed(() => groupOptions(items.value));
|
|
44
|
+
function selectionOf(group) {
|
|
45
|
+
return group.items.some((item) => item.value === model.value) ? model.value : void 0;
|
|
46
|
+
}
|
|
47
|
+
function select(value) {
|
|
48
|
+
if (isString(value) || isNumber(value) || isBoolean(value)) model.value = value;
|
|
49
|
+
}
|
|
30
50
|
</script>
|
|
31
51
|
|
|
32
52
|
<template>
|
|
33
53
|
<FormFieldShell :field="field" :path="path">
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
<div :class="cards.groupsClass.value">
|
|
55
|
+
<URadioGroup
|
|
56
|
+
v-for="(group, index) in groups"
|
|
57
|
+
:key="group.key"
|
|
58
|
+
:model-value="selectionOf(group)"
|
|
59
|
+
v-bind="choiceCardGroupAttrs(controlProps, index)"
|
|
60
|
+
value-key="value"
|
|
61
|
+
label-key="label"
|
|
62
|
+
variant="card"
|
|
63
|
+
:legend="group.heading"
|
|
64
|
+
:items="group.items"
|
|
65
|
+
:orientation="cards.orientation.value"
|
|
66
|
+
:indicator="cards.indicator.value"
|
|
67
|
+
:ui="cards.ui.value"
|
|
68
|
+
:style="cards.style.value"
|
|
69
|
+
:disabled="disabled"
|
|
70
|
+
@update:model-value="select"
|
|
71
|
+
@blur="handleBlur"
|
|
72
|
+
>
|
|
73
|
+
<template #label="{ item }">
|
|
74
|
+
<ChoiceCardLabel
|
|
75
|
+
:label="item.label"
|
|
76
|
+
:description="
|
|
77
|
+
cards.compact.value ? descriptionByKey.get(formOptionKey(item.value)) : void 0
|
|
78
|
+
"
|
|
79
|
+
:icon="item.icon"
|
|
80
|
+
:value="item.value"
|
|
81
|
+
:tile="cards.tile.value"
|
|
82
|
+
:compact="cards.compact.value"
|
|
83
|
+
:mark="cards.mark.value"
|
|
84
|
+
:selected="model === item.value"
|
|
85
|
+
:ui="cards.ui.value"
|
|
86
|
+
/>
|
|
87
|
+
</template>
|
|
88
|
+
</URadioGroup>
|
|
89
|
+
</div>
|
|
60
90
|
</FormFieldShell>
|
|
61
91
|
</template>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineFormFieldKind } from "../../utils/field-kind.js";
|
|
2
2
|
export const radioCardFieldKind = defineFormFieldKind({
|
|
3
3
|
layout: { item: true },
|
|
4
|
-
options: { enabled: true },
|
|
4
|
+
options: { enabled: true, groups: true },
|
|
5
5
|
state: "stateful",
|
|
6
6
|
transform: true,
|
|
7
7
|
type: "radio-card",
|
|
@@ -1,8 +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
|
-
import type {
|
|
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
|
|
3
|
+
import type { FormOptionGrouping, FormOptionValue, FormOptionsSource } from '../../types/options.js';
|
|
4
|
+
import type { FormChoiceCardOptionItem, FormChoiceCardProps } from '../choice-card/types.js';
|
|
5
|
+
export interface FormRadioCardField<TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>, TValue extends FormOptionValue = FormOptionValue, TOption extends FormChoiceCardOptionItem<TValue> = FormChoiceCardOptionItem<TValue>> extends FormStatefulFieldBase<'radio-card', TValue | null, TContext, TDeps, FormChoiceCardProps>, FormOptionGrouping {
|
|
6
6
|
options: FormOptionsSource<TOption, TContext, TDeps, TValue | null>;
|
|
7
7
|
}
|
|
8
8
|
export type RadioCardFieldOutput<TField> = FieldOptionValue<TField> | NullableValue;
|