@mozaic-ds/vue 2.14.0 → 2.16.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/mozaic-vue.css +1 -1
- package/dist/mozaic-vue.d.ts +1582 -500
- package/dist/mozaic-vue.js +8020 -3218
- package/dist/mozaic-vue.js.map +1 -1
- package/dist/mozaic-vue.umd.cjs +24 -5
- package/dist/mozaic-vue.umd.cjs.map +1 -1
- package/package.json +6 -4
- package/src/components/DarkMode.mdx +115 -0
- package/src/components/actionlistbox/MActionListbox.spec.ts +20 -10
- package/src/components/actionlistbox/MActionListbox.stories.ts +15 -8
- package/src/components/actionlistbox/MActionListbox.vue +15 -12
- package/src/components/actionlistbox/README.md +2 -1
- package/src/components/avatar/MAvatar.stories.ts +1 -1
- package/src/components/breadcrumb/MBreadcrumb.vue +2 -2
- package/src/components/button/README.md +2 -0
- package/src/components/combobox/MCombobox.spec.ts +246 -0
- package/src/components/combobox/MCombobox.stories.ts +190 -0
- package/src/components/combobox/MCombobox.vue +277 -0
- package/src/components/combobox/README.md +52 -0
- package/src/components/field/MField.stories.ts +105 -0
- package/src/components/optionListbox/MOptionListbox.spec.ts +527 -0
- package/src/components/optionListbox/MOptionListbox.vue +470 -0
- package/src/components/optionListbox/README.md +63 -0
- package/src/components/pageheader/MPageHeader.spec.ts +12 -12
- package/src/components/pageheader/MPageHeader.stories.ts +9 -1
- package/src/components/pageheader/MPageHeader.vue +3 -6
- package/src/components/segmentedcontrol/MSegmentedControl.spec.ts +57 -25
- package/src/components/segmentedcontrol/MSegmentedControl.stories.ts +6 -19
- package/src/components/segmentedcontrol/MSegmentedControl.vue +27 -13
- package/src/components/segmentedcontrol/README.md +6 -3
- package/src/components/select/MSelect.vue +4 -3
- package/src/components/sidebar/stories/DefaultCase.stories.vue +2 -2
- package/src/components/sidebar/stories/README.md +8 -0
- package/src/components/sidebar/stories/WithExpandOnly.stories.vue +1 -1
- package/src/components/sidebar/stories/WithProfileInfoOnly.stories.vue +2 -2
- package/src/components/sidebar/stories/WithSingleLevel.stories.vue +2 -2
- package/src/components/stepperinline/MStepperInline.spec.ts +63 -28
- package/src/components/stepperinline/MStepperInline.stories.ts +18 -10
- package/src/components/stepperinline/MStepperInline.vue +24 -10
- package/src/components/stepperinline/README.md +6 -2
- package/src/components/stepperstacked/MStepperStacked.spec.ts +162 -0
- package/src/components/stepperstacked/MStepperStacked.stories.ts +57 -0
- package/src/components/stepperstacked/MStepperStacked.vue +106 -0
- package/src/components/stepperstacked/README.md +15 -0
- package/src/components/tabs/MTabs.stories.ts +18 -0
- package/src/components/tabs/MTabs.vue +30 -14
- package/src/components/tabs/Mtabs.spec.ts +56 -10
- package/src/components/tabs/README.md +6 -3
- package/src/components/textinput/MTextInput.vue +13 -1
- package/src/components/textinput/README.md +15 -1
- package/src/components/tileclickable/README.md +1 -1
- package/src/main.ts +10 -2
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
|
2
|
+
import MCombobox from './MCombobox.vue';
|
|
3
|
+
import { computed, ref } from 'vue';
|
|
4
|
+
import type { ListboxOption } from '../optionListbox/MOptionListbox.vue';
|
|
5
|
+
import MTag from '../tag/MTag.vue';
|
|
6
|
+
import MButton from '../button/MButton.vue';
|
|
7
|
+
|
|
8
|
+
const defaultOptions = Array.from({ length: 12 }).map((_el, index) => {
|
|
9
|
+
return {
|
|
10
|
+
label: `Option ${index + 1}`,
|
|
11
|
+
value: `option${index + 1}`,
|
|
12
|
+
};
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
let optionCount = 0;
|
|
16
|
+
|
|
17
|
+
const optionsWithSections: ListboxOption[] = Array.from({ length: 12 }).map(
|
|
18
|
+
(_el, index) => {
|
|
19
|
+
const isSection = index % 3 === 0;
|
|
20
|
+
|
|
21
|
+
if (!isSection) {
|
|
22
|
+
optionCount++;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
label: `${isSection ? 'Section' : 'Option'} ${isSection ? index / 3 + 1 : optionCount}`,
|
|
27
|
+
value: !isSection ? `option${optionCount}` : undefined,
|
|
28
|
+
type: isSection ? 'section' : 'option',
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const meta: Meta<typeof MCombobox> = {
|
|
34
|
+
title: 'Form elements/Combobox',
|
|
35
|
+
component: MCombobox,
|
|
36
|
+
tags: ['v2'],
|
|
37
|
+
parameters: {
|
|
38
|
+
docs: {
|
|
39
|
+
description: {
|
|
40
|
+
component:
|
|
41
|
+
'A combobox is an input field that allows users to select an option from a dropdown list or enter a custom value. It combines the functionality of a text input and a dropdown menu, providing flexibility and ease of use in forms and user interfaces.',
|
|
42
|
+
},
|
|
43
|
+
story: {
|
|
44
|
+
height: '300px',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
args: {
|
|
49
|
+
modelValue: null,
|
|
50
|
+
checkableSections: true,
|
|
51
|
+
options: defaultOptions,
|
|
52
|
+
},
|
|
53
|
+
render: (args) => ({
|
|
54
|
+
components: { MCombobox },
|
|
55
|
+
setup() {
|
|
56
|
+
const counterLabel = computed(
|
|
57
|
+
() => `${(args.modelValue as Array<unknown>)?.length} selected`,
|
|
58
|
+
);
|
|
59
|
+
return { args, counterLabel };
|
|
60
|
+
},
|
|
61
|
+
template: `
|
|
62
|
+
<MCombobox v-bind="args" v-model="args.modelValue" :counter-label="counterLabel"></MCombobox>
|
|
63
|
+
`,
|
|
64
|
+
}),
|
|
65
|
+
};
|
|
66
|
+
export default meta;
|
|
67
|
+
type Story = StoryObj<typeof MCombobox>;
|
|
68
|
+
|
|
69
|
+
export const Default: Story = {};
|
|
70
|
+
|
|
71
|
+
export const Multiple: Story = {
|
|
72
|
+
args: {
|
|
73
|
+
multiple: true,
|
|
74
|
+
modelValue: [],
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const SearchInput: Story = {
|
|
79
|
+
args: {
|
|
80
|
+
search: true,
|
|
81
|
+
modelValue: [],
|
|
82
|
+
multiple: true,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const ActionButtons: Story = {
|
|
87
|
+
args: {
|
|
88
|
+
modelValue: [],
|
|
89
|
+
multiple: true,
|
|
90
|
+
actions: true,
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const WithSections: Story = {
|
|
95
|
+
args: {
|
|
96
|
+
options: optionsWithSections,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const SelectableSections: Story = {
|
|
101
|
+
args: {
|
|
102
|
+
modelValue: [],
|
|
103
|
+
multiple: true,
|
|
104
|
+
options: optionsWithSections,
|
|
105
|
+
checkableSections: true,
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export const Clearable = {
|
|
110
|
+
args: {
|
|
111
|
+
clearable: true,
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export const Disabled: Story = {
|
|
116
|
+
args: {
|
|
117
|
+
disabled: true,
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export const Readonly: Story = {
|
|
122
|
+
args: {
|
|
123
|
+
readonly: true,
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export const Invalid: Story = {
|
|
128
|
+
args: {
|
|
129
|
+
invalid: true,
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export const AdditionalInformation: Story = {
|
|
134
|
+
args: {
|
|
135
|
+
options: defaultOptions.map((option) => ({
|
|
136
|
+
...option,
|
|
137
|
+
content: 'Additional information',
|
|
138
|
+
})),
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export const RemovableTags: Story = {
|
|
143
|
+
args: {
|
|
144
|
+
modelValue: [],
|
|
145
|
+
options: defaultOptions,
|
|
146
|
+
multiple: true,
|
|
147
|
+
},
|
|
148
|
+
render: (args) => ({
|
|
149
|
+
components: { MCombobox, MTag, MButton },
|
|
150
|
+
setup() {
|
|
151
|
+
function findByValue(value: string | number) {
|
|
152
|
+
return args.options.find((option) => option.value === value);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const open = ref(false);
|
|
156
|
+
const maxResultsDisplayed = ref(9);
|
|
157
|
+
const counterLabel = computed(
|
|
158
|
+
() => `${(args.modelValue as Array<unknown>)?.length} selected`,
|
|
159
|
+
);
|
|
160
|
+
return { args, counterLabel, maxResultsDisplayed, findByValue, open };
|
|
161
|
+
},
|
|
162
|
+
template: `
|
|
163
|
+
<MCombobox v-bind="args" v-model="args.modelValue" :counter-label="counterLabel" @update:open="open = $event"></MCombobox>
|
|
164
|
+
|
|
165
|
+
<template
|
|
166
|
+
v-if="!open"
|
|
167
|
+
>
|
|
168
|
+
<div style="width: 300px; display: flex;align-items: center;gap: 10px;flex-wrap: wrap;margin: 16px 0;">
|
|
169
|
+
<MTag
|
|
170
|
+
v-for="(item, index) in args.modelValue?.slice(0, maxResultsDisplayed)"
|
|
171
|
+
:key="index"
|
|
172
|
+
id="tag"
|
|
173
|
+
:label="findByValue(item)?.label || ''"
|
|
174
|
+
type="removable"
|
|
175
|
+
size="s"
|
|
176
|
+
/>
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
<MButton
|
|
180
|
+
v-if="args.modelValue?.length > maxResultsDisplayed"
|
|
181
|
+
outlined
|
|
182
|
+
size="s"
|
|
183
|
+
@click="maxResultsDisplayed = args.modelValue?.length"
|
|
184
|
+
>
|
|
185
|
+
Show more
|
|
186
|
+
</MButton>
|
|
187
|
+
</template>
|
|
188
|
+
`,
|
|
189
|
+
}),
|
|
190
|
+
};
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
ref="combobox"
|
|
4
|
+
:class="{
|
|
5
|
+
'mc-combobox': true,
|
|
6
|
+
'mc-combobox--open': isOpen,
|
|
7
|
+
'mc-combobox--multiple': multiple,
|
|
8
|
+
'mc-combobox--invalid': invalid,
|
|
9
|
+
'mc-combobox--s': size === 's',
|
|
10
|
+
'mc-combobox--disabled': disabled,
|
|
11
|
+
'mc-combobox--readonly': readonly,
|
|
12
|
+
}"
|
|
13
|
+
>
|
|
14
|
+
<div class="mc-combobox__input">
|
|
15
|
+
<button
|
|
16
|
+
ref="comboboxControl"
|
|
17
|
+
:aria-expanded="isOpen ? 'true' : 'false'"
|
|
18
|
+
aria-haspopup="listbox"
|
|
19
|
+
:aria-controls="`listbox-${id}`"
|
|
20
|
+
:disabled="disabled"
|
|
21
|
+
class="mc-combobox__control"
|
|
22
|
+
aria-label="Combobox input"
|
|
23
|
+
@click="toggleListbox"
|
|
24
|
+
v-bind="
|
|
25
|
+
search
|
|
26
|
+
? {}
|
|
27
|
+
: {
|
|
28
|
+
role: 'combobox',
|
|
29
|
+
'aria-activedescendant': activeDescendantId,
|
|
30
|
+
onKeydown: listbox?.handleKeydown,
|
|
31
|
+
}
|
|
32
|
+
"
|
|
33
|
+
>
|
|
34
|
+
{{ selectionLabel }}
|
|
35
|
+
</button>
|
|
36
|
+
<span v-if="multiple && hasValue" class="mc-combobox__counter">
|
|
37
|
+
{{ counterLabel }}
|
|
38
|
+
</span>
|
|
39
|
+
|
|
40
|
+
<button
|
|
41
|
+
v-if="clearable && hasValue"
|
|
42
|
+
type="button"
|
|
43
|
+
class="mc-combobox__clear"
|
|
44
|
+
aria-label="Clear selection"
|
|
45
|
+
@click="listbox?.clearSelection"
|
|
46
|
+
>
|
|
47
|
+
<CrossCircleFilled24 />
|
|
48
|
+
</button>
|
|
49
|
+
|
|
50
|
+
<button
|
|
51
|
+
type="button"
|
|
52
|
+
:tabindex="-1"
|
|
53
|
+
class="mc-combobox__icon"
|
|
54
|
+
aria-label="Expand options list"
|
|
55
|
+
@click="toggleListbox"
|
|
56
|
+
>
|
|
57
|
+
<ChevronDown24 />
|
|
58
|
+
</button>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<MOptionListbox
|
|
62
|
+
ref="listbox"
|
|
63
|
+
v-model="selection"
|
|
64
|
+
:id
|
|
65
|
+
:open="isOpen"
|
|
66
|
+
:multiple
|
|
67
|
+
:search
|
|
68
|
+
:actions
|
|
69
|
+
:checkable-sections
|
|
70
|
+
:search-placeholder
|
|
71
|
+
:select-label
|
|
72
|
+
:clear-label
|
|
73
|
+
:options
|
|
74
|
+
class="mc-combobox__listbox"
|
|
75
|
+
@open="open"
|
|
76
|
+
@close="
|
|
77
|
+
() => {
|
|
78
|
+
close();
|
|
79
|
+
comboboxControl?.focus();
|
|
80
|
+
}
|
|
81
|
+
"
|
|
82
|
+
>
|
|
83
|
+
<template #optionPrefix>
|
|
84
|
+
<slot name="optionPrefix" />
|
|
85
|
+
</template>
|
|
86
|
+
</MOptionListbox>
|
|
87
|
+
</div>
|
|
88
|
+
</template>
|
|
89
|
+
|
|
90
|
+
<script setup lang="ts">
|
|
91
|
+
import MOptionListbox, {
|
|
92
|
+
type ListboxOption,
|
|
93
|
+
} from '../optionListbox/MOptionListbox.vue';
|
|
94
|
+
import { CrossCircleFilled24, ChevronDown24 } from '@mozaic-ds/icons-vue';
|
|
95
|
+
import {
|
|
96
|
+
computed,
|
|
97
|
+
onBeforeUnmount,
|
|
98
|
+
ref,
|
|
99
|
+
useId,
|
|
100
|
+
useTemplateRef,
|
|
101
|
+
type VNode,
|
|
102
|
+
} from 'vue';
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* A combobox is an input field that allows users to select an option from a dropdown list or enter a custom value. It combines the functionality of a text input and a dropdown menu, providing flexibility and ease of use in forms and user interfaces.
|
|
106
|
+
*/
|
|
107
|
+
const props = withDefaults(
|
|
108
|
+
defineProps<{
|
|
109
|
+
/**
|
|
110
|
+
* The current selected value(s) of the combobox.
|
|
111
|
+
*/
|
|
112
|
+
modelValue: string | number | null | (string | number)[];
|
|
113
|
+
/**
|
|
114
|
+
* Enable multiple selection mode.
|
|
115
|
+
*/
|
|
116
|
+
multiple?: boolean;
|
|
117
|
+
/**
|
|
118
|
+
* Size of the combobox: small ('s') or medium ('m').
|
|
119
|
+
*/
|
|
120
|
+
size?: 's' | 'm';
|
|
121
|
+
/**
|
|
122
|
+
* Disable the combobox input.
|
|
123
|
+
*/
|
|
124
|
+
disabled?: boolean;
|
|
125
|
+
/**
|
|
126
|
+
* Make the combobox read-only.
|
|
127
|
+
*/
|
|
128
|
+
readonly?: boolean;
|
|
129
|
+
/**
|
|
130
|
+
* Mark the combobox as invalid.
|
|
131
|
+
*/
|
|
132
|
+
invalid?: boolean;
|
|
133
|
+
/**
|
|
134
|
+
* Show a clear selection button.
|
|
135
|
+
*/
|
|
136
|
+
clearable?: boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Enable the search input inside the listbox.
|
|
139
|
+
*/
|
|
140
|
+
search?: boolean;
|
|
141
|
+
/**
|
|
142
|
+
* Show select all / clear buttons when multiple.
|
|
143
|
+
*/
|
|
144
|
+
actions?: boolean;
|
|
145
|
+
/**
|
|
146
|
+
* Enable checkable section headers in the listbox.
|
|
147
|
+
*/
|
|
148
|
+
checkableSections?: boolean;
|
|
149
|
+
/**
|
|
150
|
+
* Placeholder text when no value is selected.
|
|
151
|
+
*/
|
|
152
|
+
placeholder?: string;
|
|
153
|
+
/**
|
|
154
|
+
* Label for the selected items counter.
|
|
155
|
+
*/
|
|
156
|
+
counterLabel?: string;
|
|
157
|
+
/**
|
|
158
|
+
* Placeholder text for the search input.
|
|
159
|
+
*/
|
|
160
|
+
searchPlaceholder?: string;
|
|
161
|
+
/**
|
|
162
|
+
* Label for the "Select all" button.
|
|
163
|
+
*/
|
|
164
|
+
selectLabel?: string;
|
|
165
|
+
/**
|
|
166
|
+
* Label for the "Clear selection" button.
|
|
167
|
+
*/
|
|
168
|
+
clearLabel?: string;
|
|
169
|
+
/**
|
|
170
|
+
* Array of options to display in the listbox.
|
|
171
|
+
*/
|
|
172
|
+
options: Array<ListboxOption>;
|
|
173
|
+
}>(),
|
|
174
|
+
{
|
|
175
|
+
placeholder: 'Select an option',
|
|
176
|
+
counterLabel: '99 selected',
|
|
177
|
+
searchPlaceholder: 'Find an option...',
|
|
178
|
+
selectLabel: 'Select all',
|
|
179
|
+
clearLabel: 'Clear',
|
|
180
|
+
size: 'm',
|
|
181
|
+
},
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
const emit = defineEmits<{
|
|
185
|
+
/**
|
|
186
|
+
* Emitted when the selected value(s) change.
|
|
187
|
+
*/
|
|
188
|
+
(
|
|
189
|
+
on: 'update:modelValue',
|
|
190
|
+
value: string | number | null | (string | number)[],
|
|
191
|
+
): void;
|
|
192
|
+
/**
|
|
193
|
+
* Emitted when the listbox is opened or closed.
|
|
194
|
+
*/
|
|
195
|
+
(on: 'update:open', value: boolean): void;
|
|
196
|
+
}>();
|
|
197
|
+
|
|
198
|
+
defineSlots<{
|
|
199
|
+
/**
|
|
200
|
+
* Use this slot to add a prefix to options.
|
|
201
|
+
*/
|
|
202
|
+
optionPrefix: VNode;
|
|
203
|
+
}>();
|
|
204
|
+
|
|
205
|
+
const id = useId();
|
|
206
|
+
|
|
207
|
+
const combobox = useTemplateRef('combobox');
|
|
208
|
+
const listbox = useTemplateRef('listbox');
|
|
209
|
+
const comboboxControl = useTemplateRef('comboboxControl');
|
|
210
|
+
|
|
211
|
+
const isOpen = ref(false);
|
|
212
|
+
|
|
213
|
+
const activeDescendantId = computed(() => {
|
|
214
|
+
const activeIndex = listbox.value?.activeIndex || -1;
|
|
215
|
+
return activeIndex >= 0 ? `option-${id}-${activeIndex}` : undefined;
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const selection = computed({
|
|
219
|
+
get() {
|
|
220
|
+
return props.modelValue;
|
|
221
|
+
},
|
|
222
|
+
set(value: string | number | null | (string | number)[]) {
|
|
223
|
+
emit('update:modelValue', value);
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const selectionLabel = computed(() => {
|
|
228
|
+
if (Array.isArray(selection.value)) {
|
|
229
|
+
if (!selection.value.length) return props.placeholder;
|
|
230
|
+
return (selection.value as (string | number)[]).join(', ');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return (
|
|
234
|
+
props.options.find((option) => option.value === selection.value)?.label ||
|
|
235
|
+
props.placeholder
|
|
236
|
+
);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const hasValue = computed(() =>
|
|
240
|
+
props.multiple
|
|
241
|
+
? !!(selection.value as (string | number)[]).length
|
|
242
|
+
: !!selection.value,
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
function handleClickOutside(event: MouseEvent) {
|
|
246
|
+
if (
|
|
247
|
+
!combobox.value?.contains(event.target as Node) &&
|
|
248
|
+
!listbox.value?.listboxEl?.contains(event.target as Node)
|
|
249
|
+
) {
|
|
250
|
+
close();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function open() {
|
|
255
|
+
isOpen.value = true;
|
|
256
|
+
emit('update:open', true);
|
|
257
|
+
document.addEventListener('click', handleClickOutside);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function close() {
|
|
261
|
+
isOpen.value = false;
|
|
262
|
+
emit('update:open', false);
|
|
263
|
+
document.removeEventListener('click', handleClickOutside);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function toggleListbox() {
|
|
267
|
+
return isOpen.value ? close() : open();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
onBeforeUnmount(() => {
|
|
271
|
+
document.removeEventListener('click', handleClickOutside);
|
|
272
|
+
});
|
|
273
|
+
</script>
|
|
274
|
+
|
|
275
|
+
<style lang="scss" scoped>
|
|
276
|
+
@use '@mozaic-ds/styles/components/combobox';
|
|
277
|
+
</style>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# MCombobox
|
|
2
|
+
|
|
3
|
+
A combobox is an input field that allows users to select an option from a dropdown list or enter a custom value. It combines the functionality of a text input and a dropdown menu, providing flexibility and ease of use in forms and user interfaces.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Props
|
|
7
|
+
|
|
8
|
+
| Name | Description | Type | Default |
|
|
9
|
+
| --- | --- | --- | --- |
|
|
10
|
+
| `modelValue*` | The current selected value(s) of the combobox. | `string` `number` `(string` `number)[]` `null` | - |
|
|
11
|
+
| `multiple` | Enable multiple selection mode. | `boolean` | - |
|
|
12
|
+
| `size` | Size of the combobox: small ('s') or medium ('m'). | `"s"` `"m"` | `"m"` |
|
|
13
|
+
| `disabled` | Disable the combobox input. | `boolean` | - |
|
|
14
|
+
| `readonly` | Make the combobox read-only. | `boolean` | - |
|
|
15
|
+
| `invalid` | Mark the combobox as invalid. | `boolean` | - |
|
|
16
|
+
| `clearable` | Show a clear selection button. | `boolean` | - |
|
|
17
|
+
| `search` | Enable the search input inside the listbox. | `boolean` | - |
|
|
18
|
+
| `actions` | Show select all / clear buttons when multiple. | `boolean` | - |
|
|
19
|
+
| `checkableSections` | Enable checkable section headers in the listbox. | `boolean` | - |
|
|
20
|
+
| `placeholder` | Placeholder text when no value is selected. | `string` | `"Select an option"` |
|
|
21
|
+
| `counterLabel` | Label for the selected items counter. | `string` | `"99 selected"` |
|
|
22
|
+
| `searchPlaceholder` | Placeholder text for the search input. | `string` | `"Find an option..."` |
|
|
23
|
+
| `selectLabel` | Label for the "Select all" button. | `string` | `"Select all"` |
|
|
24
|
+
| `clearLabel` | Label for the "Clear selection" button. | `string` | `"Clear"` |
|
|
25
|
+
| `options*` | Array of options to display in the listbox. | `ListboxOption[]` | - |
|
|
26
|
+
|
|
27
|
+
## Slots
|
|
28
|
+
|
|
29
|
+
| Name | Description |
|
|
30
|
+
| --- | --- |
|
|
31
|
+
| `optionPrefix` | Use this slot to add a prefix to options. |
|
|
32
|
+
|
|
33
|
+
## Events
|
|
34
|
+
|
|
35
|
+
| Name | Description | Type |
|
|
36
|
+
| --- | --- | --- |
|
|
37
|
+
| `update:modelValue` | - | `[value: string` `number` `(string` `number)[]` `null]` |
|
|
38
|
+
| `update:open` | Emitted when the selected value(s) change. / ( on: 'update:modelValue', value: string | number | null | (string | number)[], ): void; /** Emitted when the listbox is opened or closed. | [value: boolean] |
|
|
39
|
+
|
|
40
|
+
## Dependencies
|
|
41
|
+
|
|
42
|
+
### Depends on
|
|
43
|
+
|
|
44
|
+
- [MOptionListbox](../optionListbox)
|
|
45
|
+
|
|
46
|
+
### Graph
|
|
47
|
+
|
|
48
|
+
```mermaid
|
|
49
|
+
graph TD;
|
|
50
|
+
MCombobox --> MOptionListbox
|
|
51
|
+
style MCombobox fill:#008240,stroke:#333,stroke-width:4px
|
|
52
|
+
```
|
|
@@ -5,6 +5,8 @@ import MField from './MField.vue';
|
|
|
5
5
|
import MTextInput from '../textinput/MTextInput.vue';
|
|
6
6
|
import MTextArea from '../textarea/MTextArea.vue';
|
|
7
7
|
import MSelect from '../select/MSelect.vue';
|
|
8
|
+
import MCombobox from '../combobox/MCombobox.vue';
|
|
9
|
+
import { ref } from 'vue';
|
|
8
10
|
|
|
9
11
|
const meta: Meta<typeof MField> = {
|
|
10
12
|
title: 'Form Elements/Field',
|
|
@@ -383,3 +385,106 @@ export const SelectInvalid: Story = {
|
|
|
383
385
|
`,
|
|
384
386
|
}),
|
|
385
387
|
};
|
|
388
|
+
|
|
389
|
+
export const ComboboxValid: Story = {
|
|
390
|
+
args: {
|
|
391
|
+
label: 'Label',
|
|
392
|
+
id: 'comboboxValidId',
|
|
393
|
+
requirementText: 'required',
|
|
394
|
+
helpText: 'Help text',
|
|
395
|
+
isValid: true,
|
|
396
|
+
message: 'Validation message (Be concise and use comprehensive words).',
|
|
397
|
+
default: `
|
|
398
|
+
<MCombobox
|
|
399
|
+
v-model="value"
|
|
400
|
+
:options="
|
|
401
|
+
[
|
|
402
|
+
{
|
|
403
|
+
label: 'Option 1',
|
|
404
|
+
value: 'option1',
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
label: 'Option 2',
|
|
408
|
+
value: 'option2',
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
label: 'Option 3',
|
|
412
|
+
value: 'option3',
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
label: 'Option 4',
|
|
416
|
+
value: 'option4',
|
|
417
|
+
},
|
|
418
|
+
]
|
|
419
|
+
"
|
|
420
|
+
@update:modelValue="handleUpdate"
|
|
421
|
+
/>
|
|
422
|
+
`,
|
|
423
|
+
},
|
|
424
|
+
render: (args) => ({
|
|
425
|
+
components: { MField, MCombobox },
|
|
426
|
+
setup() {
|
|
427
|
+
const value = ref(null);
|
|
428
|
+
const handleUpdate = action('update:modelValue');
|
|
429
|
+
|
|
430
|
+
return { args, handleUpdate, value };
|
|
431
|
+
},
|
|
432
|
+
template: `
|
|
433
|
+
<MField v-bind="args">
|
|
434
|
+
${args.default}
|
|
435
|
+
</MField>
|
|
436
|
+
`,
|
|
437
|
+
}),
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
export const ComboboxInvalid: Story = {
|
|
441
|
+
args: {
|
|
442
|
+
label: 'Label',
|
|
443
|
+
id: 'comboboxValidId',
|
|
444
|
+
requirementText: 'required',
|
|
445
|
+
helpText: 'Help text',
|
|
446
|
+
isInvalid: true,
|
|
447
|
+
message: 'Error message (Be concise and use comprehensive words)',
|
|
448
|
+
default: `
|
|
449
|
+
<MCombobox
|
|
450
|
+
v-model="value"
|
|
451
|
+
invalid
|
|
452
|
+
:options="
|
|
453
|
+
[
|
|
454
|
+
{
|
|
455
|
+
label: 'Option 1',
|
|
456
|
+
value: 'option1',
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
label: 'Option 2',
|
|
460
|
+
value: 'option2',
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
label: 'Option 3',
|
|
464
|
+
value: 'option3',
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
label: 'Option 4',
|
|
468
|
+
value: 'option4',
|
|
469
|
+
},
|
|
470
|
+
]
|
|
471
|
+
"
|
|
472
|
+
@update:modelValue="handleUpdate"
|
|
473
|
+
/>
|
|
474
|
+
`,
|
|
475
|
+
},
|
|
476
|
+
render: (args) => ({
|
|
477
|
+
components: { MField, MCombobox },
|
|
478
|
+
setup() {
|
|
479
|
+
const value = ref(null);
|
|
480
|
+
const handleUpdate = action('update:modelValue');
|
|
481
|
+
|
|
482
|
+
return { args, handleUpdate, value };
|
|
483
|
+
},
|
|
484
|
+
template: `
|
|
485
|
+
<MField v-bind="args">
|
|
486
|
+
${args.default}
|
|
487
|
+
</MField>
|
|
488
|
+
`,
|
|
489
|
+
}),
|
|
490
|
+
};
|