vueless 0.0.710 → 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/constants.js CHANGED
@@ -18,6 +18,8 @@ export const COLOR_MODE_KEY = "vl-color-mode";
18
18
  export const DEFAULT_BRAND_COLOR = GRAYSCALE_COLOR;
19
19
  export const DEFAULT_GRAY_COLOR = COOL_COLOR;
20
20
  export const DEFAULT_RING = 2; /* pixels */
21
+ export const RING_DECREMENT = 1; /* pixels */
22
+ export const RING_INCREMENT = 1; /* pixels */
21
23
  export const DEFAULT_RING_OFFSET_COLOR_LIGHT = "#ffffff"; // white
22
24
  export const DEFAULT_RING_OFFSET_COLOR_DARK = "#111827"; // gray-900
23
25
  export const DEFAULT_ROUNDING = 8; /* pixels */
@@ -171,7 +173,7 @@ export const TAILWIND_MERGE_EXTENSION = {
171
173
  spacing: ["safe-top", "safe-bottom", "safe-left", "safe-right"],
172
174
  },
173
175
  classGroups: {
174
- "ring-w": [{ ring: ["dynamic", "dynamic-1"] }],
176
+ "ring-w": [{ ring: ["dynamic", "dynamic-sm", "dynamic-lg"] }],
175
177
  "ring-offset-color": [{ "ring-offset": ["color-dynamic"] }],
176
178
  "font-size": [{ text: ["2xs"] }],
177
179
  rounded: [{ rounded: ["dynamic", "dynamic-sm", "dynamic-lg", "inherit"] }],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vueless",
3
- "version": "0.0.710",
3
+ "version": "0.0.712",
4
4
  "license": "MIT",
5
5
  "description": "Vue Styleless UI Component Library, powered by Tailwind CSS.",
6
6
  "keywords": [
@@ -17,6 +17,10 @@ import {
17
17
  DEFAULT_BRAND_COLOR,
18
18
  DEFAULT_GRAY_COLOR,
19
19
  GRAYSCALE_COLOR,
20
+ ROUNDING_DECREMENT,
21
+ ROUNDING_INCREMENT,
22
+ RING_INCREMENT,
23
+ RING_DECREMENT,
20
24
  } from "./constants.js";
21
25
 
22
26
  const globalSettings = process.env.VUELESS_GLOBAL_SETTINGS || {};
@@ -92,8 +96,9 @@ export const vuelessTailwindConfig = {
92
96
  "dynamic-lg": "var(--vl-rounding-lg)",
93
97
  },
94
98
  ringWidth: {
99
+ "dynamic-sm": "var(--vl-ring-sm)",
95
100
  dynamic: "var(--vl-ring)",
96
- "dynamic-1": "calc(var(--vl-ring) - 1px)",
101
+ "dynamic-lg": "var(--vl-ring-lg)",
97
102
  },
98
103
  ringOffsetColor: {
99
104
  dynamic: twColorWithOpacity("--vl-ring-offset-color"),
@@ -101,15 +106,17 @@ export const vuelessTailwindConfig = {
101
106
  },
102
107
  configViewer: {
103
108
  themeReplacements: {
104
- /* eslint-disable prettier/prettier */
109
+ /* eslint-disable prettier/prettier, vue/max-len */
110
+ "var(--vl-ring-sm)": globalSettings.ringSm || Math.max(0, (globalSettings.ring || DEFAULT_RING) - RING_DECREMENT),
105
111
  "var(--vl-ring)": globalSettings.ring || DEFAULT_RING,
112
+ "var(--vl-ring-lg)": globalSettings.ringLg || Math.max(0, (globalSettings.ring || DEFAULT_RING) + RING_INCREMENT),
106
113
  "var(--vl-ring-offset-color)": globalSettings.ringOffsetColorLight || DEFAULT_RING_OFFSET_COLOR_LIGHT,
107
- "var(--vl-rounding-sm)": globalSettings.roundingSm || (globalSettings.ring || DEFAULT_ROUNDING) / 2,
114
+ "var(--vl-rounding-sm)": globalSettings.roundingSm || Math.max(0, (globalSettings.rounding || DEFAULT_ROUNDING) - ROUNDING_DECREMENT),
108
115
  "var(--vl-rounding)": globalSettings.ring || DEFAULT_ROUNDING,
109
- "var(--vl-rounding-lg)": globalSettings.roundingLg || (globalSettings.ring || DEFAULT_ROUNDING) * 2,
116
+ "var(--vl-rounding-lg)": globalSettings.roundingLg || Math.max(0, (globalSettings.rounding || DEFAULT_ROUNDING) - ROUNDING_INCREMENT),
110
117
  ...getReplacementColors(GRAY_COLOR, globalSettings.gray || DEFAULT_GRAY_COLOR),
111
118
  ...getReplacementColors(BRAND_COLOR, globalSettings.brand || DEFAULT_BRAND_COLOR),
112
- /* eslint-enable prettier/prettier */
119
+ /* eslint-enable prettier/prettier, vue/max-len */
113
120
  },
114
121
  },
115
122
  },
package/types.ts CHANGED
@@ -81,7 +81,7 @@ export interface ThemeConfig {
81
81
  gray?: GrayColors;
82
82
 
83
83
  /**
84
- * Default components "sm" size rounding (border-radius).
84
+ * Default components small size rounding (border-radius).
85
85
  */
86
86
  roundingSm?: number;
87
87
 
@@ -91,15 +91,25 @@ export interface ThemeConfig {
91
91
  rounding?: number;
92
92
 
93
93
  /**
94
- * Default components "lg" size rounding (border-radius).
94
+ * Default components large size rounding (border-radius).
95
95
  */
96
96
  roundingLg?: number;
97
97
 
98
+ /**
99
+ * Default components small size ring width.
100
+ */
101
+ ringSm?: number;
102
+
98
103
  /**
99
104
  * Default components ring width.
100
105
  */
101
106
  ring?: number;
102
107
 
108
+ /**
109
+ * Default components large size ring width.
110
+ */
111
+ ringLg?: number;
112
+
103
113
  /**
104
114
  * Default components ring color for light theme.
105
115
  */
@@ -339,7 +349,9 @@ export interface TailwindColorShades {
339
349
  }
340
350
 
341
351
  export interface VuelessCssVariables {
352
+ "--vl-ring-sm": string;
342
353
  "--vl-ring": string;
354
+ "--vl-ring-lg": string;
343
355
  "--vl-ring-offset-color": string;
344
356
  "--vl-rounding-sm": string;
345
357
  "--vl-rounding": string;
@@ -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>
@@ -4,7 +4,7 @@ export default /*tw*/ {
4
4
  base: `
5
5
  border rounded-dynamic border-gray-300 relative flex w-full bg-white transition
6
6
  hover:border-gray-400 hover:focus-within:border-brand-600 focus-within:border-brand-600
7
- focus-within:ring-dynamic-1 focus-within:ring-brand-600
7
+ focus-within:ring-dynamic-sm focus-within:ring-brand-600
8
8
  `,
9
9
  variants: {
10
10
  error: {
@@ -5,7 +5,7 @@ export default /*tw*/ {
5
5
  p-3 size-auto w-full bg-white transition
6
6
  rounded-dynamic border border-solid border-gray-300
7
7
  hover:border-gray-400 hover:focus-within:border-brand-600 focus-within:border-brand-600
8
- focus-within:ring-brand-600 focus-within:ring-dynamic-1
8
+ focus-within:ring-brand-600 focus-within:ring-dynamic-sm
9
9
  `,
10
10
  variants: {
11
11
  error: {
@@ -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;
@@ -5,7 +5,7 @@ export default /*tw*/ {
5
5
  flex flex-row-reverse justify-between w-full min-h-full box-border relative
6
6
  rounded-dynamic border border-gray-300 bg-white
7
7
  hover:border-gray-400 hover:transition hover:focus-within:border-brand-600
8
- focus-within:ring-brand-600 focus-within:ring-dynamic-1
8
+ focus-within:ring-brand-600 focus-within:ring-dynamic-sm
9
9
  focus-within:border-brand-600 focus-within:outline-none
10
10
  `,
11
11
  variants: {
@@ -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.
@@ -7,7 +7,7 @@ export default /*tw*/ {
7
7
  base: `
8
8
  flex bg-white transition w-full
9
9
  rounded-dynamic border border-gray-300 hover:border-gray-400 hover:focus-within:border-brand-600
10
- focus-within:border-brand-600 focus-within:ring-dynamic-1
10
+ focus-within:border-brand-600 focus-within:ring-dynamic-sm
11
11
  focus-within:ring-brand-600 focus-within:outline-none
12
12
  `,
13
13
  variants: {
package/utils/theme.ts CHANGED
@@ -10,12 +10,14 @@ import {
10
10
  DARK_MODE_SELECTOR,
11
11
  GRAYSCALE_COLOR,
12
12
  TAILWIND_COLORS,
13
- DEFAULT_RING,
14
- DEFAULT_ROUNDING,
15
13
  DEFAULT_BRAND_COLOR,
16
14
  DEFAULT_GRAY_COLOR,
15
+ DEFAULT_RING,
17
16
  DEFAULT_RING_OFFSET_COLOR_LIGHT,
18
17
  DEFAULT_RING_OFFSET_COLOR_DARK,
18
+ RING_DECREMENT,
19
+ RING_INCREMENT,
20
+ DEFAULT_ROUNDING,
19
21
  ROUNDING_DECREMENT,
20
22
  ROUNDING_INCREMENT,
21
23
  } from "../constants.js";
@@ -40,7 +42,9 @@ declare interface RootCSSVariableOptions {
40
42
  colors: Colors;
41
43
  brand: string;
42
44
  gray: string;
45
+ ringSm: number;
43
46
  ring: number;
47
+ ringLg: number;
44
48
  ringOffsetColorDark: string;
45
49
  ringOffsetColorLight: string;
46
50
  roundingSm: number;
@@ -155,7 +159,11 @@ export function setTheme(config: Config = {}) {
155
159
  let gray: GrayColors =
156
160
  config.gray ?? getSelectedGrayColor() ?? vuelessConfig.gray ?? DEFAULT_GRAY_COLOR;
157
161
 
158
- const ring = config.ring ?? vuelessConfig.ring ?? DEFAULT_RING;
162
+ const { ringSm, ring, ringLg } = getRings(
163
+ config.ringSm ?? vuelessConfig.ringSm,
164
+ config.ring ?? vuelessConfig.ring,
165
+ config.ringLg ?? vuelessConfig.ringLg,
166
+ );
159
167
 
160
168
  const ringOffsetColorDark =
161
169
  config.ringOffsetColorDark ??
@@ -198,7 +206,9 @@ export function setTheme(config: Config = {}) {
198
206
  colors,
199
207
  brand,
200
208
  gray,
209
+ ringSm,
201
210
  ring,
211
+ ringLg,
202
212
  ringOffsetColorDark,
203
213
  ringOffsetColorLight,
204
214
  roundingSm,
@@ -207,6 +217,22 @@ export function setTheme(config: Config = {}) {
207
217
  });
208
218
  }
209
219
 
220
+ function getRings(sm?: number, md?: number, lg?: number) {
221
+ const ring = Math.max(0, md ?? DEFAULT_RING);
222
+ const ringSm = Math.max(0, ring - RING_DECREMENT);
223
+ let ringLg = Math.max(0, ring + RING_INCREMENT);
224
+
225
+ if (ring === 0) {
226
+ ringLg = 0;
227
+ }
228
+
229
+ return {
230
+ ring,
231
+ ringSm: sm === undefined ? ringSm : Math.max(0, sm),
232
+ ringLg: lg === undefined ? ringLg : Math.max(0, lg),
233
+ };
234
+ }
235
+
210
236
  function getRoundings(sm?: number, md?: number, lg?: number) {
211
237
  const rounding = Math.max(0, md ?? DEFAULT_ROUNDING);
212
238
  let roundingSm = Math.max(0, rounding - ROUNDING_DECREMENT);
@@ -240,7 +266,9 @@ function setRootCSSVariables(options: RootCSSVariableOptions) {
240
266
  colors,
241
267
  brand,
242
268
  gray,
269
+ ringSm,
243
270
  ring,
271
+ ringLg,
244
272
  ringOffsetColorDark,
245
273
  ringOffsetColorLight,
246
274
  roundingSm,
@@ -257,7 +285,9 @@ function setRootCSSVariables(options: RootCSSVariableOptions) {
257
285
  "--vl-rounding-sm": `${Number(roundingSm) / PX_IN_REM}rem`,
258
286
  "--vl-rounding": `${Number(rounding) / PX_IN_REM}rem`,
259
287
  "--vl-rounding-lg": `${Number(roundingLg) / PX_IN_REM}rem`,
260
- "--vl-ring": `${Math.max(0, ring)}px`,
288
+ "--vl-ring-sm": `${ringSm}px`,
289
+ "--vl-ring": `${ring}px`,
290
+ "--vl-ring-lg": `${ringLg}px`,
261
291
  "--vl-ring-offset-color": convertHexInRgb(defaultRingOffsetColor),
262
292
  "--vl-color-gray-default": convertHexInRgb(colors[gray]?.[defaultBrandShade]),
263
293
  "--vl-color-brand-default": convertHexInRgb(colors[brand]?.[defaultGrayShade]),