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
@@ -1,21 +1,17 @@
1
1
  <script setup lang="ts">
2
- import { nextTick, computed, ref, useId, useTemplateRef } from "vue";
3
- import { isEqual } from "lodash-es";
2
+ import { computed, useTemplateRef } from "vue";
4
3
 
5
4
  import { useUI } from "../composables/useUI";
6
5
  import { getDefaults } from "../utils/ui";
7
6
 
8
7
  import UIcon from "../ui.image-icon/UIcon.vue";
9
8
  import UButton from "../ui.button/UButton.vue";
10
- import UListbox from "../ui.form-listbox/UListbox.vue";
11
-
12
- import vClickOutside from "../v.click-outside/vClickOutside";
9
+ import UDropdown from "../ui.dropdown/UDropdown.vue";
13
10
 
14
11
  import defaultConfig from "./config";
15
12
  import { COMPONENT_NAME } from "./constants";
16
13
 
17
14
  import type { Props, Config } from "./types";
18
- import type { Option, SelectedValue } from "../ui.form-listbox/types";
19
15
 
20
16
  defineOptions({ inheritAttrs: false });
21
17
 
@@ -63,66 +59,9 @@ const emit = defineEmits([
63
59
  "update:search",
64
60
  ]);
65
61
 
66
- type UListboxRef = InstanceType<typeof UListbox>;
67
-
68
- const isShownOptions = ref(false);
69
- const isClickingOption = ref(false);
70
- const listboxRef = useTemplateRef<UListboxRef>("dropdown-list");
71
- const wrapperRef = useTemplateRef<HTMLDivElement>("wrapper");
72
-
73
- const elementId = props.id || useId();
74
-
75
- const dropdownValue = computed({
76
- get: () => {
77
- if (props.multiple && !Array.isArray(props.modelValue)) {
78
- return props.modelValue ? [props.modelValue] : [];
79
- }
80
-
81
- return props.modelValue;
82
- },
83
- set: (value) => emit("update:modelValue", value),
84
- });
85
-
86
- const dropdownSearch = computed({
87
- get: () => props.search ?? "",
88
- set: (value: string) => emit("update:search", value),
89
- });
90
-
91
- const selectedOptions = computed(() => {
92
- if (props.multiple) {
93
- return props.options.filter((option) => {
94
- return (
95
- option[props.valueKey] &&
96
- (dropdownValue.value as SelectedValue[]).find((selected) =>
97
- isEqual(selected, option[props.valueKey]),
98
- )
99
- );
100
- });
101
- }
102
-
103
- return [
104
- props.options.find(
105
- (option) => option[props.valueKey] && isEqual(option[props.valueKey], dropdownValue.value),
106
- ),
107
- ].filter((option) => !!option);
108
- });
109
-
110
- const buttonLabel = computed(() => {
111
- if (!props.labelDisplayCount || !selectedOptions.value.length) {
112
- return props.label;
113
- }
62
+ type UDropdownRef = InstanceType<typeof UDropdown>;
114
63
 
115
- const selectedLabels = selectedOptions.value
116
- .slice(0, props.labelDisplayCount)
117
- .map((option) => option[props.labelKey]);
118
- const restLabelCount = selectedOptions.value.length - props.labelDisplayCount;
119
-
120
- if (restLabelCount > 0) {
121
- selectedLabels.push(`+${restLabelCount}`);
122
- }
123
-
124
- return selectedLabels.join(", ");
125
- });
64
+ const dropdownRef = useTemplateRef<UDropdownRef>("dropdown");
126
65
 
