vueless 1.3.7-beta.1 → 1.3.7-beta.3

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.
Files changed (33) hide show
  1. package/components.d.ts +1 -0
  2. package/components.ts +1 -0
  3. package/constants.d.ts +2 -0
  4. package/constants.js +2 -0
  5. package/package.json +2 -2
  6. package/ui.container-col/UCol.vue +0 -1
  7. package/ui.container-collapsible/UCollapsible.vue +190 -0
  8. package/ui.container-collapsible/config.ts +45 -0
  9. package/ui.container-collapsible/constants.ts +1 -0
  10. package/ui.container-collapsible/storybook/docs.mdx +17 -0
  11. package/ui.container-collapsible/storybook/stories.ts +261 -0
  12. package/ui.container-collapsible/tests/UCollapsible.test.ts +571 -0
  13. package/ui.container-collapsible/types.ts +57 -0
  14. package/ui.dropdown/UDropdown.vue +324 -0
  15. package/ui.dropdown/config.ts +27 -0
  16. package/ui.dropdown/constants.ts +1 -0
  17. package/ui.dropdown/storybook/docs.mdx +17 -0
  18. package/ui.dropdown/storybook/stories.ts +286 -0
  19. package/ui.dropdown/tests/UDropdown.test.ts +631 -0
  20. package/ui.dropdown/types.ts +127 -0
  21. package/ui.dropdown-badge/UDropdownBadge.vue +119 -227
  22. package/ui.dropdown-badge/config.ts +18 -15
  23. package/ui.dropdown-badge/tests/UDropdownBadge.test.ts +201 -67
  24. package/ui.dropdown-button/UDropdownButton.vue +121 -226
  25. package/ui.dropdown-button/config.ts +32 -28
  26. package/ui.dropdown-button/tests/UDropdownButton.test.ts +189 -73
  27. package/ui.dropdown-link/UDropdownLink.vue +123 -233
  28. package/ui.dropdown-link/config.ts +15 -18
  29. package/ui.dropdown-link/tests/UDropdownLink.test.ts +190 -71
  30. package/ui.form-listbox/UListbox.vue +2 -3
  31. package/ui.form-listbox/config.ts +2 -2
  32. package/ui.form-select/config.ts +1 -1
  33. package/utils/node/helper.js +6 -5
