@nuxt/ui 2.8.1 → 2.10.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.
Files changed (120) hide show
  1. package/README.md +1 -0
  2. package/dist/module.d.mts +1411 -1092
  3. package/dist/module.d.ts +1411 -1092
  4. package/dist/module.json +1 -1
  5. package/dist/module.mjs +55 -11
  6. package/dist/runtime/components/data/Table.vue +47 -32
  7. package/dist/runtime/components/data/Table.vue.d.ts +22 -14
  8. package/dist/runtime/components/elements/Accordion.vue +33 -28
  9. package/dist/runtime/components/elements/Accordion.vue.d.ts +25 -18
  10. package/dist/runtime/components/elements/Alert.vue +22 -18
  11. package/dist/runtime/components/elements/Alert.vue.d.ts +26 -19
  12. package/dist/runtime/components/elements/Avatar.vue +22 -16
  13. package/dist/runtime/components/elements/Avatar.vue.d.ts +28 -19
  14. package/dist/runtime/components/elements/AvatarGroup.d.ts +17 -8
  15. package/dist/runtime/components/elements/AvatarGroup.mjs +18 -13
  16. package/dist/runtime/components/elements/Badge.vue +21 -17
  17. package/dist/runtime/components/elements/Badge.vue.d.ts +28 -19
  18. package/dist/runtime/components/elements/Button.vue +22 -18
  19. package/dist/runtime/components/elements/Button.vue.d.ts +28 -19
  20. package/dist/runtime/components/elements/ButtonGroup.d.ts +17 -8
  21. package/dist/runtime/components/elements/ButtonGroup.mjs +22 -15
  22. package/dist/runtime/components/elements/Dropdown.vue +45 -37
  23. package/dist/runtime/components/elements/Dropdown.vue.d.ts +22 -17
  24. package/dist/runtime/components/elements/Kbd.vue +16 -12
  25. package/dist/runtime/components/elements/Kbd.vue.d.ts +20 -12
  26. package/dist/runtime/components/elements/Link.vue +18 -5
  27. package/dist/runtime/components/elements/Meter.vue +166 -0
  28. package/dist/runtime/components/elements/Meter.vue.d.ts +123 -0
  29. package/dist/runtime/components/elements/MeterGroup.d.ts +78 -0
  30. package/dist/runtime/components/elements/MeterGroup.mjs +172 -0
  31. package/dist/runtime/components/elements/Progress.vue +284 -0
  32. package/dist/runtime/components/elements/Progress.vue.d.ts +2 -0
  33. package/dist/runtime/components/forms/Checkbox.vue +27 -17
  34. package/dist/runtime/components/forms/Checkbox.vue.d.ts +33 -15
  35. package/dist/runtime/components/forms/Form.vue +34 -9
  36. package/dist/runtime/components/forms/Form.vue.d.ts +2 -1
  37. package/dist/runtime/components/forms/FormGroup.vue +51 -27
  38. package/dist/runtime/components/forms/FormGroup.vue.d.ts +23 -15
  39. package/dist/runtime/components/forms/Input.vue +41 -29
  40. package/dist/runtime/components/forms/Input.vue.d.ts +51 -24
  41. package/dist/runtime/components/forms/Radio.vue +32 -21
  42. package/dist/runtime/components/forms/Radio.vue.d.ts +36 -18
  43. package/dist/runtime/components/forms/RadioGroup.vue +136 -0
  44. package/dist/runtime/components/forms/RadioGroup.vue.d.ts +118 -0
  45. package/dist/runtime/components/forms/Range.vue +26 -16
  46. package/dist/runtime/components/forms/Range.vue.d.ts +37 -18
  47. package/dist/runtime/components/forms/Select.vue +34 -26
  48. package/dist/runtime/components/forms/Select.vue.d.ts +42 -24
  49. package/dist/runtime/components/forms/SelectMenu.vue +104 -89
  50. package/dist/runtime/components/forms/SelectMenu.vue.d.ts +53 -33
  51. package/dist/runtime/components/forms/Textarea.vue +43 -31
  52. package/dist/runtime/components/forms/Textarea.vue.d.ts +50 -23
  53. package/dist/runtime/components/forms/Toggle.vue +26 -15
  54. package/dist/runtime/components/forms/Toggle.vue.d.ts +31 -12
  55. package/dist/runtime/components/layout/Card.vue +15 -10
  56. package/dist/runtime/components/layout/Card.vue.d.ts +17 -9
  57. package/dist/runtime/components/layout/Container.vue +15 -10
  58. package/dist/runtime/components/layout/Container.vue.d.ts +17 -9
  59. package/dist/runtime/components/layout/Divider.vue +107 -0
  60. package/dist/runtime/components/layout/Divider.vue.d.ts +82 -0
  61. package/dist/runtime/components/layout/Skeleton.vue +15 -10
  62. package/dist/runtime/components/layout/Skeleton.vue.d.ts +17 -9
  63. package/dist/runtime/components/navigation/CommandPalette.vue +57 -38
  64. package/dist/runtime/components/navigation/CommandPalette.vue.d.ts +24 -23
  65. package/dist/runtime/components/navigation/CommandPaletteGroup.vue +1 -1
  66. package/dist/runtime/components/navigation/CommandPaletteGroup.vue.d.ts +3 -4
  67. package/dist/runtime/components/navigation/Pagination.vue +129 -64
  68. package/dist/runtime/components/navigation/Pagination.vue.d.ts +70 -25
  69. package/dist/runtime/components/navigation/Tabs.vue +17 -14
  70. package/dist/runtime/components/navigation/Tabs.vue.d.ts +18 -11
  71. package/dist/runtime/components/navigation/VerticalNavigation.vue +15 -13
  72. package/dist/runtime/components/navigation/VerticalNavigation.vue.d.ts +20 -16
  73. package/dist/runtime/components/overlays/ContextMenu.vue +21 -11
  74. package/dist/runtime/components/overlays/ContextMenu.vue.d.ts +20 -11
  75. package/dist/runtime/components/overlays/Modal.vue +16 -14
  76. package/dist/runtime/components/overlays/Modal.vue.d.ts +18 -11
  77. package/dist/runtime/components/overlays/Notification.vue +25 -15
  78. package/dist/runtime/components/overlays/Notification.vue.d.ts +25 -19
  79. package/dist/runtime/components/overlays/Notifications.vue +19 -13
  80. package/dist/runtime/components/overlays/Notifications.vue.d.ts +22 -14
  81. package/dist/runtime/components/overlays/Popover.vue +39 -22
  82. package/dist/runtime/components/overlays/Popover.vue.d.ts +32 -11
  83. package/dist/runtime/components/overlays/Slideover.vue +15 -13
  84. package/dist/runtime/components/overlays/Slideover.vue.d.ts +18 -11
  85. package/dist/runtime/components/overlays/Tooltip.vue +31 -23
  86. package/dist/runtime/components/overlays/Tooltip.vue.d.ts +19 -11
  87. package/dist/runtime/composables/useFormGroup.d.ts +12 -2
  88. package/dist/runtime/composables/useFormGroup.mjs +21 -4
  89. package/dist/runtime/composables/usePopper.d.ts +3 -3
  90. package/dist/runtime/composables/usePopper.mjs +47 -31
  91. package/dist/runtime/composables/useToast.d.ts +3 -3
  92. package/dist/runtime/composables/useUI.d.ts +10 -0
  93. package/dist/runtime/composables/useUI.mjs +24 -0
  94. package/dist/runtime/types/avatar.d.ts +10 -4
  95. package/dist/runtime/types/badge.d.ts +14 -0
  96. package/dist/runtime/types/button.d.ts +10 -3
  97. package/dist/runtime/types/command-palette.d.ts +3 -1
  98. package/dist/runtime/types/form.d.ts +18 -4
  99. package/dist/runtime/types/index.d.ts +4 -0
  100. package/dist/runtime/types/link.d.ts +1 -0
  101. package/dist/runtime/types/meter.d.ts +5 -0
  102. package/dist/runtime/types/notification.d.ts +5 -3
  103. package/dist/runtime/types/popper.d.ts +1 -0
  104. package/dist/runtime/types/progress.d.ts +4 -0
  105. package/dist/runtime/types/tabs.d.ts +1 -0
  106. package/dist/runtime/types/utils.d.ts +11 -0
  107. package/dist/runtime/ui.config.d.ts +1470 -0
  108. package/dist/runtime/{app.config.mjs → ui.config.mjs} +313 -97
  109. package/dist/runtime/utils/index.d.ts +5 -11
  110. package/dist/runtime/utils/index.mjs +21 -9
  111. package/dist/runtime/utils/lodash.d.ts +2 -0
  112. package/dist/runtime/utils/lodash.mjs +23 -0
  113. package/dist/runtime/utils/uid.d.ts +1 -0
  114. package/dist/runtime/utils/uid.mjs +4 -0
  115. package/dist/types.d.mts +1 -1
  116. package/dist/types.d.ts +1 -1
  117. package/package.json +27 -29
  118. package/dist/runtime/app.config.d.ts +0 -1203
  119. package/dist/runtime/utils/StateEmitter.d.ts +0 -17
  120. package/dist/runtime/utils/StateEmitter.mjs +0 -21