127
66
  const toggleIconName = computed(() => {
128
67
  if (typeof props.toggleIcon === "string") {
@@ -132,55 +71,8 @@ const toggleIconName = computed(() => {
132
71
  return props.toggleIcon ? config.value.defaults.toggleIcon : "";
133
72
  });
134
73
 
135
- function onSearchChange(query: string) {
136
- emit("searchChange", query);
137
- }
138
-
139
- function getFullOptionLabels(value: Option | Option[]) {
140
- const labelKey = props.labelKey;
141
-
142
- if (Array.isArray(value)) {
143
- return value.map((item) => item[labelKey]).join(", ");
144
- }
145
-
146
- return "";
147
- }
148
-
149
- function onClickOption(option: Option) {
150
- isClickingOption.value = true;
151
-
152
- emit("clickOption", option);
153
-
154
- if (!props.multiple && props.closeOnSelect) hideOptions();
155
-
156
- nextTick(() => {
157
- setTimeout(() => {
158
- isClickingOption.value = false;
159
- }, 10);
160
- });
161
- }
162
-
163
- function handleClickOutside() {
164
- if (isClickingOption.value) return;
165
-
166
- hideOptions();
167
- }
168
-
169
- function onClickButton() {
170
- isShownOptions.value = !isShownOptions.value;
171
-
172
- if (isShownOptions.value) {
173
- nextTick(() => listboxRef.value?.wrapperRef?.focus());
174
-
175
- emit("open");
176
- }
177
- }
178
-
179
- function hideOptions() {
180
- isShownOptions.value = false;
181
- dropdownSearch.value = "";
182
-
183
- emit("close");
74
+ function hide() {
75
+ dropdownRef.value?.hide();
184
76
  }
185
77
 
186
78
  defineExpose({
@@ -188,136 +80,139 @@ defineExpose({
188
80
  * A reference to the component's wrapper element for direct DOM manipulation.
189
81
  * @property {HTMLDivElement}
190
82
  */
191
- wrapperRef,
83
+ wrapperRef: computed(() => dropdownRef.value?.wrapperRef),
192
84
 
193
85
  /**
194
- * Hides the dropdown options.
86
+ * Hides the dropdown.
195
87
  * @property {function}
196
88
  */
197
- hideOptions,
89
+ hide,
198
90
  });
199
91
 
200
- /**
201
- * Get element / nested component attributes for each config token ✨
92
+ /*
93
+ * Vueless: Get element / nested component attributes for each config token ✨
202
94
  * Applies: `class`, `config`, redefined default `props` and dev `vl-...` attributes.
203
95
  */
204
96
  const mutatedProps = computed(() => ({
205
97
  /* component state, not a props */
206
- opened: isShownOptions.value,
98
+ opened: dropdownRef.value?.isOpened ?? false,
207
99
  }));
208
100
 
209
- const { getDataTest, config, dropdownButtonAttrs, listboxAttrs, toggleIconAttrs, wrapperAttrs } =
210
- useUI<Config>(defaultConfig, mutatedProps, "dropdownButton");
101
+ const { getDataTest, config, toggleButtonAttrs, dropdownButtonAttrs, toggleIconAttrs } =
102
+ useUI<Config>(defaultConfig, mutatedProps, "toggleButton");
211
103
  </script>
212
104
 
213
105
  <template>
214
- <div
215
- ref="wrapper"
216
- v-click-outside="handleClickOutside"
217
- v-bind="wrapperAttrs"
218
- :data-test="getDataTest('wrapper')"
106
+ <UDropdown
107
+ :id="id"
108
+ ref="dropdown"
109
+ :model-value="modelValue"
110
+ :label="label"
111
+ :label-display-count="labelDisplayCount"
112
+ :search="search"
113
+ :y-position="yPosition"
114
+ :x-position="xPosition"
115
+ :disabled="disabled"
116
+ :options="options"
117
+ :options-limit="optionsLimit"
118
+ :visible-options="visibleOptions"
119
+ :label-key="labelKey"
120
+ :value-key="valueKey"
121
+ :group-label-key="groupLabelKey"
122
+ :group-value-key="groupValueKey"
123
+ :searchable="searchable"
124
+ :multiple="multiple"
125
+ :color="color"
126
+ :close-on-select="closeOnSelect"
127
+ v-bind="dropdownButtonAttrs"
128
+ :data-test="dataTest"
129
+ @click-option="(option) => emit('clickOption', option)"
130
+ @update:model-value="(value) => emit('update:modelValue', value)"
131
+ @update:search="(value) => emit('update:search', value)"
132
+ @search-change="(query) => emit('searchChange', query)"
133
+ @open="emit('open')"
134
+ @close="emit('close')"
219
135
  >
220
- <UButton
221
- :id="elementId"
222
- :label="buttonLabel"
223
- :size="size"
224
- :color="color"
225
- :block="block"
226
- :round="round"
227
- :square="square"
228
- :variant="variant"
229
- :disabled="disabled"
230
- :title="getFullOptionLabels(selectedOptions)"
231
- v-bind="dropdownButtonAttrs"
232
- :data-test="getDataTest()"
233
- @click="onClickButton"
234
- >
235
- <template #left>
236
- <!--
237
- @slot Use it to add something before the label.
238
- @binding {boolean} opened
239
- -->
240
- <slot name="left" :opened="isShownOptions" />
241
- </template>
242
-
243
- <template #default>
244
- <!--
245
- @slot Use it to add something instead of the default label.
246
- @binding {string} label
247
- @binding {boolean} opened
248
- -->
249
- <slot :label="buttonLabel" :opened="isShownOptions" />
250
- </template>
251
-
252
- <template #right>
253
- <!--
254
- @slot Use it to add something instead of the toggle icon.
255
- @binding {boolean} opened
256
- -->
257
- <slot name="toggle" :opened="isShownOptions">
258
- <UIcon
259
- v-if="toggleIconName"
260
- color="inherit"
261
- :name="toggleIconName"
262
- v-bind="toggleIconAttrs"
263
- :data-test="getDataTest('dropdown')"
264
- />
265
- </slot>
266
- </template>
267
- </UButton>
268
-
269
- <UListbox
270
- v-if="isShownOptions"
271
- ref="dropdown-list"
272
- v-model="dropdownValue"
273
- v-model:search="dropdownSearch"
274
- :searchable="searchable"
275
- :multiple="multiple"
276
- :color="color"
277
- :options="options"
278
- :options-limit="optionsLimit"
279
- :visible-options="visibleOptions"
280
- :label-key="labelKey"
281
- :value-key="valueKey"
282
- :group-label-key="groupLabelKey"
283
- :group-value-key="groupValueKey"
284
- v-bind="listboxAttrs"
285
- :data-test="getDataTest('list')"
286
- @click-option="onClickOption"
287
- @search-change="onSearchChange"
288
- @update:search="(value) => emit('update:search', value)"
289
- >
290
- <template #before-option="{ option, index }">
291
- <!--
292
- @slot Use it to add something before option.
293
- @binding {object} option
294
- @binding {number} index
136
+ <template #default="{ opened, displayLabel, fullLabel }">
137
+ <UButton
138
+ :label="displayLabel"
139
+ :size="size"
140
+ :color="color"
141
+ :block="block"
142
+ :round="round"
143
+ :square="square"
144
+ :variant="variant"
145
+ :disabled="disabled"
146
+ :title="fullLabel"
147
+ v-bind="toggleButtonAttrs"
148
+ tabindex="-1"
149
+ :data-test="getDataTest()"
150
+ >
151
+ <template #left>
152
+ <!--
153
+ @slot Use it to add something before the label.
154
+ @binding {boolean} opened
295
155
  -->
296
- <slot name="before-option" :option="option" :index="index" />
297
- </template>
298
-
299
- <template #option="{ option, index }">
300
- <!--
301
- @slot Use it to customize the option.
302
- @binding {object} option
303
- @binding {number} index
156
+ <slot name="left" :opened="opened" />
157
+ </template>
158
+
159
+ <template #default>
160
+ <!--
161
+ @slot Use it to add something instead of the default label.
162
+ @binding {string} label
163
+ @binding {boolean} opened
304
164
  -->
305
- <slot name="option" :option="option" :index="index" />
306
- </template>
165
+ <slot :label="displayLabel" :opened="opened" />
166
+ </template>
307
167
 
308
- <template #after-option="{ option, index }">
309
- <!--
310
- @slot Use it to add something after option.
311
- @binding {object} option
312
- @binding {number} index
168
+ <template #right>
169
+ <!--
170
+ @slot Use it to add something instead of the toggle icon.
171
+ @binding {boolean} opened
313
172
  -->
314
- <slot name="after-option" :option="option" :index="index" />
315
- </template>
316
-
317
- <template #empty>
318
- <!-- @slot Use it to add something instead of empty state. -->
319
- <slot name="empty" />
320
- </template>
321
- </UListbox>
322
- </div>
173
+ <slot name="toggle" :opened="opened">
174
+ <UIcon
175
+ v-if="toggleIconName"
176
+ color="inherit"
177
+ :name="toggleIconName"
178
+ v-bind="toggleIconAttrs"
179
+ :data-test="getDataTest('dropdown')"
180
+ />
181
+ </slot>
182
+ </template>
183
+ </UButton>
184
+ </template>
185
+
186
+ <template #before-option="{ option, index }">
187
+ <!--
188
+ @slot Use it to add something before option.
189
+ @binding {object} option
190
+ @binding {number} index
191
+ -->
192
+ <slot name="before-option" :option="option" :index="index" />
193
+ </template>
194
+
195
+ <template #option="{ option, index }">
196
+ <!--
197
+ @slot Use it to customize the option.
198
+ @binding {object} option
199
+ @binding {number} index
200
+ -->
201
+ <slot name="option" :option="option" :index="index" />
202
+ </template>
203
+
204
+ <template #after-option="{ option, index }">
205
+ <!--
206
+ @slot Use it to add something after option.
207
+ @binding {object} option
208
+ @binding {number} index
209
+ -->
210
+ <slot name="after-option" :option="option" :index="index" />
211
+ </template>
212
+
213
+ <template #empty>
214
+ <!-- @slot Use it to add something instead of empty state. -->
215
+ <slot name="empty" />
216
+ </template>
217
+ </UDropdown>
323
218
  </template>
@@ -1,49 +1,53 @@
1
1
  export default /*tw*/ {
2
- wrapper: {
3
- base: "relative inline-block h-max",
2
+ dropdownButton: {
3
+ base: "{UDropdown}",
4
4
  variants: {
5
5
  block: {
6
- true: "w-full",
6
+ true: "!block w-full",
7
7
  },
8
8
  },
9
- },
10
- dropdownButton: "{UButton} justify-between",
11
- toggleIcon: {
12
- base: "{UIcon} transition duration-300 -mr-1",
13
9
  defaults: {
14
10
  size: {
15
- "2xs": "2xs",
16
- xs: "xs",
17
- sm: "sm",
18
- md: "sm",
19
- lg: "sm",
20
- xl: "sm",
11
+ "2xs": "sm",
12
+ xs: "sm",
13
+ sm: "md",
14
+ md: "md",
15
+ lg: "lg",
16
+ xl: "lg",
21
17
  },
22
18
  },
23
- compoundVariants: [{ opened: true, class: "rotate-180" }],
24
19
  },
25
- listbox: {
26
- base: "{UListbox} w-fit",
20
+ toggleButton: {
21
+ base: "{UButton} justify-between",
27
22
  variants: {
28
- yPosition: {
29
- top: "bottom-full mb-1.5",
30
- bottom: "top-full mt-1.5",
23
+ block: {
24
+ true: "w-full",
31
25
  },
32
- xPosition: {
33
- left: "left-0",
34
- right: "right-0",
26
+ },
27
+ },
28
+ toggleIcon: {
29
+ base: "{UIcon} transition duration-300",
30
+ variants: {
31
+ size: {
32
+ "2xs": "-ml-0.5 -mr-1",
33
+ xs: "-ml-1 -mr-1",
34
+ sm: "-ml-1 -mr-1.5",
35
+ md: "-ml-1 -mr-2",
36
+ lg: "-ml-1.5 -mr-2.5",
37
+ xl: "-ml-1.5 -mr-2.5",
35
38
  },
36
39
  },
37
40
  defaults: {
38
41
  size: {
39
- "2xs": "sm",
40
- xs: "sm",
41
- sm: "md",
42
- md: "md",
43
- lg: "lg",
44
- xl: "lg",
42
+ "2xs": "2xs",
43
+ xs: "xs",
44
+ sm: "sm",
45
+ md: "sm",
46
+ lg: "md",
47
+ xl: "md",
45
48
  },
46
49
  },
50
+ compoundVariants: [{ opened: true, class: "rotate-180" }],
47
51
  },
48
52
  defaults: {
49
53
  color: "primary",