@@ -0,0 +1,324 @@
1
+ <script setup lang="ts">
2
+ import { nextTick, computed, ref, useTemplateRef } from "vue";
3
+ import { isEqual } from "lodash-es";
4
+
5
+ import { useUI } from "../composables/useUI";
6
+ import { getDefaults, cx } from "../utils/ui";
7
+
8
+ import UListbox from "../ui.form-listbox/UListbox.vue";
9
+ import UCollapsible from "../ui.container-collapsible/UCollapsible.vue";
10
+
11
+ import defaultConfig from "./config";
12
+ import { COMPONENT_NAME } from "./constants";
13
+
14
+ import type { Props, Config } from "./types";
15
+ import type { Option, SelectedValue } from "../ui.form-listbox/types";
16
+
17
+ defineOptions({ inheritAttrs: false });
18
+
19
+ const props = withDefaults(defineProps<Props>(), {
20
+ ...getDefaults<Props, Config>(defaultConfig, COMPONENT_NAME),
21
+ options: () => [],
22
+ modelValue: "",
23
+ label: "",
24
+ });
25
+
26
+ const emit = defineEmits([
27
+ /**
28
+ * Triggers on a dropdown option click.
29
+ * @property {string} value
30
+ */
31
+ "clickOption",
32
+
33
+ /**
34
+ * Triggers when an option is selected.
35
+ * @property {string} value
36
+ * @property {number} value
37
+ */
38
+ "update:modelValue",
39
+
40
+ /**
41
+ * Triggers when a dropdown list is opened.
42
+ */
43
+ "open",
44
+
45
+ /**
46
+ * Triggers when a dropdown list is closed.
47
+ */
48
+ "close",
49
+
50
+ /**
51
+ * Triggers when the search value is changed.
52
+ * @property {string} query
53
+ */
54
+ "searchChange",
55
+
56
+ /**
57
+ * Triggers when the search v-model updates.
58
+ * @property {string} query
59
+ */
60
+ "update:search",
61
+ ]);
62
+
63
+ type UListboxRef = InstanceType<typeof UListbox>;
64
+ type UCollapsibleRef = InstanceType<typeof UCollapsible>;
65
+
66
+ const isClickingOption = ref(false);
67
+ const listboxRef = useTemplateRef<UListboxRef>("dropdown-list");
68
+ const collapsibleRef = useTemplateRef<UCollapsibleRef>("collapsible");
69
+
70
+ const dropdownValue = computed({
71
+ get: () => {
72
+ if (props.multiple && !Array.isArray(props.modelValue)) {
73
+ return props.modelValue ? [props.modelValue] : [];
74
+ }
75
+
76
+ return props.modelValue;
77
+ },
78
+ set: (value) => emit("update:modelValue", value),
79
+ });
80
+
81
+ const dropdownSearch = computed({
82
+ get: () => props.search ?? "",
83
+ set: (value: string) => emit("update:search", value),
84
+ });
85
+
86
+ const selectedOptions = computed(() => {
87
+ if (props.multiple) {
88
+ return props.options.filter((option) => {
89
+ return (
90
+ option[props.valueKey] &&
91
+ (dropdownValue.value as SelectedValue[]).find((selected) =>
92
+ isEqual(selected, option[props.valueKey]),
93
+ )
94
+ );
95
+ });
96
+ }
97
+
98
+ return [
99
+ props.options.find(
100
+ (option) => option[props.valueKey] && isEqual(option[props.valueKey], dropdownValue.value),
101
+ ),
102
+ ].filter((option) => !!option);
103
+ });
104
+
105
+ const displayLabel = computed(() => {
106
+ if (!props.labelDisplayCount || !selectedOptions.value.length) {
107
+ return props.label;
108
+ }
109
+
110
+ const selectedLabels = selectedOptions.value
111
+ .slice(0, props.labelDisplayCount)
112
+ .map((option) => option[props.labelKey]);
113
+ const restLabelCount = selectedOptions.value.length - props.labelDisplayCount;
114
+
115
+ if (restLabelCount > 0) {
116
+ selectedLabels.push(`+${restLabelCount}`);
117
+ }
118
+
119
+ return selectedLabels.join(", ");
120
+ });
121
+
122
+ const fullLabel = computed(() => {
123
+ if (!selectedOptions.value.length) {
124
+ return "";
125
+ }
126
+
127
+ return selectedOptions.value.map((option) => option[props.labelKey]).join(", ");
128
+ });
129
+
130
+ function onSearchChange(query: string) {
131
+ emit("searchChange", query);
132
+ }
133
+
134
+ function onClickOption(option: Option) {
135
+ isClickingOption.value = true;
136
+
137
+ emit("clickOption", option);
138
+
139
+ if (props.closeOnSelect) {
140
+ hide();
141
+ }
142
+
143
+ nextTick(() => {
144
+ setTimeout(() => {
145
+ isClickingOption.value = false;
146
+ }, 10);
147
+ });
148
+ }
149
+
150
+ function onOpen() {
151
+ nextTick(() => listboxRef.value?.wrapperRef?.focus());
152
+
153
+ emit("open");
154
+ }
155
+
156
+ function onClose() {
157
+ dropdownSearch.value = "";
158
+
159
+ emit("close");
160
+ }
161
+
162
+ function toggle() {
163
+ collapsibleRef.value?.toggle();
164
+ }
165
+
166
+ function hide() {
167
+ collapsibleRef.value?.hide();
168
+ }
169
+
170
+ defineExpose({
171
+ /**
172
+ * A reference to the component's wrapper element for direct DOM manipulation.
173
+ * @property {HTMLDivElement}
174
+ */
175
+ wrapperRef: computed(() => collapsibleRef.value?.wrapperRef),
176
+
177
+ /**
178
+ * Hides the dropdown.
179
+ * @property {function}
180
+ */
181
+ hide,
182
+
183
+ /**
184
+ * Toggles the dropdown visibility.
185
+ * @property {function}
186
+ */
187
+ toggle,
188
+
189
+ /**
190
+ * Indicates whether the dropdown is opened.
191
+ * @property {boolean}
192
+ */
193
+ isOpened: computed(() => collapsibleRef.value?.isOpened ?? false),
194
+
195
+ /**
196
+ * The currently selected options.
197
+ * @property {Option[]}
198
+ */
199
+ selectedOptions,
200
+
201
+ /**
202
+ * The display label for the dropdown.
203
+ * @property {string}
204
+ */
205
+ displayLabel,
206
+
207
+ /**
208
+ * Returns full option labels joined by comma.
209
+ * @property {string}
210
+ */
211
+ fullLabel,
212
+ });
213
+
214
+ /*
215
+ * Vueless: Get element / nested component attributes for each config token ✨
216
+ * Applies: `class`, `config`, redefined default `props` and dev `vl-...` attributes.
217
+ */
218
+ const mutatedProps = computed(() => ({
219
+ /* component state, not a props */
220
+ opened: collapsibleRef.value?.isOpened ?? false,
221
+ }));
222
+
223
+ const { getDataTest, dropdownAttrs, listboxAttrs } = useUI<Config>(
224
+ defaultConfig,
225
+ mutatedProps,
226
+ "dropdown",
227
+ );
228
+ </script>
229
+
230
+ <template>
231
+ <UCollapsible
232
+ :id="id"
233
+ ref="collapsible"
234
+ :y-position="yPosition"
235
+ :x-position="xPosition"
236
+ :close-on-click-outside="!isClickingOption"
237
+ :close-on-content-click="false"
238
+ :disabled="disabled"
239
+ v-bind="dropdownAttrs"
240
+ :data-test="dataTest"
241
+ @open="onOpen"
242
+ @close="onClose"
243
+ >
244
+ <template #default="{ opened }">
245
+ <!--
246
+ @slot Use it to add custom trigger element for the dropdown.
247
+ @binding {boolean} opened
248
+ @binding {string} displayLabel
249
+ @binding {string} fullLabel
250
+ @binding {Option[]} selectedOptions
251
+ -->
252
+ <slot
253
+ :opened="opened"
254
+ :display-label="displayLabel"
255
+ :full-label="fullLabel"
256
+ :selected-options="selectedOptions"
257
+ />
258
+ </template>
259
+
260
+ <template #content="{ opened, contentClasses }">
261
+ <!--
262
+ @slot Use it to replace the UListbox with custom dropdown content.
263
+ @binding {boolean} opened
264
+ @binding {string} contentClasses
265
+ -->
266
+ <slot name="dropdown" :opened="opened" :content-classes="contentClasses">
267
+ <UListbox
268
+ ref="dropdown-list"
269
+ v-model="dropdownValue"
270
+ v-model:search="dropdownSearch"
271
+ :size="size"
272
+ :searchable="searchable"
273
+ :multiple="multiple"
274
+ :color="color"
275
+ :options="options"
276
+ :options-limit="optionsLimit"
277
+ :visible-options="visibleOptions"
278
+ :label-key="labelKey"
279
+ :value-key="valueKey"
280
+ :group-label-key="groupLabelKey"
281
+ :group-value-key="groupValueKey"
282
+ v-bind="listboxAttrs"
283
+ :class="cx([contentClasses, listboxAttrs.class])"
284
+ :data-test="getDataTest('list')"
285
+ @click-option="onClickOption"
286
+ @search-change="onSearchChange"
287
+ @update:search="(value) => emit('update:search', value)"
288
+ >
289
+ <template #before-option="{ option, index }">
290
+ <!--
291
+ @slot Use it to add something before option.
292
+ @binding {object} option
293
+ @binding {number} index
294
+ -->
295
+ <slot name="before-option" :option="option" :index="index" />
296
+ </template>
297
+
298
+ <template #option="{ option, index }">
299
+ <!--
300
+ @slot Use it to customize the option.
301
+ @binding {object} option
302
+ @binding {number} index
303
+ -->
304
+ <slot name="option" :option="option" :index="index" />
305
+ </template>
306
+
307
+ <template #after-option="{ option, index }">
308
+ <!--
309
+ @slot Use it to add something after option.
310
+ @binding {object} option
311
+ @binding {number} index
312
+ -->
313
+ <slot name="after-option" :option="option" :index="index" />
314
+ </template>
315
+
316
+ <template #empty>
317
+ <!-- @slot Use it to add something instead of empty state. -->
318
+ <slot name="empty" />
319
+ </template>
320
+ </UListbox>
321
+ </slot>
322
+ </template>
323
+ </UCollapsible>
324
+ </template>
@@ -0,0 +1,27 @@
1
+ export default /*tw*/ {
2
+ dropdown: "{UCollapsible}",
3
+ listbox: {
4
+ base: "{UListbox}",
5
+ defaults: {
6
+ size: {
7
+ sm: "sm",
8
+ md: "md",
9
+ lg: "lg",
10
+ },
11
+ },
12
+ },
13
+ defaults: {
14
+ color: "primary",
15
+ size: "md",
16
+ labelKey: "label",
17
+ valueKey: "value",
18
+ groupLabelKey: "label",
19
+ yPosition: "bottom",
20
+ xPosition: "left",
21
+ optionsLimit: 0,
22
+ visibleOptions: 8,
23
+ searchable: false,
24
+ multiple: false,
25
+ closeOnSelect: true,
26
+ },
27
+ };
@@ -0,0 +1 @@
1
+ export const COMPONENT_NAME = "UDropdown";
@@ -0,0 +1,17 @@
1
+ import { Meta, Title, Subtitle, Description, Primary, Controls, Stories, Source } from "@storybook/addon-docs/blocks";
2
+ import { getSource } from "../../utils/storybook.ts";
3
+
4
+ import * as stories from "./stories.ts";
5
+ import defaultConfig from "../config.ts?raw"
6
+
7
+ <Meta of={stories} />
8
+ <Title of={stories} />
9
+ <Subtitle of={stories} />
10
+ <Description of={stories} />
11
+ <Primary of={stories} />
12
+ <Controls of={stories.Default} />
13
+ <Stories of={stories} />
14
+
15
+ ## Default config
16
+ <Source code={getSource(defaultConfig)} language="jsx" dark />
17
+
@@ -0,0 +1,286 @@
1
+ import {
2
+ getArgs,
3
+ getArgTypes,
4
+ getSlotNames,
5
+ getSlotsFragment,
6
+ getDocsDescription,
7
+ } from "../../utils/storybook";
8
+
9
+ import UDropdown from "../../ui.dropdown/UDropdown.vue";
10
+ import URow from "../../ui.container-row/URow.vue";
11
+ import UCol from "../../ui.container-col/UCol.vue";
12
+ import UButton from "../../ui.button/UButton.vue";
13
+ import UIcon from "../../ui.image-icon/UIcon.vue";
14
+ import ULink from "../../ui.button-link/ULink.vue";
15
+ import UBadge from "../../ui.text-badge/UBadge.vue";
16
+ import UText from "../../ui.text-block/UText.vue";
17
+ import ULoader from "../../ui.loader/ULoader.vue";
18
+ import UAvatar from "../../ui.image-avatar/UAvatar.vue";
19
+
20
+ import type { Meta, StoryFn } from "@storybook/vue3-vite";
21
+ import type { Props } from "../types";
22
+
23
+ interface DefaultUDropdownArgs extends Props {
24
+ slotTemplate?: string;
25
+ }
26
+
27
+ interface EnumUDropdownArgs extends DefaultUDropdownArgs {
28
+ enum: keyof Pick<Props, "size" | "xPosition" | "yPosition" | "color">;
29
+ class?: string;
30
+ buttonClass?: string;
31
+ }
32
+
33
+ export default {
34
+ id: "2000",
35
+ title: "Dropdowns / Dropdown",
36
+ component: UDropdown,
37
+ args: {
38
+ label: "Select option",
39
+ options: [
40
+ { label: "Edit", value: "edit" },
41
+ { label: "Copy", value: "copy" },
42
+ { label: "Remove", value: "delete" },
43
+ ],
44
+ },
45
+ argTypes: {
46
+ ...getArgTypes(UDropdown.__name),
47
+ },
48
+ parameters: {
49
+ docs: {
50
+ ...getDocsDescription(UDropdown.__name),
51
+ story: {
52
+ height: "230px",
53
+ },
54
+ },
55
+ },
56
+ } as Meta;
57
+
58
+ const defaultTemplate = `
59
+ <UAvatar label="JD" interactive />
60
+ `;
61
+
62
+ const DefaultTemplate: StoryFn<DefaultUDropdownArgs> = (args: DefaultUDropdownArgs) => ({
63
+ components: { UDropdown, UButton, UIcon, ULink, UBadge, ULoader, URow, UCol, UText, UAvatar },
64
+ setup: () => ({ args, slots: getSlotNames(UDropdown.__name) }),
65
+ template: `
66
+ <UDropdown v-bind="args">
67
+ ${args.slotTemplate || getSlotsFragment(defaultTemplate)}
68
+ </UDropdown>
69
+ `,
70
+ });
71
+
72
+ const SelectableTemplate: StoryFn<DefaultUDropdownArgs> = (args: DefaultUDropdownArgs) => ({
73
+ components: { UDropdown, UButton, UIcon, UAvatar },
74
+ setup: () => ({ args, slots: getSlotNames(UDropdown.__name) }),
75
+ template: `
76
+ <UDropdown v-bind="args" v-model="args.modelValue">
77
+ ${args.slotTemplate || getSlotsFragment(defaultTemplate)}
78
+ </UDropdown>
79
+ `,
80
+ });
81
+
82
+ const EnumTemplate: StoryFn<EnumUDropdownArgs> = (args: EnumUDropdownArgs, { argTypes }) => ({
83
+ components: { UDropdown, UButton, UIcon, URow, UAvatar },
84
+ setup: () => ({ args, argTypes, getArgs }),
85
+ template: `
86
+ <URow>
87
+ <UDropdown
88
+ v-for="option in argTypes?.[args.enum]?.options"
89
+ v-bind="getArgs(args, option)"
90
+ :key="option"
91
+ #default="{ opened }"
92
+ >
93
+ <UButton :[args.enum]="option" :label="option" :class="args.buttonClass" interactive />
94
+ </UDropdown>
95
+ </URow>
96
+ `,
97
+ });
98
+
99
+ export const Default = DefaultTemplate.bind({});
100
+ Default.args = {};
101
+
102
+ export const Disabled = DefaultTemplate.bind({});
103
+ Disabled.args = { disabled: true };
104
+ Disabled.parameters = {
105
+ docs: {
106
+ story: {
107
+ height: "120px",
108
+ },
109
+ },
110
+ };
111
+
112
+ export const Searchable = DefaultTemplate.bind({});
113
+ Searchable.args = { searchable: true };
114
+ Searchable.parameters = {
115
+ docs: {
116
+ story: {
117
+ height: "270px",
118
+ },
119
+ },
120
+ };
121
+
122
+ export const SearchModelValue = DefaultTemplate.bind({});
123
+ SearchModelValue.args = { searchable: true, search: "Copy" };
124
+ SearchModelValue.parameters = {
125
+ docs: {
126
+ story: {
127
+ height: "270px",
128
+ },
129
+ },
130
+ };
131
+
132
+ export const NoCloseOnSelect = SelectableTemplate.bind({});
133
+ NoCloseOnSelect.args = {
134
+ modelValue: "pending",
135
+ closeOnSelect: false,
136
+ options: [
137
+ { label: "Active", value: "active" },
138
+ { label: "Pending", value: "pending" },
139
+ { label: "Archived", value: "archived" },
140
+ ],
141
+ slotTemplate: `
142
+ <template #default>
143
+ <UButton label="Select status" />
144
+ </template>
145
+ `,
146
+ };
147
+
148
+ export const OptionSelection = SelectableTemplate.bind({});
149
+ OptionSelection.args = {
150
+ modelValue: "active",
151
+ options: [
152
+ { label: "Active", value: "active" },
153
+ { label: "Pending", value: "pending" },
154
+ { label: "Archived", value: "archived" },
155
+ ],
156
+ slotTemplate: `
157
+ <template #default>
158
+ <UButton label="Select status" />
159
+ </template>
160
+ `,
161
+ };
162
+
163
+ export const MultipleOptionSelection = SelectableTemplate.bind({});
164
+ MultipleOptionSelection.args = {
165
+ modelValue: ["active", "pending", "archived"],
166
+ multiple: true,
167
+ options: [
168
+ { label: "Active", value: "active" },
169
+ { label: "Pending", value: "pending" },
170
+ { label: "Archived", value: "archived" },
171
+ ],
172
+ slotTemplate: `
173
+ <template #default>
174
+ <UButton label="Select status" />
175
+ </template>
176
+ `,
177
+ };
178
+
179
+ export const Size = EnumTemplate.bind({});
180
+ Size.args = { enum: "size" };
181
+
182
+ export const Color = EnumTemplate.bind({});
183
+ Color.args = { enum: "color" };
184
+
185
+ export const ListboxXPosition = EnumTemplate.bind({});
186
+ ListboxXPosition.args = { enum: "xPosition", buttonClass: "min-w-32" };
187
+
188
+ export const ListboxYPosition = EnumTemplate.bind({});
189
+ ListboxYPosition.args = { enum: "yPosition" };
190
+ ListboxYPosition.parameters = {
191
+ storyClasses: "h-[350px] flex items-center px-6 pt-8 pb-12",
192
+ };
193
+
194
+ export const OptionsLimit = DefaultTemplate.bind({});
195
+ OptionsLimit.args = { optionsLimit: 2 };
196
+ OptionsLimit.parameters = {
197
+ docs: {
198
+ description: {
199
+ story: "`optionsLimit` prop controls the number of options displayed in the dropdown.",
200
+ },
201
+ },
202
+ };
203
+
204
+ export const VisibleOptions = DefaultTemplate.bind({});
205
+ VisibleOptions.args = { visibleOptions: 2 };
206
+ VisibleOptions.parameters = {
207
+ docs: {
208
+ description: {
209
+ story: "`visibleOptions` prop controls the number of options you can see without a scroll.",
210
+ },
211
+ },
212
+ };
213
+
214
+ export const EmptySlot = DefaultTemplate.bind({});
215
+ EmptySlot.args = {
216
+ options: [],
217
+ slotTemplate: `
218
+ ${defaultTemplate}
219
+ <template #empty>
220
+ <UCol align="center" gap="xs" class="w-32 p-2">
221
+ <ULoader loading size="sm" />
222
+ <UText align="center" label="Loading, this may take a while..." />
223
+ </UCol>
224
+ </template>
225
+ `,
226
+ };
227
+
228
+ export const OptionSlots: StoryFn<DefaultUDropdownArgs> = (args) => ({
229
+ components: { UDropdown, UAvatar, UIcon, URow, UCol, UBadge, UText },
230
+ setup: () => ({ args }),
231
+ template: `
232
+ <URow>
233
+ <UDropdown
234
+ :options="[
235
+ { label: 'John Doe', value: '1', role: 'Developer' },
236
+ { label: 'Jane Smith', value: '2', role: 'Designer' },
237
+ { label: 'Mike Johnson', value: '3', role: 'Product Manager' },
238
+ ]"
239
+ >
240
+ <UAvatar label="JD" interactive />
241
+ <template #before-option="{ option }">
242
+ <UIcon name="person" size="xs" color="neutral" />
243
+ </template>
244
+ </UDropdown>
245
+
246
+ <UDropdown
247
+ :options="[
248
+ { label: 'John Doe', value: '1', role: 'Developer', status: 'online', statusColor: 'success' },
249
+ { label: 'Jane Smith', value: '2', role: 'Designer', status: 'away', statusColor: 'warning' },
250
+ { label: 'Mike Johnson', value: '3', role: 'Product Manager', status: 'offline', statusColor: 'grayscale' },
251
+ ]"
252
+ >
253
+ <UAvatar label="JD" interactive />
254
+ <template #option="{ option }">
255
+ <URow align="center" gap="xs" justify="between" class="w-40">
256
+ <UCol gap="none">
257
+ <UText size="sm">{{ option.label }}</UText>
258
+ <UText variant="lifted" size="xs">{{ option.role }}</UText>
259
+ </UCol>
260
+ <UBadge :label="option.status" :color="option.statusColor" size="sm" variant="subtle" />
261
+ </URow>
262
+ </template>
263
+ </UDropdown>
264
+
265
+ <UDropdown
266
+ :options="[
267
+ { label: 'John Doe', value: '1', verified: true },
268
+ { label: 'Jane Smith', value: '2', verified: true },
269
+ { label: 'Mike Johnson', value: '3', verified: false },
270
+ ]"
271
+ >
272
+ <UAvatar label="JD" interactive />
273
+ <template #after-option="{ option }">
274
+ <UIcon v-if="option.verified" name="verified" size="xs" color="success" />
275
+ </template>
276
+ </UDropdown>
277
+ </URow>
278
+ `,
279
+ });
280
+ OptionSlots.parameters = {
281
+ docs: {
282
+ story: {
283
+ height: "300px",
284
+ },
285
+ },
286
+ };