@@ -1,6 +1,7 @@
1
1
  <template>
2
- <div :class="wrapperClass">
2
+ <div :class="ui.wrapper">
3
3
  <select
4
+ :id="inputId"
4
5
  :name="name"
5
6
  :value="modelValue"
6
7
  :required="required"
@@ -53,14 +54,15 @@
53
54
  </template>
54
55
 
55
56
  <script>
56
- import { computed, defineComponent } from "vue";
57
- import { get, omit } from "lodash-es";
57
+ import { computed, toRef, defineComponent } from "vue";
58
58
  import { twMerge, twJoin } from "tailwind-merge";
59
59
  import UIcon from "../elements/Icon.vue";
60
- import { defuTwMerge } from "../../utils";
60
+ import { useUI } from "../../composables/useUI";
61
61
  import { useFormGroup } from "../../composables/useFormGroup";
62
- import { useAppConfig } from "#imports";
62
+ import { mergeConfig, get } from "../../utils";
63
63
  import appConfig from "#build/app.config";
64
+ import { select } from "#ui/ui.config";
65
+ const config = mergeConfig(appConfig.ui.strategy, appConfig.ui.select, select);
64
66
  export default defineComponent({
65
67
  components: {
66
68
  UIcon
@@ -71,6 +73,10 @@ export default defineComponent({
71
73
  type: [String, Number, Object],
72
74
  default: ""
73
75
  },
76
+ id: {
77
+ type: String,
78
+ default: null
79
+ },
74
80
  name: {
75
81
  type: String,
76
82
  default: null
@@ -93,7 +99,7 @@ export default defineComponent({
93
99
  },
94
100
  loadingIcon: {
95
101
  type: String,
96
- default: () => appConfig.ui.input.default.loadingIcon
102
+ default: () => config.default.loadingIcon
97
103
  },
98
104
  leadingIcon: {
99
105
  type: String,
@@ -101,7 +107,7 @@ export default defineComponent({
101
107
  },
102
108
  trailingIcon: {
103
109
  type: String,
104
- default: () => appConfig.ui.select.default.trailingIcon
110
+ default: () => config.default.trailingIcon
105
111
  },
106
112
  trailing: {
107
113
  type: Boolean,
@@ -125,25 +131,25 @@ export default defineComponent({
125
131
  },
126
132
  size: {
127
133
  type: String,
128
- default: () => appConfig.ui.select.default.size,
134
+ default: null,
129
135
  validator(value) {
130
- return Object.keys(appConfig.ui.select.size).includes(value);
136
+ return Object.keys(config.size).includes(value);
131
137
  }
132
138
  },
133
139
  color: {
134
140
  type: String,
135
- default: () => appConfig.ui.select.default.color,
141
+ default: () => config.default.color,
136
142
  validator(value) {
137
- return [...appConfig.ui.colors, ...Object.keys(appConfig.ui.select.color)].includes(value);
143
+ return [...appConfig.ui.colors, ...Object.keys(config.color)].includes(value);
138
144
  }
139
145
  },
140
146
  variant: {
141
147
  type: String,
142
- default: () => appConfig.ui.select.default.variant,
148
+ default: () => config.default.variant,
143
149
  validator(value) {
144
150
  return [
145
- ...Object.keys(appConfig.ui.select.variant),
146
- ...Object.values(appConfig.ui.select.color).flatMap((value2) => Object.keys(value2))
151
+ ...Object.keys(config.variant),
152
+ ...Object.values(config.color).flatMap((value2) => Object.keys(value2))
147
153
  ].includes(value);
148
154
  }
149
155
  },
@@ -159,18 +165,19 @@ export default defineComponent({
159
165
  type: String,
160
166
  default: null
161
167
  },
168
+ class: {
169
+ type: [String, Object, Array],
170
+ default: void 0
171
+ },
162
172
  ui: {
163
173
  type: Object,
164
- default: () => ({})
174
+ default: void 0
165
175
  }
166
176
  },
167
177
  emits: ["update:modelValue", "change"],
168
- setup(props, { emit, attrs, slots }) {
169
- const appConfig2 = useAppConfig();
170
- const ui = computed(() => defuTwMerge({}, props.ui, appConfig2.ui.select));
171
- const { emitFormChange, formGroup } = useFormGroup();
172
- const color = computed(() => formGroup?.error?.value ? "red" : props.color);
173
- const size = computed(() => formGroup?.size?.value ?? props.size);
178
+ setup(props, { emit, slots }) {
179
+ const { ui, attrs } = useUI("select", toRef(props, "ui"), config, toRef(props, "class"));
180
+ const { emitFormChange, inputId, color, size, name } = useFormGroup(props, config);
174
181
  const onInput = (event) => {
175
182
  emit("update:modelValue", event.target.value);
176
183
  };
@@ -221,7 +228,6 @@ export default defineComponent({
221
228
  }
222
229
  return foundOption[props.valueAttribute];
223
230
  });
224
- const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class));
225
231
  const selectClass = computed(() => {
226
232
  const variant = ui.value.color?.[color.value]?.[props.variant] || ui.value.variant[props.variant];
227
233
  return twMerge(twJoin(
@@ -262,7 +268,7 @@ export default defineComponent({
262
268
  const leadingIconClass = computed(() => {
263
269
  return twJoin(
264
270
  ui.value.icon.base,
265
- appConfig2.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll("{color}", color.value),
271
+ appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll("{color}", color.value),
266
272
  ui.value.icon.size[size.value],
267
273
  props.loading && "animate-spin"
268
274
  );
@@ -277,20 +283,22 @@ export default defineComponent({
277
283
  const trailingIconClass = computed(() => {
278
284
  return twJoin(
279
285
  ui.value.icon.base,
280
- appConfig2.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll("{color}", color.value),
286
+ appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll("{color}", color.value),
281
287
  ui.value.icon.size[size.value],
282
288
  props.loading && !isLeading.value && "animate-spin"
283
289
  );
284
290
  });
285
291
  return {
286
- attrs: omit(attrs, ["class"]),
287
292
  // eslint-disable-next-line vue/no-dupe-keys
288
293
  ui,
294
+ attrs,
295
+ // eslint-disable-next-line vue/no-dupe-keys
296
+ name,
297
+ inputId,
289
298
  normalizedOptionsWithPlaceholder,
290
299
  normalizedValue,
291
300
  isLeading,
292
301
  isTrailing,
293
- wrapperClass,
294
302
  // eslint-disable-next-line vue/no-dupe-keys
295
303
  selectClass,
296
304
  leadingIconName,
@@ -1,10 +1,13 @@
1
- /// <reference types="lodash" />
2
1
  import type { PropType, ComputedRef } from 'vue';
3
2
  declare const _default: import("vue").DefineComponent<{
4
3
  modelValue: {
5
- type: (ObjectConstructor | StringConstructor | NumberConstructor)[];
4
+ type: (ObjectConstructor | NumberConstructor | StringConstructor)[];
6
5
  default: string;
7
6
  };
7
+ id: {
8
+ type: StringConstructor;
9
+ default: null;
10
+ };
8
11
  name: {
9
12
  type: StringConstructor;
10
13
  default: null;
@@ -58,17 +61,17 @@ declare const _default: import("vue").DefineComponent<{
58
61
  default: () => never[];
59
62
  };
60
63
  size: {
61
- type: StringConstructor;
62
- default: () => any;
64
+ type: PropType<string | number | symbol>;
65
+ default: null;
63
66
  validator(value: string): boolean;
64
67
  };
65
68
  color: {
66
- type: StringConstructor;
69
+ type: PropType<any>;
67
70
  default: () => any;
68
71
  validator(value: string): boolean;
69
72
  };
70
73
  variant: {
71
- type: StringConstructor;
74
+ type: PropType<any>;
72
75
  default: () => any;
73
76
  validator(value: string): boolean;
74
77
  };
@@ -84,15 +87,21 @@ declare const _default: import("vue").DefineComponent<{
84
87
  type: StringConstructor;
85
88
  default: null;
86
89
  };
87
- ui: {
90
+ class: {
88
91
  type: PropType<any>;
89
- default: () => {};
92
+ default: undefined;
93
+ };
94
+ ui: {
95
+ type: PropType<Partial<any>>;
96
+ default: undefined;
90
97
  };
91
98
  }, {
92
- attrs: import("lodash").Omit<{
93
- [x: string]: unknown;
94
- }, "class">;
95
99
  ui: ComputedRef<any>;
100
+ attrs: ComputedRef<Pick<{
101
+ [x: string]: unknown;
102
+ }, string>>;
103
+ name: ComputedRef<string | undefined>;
104
+ inputId: import("vue").Ref<string | undefined>;
96
105
  normalizedOptionsWithPlaceholder: ComputedRef<{
97
106
  disabled?: boolean | undefined;
98
107
  children: {
@@ -102,7 +111,6 @@ declare const _default: import("vue").DefineComponent<{
102
111
  normalizedValue: ComputedRef<any>;
103
112
  isLeading: ComputedRef<string | true>;
104
113
  isTrailing: ComputedRef<string | true | undefined>;
105
- wrapperClass: ComputedRef<string>;
106
114
  selectClass: ComputedRef<string>;
107
115
  leadingIconName: ComputedRef<string | undefined>;
108
116
  leadingIconClass: ComputedRef<string>;
@@ -114,9 +122,13 @@ declare const _default: import("vue").DefineComponent<{
114
122
  onChange: (event: Event) => void;
115
123
  }, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("change" | "update:modelValue")[], "change" | "update:modelValue", import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
116
124
  modelValue: {
117
- type: (ObjectConstructor | StringConstructor | NumberConstructor)[];
125
+ type: (ObjectConstructor | NumberConstructor | StringConstructor)[];
118
126
  default: string;
119
127
  };
128
+ id: {
129
+ type: StringConstructor;
130
+ default: null;
131
+ };
120
132
  name: {
121
133
  type: StringConstructor;
122
134
  default: null;
@@ -170,17 +182,17 @@ declare const _default: import("vue").DefineComponent<{
170
182
  default: () => never[];
171
183
  };
172
184
  size: {
173
- type: StringConstructor;
174
- default: () => any;
185
+ type: PropType<string | number | symbol>;
186
+ default: null;
175
187
  validator(value: string): boolean;
176
188
  };
177
189
  color: {
178
- type: StringConstructor;
190
+ type: PropType<any>;
179
191
  default: () => any;
180
192
  validator(value: string): boolean;
181
193
  };
182
194
  variant: {
183
- type: StringConstructor;
195
+ type: PropType<any>;
184
196
  default: () => any;
185
197
  validator(value: string): boolean;
186
198
  };
@@ -196,25 +208,31 @@ declare const _default: import("vue").DefineComponent<{
196
208
  type: StringConstructor;
197
209
  default: null;
198
210
  };
199
- ui: {
211
+ class: {
200
212
  type: PropType<any>;
201
- default: () => {};
213
+ default: undefined;
214
+ };
215
+ ui: {
216
+ type: PropType<Partial<any>>;
217
+ default: undefined;
202
218
  };
203
219
  }>> & {
204
220
  "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
205
221
  onChange?: ((...args: any[]) => any) | undefined;
206
222
  }, {
223
+ id: string;
207
224
  icon: string;
208
- color: string;
209
- ui: any;
210
- name: string;
225
+ color: any;
226
+ ui: Partial<any>;
211
227
  options: unknown[];
228
+ name: string;
229
+ class: any;
212
230
  modelValue: string | number | Record<string, any>;
213
231
  loading: boolean;
214
232
  disabled: boolean;
215
- variant: string;
233
+ variant: any;
216
234
  required: boolean;
217
- size: string;
235
+ size: string | number | symbol;
218
236
  placeholder: string;
219
237
  padded: boolean;
220
238
  loadingIcon: string;
@@ -8,7 +8,7 @@
8
8
  :multiple="multiple"
9
9
  :disabled="disabled || loading"
10
10
  as="div"
11
- :class="wrapperClass"
11
+ :class="ui.wrapper"
12
12
  @update:model-value="onUpdate"
13
13
  >
14
14
  <input
@@ -28,7 +28,7 @@
28
28
  class="inline-flex w-full"
29
29
  >
30
30
  <slot :open="open" :disabled="disabled" :loading="loading">
31
- <button :class="selectClass" :disabled="disabled || loading" type="button" v-bind="attrs">
31
+ <button :id="inputId" :class="selectClass" :disabled="disabled || loading" type="button" v-bind="attrs">
32
32
  <span v-if="(isLeading && leadingIconName) || $slots.leading" :class="leadingWrapperIconClass">
33
33
  <slot name="leading" :disabled="disabled" :loading="loading">
34
34
  <UIcon :name="leadingIconName" :class="leadingIconClass" />
@@ -37,7 +37,7 @@
37
37
 
38
38
  <slot name="label">
39
39
  <span v-if="multiple && Array.isArray(modelValue) && modelValue.length" class="block truncate">{{ modelValue.length }} selected</span>
40
- <span v-else-if="!multiple && modelValue" class="block truncate">{{ typeof modelValue === 'string' ? modelValue : modelValue[optionAttribute] }}</span>
40
+ <span v-else-if="!multiple && modelValue" class="block truncate">{{ ['string', 'number'].includes(typeof modelValue) ? modelValue : modelValue[optionAttribute] }}</span>
41
41
  <span v-else class="block truncate" :class="uiMenu.placeholder">{{ placeholder || '&nbsp;' }}</span>
42
42
  </slot>
43
43
 
@@ -52,71 +52,74 @@
52
52
 
53
53
  <div v-if="open" ref="container" :class="[uiMenu.container, uiMenu.width]">
54
54
  <Transition appear v-bind="uiMenu.transition">
55
- <component :is="searchable ? 'HComboboxOptions' : 'HListboxOptions'" static :class="[uiMenu.base, uiMenu.divide, uiMenu.ring, uiMenu.rounded, uiMenu.shadow, uiMenu.background, uiMenu.padding, uiMenu.height]">
56
- <HComboboxInput
57
- v-if="searchable"
58
- ref="searchInput"
59
- :display-value="() => query"
60
- name="q"
61
- :placeholder="searchablePlaceholder"
62
- autofocus
63
- autocomplete="off"
64
- :class="uiMenu.input"
65
- @change="query = $event.target.value"
66
- />
67
- <component
68
- :is="searchable ? 'HComboboxOption' : 'HListboxOption'"
69
- v-for="(option, index) in filteredOptions"
70
- v-slot="{ active, selected, disabled: optionDisabled }"
71
- :key="index"
72
- as="template"
73
- :value="valueAttribute ? option[valueAttribute] : option"
74
- :disabled="option.disabled"
75
- >
76
- <li :class="[uiMenu.option.base, uiMenu.option.rounded, uiMenu.option.padding, uiMenu.option.size, uiMenu.option.color, active ? uiMenu.option.active : uiMenu.option.inactive, selected && uiMenu.option.selected, optionDisabled && uiMenu.option.disabled]">
77
- <div :class="uiMenu.option.container">
78
- <slot name="option" :option="option" :active="active" :selected="selected">
79
- <UIcon v-if="option.icon" :name="option.icon" :class="[uiMenu.option.icon.base, active ? uiMenu.option.icon.active : uiMenu.option.icon.inactive, option.iconClass]" aria-hidden="true" />
80
- <UAvatar
81
- v-else-if="option.avatar"
82
- v-bind="{ size: uiMenu.option.avatar.size, ...option.avatar }"
83
- :class="uiMenu.option.avatar.base"
84
- aria-hidden="true"
85
- />
86
- <span v-else-if="option.chip" :class="uiMenu.option.chip.base" :style="{ background: `#${option.chip}` }" />
87
-
88
- <span class="truncate">{{ typeof option === 'string' ? option : option[optionAttribute] }}</span>
89
- </slot>
90
- </div>
91
-
92
- <span v-if="selected" :class="[uiMenu.option.selectedIcon.wrapper, uiMenu.option.selectedIcon.padding]">
93
- <UIcon :name="selectedIcon" :class="uiMenu.option.selectedIcon.base" aria-hidden="true" />
94
- </span>
95
- </li>
55
+ <div>
56
+ <div v-if="popper.arrow" data-popper-arrow :class="['invisible before:visible before:block before:rotate-45 before:z-[-1]', Object.values(uiMenu.arrow)]" />
57
+ <component :is="searchable ? 'HComboboxOptions' : 'HListboxOptions'" static :class="[uiMenu.base, uiMenu.divide, uiMenu.ring, uiMenu.rounded, uiMenu.shadow, uiMenu.background, uiMenu.padding, uiMenu.height]">
58
+ <HComboboxInput
59
+ v-if="searchable"
60
+ ref="searchInput"
61
+ :display-value="() => query"
62
+ name="q"
63
+ :placeholder="searchablePlaceholder"
64
+ autofocus
65
+ autocomplete="off"
66
+ :class="uiMenu.input"
67
+ @change="query = $event.target.value"
68
+ />
69
+ <component
70
+ :is="searchable ? 'HComboboxOption' : 'HListboxOption'"
71
+ v-for="(option, index) in filteredOptions"
72
+ v-slot="{ active, selected, disabled: optionDisabled }"
73
+ :key="index"
74
+ as="template"
75
+ :value="valueAttribute ? option[valueAttribute] : option"
76
+ :disabled="option.disabled"
77
+ >
78
+ <li :class="[uiMenu.option.base, uiMenu.option.rounded, uiMenu.option.padding, uiMenu.option.size, uiMenu.option.color, active ? uiMenu.option.active : uiMenu.option.inactive, selected && uiMenu.option.selected, optionDisabled && uiMenu.option.disabled]">
79
+ <div :class="uiMenu.option.container">
80
+ <slot name="option" :option="option" :active="active" :selected="selected">
81
+ <UIcon v-if="option.icon" :name="option.icon" :class="[uiMenu.option.icon.base, active ? uiMenu.option.icon.active : uiMenu.option.icon.inactive, option.iconClass]" aria-hidden="true" />
82
+ <UAvatar
83
+ v-else-if="option.avatar"
84
+ v-bind="{ size: uiMenu.option.avatar.size, ...option.avatar }"
85
+ :class="uiMenu.option.avatar.base"
86
+ aria-hidden="true"
87
+ />
88
+ <span v-else-if="option.chip" :class="uiMenu.option.chip.base" :style="{ background: `#${option.chip}` }" />
89
+
90
+ <span class="truncate">{{ ['string', 'number'].includes(typeof option) ? option : option[optionAttribute] }}</span>
91
+ </slot>
92
+ </div>
93
+
94
+ <span v-if="selected" :class="[uiMenu.option.selectedIcon.wrapper, uiMenu.option.selectedIcon.padding]">
95
+ <UIcon :name="selectedIcon" :class="uiMenu.option.selectedIcon.base" aria-hidden="true" />
96
+ </span>
97
+ </li>
98
+ </component>
99
+
100
+ <component :is="searchable ? 'HComboboxOption' : 'HListboxOption'" v-if="creatable && queryOption && !filteredOptions.length" v-slot="{ active, selected }" :value="queryOption" as="template">
101
+ <li :class="[uiMenu.option.base, uiMenu.option.rounded, uiMenu.option.padding, uiMenu.option.size, uiMenu.option.color, active ? uiMenu.option.active : uiMenu.option.inactive]">
102
+ <div :class="uiMenu.option.container">
103
+ <slot name="option-create" :option="queryOption" :active="active" :selected="selected">
104
+ <span class="block truncate">Create "{{ queryOption[optionAttribute] }}"</span>
105
+ </slot>
106
+ </div>
107
+ </li>
108
+ </component>
109
+ <p v-else-if="searchable && query && !filteredOptions.length" :class="uiMenu.option.empty">
110
+ <slot name="option-empty" :query="query">
111
+ No results for "{{ query }}".
112
+ </slot>
113
+ </p>
96
114
  </component>
97
-
98
- <component :is="searchable ? 'HComboboxOption' : 'HListboxOption'" v-if="creatable && queryOption && !filteredOptions.length" v-slot="{ active, selected }" :value="queryOption" as="template">
99
- <li :class="[uiMenu.option.base, uiMenu.option.rounded, uiMenu.option.padding, uiMenu.option.size, uiMenu.option.color, active ? uiMenu.option.active : uiMenu.option.inactive]">
100
- <div :class="uiMenu.option.container">
101
- <slot name="option-create" :option="queryOption" :active="active" :selected="selected">
102
- <span class="block truncate">Create "{{ queryOption[optionAttribute] }}"</span>
103
- </slot>
104
- </div>
105
- </li>
106
- </component>
107
- <p v-else-if="searchable && query && !filteredOptions.length" :class="uiMenu.option.empty">
108
- <slot name="option-empty" :query="query">
109
- No results for "{{ query }}".
110
- </slot>
111
- </p>
112
- </component>
115
+ </div>
113
116
  </Transition>
114
117
  </div>
115
118
  </component>
116
119
  </template>
117
120
 
118
121
  <script>
119
- import { ref, computed, watch, defineComponent } from "vue";
122
+ import { ref, computed, toRef, watch, defineComponent } from "vue";
120
123
  import {
121
124
  Combobox as HCombobox,
122
125
  ComboboxButton as HComboboxButton,
@@ -130,15 +133,17 @@ import {
130
133
  } from "@headlessui/vue";
131
134
  import { computedAsync, useDebounceFn } from "@vueuse/core";
132
135
  import { defu } from "defu";
133
- import { omit } from "lodash-es";
134
136
  import { twMerge, twJoin } from "tailwind-merge";
135
137
  import UIcon from "../elements/Icon.vue";
136
138
  import UAvatar from "../elements/Avatar.vue";
137
- import { defuTwMerge } from "../../utils";
139
+ import { useUI } from "../../composables/useUI";
138
140
  import { usePopper } from "../../composables/usePopper";
139
141
  import { useFormGroup } from "../../composables/useFormGroup";
140
- import { useAppConfig } from "#imports";
142
+ import { mergeConfig } from "../../utils";
141
143
  import appConfig from "#build/app.config";
144
+ import { select, selectMenu } from "#ui/ui.config";
145
+ const config = mergeConfig(appConfig.ui.strategy, appConfig.ui.select, select);
146
+ const configMenu = mergeConfig(appConfig.ui.strategy, appConfig.ui.selectMenu, selectMenu);
142
147
  export default defineComponent({
143
148
  components: {
144
149
  HCombobox,
@@ -167,6 +172,10 @@ export default defineComponent({
167
172
  type: Array,
168
173
  default: () => []
169
174
  },
175
+ id: {
176
+ type: String,
177
+ default: null
178
+ },
170
179
  name: {
171
180
  type: String,
172
181
  default: null
@@ -181,7 +190,7 @@ export default defineComponent({
181
190
  },
182
191
  loadingIcon: {
183
192
  type: String,
184
- default: () => appConfig.ui.input.default.loadingIcon
193
+ default: () => config.default.loadingIcon
185
194
  },
186
195
  leadingIcon: {
187
196
  type: String,
@@ -189,7 +198,7 @@ export default defineComponent({
189
198
  },
190
199
  trailingIcon: {
191
200
  type: String,
192
- default: () => appConfig.ui.select.default.trailingIcon
201
+ default: () => config.default.trailingIcon
193
202
  },
194
203
  trailing: {
195
204
  type: Boolean,
@@ -205,7 +214,7 @@ export default defineComponent({
205
214
  },
206
215
  selectedIcon: {
207
216
  type: String,
208
- default: () => appConfig.ui.selectMenu.default.selectedIcon
217
+ default: () => configMenu.default.selectedIcon
209
218
  },
210
219
  disabled: {
211
220
  type: Boolean,
@@ -241,25 +250,25 @@ export default defineComponent({
241
250
  },
242
251
  size: {
243
252
  type: String,
244
- default: () => appConfig.ui.select.default.size,
253
+ default: null,
245
254
  validator(value) {
246
- return Object.keys(appConfig.ui.select.size).includes(value);
255
+ return Object.keys(config.size).includes(value);
247
256
  }
248
257
  },
249
258
  color: {
250
259
  type: String,
251
- default: () => appConfig.ui.select.default.color,
260
+ default: () => config.default.color,
252
261
  validator(value) {
253
- return [...appConfig.ui.colors, ...Object.keys(appConfig.ui.select.color)].includes(value);
262
+ return [...appConfig.ui.colors, ...Object.keys(config.color)].includes(value);
254
263
  }
255
264
  },
256
265
  variant: {
257
266
  type: String,
258
- default: () => appConfig.ui.select.default.variant,
267
+ default: () => config.default.variant,
259
268
  validator(value) {
260
269
  return [
261
- ...Object.keys(appConfig.ui.select.variant),
262
- ...Object.values(appConfig.ui.select.color).flatMap((value2) => Object.keys(value2))
270
+ ...Object.keys(config.variant),
271
+ ...Object.values(config.color).flatMap((value2) => Object.keys(value2))
263
272
  ].includes(value);
264
273
  }
265
274
  },
@@ -283,28 +292,28 @@ export default defineComponent({
283
292
  type: String,
284
293
  default: null
285
294
  },
295
+ class: {
296
+ type: [String, Object, Array],
297
+ default: void 0
298
+ },
286
299
  ui: {
287
300
  type: Object,
288
- default: () => ({})
301
+ default: void 0
289
302
  },
290
303
  uiMenu: {
291
304
  type: Object,
292
- default: () => ({})
305
+ default: void 0
293
306
  }
294
307
  },
295
308
  emits: ["update:modelValue", "open", "close", "change"],
296
- setup(props, { emit, attrs, slots }) {
297
- const appConfig2 = useAppConfig();
298
- const ui = computed(() => defuTwMerge({}, props.ui, appConfig2.ui.select));
299
- const uiMenu = computed(() => defuTwMerge({}, props.uiMenu, appConfig2.ui.selectMenu));
309
+ setup(props, { emit, slots }) {
310
+ const { ui, attrs } = useUI("select", toRef(props, "ui"), config, toRef(props, "class"));
311
+ const { ui: uiMenu } = useUI("selectMenu", toRef(props, "uiMenu"), configMenu);
300
312
  const popper = computed(() => defu({}, props.popper, uiMenu.value.popper));
301
313
  const [trigger, container] = usePopper(popper.value);
302
- const { emitFormBlur, emitFormChange, formGroup } = useFormGroup();
303
- const color = computed(() => formGroup?.error?.value ? "red" : props.color);
304
- const size = computed(() => formGroup?.size?.value ?? props.size);
314
+ const { emitFormBlur, emitFormChange, inputId, color, size, name } = useFormGroup(props, config);
305
315
  const query = ref("");
306
316
  const searchInput = ref();
307
- const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class));
308
317
  const selectClass = computed(() => {
309
318
  const variant = ui.value.color?.[color.value]?.[props.variant] || ui.value.variant[props.variant];
310
319
  return twMerge(twJoin(
@@ -348,7 +357,7 @@ export default defineComponent({
348
357
  const leadingIconClass = computed(() => {
349
358
  return twJoin(
350
359
  ui.value.icon.base,
351
- appConfig2.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll("{color}", color.value),
360
+ appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll("{color}", color.value),
352
361
  ui.value.icon.size[size.value],
353
362
  props.loading && "animate-spin"
354
363
  );
@@ -363,7 +372,7 @@ export default defineComponent({
363
372
  const trailingIconClass = computed(() => {
364
373
  return twJoin(
365
374
  ui.value.icon.base,
366
- appConfig2.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll("{color}", color.value),
375
+ appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll("{color}", color.value),
367
376
  ui.value.icon.size[size.value],
368
377
  props.loading && !isLeading.value && "animate-spin"
369
378
  );
@@ -378,7 +387,7 @@ export default defineComponent({
378
387
  }
379
388
  return props.options.filter((option) => {
380
389
  return (props.searchAttributes?.length ? props.searchAttributes : [props.optionAttribute]).some((searchAttribute) => {
381
- return typeof option === "string" ? option.search(new RegExp(query.value, "i")) !== -1 : option[searchAttribute] && option[searchAttribute].search(new RegExp(query.value, "i")) !== -1;
390
+ return ["string", "number"].includes(typeof option) ? option.toString().search(new RegExp(query.value, "i")) !== -1 : option[searchAttribute] && option[searchAttribute].search(new RegExp(query.value, "i")) !== -1;
382
391
  });
383
392
  });
384
393
  });
@@ -403,14 +412,20 @@ export default defineComponent({
403
412
  emitFormChange();
404
413
  }
405
414
  return {
406
- attrs: omit(attrs, ["class"]),
415
+ // eslint-disable-next-line vue/no-dupe-keys
416
+ ui,
407
417
  // eslint-disable-next-line vue/no-dupe-keys
408
418
  uiMenu,
419
+ attrs,
420
+ // eslint-disable-next-line vue/no-dupe-keys
421
+ name,
422
+ inputId,
423
+ // eslint-disable-next-line vue/no-dupe-keys
424
+ popper,
409
425
  trigger,
410
426
  container,
411
427
  isLeading,
412
428
  isTrailing,
413
- wrapperClass,
414
429
  // eslint-disable-next-line vue/no-dupe-keys
415
430
  selectClass,
416
431
  leadingIconName,