vueless 0.0.711 → 0.0.712

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vueless",
3
- "version": "0.0.711",
3
+ "version": "0.0.712",
4
4
  "license": "MIT",
5
5
  "description": "Vue Styleless UI Component Library, powered by Tailwind CSS.",
6
6
  "keywords": [
@@ -12,7 +12,7 @@ import defaultConfig from "../config.ts?raw"
12
12
  <Controls of={stories.Default} />
13
13
  <Stories of={stories} />
14
14
 
15
- ## Row meta keys
15
+ ## Option meta keys
16
16
  Keys you may/have to provide to component in an option object.
17
17
 
18
18
  <Markdown>
@@ -10,6 +10,7 @@ import useUI from "../composables/useUI.ts";
10
10
  import { createDebounce, hasSlotContent } from "../utils/helper.ts";
11
11
  import { getDefaults } from "../utils/ui.ts";
12
12
  import { isMac } from "../utils/platform.ts";
13
+ import { useMutationObserver } from "../composables/useMutationObserver.ts";
13
14
 
14
15
  import {
15
16
  filterOptions,
@@ -334,6 +335,12 @@ function onMouseDownClear() {
334
335
  emit("remove", props.options);
335
336
  }
336
337
 
338
+ useMutationObserver(leftSlotWrapperRef, (mutations) => mutations.forEach(setLabelPosition), {
339
+ childList: true,
340
+ characterData: true,
341
+ subtree: true,
342
+ });
343
+
337
344
  function setLabelPosition() {
338
345
  if (props.labelAlign === "top" || (!hasSlotContent(slots["left"]) && !props.leftIcon)) {
339
346
  return;
@@ -114,10 +114,8 @@ export default /*tw*/ {
114
114
  selected: {
115
115
  false: "w-full",
116
116
  },
117
- opened: {
118
- true: "w-full",
119
- },
120
117
  },
118
+ compoundVariants: [{ opened: true, searchable: false, class: "w-0" }],
121
119
  },
122
120
  searchInput: {
123
121
  base: `
@@ -12,5 +12,8 @@ import defaultConfig from "../config.ts?raw"
12
12
  <Controls of={stories.Default} />
13
13
  <Stories of={stories} />
14
14
 
15
+ ## Option meta keys
16
+ Full list of keys you may/have to provide to component in an option object can be found here: [UDropdownList docs](https://ui.vueless.com/?path=/docs/2050--docs).
17
+
15
18
  ## Default config
16
19
  <Source code={getSource(defaultConfig)} language="jsx" dark />
@@ -1,3 +1,4 @@
1
+ import { ref, computed } from "vue";
1
2
  import {
2
3
  getArgTypes,
3
4
  getSlotNames,
@@ -7,16 +8,18 @@ import {
7
8
 
8
9
  import USelect from "../../ui.form-select/USelect.vue";
9
10
  import URow from "../../ui.container-row/URow.vue";
11
+ import UCol from "../../ui.container-col/UCol.vue";
10
12
  import UBadge from "../../ui.text-badge/UBadge.vue";
11
13
  import UIcon from "../../ui.image-icon/UIcon.vue";
12
14
  import ULink from "../../ui.button-link/ULink.vue";
15
+ import UAvatar from "../../ui.image-avatar/UAvatar.vue";
13
16
 
14
17
  import type { Meta, StoryFn } from "@storybook/vue3";
15
18
  import type { Props } from "../types.ts";
16
19
 
17
20
  interface USelectArgs extends Props {
18
21
  slotTemplate?: string;
19
- enum: "size" | "openDirection";
22
+ enum: "size" | "openDirection" | "labelAlign";
20
23
  }
21
24
 
22
25
  interface SelectOption {
@@ -30,13 +33,14 @@ export default {
30
33
  title: "Form Inputs & Controls / Select",
31
34
  component: USelect,
32
35
  args: {
33
- label: "Label",
36
+ label: "Choose a city",
34
37
  modelValue: null,
35
38
  options: [
36
- { id: 1, label: "value 1", badge: "10%" },
37
- { id: 2, label: "value 2", badge: "20%" },
38
- { id: 3, label: "value 3", badge: "30%" },
39
- { id: 4, label: "value 4", badge: "40%" },
39
+ { label: "New York", id: "1" },
40
+ { label: "Los Angeles", id: "2" },
41
+ { label: "Chicago", id: "3" },
42
+ { label: "Houston", id: "4" },
43
+ { label: "San Francisco", id: "5" },
40
44
  ],
41
45
  },
42
46
  argTypes: {
@@ -46,49 +50,63 @@ export default {
46
50
  docs: {
47
51
  ...getDocsDescription(USelect.__name),
48
52
  story: {
49
- height: "280px",
53
+ height: "300px",
50
54
  },
51
55
  },
52
56
  },
53
57
  } as Meta;
54
58
 
55
59
  const DefaultTemplate: StoryFn<USelectArgs> = (args: USelectArgs) => ({
56
- components: { USelect, UIcon, UBadge, ULink },
60
+ components: { USelect, UIcon, UBadge, ULink, UAvatar },
57
61
  setup() {
58
62
  function getSelectedBadge(options: SelectOption[], currentValue: string | number) {
59
63
  return options?.find((option) => option.id === currentValue);
60
64
  }
61
65
 
62
66
  const slots = getSlotNames(USelect.__name);
67
+ const errorMessage = computed(() => (!args.modelValue ? args.error : ""));
68
+ const showAlert = (message: string) => alert(message);
63
69
 
64
- return { args, slots, getSelectedBadge };
70
+ return { args, slots, getSelectedBadge, errorMessage, showAlert };
65
71
  },
66
72
  template: `
67
- <USelect v-bind="args" v-model="args.modelValue">
73
+ <USelect
74
+ v-bind="args"
75
+ v-model="args.modelValue"
76
+ :error="errorMessage"
77
+ class="max-w-96"
78
+ @add="showAlert('You triggered the add action!')"
79
+ >
68
80
  ${args.slotTemplate || getSlotsFragment("")}
69
81
  </USelect>
70
82
  `,
71
83
  });
72
84
 
73
85
  const EnumVariantTemplate: StoryFn<USelectArgs> = (args: USelectArgs, { argTypes }) => ({
74
- components: { USelect, URow },
86
+ components: { USelect, UCol },
75
87
  setup() {
76
- return {
77
- args,
78
- options: argTypes?.[args.enum]?.options,
79
- };
88
+ let filteredOptions = argTypes?.[args.enum]?.options;
89
+
90
+ if (args.enum === "labelAlign") {
91
+ filteredOptions = argTypes?.[args.enum]?.options?.filter(
92
+ (item) => item !== "right" && item !== "topWithDesc",
93
+ );
94
+ }
95
+
96
+ return { args, filteredOptions };
80
97
  },
81
98
  template: `
82
- <URow>
99
+ <UCol>
83
100
  <USelect
84
- v-for="(option, index) in options"
101
+ v-for="(option, index) in filteredOptions"
85
102
  :key="index"
86
103
  v-bind="args"
87
104
  v-model="args.modelValue"
88
105
  :[args.enum]="option"
89
- :label="option"
106
+ :description="option"
107
+ class="max-w-96"
90
108
  />
91
- </URow>
109
+ </UCol>
92
110
  `,
93
111
  });
94
112
 
@@ -118,6 +136,24 @@ const GroupValuesTemplate: StoryFn<USelectArgs> = (args: USelectArgs) => ({
118
136
  export const Default = DefaultTemplate.bind({});
119
137
  Default.args = {};
120
138
 
139
+ export const Placeholder = DefaultTemplate.bind({});
140
+ Placeholder.args = { placeholder: "Start typing to search for a city..." };
141
+
142
+ export const Description = DefaultTemplate.bind({});
143
+ Description.args = { description: "You can only select a city from the list." };
144
+
145
+ export const Error = DefaultTemplate.bind({});
146
+ Error.args = { error: "Please select a city from the list" };
147
+
148
+ export const Disabled = DefaultTemplate.bind({});
149
+ Disabled.args = { disabled: true };
150
+
151
+ export const LabelPlacement = EnumVariantTemplate.bind({});
152
+ LabelPlacement.args = { enum: "labelAlign" };
153
+
154
+ export const Sizes = EnumVariantTemplate.bind({});
155
+ Sizes.args = { enum: "size", multiple: true, modelValue: [] };
156
+
121
157
  export const LargeItemList = DefaultTemplate.bind({});
122
158
  LargeItemList.args = {
123
159
  options: [...new Array(1000)].map((_, index) => {
@@ -128,6 +164,18 @@ LargeItemList.args = {
128
164
  export const Multiple = DefaultTemplate.bind({});
129
165
  Multiple.args = { multiple: true, modelValue: [] };
130
166
 
167
+ export const ClearableAndSearchable = DefaultTemplate.bind({});
168
+ ClearableAndSearchable.args = { clearable: false, searchable: false };
169
+ ClearableAndSearchable.parameters = {
170
+ docs: {
171
+ description: {
172
+ story:
173
+ // eslint-disable-next-line vue/max-len
174
+ "The `clearable` and `searchable` props control whether users can clear the selected value or search within the list. <br/> In this example, both are set to `false`, meaning the selection cannot be cleared, and searching is disabled.",
175
+ },
176
+ },
177
+ };
178
+
131
179
  export const OpenDirection = EnumVariantTemplate.bind({});
132
180
  OpenDirection.args = { enum: "openDirection" };
133
181
 
@@ -159,36 +207,181 @@ GroupValue.args = {
159
207
  ],
160
208
  };
161
209
 
162
- export const Disabled = DefaultTemplate.bind({});
163
- Disabled.args = { disabled: true };
164
-
165
- export const Error = DefaultTemplate.bind({});
166
- Error.args = { error: "some error text" };
167
-
168
- export const Placeholder = DefaultTemplate.bind({});
169
- Placeholder.args = { placeholder: "some placeholder text" };
170
-
171
- export const Description = DefaultTemplate.bind({});
172
- Description.args = { description: "some description text" };
173
-
174
210
  export const OptionsLimit2 = DefaultTemplate.bind({});
175
211
  OptionsLimit2.args = { optionsLimit: 2 };
212
+ OptionsLimit2.parameters = {
213
+ docs: {
214
+ description: {
215
+ story: "`optionsLimit` prop controls the number of options displayed in the dropdown.",
216
+ },
217
+ },
218
+ };
219
+
220
+ export const VisibleOptions = DefaultTemplate.bind({});
221
+ VisibleOptions.args = { visibleOptions: 3 };
222
+ VisibleOptions.parameters = {
223
+ docs: {
224
+ description: {
225
+ story: "`visibleOptions` prop controls the number of options you can see without a scroll.",
226
+ },
227
+ },
228
+ };
176
229
 
177
230
  export const AddOption = DefaultTemplate.bind({});
178
231
  AddOption.args = { addOption: true };
232
+ AddOption.parameters = {
233
+ docs: {
234
+ description: {
235
+ story:
236
+ // eslint-disable-next-line vue/max-len
237
+ "The `addOption` prop displays an 'Add option' button, while the `add` event allows handling custom functionality when the button is clicked.",
238
+ },
239
+ },
240
+ };
179
241
 
180
- export const OptionIsHidden = DefaultTemplate.bind({});
181
- OptionIsHidden.args = {
242
+ export const OptionSettings = DefaultTemplate.bind({});
243
+ OptionSettings.args = {
182
244
  options: [
183
- { id: 1, label: "value 1", isHidden: true },
184
- { id: 2, label: "value 2" },
185
- { id: 3, label: "value 3" },
186
- { id: 4, label: "value 4" },
245
+ { label: "1. New York", id: "1" },
246
+ { label: "2. Los Angeles", id: "2", isHidden: true },
247
+ {
248
+ label: "3. Chicago",
249
+ id: "3",
250
+ onClick: (option) =>
251
+ alert("onClick function for the third option: " + JSON.stringify(option)),
252
+ },
253
+ { label: "4. Houston", id: "4" },
254
+ { label: "5. San Francisco", id: "5" },
187
255
  ],
188
256
  };
257
+ OptionSettings.parameters = {
258
+ docs: {
259
+ description: {
260
+ story:
261
+ // eslint-disable-next-line vue/max-len
262
+ "The second option of the array is hidden (`isHidden` object property is set to `true`). <br/> The third option has `onClick` event handler: <br/> `onClick: (option: Option) => alert('onClick function for option 3: ' + JSON.stringify(option))`",
263
+ },
264
+ },
265
+ };
189
266
 
190
- export const Sizes = EnumVariantTemplate.bind({});
191
- Sizes.args = { enum: "size", multiple: true, modelValue: [] };
267
+ export const IconProps: StoryFn<USelectArgs> = (args) => ({
268
+ components: { USelect, URow },
269
+ setup() {
270
+ const levelOptions = [
271
+ { label: "Awesome", id: "1" },
272
+ { label: "Good", id: "2" },
273
+ { label: "Could be better", id: "3" },
274
+ { label: "Terrible", id: "4" },
275
+ ];
276
+
277
+ const roleOptions = [
278
+ { label: "Admin", id: "1" },
279
+ { label: "CEO", id: "2" },
280
+ { label: "Manager", id: "3" },
281
+ { label: "Guest", id: "4" },
282
+ ];
283
+
284
+ return { args, levelOptions, roleOptions };
285
+ },
286
+ template: `
287
+ <URow no-mobile>
288
+ <USelect
289
+ left-icon="feedback"
290
+ label="Choose the level of our services"
291
+ placeholder="Share your feedback with us"
292
+ :options="levelOptions"
293
+ />
294
+ <USelect
295
+ right-icon="person"
296
+ label="Select your role"
297
+ placeholder="Choose a role from the list"
298
+ :options="roleOptions"
299
+ />
300
+ </URow>
301
+ `,
302
+ });
303
+
304
+ export const Slots: StoryFn<USelectArgs> = (args) => ({
305
+ components: { USelect, UCol, URow, ULink, UBadge, UAvatar },
306
+ setup() {
307
+ const clearModel = ref(null);
308
+ const clearMultipleModel = ref([]);
309
+ const beforeToggleModel = ref(null);
310
+ const afterToggleModel = ref(null);
311
+ const leftModel = ref(null);
312
+ const rightModel = ref(null);
313
+
314
+ return {
315
+ args,
316
+ clearModel,
317
+ clearMultipleModel,
318
+ beforeToggleModel,
319
+ afterToggleModel,
320
+ leftModel,
321
+ rightModel,
322
+ };
323
+ },
324
+ template: `
325
+ <UCol no-mobile>
326
+ <USelect v-bind="args" v-model="args.clearModel" label="Slot clear">
327
+ <template #clear>
328
+ <ULink label="Close" />
329
+ </template>
330
+ </USelect>
331
+
332
+ <USelect
333
+ v-bind="args"
334
+ v-model="args.clearMultipleModel"
335
+ multiple
336
+ label="Slot clear-multiple"
337
+ >
338
+ <template #clear-multiple>
339
+ <ULink label="Close" color="green" />
340
+ </template>
341
+ </USelect>
342
+
343
+ <URow no-mobile>
344
+ <USelect v-bind="args" v-model="args.beforeToggleModel" label="Slot before-toggle">
345
+ <template #before-toggle>
346
+ <UAvatar />
347
+ </template>
348
+ </USelect>
349
+
350
+ <USelect
351
+ v-bind="args"
352
+ v-model="args.afterToggleModel"
353
+ :config="{ afterToggle: 'pt-0 items-center' }"
354
+ label="Slot after-toggle"
355
+ >
356
+ <template #after-toggle>
357
+ <UAvatar />
358
+ </template>
359
+ </USelect>
360
+ </URow>
361
+
362
+ <URow no-mobile>
363
+ <USelect v-bind="args" v-model="args.leftModel" label="Slot left">
364
+ <template #left>
365
+ <UAvatar />
366
+ </template>
367
+ </USelect>
368
+
369
+ <USelect v-bind="args" v-model="args.rightModel" label="Slot right">
370
+ <template #right>
371
+ <UAvatar />
372
+ </template>
373
+ </USelect>
374
+ </URow>
375
+ </UCol>
376
+ `,
377
+ });
378
+ Slots.parameters = {
379
+ docs: {
380
+ story: {
381
+ height: "500px",
382
+ },
383
+ },
384
+ };
192
385
 
193
386
  export const SlotToggle = DefaultTemplate.bind({});
194
387
  SlotToggle.args = {
@@ -202,67 +395,29 @@ SlotToggle.args = {
202
395
  `,
203
396
  };
204
397
 
205
- export const SlotClear = DefaultTemplate.bind({});
206
- SlotClear.args = {
207
- slotTemplate: `
208
- <template #clear>
209
- <ULink label="Close" />
210
- </template>
211
- `,
212
- };
213
-
214
- export const SlotClearMultiple = DefaultTemplate.bind({});
215
- SlotClearMultiple.args = {
216
- multiple: true,
217
- modelValue: [],
218
- slotTemplate: `
219
- <template #clear-multiple>
220
- <ULink label="Close" />
221
- </template>
222
- `,
223
- };
224
-
225
398
  export const SlotSelectedValueLabel = DefaultTemplate.bind({});
226
399
  SlotSelectedValueLabel.args = {
227
400
  slotTemplate: `
228
- <template #selected-label>
229
- 🤘🤘🤘
401
+ <template #selected-label="{ selectedLabel }">
402
+ <UBadge :label="selectedLabel" color="green" />
230
403
  </template>
231
404
  `,
232
405
  };
233
406
 
234
407
  export const SlotSelectedValueLabelAfter = DefaultTemplate.bind({});
235
408
  SlotSelectedValueLabelAfter.args = {
409
+ options: [
410
+ { label: "Venice", id: "1", icon: "sailing", color: "green" },
411
+ { label: "Paris", id: "2", icon: "flight", color: "orange" },
412
+ ],
236
413
  slotTemplate: `
237
- <template #selected-label-after>
238
- 🤘🤘🤘
239
- </template>
240
- `,
241
- };
242
-
243
- export const SlotOption = DefaultTemplate.bind({});
244
- SlotOption.args = {
245
- slotTemplate: `
246
- <template #option>
247
- 🤘🤘🤘
248
- </template>
249
- `,
250
- };
251
-
252
- export const SlotAfterToggle = DefaultTemplate.bind({});
253
- SlotAfterToggle.args = {
254
- slotTemplate: `
255
- <template #after-toggle>
256
- 🤘
257
- </template>
258
- `,
259
- };
260
-
261
- export const SlotBeforeToggle = DefaultTemplate.bind({});
262
- SlotBeforeToggle.args = {
263
- slotTemplate: `
264
- <template #before-toggle>
265
- 🤘
414
+ <template #selected-label-after="{ option }">
415
+ <UIcon
416
+ :name="option.icon"
417
+ :color="option.color"
418
+ size="xs"
419
+ class="ml-1"
420
+ />
266
421
  </template>
267
422
  `,
268
423
  };
@@ -270,32 +425,26 @@ SlotBeforeToggle.args = {
270
425
  export const SlotBeforeOption = DefaultTemplate.bind({});
271
426
  SlotBeforeOption.args = {
272
427
  slotTemplate: `
273
- <template #before-option>
274
- 🤘
428
+ <template #before-option="{ option, index }">
429
+ <UBadge v-if="index === 3" label="Special offer!" color="blue" class="mr-1" />
275
430
  </template>
276
431
  `,
277
432
  };
278
433
 
279
- export const LeftIcon = DefaultTemplate.bind({});
280
- LeftIcon.args = { leftIcon: "star" };
281
-
282
- export const RightIcon = DefaultTemplate.bind({});
283
- RightIcon.args = { rightIcon: "star" };
284
-
285
- export const SlotLeft = DefaultTemplate.bind({});
286
- SlotLeft.args = {
434
+ export const SlotOption = DefaultTemplate.bind({});
435
+ SlotOption.args = {
287
436
  slotTemplate: `
288
- <template #left>
289
- 🤘
437
+ <template #option="{ option, index }">
438
+ <UBadge v-if="index === 1" :label="option.label" />
290
439
  </template>
291
440
  `,
292
441
  };
293
442
 
294
- export const SlotRight = DefaultTemplate.bind({});
295
- SlotRight.args = {
443
+ export const SlotAfterOption = DefaultTemplate.bind({});
444
+ SlotAfterOption.args = {
296
445
  slotTemplate: `
297
- <template #right>
298
- 🤘
446
+ <template #after-option="{ option, index }">
447
+ <UBadge v-if="index === 2" label="Special offer!" color="blue" class="ml-1" />
299
448
  </template>
300
449
  `,
301
450
  };
@@ -24,7 +24,7 @@ export interface Props {
24
24
  /**
25
25
  * Label placement.
26
26
  */
27
- labelAlign?: "top" | "topInside" | "topWithDesc" | "left" | "right";
27
+ labelAlign?: "topInside" | "top" | "topWithDesc" | "left" | "right";
28
28
 
29
29
  /**
30
30
  * Select placeholder.