@nuxt/ui 4.10.0 → 4.11.1

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 (117) hide show
  1. package/.nuxt/ui.static.css +11 -11
  2. package/README.md +1 -0
  3. package/cli/commands/make/locale.mjs +40 -11
  4. package/cli/templates.mjs +2 -2
  5. package/dist/module.d.mts +1 -1
  6. package/dist/module.json +2 -2
  7. package/dist/module.mjs +5 -2
  8. package/dist/runtime/components/AuthForm.d.vue.ts +7 -7
  9. package/dist/runtime/components/AuthForm.vue.d.ts +7 -7
  10. package/dist/runtime/components/Button.vue +11 -2
  11. package/dist/runtime/components/Carousel.d.vue.ts +6 -6
  12. package/dist/runtime/components/Carousel.vue.d.ts +6 -6
  13. package/dist/runtime/components/ChatMessages.d.vue.ts +2 -2
  14. package/dist/runtime/components/ChatMessages.vue.d.ts +2 -2
  15. package/dist/runtime/components/ChatPromptSubmit.d.vue.ts +2 -2
  16. package/dist/runtime/components/ChatPromptSubmit.vue.d.ts +2 -2
  17. package/dist/runtime/components/Checkbox.d.vue.ts +1 -1
  18. package/dist/runtime/components/Checkbox.vue +12 -6
  19. package/dist/runtime/components/Checkbox.vue.d.ts +1 -1
  20. package/dist/runtime/components/CheckboxGroup.d.vue.ts +6 -0
  21. package/dist/runtime/components/CheckboxGroup.vue +10 -4
  22. package/dist/runtime/components/CheckboxGroup.vue.d.ts +6 -0
  23. package/dist/runtime/components/Editor.vue +4 -1
  24. package/dist/runtime/components/Error.d.vue.ts +1 -1
  25. package/dist/runtime/components/Error.vue.d.ts +1 -1
  26. package/dist/runtime/components/FileUpload.vue +17 -10
  27. package/dist/runtime/components/Form.vue +1 -1
  28. package/dist/runtime/components/Input.vue +12 -7
  29. package/dist/runtime/components/InputDate.vue +12 -8
  30. package/dist/runtime/components/InputMenu.vue +25 -11
  31. package/dist/runtime/components/InputNumber.vue +22 -14
  32. package/dist/runtime/components/InputRating.vue +10 -7
  33. package/dist/runtime/components/InputTags.vue +12 -7
  34. package/dist/runtime/components/InputTime.vue +12 -7
  35. package/dist/runtime/components/Link.vue +61 -7
  36. package/dist/runtime/components/Listbox.vue +10 -6
  37. package/dist/runtime/components/Modal.vue +12 -5
  38. package/dist/runtime/components/NavigationMenu.vue +10 -10
  39. package/dist/runtime/components/PinInput.vue +13 -8
  40. package/dist/runtime/components/Popover.vue +0 -1
  41. package/dist/runtime/components/PricingTable.d.vue.ts +1 -1
  42. package/dist/runtime/components/PricingTable.vue.d.ts +1 -1
  43. package/dist/runtime/components/Progress.d.vue.ts +2 -1
  44. package/dist/runtime/components/Progress.vue +5 -6
  45. package/dist/runtime/components/Progress.vue.d.ts +2 -1
  46. package/dist/runtime/components/ProgressGroup.d.vue.ts +91 -0
  47. package/dist/runtime/components/ProgressGroup.vue +99 -0
  48. package/dist/runtime/components/ProgressGroup.vue.d.ts +91 -0
  49. package/dist/runtime/components/RadioGroup.d.vue.ts +7 -1
  50. package/dist/runtime/components/RadioGroup.vue +14 -5
  51. package/dist/runtime/components/RadioGroup.vue.d.ts +7 -1
  52. package/dist/runtime/components/Select.d.vue.ts +2 -0
  53. package/dist/runtime/components/Select.vue +23 -8
  54. package/dist/runtime/components/Select.vue.d.ts +2 -0
  55. package/dist/runtime/components/SelectMenu.d.vue.ts +3 -0
  56. package/dist/runtime/components/SelectMenu.vue +35 -14
  57. package/dist/runtime/components/SelectMenu.vue.d.ts +3 -0
  58. package/dist/runtime/components/Slider.vue +15 -8
  59. package/dist/runtime/components/Splitter.d.vue.ts +266 -0
  60. package/dist/runtime/components/Splitter.vue +99 -0
  61. package/dist/runtime/components/Splitter.vue.d.ts +266 -0
  62. package/dist/runtime/components/Switch.vue +8 -4
  63. package/dist/runtime/components/Table.vue +5 -5
  64. package/dist/runtime/components/Textarea.vue +17 -7
  65. package/dist/runtime/components/Theme.d.vue.ts +1 -1
  66. package/dist/runtime/components/Theme.vue +4 -3
  67. package/dist/runtime/components/Theme.vue.d.ts +1 -1
  68. package/dist/runtime/components/Tree.vue +1 -1
  69. package/dist/runtime/components/color-mode/ColorModeSelect.vue +1 -0
  70. package/dist/runtime/components/content/ContentNavigation.vue +8 -1
  71. package/dist/runtime/components/content/ContentToc.vue +4 -1
  72. package/dist/runtime/components/locale/LocaleSelect.vue +1 -0
  73. package/dist/runtime/components/prose/CodeTree.vue +31 -18
  74. package/dist/runtime/composables/useComponentProps.d.ts +1 -1
  75. package/dist/runtime/composables/useComponentProps.js +6 -0
  76. package/dist/runtime/composables/useFormField.d.ts +10 -4
  77. package/dist/runtime/composables/useFormField.js +10 -3
  78. package/dist/runtime/keyframes.css +1 -1
  79. package/dist/runtime/locale/da.js +1 -1
  80. package/dist/runtime/locale/de_at.js +7 -0
  81. package/dist/runtime/locale/de_ch.js +1 -1
  82. package/dist/runtime/locale/eu.js +1 -1
  83. package/dist/runtime/locale/fi.js +1 -1
  84. package/dist/runtime/locale/he.js +1 -1
  85. package/dist/runtime/locale/hi.js +1 -1
  86. package/dist/runtime/locale/index.d.ts +2 -1
  87. package/dist/runtime/locale/index.js +2 -1
  88. package/dist/runtime/locale/is.js +1 -1
  89. package/dist/runtime/locale/lb.js +1 -1
  90. package/dist/runtime/locale/tg.d.ts +3 -0
  91. package/dist/runtime/locale/{tj.js → tg.js} +1 -1
  92. package/dist/runtime/locale/ur.js +1 -1
  93. package/dist/runtime/types/form.d.ts +1 -1
  94. package/dist/runtime/types/html.d.ts +3 -1
  95. package/dist/runtime/types/index.d.ts +2 -0
  96. package/dist/runtime/types/index.js +2 -0
  97. package/dist/runtime/types/theme.d.ts +2 -0
  98. package/dist/runtime/types/tv.d.ts +6 -2
  99. package/dist/runtime/utils/form.d.ts +1 -1
  100. package/dist/runtime/utils/form.js +3 -1
  101. package/dist/runtime/utils/link-keys.d.ts +1 -0
  102. package/dist/runtime/utils/link-keys.js +40 -0
  103. package/dist/runtime/utils/link.d.ts +2 -1
  104. package/dist/runtime/utils/link.js +2 -40
  105. package/dist/runtime/utils/prefetch.d.ts +5 -0
  106. package/dist/runtime/utils/prefetch.js +45 -0
  107. package/dist/runtime/utils/search.js +11 -18
  108. package/dist/runtime/utils/tv.d.ts +1 -1
  109. package/dist/runtime/utils/tv.js +103 -25
  110. package/dist/runtime/vue/components/Icon.vue +5 -2
  111. package/dist/runtime/vue/components/color-mode/ColorModeSelect.vue +1 -0
  112. package/dist/shared/{ui.XkBrgXFP.mjs → ui.DCJ9twx8.mjs} +597 -220
  113. package/dist/unplugin.d.mts +1 -1
  114. package/dist/unplugin.mjs +22 -6
  115. package/dist/vite.mjs +4 -4
  116. package/package.json +54 -53
  117. /package/dist/runtime/locale/{tj.d.ts → de_at.d.ts} +0 -0
@@ -1,9 +1,9 @@
1
1
  <script>
2
- import { computed, toRaw, toRef } from "vue";
3
2
  import theme from "#build/ui/listbox";
4
3
  </script>
5
4
 
6
5
  <script setup>
6
+ import { computed, toRaw, toRef } from "vue";
7
7
  import { ListboxRoot, ListboxContent, ListboxGroup, ListboxGroupLabel, ListboxVirtualizer, ListboxItem as RekaListboxItem, ListboxItemIndicator, ListboxFilter } from "reka-ui";
8
8
  import { useForwardProps } from "../composables/useForwardProps";
9
9
  import { createReusableTemplate, reactivePick } from "@vueuse/core";
@@ -65,11 +65,15 @@ const rootProps = useForwardProps(reactivePick(props, "as", "modelValue", "defau
65
65
  const virtualizerProps = toRef(() => {
66
66
  if (!props.virtualize) return false;
67
67
  return defu(typeof props.virtualize === "boolean" ? {} : props.virtualize, {
68
- estimateSize: getEstimateSize(filteredItems.value, size.value || "md", props.descriptionKey, !!slots["item-description"])
68
+ estimateSize: getEstimateSize(filteredItems.value, size.value ?? "md", props.descriptionKey, !!slots["item-description"])
69
69
  });
70
70
  });
71
71
  const inputProps = toRef(() => defu(typeof props.filter === "object" ? props.filter : {}, { placeholder: t("listbox.search"), variant: "none" }));
72
- const { emitFormChange, emitFormInput, name, size, color, id, highlight, disabled, ariaAttrs } = useFormField(_props, { bind: false });
72
+ const { emitFormChange, emitFormInput, name, size: formFieldSize, color: formFieldColor, id, highlight: formFieldHighlight, disabled: formFieldDisabled, ariaAttrs } = useFormField(_props, { bind: false });
73
+ const color = computed(() => formFieldColor.value ?? props.color);
74
+ const highlight = computed(() => formFieldHighlight.value ?? props.highlight);
75
+ const size = computed(() => formFieldSize.value ?? props.size);
76
+ const disabled = computed(() => formFieldDisabled.value ?? props.disabled);
73
77
  const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate({
74
78
  props: {
75
79
  item: {
@@ -83,9 +87,9 @@ const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate({
83
87
  }
84
88
  });
85
89
  const ui = computed(() => tv({ extend: theme, ...appConfig.ui?.listbox || {} })({
86
- color: color.value ?? props.color,
87
- size: size.value ?? props.size,
88
- highlight: highlight.value ?? props.highlight,
90
+ color: color.value,
91
+ size: size.value,
92
+ highlight: highlight.value,
89
93
  disabled: disabled.value,
90
94
  virtualize: !!props.virtualize
91
95
  }));
@@ -73,10 +73,10 @@ const ui = computed(() => tv({ extend: theme, ...appConfig.ui?.modal || {} })({
73
73
  data-slot="content"
74
74
  :class="ui.content({ class: [!slots.default && props.class, props.ui?.content] })"
75
75
  v-bind="contentProps"
76
- @enter="emits('enter')"
77
- @after-enter="emits('after:enter')"
78
- @leave="emits('leave')"
79
- @after-leave="emits('after:leave')"
76
+ @enter="!props.scrollable && emits('enter')"
77
+ @after-enter="!props.scrollable && emits('after:enter')"
78
+ @leave="!props.scrollable && emits('leave')"
79
+ @after-leave="!props.scrollable && emits('after:leave')"
80
80
  v-on="contentEvents"
81
81
  >
82
82
  <VisuallyHidden v-if="!props.title && !slots.title || !props.description && !slots.description || !!slots.content">
@@ -149,7 +149,14 @@ const ui = computed(() => tv({ extend: theme, ...appConfig.ui?.modal || {} })({
149
149
  <DialogPortal v-bind="portalProps" :force-mount="portalProps.disabled && props.unmountOnHide === false || void 0">
150
150
  <FieldGroupReset>
151
151
  <template v-if="props.scrollable">
152
- <DialogOverlay data-slot="overlay" :class="ui.overlay({ class: props.ui?.overlay })">
152
+ <DialogOverlay
153
+ data-slot="overlay"
154
+ :class="ui.overlay({ class: props.ui?.overlay })"
155
+ @enter="emits('enter')"
156
+ @after-enter="emits('after:enter')"
157
+ @leave="emits('leave')"
158
+ @after-leave="emits('after:leave')"
159
+ >
153
160
  <ReuseContentTemplate />
154
161
  </DialogOverlay>
155
162
  </template>
@@ -106,13 +106,13 @@ function getAccordionDefaultValue(list, level = 0, listIndex = 0) {
106
106
  }, []);
107
107
  return props.type === "single" ? indexes[0] : indexes;
108
108
  }
109
- function onLinkTrailingClick(e, item) {
109
+ function onLinkTrailingClick(e, item, trailingTrigger) {
110
110
  if (!item.children?.length) {
111
111
  return;
112
112
  }
113
113
  if (props.orientation === "horizontal") {
114
114
  e.preventDefault();
115
- } else if (props.orientation === "vertical" && !props.collapsed) {
115
+ } else if (props.orientation === "vertical" && !props.collapsed && trailingTrigger) {
116
116
  e.preventDefault();
117
117
  e.stopPropagation();
118
118
  }
@@ -120,7 +120,7 @@ function onLinkTrailingClick(e, item) {
120
120
  </script>
121
121
 
122
122
  <template>
123
- <DefineLinkTemplate v-slot="{ item, active, index }">
123
+ <DefineLinkTemplate v-slot="{ item, active, index, trailingTrigger }">
124
124
  <slot :name="item.slot || 'item'" :item="item" :index="index" :active="active" :ui="ui">
125
125
  <slot :name="item.slot ? `${item.slot}-leading` : 'item-leading'" :item="item" :active="active" :index="index" :ui="ui">
126
126
  <UAvatar v-if="item.avatar" :size="item.ui?.linkLeadingAvatarSize || props.ui?.linkLeadingAvatarSize || ui.linkLeadingAvatarSize()" v-bind="item.avatar" data-slot="linkLeadingAvatar" :class="ui.linkLeadingAvatar({ class: [props.ui?.linkLeadingAvatar, item.ui?.linkLeadingAvatar], active, disabled: !!item.disabled })" />
@@ -149,12 +149,12 @@ function onLinkTrailingClick(e, item) {
149
149
  </span>
150
150
 
151
151
  <component
152
- :is="props.orientation === 'vertical' && item.children?.length && !props.collapsed ? AccordionTrigger : 'span'"
152
+ :is="props.orientation === 'vertical' && item.children?.length && !props.collapsed && trailingTrigger ? AccordionTrigger : 'span'"
153
153
  v-if="item.badge || item.badge === 0 || props.orientation === 'horizontal' && (item.children?.length || !!slots[item.slot ? `${item.slot}-content` : 'item-content']) || props.orientation === 'vertical' && item.children?.length || item.trailingIcon || !!slots[item.slot ? `${item.slot}-trailing` : 'item-trailing']"
154
- :as="props.orientation === 'vertical' && item.children?.length && !props.collapsed ? 'span' : void 0"
154
+ :as="props.orientation === 'vertical' && item.children?.length && !props.collapsed && trailingTrigger ? 'span' : void 0"
155
155
  data-slot="linkTrailing"
156
156
  :class="ui.linkTrailing({ class: [props.ui?.linkTrailing, item.ui?.linkTrailing] })"
157
- @click="(e) => onLinkTrailingClick(e, item)"
157
+ @click="(e) => onLinkTrailingClick(e, item, trailingTrigger)"
158
158
  >
159
159
  <slot :name="item.slot ? `${item.slot}-trailing` : 'item-trailing'" :item="item" :active="active" :index="index" :ui="ui">
160
160
  <UBadge
@@ -182,7 +182,7 @@ function onLinkTrailingClick(e, item) {
182
182
  :value="getItemValue(item, index, level, listIndex)"
183
183
  >
184
184
  <div v-if="props.orientation === 'vertical' && item.type === 'label' && !props.collapsed" data-slot="label" :class="ui.label({ class: [props.ui?.label, item.ui?.label, item.class] })">
185
- <ReuseLinkTemplate :item="item" :index="index" />
185
+ <ReuseLinkTemplate :item="item" :index="index" :trailing-trigger="true" />
186
186
  </div>
187
187
  <ULink v-else-if="item.type !== 'label'" v-slot="{ active, ...slotProps }" v-bind="props.orientation === 'vertical' && item.children?.length && !props.collapsed && item.type === 'trigger' ? {} : pickLinkProps(item)" custom>
188
188
  <component
@@ -194,7 +194,7 @@ function onLinkTrailingClick(e, item) {
194
194
  >
195
195
  <UPopover v-if="props.orientation === 'vertical' && props.collapsed && item.children?.length && (!!props.popover || !!item.popover)" v-bind="{ ...popoverProps, ...typeof item.popover === 'boolean' ? {} : item.popover || {} }" :ui="{ content: ui.content({ class: [props.ui?.content, item.ui?.content] }) }">
196
196
  <ULinkBase v-bind="slotProps" data-slot="link" :class="ui.link({ class: [props.ui?.link, item.ui?.link, item.class], active: active || item.active, disabled: !!item.disabled, level: level > 0 })">
197
- <ReuseLinkTemplate :item="item" :active="active || item.active" :index="index" />
197
+ <ReuseLinkTemplate :item="item" :active="active || item.active" :index="index" :trailing-trigger="!!slotProps.href" />
198
198
  </ULinkBase>
199
199
 
200
200
  <template #content="{ close }">
@@ -231,11 +231,11 @@ function onLinkTrailingClick(e, item) {
231
231
  </UPopover>
232
232
  <UTooltip v-else-if="props.orientation === 'vertical' && props.collapsed && (!!props.tooltip || !!item.tooltip) || props.orientation === 'horizontal' && !!item.tooltip" :text="get(item, props.labelKey)" v-bind="{ ...tooltipProps, ...typeof item.tooltip === 'boolean' ? {} : item.tooltip || {} }">
233
233
  <ULinkBase v-bind="slotProps" data-slot="link" :class="ui.link({ class: [props.ui?.link, item.ui?.link, item.class], active: active || item.active, disabled: !!item.disabled, level: level > 0 })">
234
- <ReuseLinkTemplate :item="item" :active="active || item.active" :index="index" />
234
+ <ReuseLinkTemplate :item="item" :active="active || item.active" :index="index" :trailing-trigger="!!slotProps.href" />
235
235
  </ULinkBase>
236
236
  </UTooltip>
237
237
  <ULinkBase v-else v-bind="slotProps" data-slot="link" :class="ui.link({ class: [props.ui?.link, item.ui?.link, item.class], active: active || item.active, disabled: !!item.disabled, level: props.orientation === 'horizontal' || level > 0 })">
238
- <ReuseLinkTemplate :item="item" :active="active || item.active" :index="index" />
238
+ <ReuseLinkTemplate :item="item" :active="active || item.active" :index="index" :trailing-trigger="!!slotProps.href" />
239
239
  </ULinkBase>
240
240
  </component>
241
241
 
@@ -3,7 +3,7 @@ import theme from "#build/ui/pin-input";
3
3
  </script>
4
4
 
5
5
  <script setup>
6
- import { ref, computed, onMounted } from "vue";
6
+ import { ref, computed, onMounted, onScopeDispose } from "vue";
7
7
  import { PinInputInput, PinInputRoot } from "reka-ui";
8
8
  import { useForwardProps } from "../composables/useForwardProps";
9
9
  import { reactivePick } from "@vueuse/core";
@@ -41,26 +41,29 @@ defineSlots();
41
41
  const props = useComponentProps("pinInput", _props);
42
42
  const appConfig = useAppConfig();
43
43
  const rootProps = useForwardProps(reactivePick(props, "disabled", "id", "mask", "name", "otp", "required", "type"), emits);
44
- const { emitFormInput, emitFormFocus, emitFormChange, emitFormBlur, size, color, id, name, highlight, disabled, ariaAttrs } = useFormField(_props);
44
+ const { emitFormInput, emitFormFocus, emitFormChange, emitFormBlur, size: formFieldSize, color: formFieldColor, id, name, highlight: formFieldHighlight, disabled: formFieldDisabled, ariaAttrs } = useFormField(_props);
45
+ const color = computed(() => formFieldColor.value ?? props.color);
46
+ const highlight = computed(() => formFieldHighlight.value ?? props.highlight);
47
+ const size = computed(() => formFieldSize.value ?? props.size);
48
+ const disabled = computed(() => formFieldDisabled.value ?? props.disabled);
45
49
  const ui = computed(() => tv({ extend: theme, ...appConfig.ui?.pinInput || {} })({
46
- color: color.value ?? props.color,
50
+ color: color.value,
47
51
  variant: props.variant,
48
- size: size.value ?? props.size,
49
- highlight: highlight.value ?? props.highlight,
52
+ size: size.value,
53
+ highlight: highlight.value,
50
54
  fixed: props.fixed
51
55
  }));
52
56
  const inputsRef = ref([]);
53
57
  function setInputRef(index, el) {
54
58
  inputsRef.value[index] = el;
55
59
  }
56
- const completed = ref(false);
57
60
  function onComplete(value) {
58
61
  const event = new Event("change", { target: { value } });
59
62
  emits("change", event);
60
63
  emitFormChange();
61
64
  }
62
65
  function onBlur(event) {
63
- if (!event.relatedTarget || completed.value) {
66
+ if (!event.relatedTarget) {
64
67
  emits("blur", event);
65
68
  emitFormBlur();
66
69
  }
@@ -84,11 +87,13 @@ function shouldInsertSeparator(index) {
84
87
  const separator = looseToNumber(props.separator);
85
88
  return Number.isInteger(separator) && separator > 0 && position % separator === 0;
86
89
  }
90
+ let autofocusTimeoutId;
87
91
  onMounted(() => {
88
- setTimeout(() => {
92
+ autofocusTimeoutId = setTimeout(() => {
89
93
  autoFocus();
90
94
  }, props.autofocusDelay);
91
95
  });
96
+ onScopeDispose(() => clearTimeout(autofocusTimeoutId));
92
97
  defineExpose({
93
98
  inputsRef
94
99
  });
@@ -5,7 +5,6 @@ import theme from "#build/ui/popover";
5
5
  <script setup>
6
6
  import { computed, toRef } from "vue";
7
7
  import { defu } from "defu";
8
- import {} from "reka-ui";
9
8
  import { useForwardProps } from "../composables/useForwardProps";
10
9
  import { Popover, HoverCard } from "reka-ui/namespaced";
11
10
  import { reactivePick } from "@vueuse/core";
@@ -49,7 +49,7 @@ export interface PricingTableProps<T extends PricingTableTier = PricingTableTier
49
49
  as?: any;
50
50
  /**
51
51
  * The caption to display above the table.
52
- * @defeaultValue t('pricingTable.caption')
52
+ * @defaultValue t('pricingTable.caption')
53
53
  */
54
54
  caption?: string;
55
55
  /**
@@ -49,7 +49,7 @@ export interface PricingTableProps<T extends PricingTableTier = PricingTableTier
49
49
  as?: any;
50
50
  /**
51
51
  * The caption to display above the table.
52
- * @defeaultValue t('pricingTable.caption')
52
+ * @defaultValue t('pricingTable.caption')
53
53
  */
54
54
  caption?: string;
55
55
  /**
@@ -21,9 +21,10 @@ export interface ProgressProps extends Pick<ProgressRootProps, 'getValueLabel' |
21
21
  */
22
22
  size?: Progress['variants']['size'];
23
23
  /**
24
+ * Any theme color, or any CSS color value for palettes outside the theme.
24
25
  * @defaultValue 'primary'
25
26
  */
26
- color?: Progress['variants']['color'];
27
+ color?: Progress['variants']['color'] | (string & {});
27
28
  /**
28
29
  * The orientation of the progress bar.
29
30
  * @defaultValue 'horizontal'
@@ -76,10 +76,7 @@ const indicatorStyle = computed(() => {
76
76
  }
77
77
  }
78
78
  });
79
- const statusStyle = computed(() => {
80
- const value = `${Math.max(percent.value ?? 0, 0)}%`;
81
- return props.orientation === "vertical" ? { height: value } : { width: value };
82
- });
79
+ const statusStyle = computed(() => ({ "--percent": `${Math.max(percent.value ?? 0, 0)}%` }));
83
80
  function isActive(index) {
84
81
  return index === Number(props.modelValue);
85
82
  }
@@ -109,6 +106,8 @@ const ui = computed(() => tv({ extend: theme, ...appConfig.ui?.progress || {} })
109
106
  orientation: props.orientation,
110
107
  inverted: props.inverted
111
108
  }));
109
+ const themeColors = computed(() => Object.keys({ ...theme.variants?.color, ...appConfig.ui?.progress?.variants?.color }));
110
+ const customColor = computed(() => props.color && !themeColors.value.includes(props.color) ? props.color : void 0);
112
111
  </script>
113
112
 
114
113
  <template>
@@ -120,10 +119,10 @@ const ui = computed(() => tv({ extend: theme, ...appConfig.ui?.progress || {} })
120
119
  </div>
121
120
 
122
121
  <ProgressRoot v-bind="rootProps" :max="realMax" data-slot="base" :class="ui.base({ class: props.ui?.base })" style="transform: translateZ(0)">
123
- <ProgressIndicator data-slot="indicator" :class="ui.indicator({ class: props.ui?.indicator })" :style="indicatorStyle" />
122
+ <ProgressIndicator data-slot="indicator" :class="ui.indicator({ class: props.ui?.indicator })" :style="[indicatorStyle, customColor ? { backgroundColor: customColor } : void 0]" />
124
123
  </ProgressRoot>
125
124
 
126
- <div v-if="hasSteps" data-slot="steps" :class="ui.steps({ class: props.ui?.steps })">
125
+ <div v-if="hasSteps" data-slot="steps" :class="ui.steps({ class: props.ui?.steps })" :style="customColor ? { color: customColor } : void 0">
127
126
  <div v-for="(step, index) in props.max" :key="index" data-slot="step" :class="ui.step({ class: props.ui?.step, step: stepVariant(index) })">
128
127
  <slot :name="`step-${index}`" :step="step">
129
128
  {{ step }}
@@ -21,9 +21,10 @@ export interface ProgressProps extends Pick<ProgressRootProps, 'getValueLabel' |
21
21
  */
22
22
  size?: Progress['variants']['size'];
23
23
  /**
24
+ * Any theme color, or any CSS color value for palettes outside the theme.
24
25
  * @defaultValue 'primary'
25
26
  */
26
- color?: Progress['variants']['color'];
27
+ color?: Progress['variants']['color'] | (string & {});
27
28
  /**
28
29
  * The orientation of the progress bar.
29
30
  * @defaultValue 'horizontal'
@@ -0,0 +1,91 @@
1
+ import type { VNode } from 'vue';
2
+ import type { AppConfig } from '@nuxt/schema';
3
+ import theme from '#build/ui/progress-group';
4
+ import type { IconProps } from './Icon.vue';
5
+ import type { DynamicSlots } from '../types/utils';
6
+ import type { ComponentConfig } from '../types/tv';
7
+ type ProgressGroup = ComponentConfig<typeof theme, AppConfig, 'progressGroup'>;
8
+ export interface ProgressGroupItem {
9
+ label?: string;
10
+ /**
11
+ * @IconifyIcon
12
+ */
13
+ icon?: IconProps['name'];
14
+ /** The part of `max` this segment takes up. */
15
+ value?: number;
16
+ /**
17
+ * Any theme color, or any CSS color value for palettes outside the theme.
18
+ * @defaultValue 'primary'
19
+ */
20
+ color?: ProgressGroup['variants']['color'] | (string & {});
21
+ slot?: string;
22
+ class?: any;
23
+ ui?: Pick<ProgressGroup['slots'], 'segment' | 'indicator' | 'item' | 'itemLeadingIcon' | 'itemLeadingDot' | 'itemLabel' | 'itemTrailing'>;
24
+ [key: string]: any;
25
+ }
26
+ export interface ProgressGroupProps<T extends ProgressGroupItem = ProgressGroupItem> {
27
+ /**
28
+ * The element or component this component should render as.
29
+ * @defaultValue 'div'
30
+ */
31
+ as?: any;
32
+ items?: T[];
33
+ /**
34
+ * The value all items add up to, used to compute each segment's share of the track.
35
+ * @defaultValue 100
36
+ */
37
+ max?: number;
38
+ /** Display the summed progress value. */
39
+ status?: boolean;
40
+ /**
41
+ * @defaultValue 'md'
42
+ */
43
+ size?: ProgressGroup['variants']['size'];
44
+ /**
45
+ * Any theme color, or any CSS color value for palettes outside the theme.
46
+ * @defaultValue 'primary'
47
+ */
48
+ color?: ProgressGroup['variants']['color'] | (string & {});
49
+ /**
50
+ * The orientation of the progress bar.
51
+ * @defaultValue 'horizontal'
52
+ */
53
+ orientation?: ProgressGroup['variants']['orientation'];
54
+ class?: any;
55
+ ui?: ProgressGroup['slots'];
56
+ }
57
+ type SlotProps<T extends ProgressGroupItem> = (props: {
58
+ item: T;
59
+ index: number;
60
+ percent: number;
61
+ }) => VNode[];
62
+ export type ProgressGroupSlots<T extends ProgressGroupItem = ProgressGroupItem> = {
63
+ 'status'?: (props: {
64
+ percent: number;
65
+ }) => VNode[];
66
+ 'item'?: SlotProps<T>;
67
+ 'item-leading'?: SlotProps<T>;
68
+ 'item-label'?: SlotProps<T>;
69
+ 'item-trailing'?: SlotProps<T>;
70
+ } & DynamicSlots<T, 'leading' | 'label' | 'trailing', {
71
+ index: number;
72
+ percent: number;
73
+ }>;
74
+ declare const _default: typeof __VLS_export;
75
+ export default _default;
76
+ declare const __VLS_export: <T extends ProgressGroupItem>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
77
+ props: import("vue").PublicProps & __VLS_PrettifyLocal<ProgressGroupProps<T>> & (typeof globalThis extends {
78
+ __VLS_PROPS_FALLBACK: infer P;
79
+ } ? P : {});
80
+ expose: (exposed: {}) => void;
81
+ attrs: any;
82
+ slots: ProgressGroupSlots<T>;
83
+ emit: {};
84
+ }>) => import("vue").VNode & {
85
+ __ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
86
+ };
87
+ type __VLS_PrettifyLocal<T> = (T extends any ? {
88
+ [K in keyof T]: T[K];
89
+ } : {
90
+ [K in keyof T as K]: T[K];
91
+ }) & {};
@@ -0,0 +1,99 @@
1
+ <script>
2
+ import theme from "#build/ui/progress-group";
3
+ </script>
4
+
5
+ <script setup>
6
+ import { computed } from "vue";
7
+ import { Primitive, ProgressRoot, ProgressIndicator } from "reka-ui";
8
+ import { useAppConfig } from "#imports";
9
+ import { useComponentProps } from "../composables/useComponentProps";
10
+ import { tv } from "../utils/tv";
11
+ import UIcon from "./Icon.vue";
12
+ const _props = defineProps({
13
+ as: { type: null, required: false },
14
+ items: { type: Array, required: false },
15
+ max: { type: Number, required: false, default: 100 },
16
+ status: { type: Boolean, required: false },
17
+ size: { type: null, required: false },
18
+ color: { type: null, required: false },
19
+ orientation: { type: null, required: false, default: "horizontal" },
20
+ class: { type: null, required: false },
21
+ ui: { type: Object, required: false }
22
+ });
23
+ const slots = defineSlots();
24
+ const props = useComponentProps("progressGroup", _props);
25
+ const appConfig = useAppConfig();
26
+ const ui = computed(() => tv({ extend: theme, ...appConfig.ui?.progressGroup || {} })({
27
+ size: props.size,
28
+ color: props.color,
29
+ orientation: props.orientation
30
+ }));
31
+ const max = computed(() => {
32
+ const value = Number(props.max);
33
+ return Number.isFinite(value) && value > 0 ? value : 100;
34
+ });
35
+ const values = computed(() => (props.items ?? []).map((item) => Math.min(Math.max(Number(item.value) || 0, 0), max.value)));
36
+ const percents = computed(() => values.value.map((value) => value / max.value * 100));
37
+ const percent = computed(() => Math.min(100, Math.round(percents.value.reduce((total, value) => total + value, 0))));
38
+ const statusStyle = computed(() => ({ "--percent": `${percent.value}%` }));
39
+ const themeColors = computed(() => Object.keys({ ...theme.variants?.color, ...appConfig.ui?.progressGroup?.variants?.color }));
40
+ const itemColors = computed(() => (props.items ?? []).map((item) => item.color || props.color));
41
+ const customColors = computed(() => itemColors.value.map((color) => color && !themeColors.value.includes(color) ? color : void 0));
42
+ const hasList = computed(() => !!props.items?.length && (props.items.some((item) => item.label || item.icon || item.slot) || !!slots.item || !!slots["item-leading"] || !!slots["item-label"] || !!slots["item-trailing"]));
43
+ function segmentStyle(index) {
44
+ const value = `${percents.value[index] ?? 0}%`;
45
+ return props.orientation === "vertical" ? { height: value } : { width: value };
46
+ }
47
+ const valueLabels = computed(() => (props.items ?? []).map((item) => {
48
+ const label = item.label;
49
+ return label ? () => label : void 0;
50
+ }));
51
+ </script>
52
+
53
+ <template>
54
+ <Primitive :as="props.as" :data-orientation="props.orientation" data-slot="root" :class="ui.root({ class: [props.ui?.root, props.class] })">
55
+ <div v-if="props.status || !!slots.status" data-slot="status" :class="ui.status({ class: props.ui?.status })" :style="statusStyle">
56
+ <slot name="status" :percent="percent">
57
+ {{ percent }}%
58
+ </slot>
59
+ </div>
60
+
61
+ <div data-slot="base" :class="ui.base({ class: props.ui?.base })">
62
+ <ProgressRoot
63
+ v-for="(item, index) in props.items"
64
+ :key="index"
65
+ :model-value="values[index]"
66
+ :max="max"
67
+ :get-value-label="valueLabels[index]"
68
+ data-slot="segment"
69
+ :class="ui.segment({ class: [props.ui?.segment, item.ui?.segment] })"
70
+ :style="segmentStyle(index)"
71
+ >
72
+ <ProgressIndicator data-slot="indicator" :class="ui.indicator({ color: itemColors[index], class: [props.ui?.indicator, item.ui?.indicator] })" :style="customColors[index] ? { backgroundColor: customColors[index] } : void 0" />
73
+ </ProgressRoot>
74
+ </div>
75
+
76
+ <ul v-if="hasList" data-slot="list" :class="ui.list({ class: props.ui?.list })">
77
+ <li v-for="(item, index) in props.items" :key="index" data-slot="item" :class="ui.item({ class: [props.ui?.item, item.ui?.item, item.class] })">
78
+ <slot :name="item.slot || 'item'" :item="item" :index="index" :percent="percents[index] ?? 0">
79
+ <slot :name="item.slot ? `${item.slot}-leading` : 'item-leading'" :item="item" :index="index" :percent="percents[index] ?? 0">
80
+ <UIcon v-if="item.icon" :name="item.icon" data-slot="itemLeadingIcon" :class="ui.itemLeadingIcon({ color: itemColors[index], class: [props.ui?.itemLeadingIcon, item.ui?.itemLeadingIcon] })" :style="customColors[index] ? { color: customColors[index] } : void 0" />
81
+ <span v-else data-slot="itemLeadingDot" :class="ui.itemLeadingDot({ color: itemColors[index], class: [props.ui?.itemLeadingDot, item.ui?.itemLeadingDot] })" :style="customColors[index] ? { backgroundColor: customColors[index] } : void 0" />
82
+ </slot>
83
+
84
+ <span v-if="item.label || !!slots[item.slot ? `${item.slot}-label` : 'item-label']" data-slot="itemLabel" :class="ui.itemLabel({ class: [props.ui?.itemLabel, item.ui?.itemLabel] })">
85
+ <slot :name="item.slot ? `${item.slot}-label` : 'item-label'" :item="item" :index="index" :percent="percents[index] ?? 0">
86
+ {{ item.label }}
87
+ </slot>
88
+ </span>
89
+
90
+ <span data-slot="itemTrailing" :class="ui.itemTrailing({ class: [props.ui?.itemTrailing, item.ui?.itemTrailing] })">
91
+ <slot :name="item.slot ? `${item.slot}-trailing` : 'item-trailing'" :item="item" :index="index" :percent="percents[index] ?? 0">
92
+ {{ Math.round(percents[index] ?? 0) }}%
93
+ </slot>
94
+ </span>
95
+ </slot>
96
+ </li>
97
+ </ul>
98
+ </Primitive>
99
+ </template>
@@ -0,0 +1,91 @@
1
+ import type { VNode } from 'vue';
2
+ import type { AppConfig } from '@nuxt/schema';
3
+ import theme from '#build/ui/progress-group';
4
+ import type { IconProps } from './Icon.vue';
5
+ import type { DynamicSlots } from '../types/utils';
6
+ import type { ComponentConfig } from '../types/tv';
7
+ type ProgressGroup = ComponentConfig<typeof theme, AppConfig, 'progressGroup'>;
8
+ export interface ProgressGroupItem {
9
+ label?: string;
10
+ /**
11
+ * @IconifyIcon
12
+ */
13
+ icon?: IconProps['name'];
14
+ /** The part of `max` this segment takes up. */
15
+ value?: number;
16
+ /**
17
+ * Any theme color, or any CSS color value for palettes outside the theme.
18
+ * @defaultValue 'primary'
19
+ */
20
+ color?: ProgressGroup['variants']['color'] | (string & {});
21
+ slot?: string;
22
+ class?: any;
23
+ ui?: Pick<ProgressGroup['slots'], 'segment' | 'indicator' | 'item' | 'itemLeadingIcon' | 'itemLeadingDot' | 'itemLabel' | 'itemTrailing'>;
24
+ [key: string]: any;
25
+ }
26
+ export interface ProgressGroupProps<T extends ProgressGroupItem = ProgressGroupItem> {
27
+ /**
28
+ * The element or component this component should render as.
29
+ * @defaultValue 'div'
30
+ */
31
+ as?: any;
32
+ items?: T[];
33
+ /**
34
+ * The value all items add up to, used to compute each segment's share of the track.
35
+ * @defaultValue 100
36
+ */
37
+ max?: number;
38
+ /** Display the summed progress value. */
39
+ status?: boolean;
40
+ /**
41
+ * @defaultValue 'md'
42
+ */
43
+ size?: ProgressGroup['variants']['size'];
44
+ /**
45
+ * Any theme color, or any CSS color value for palettes outside the theme.
46
+ * @defaultValue 'primary'
47
+ */
48
+ color?: ProgressGroup['variants']['color'] | (string & {});
49
+ /**
50
+ * The orientation of the progress bar.
51
+ * @defaultValue 'horizontal'
52
+ */
53
+ orientation?: ProgressGroup['variants']['orientation'];
54
+ class?: any;
55
+ ui?: ProgressGroup['slots'];
56
+ }
57
+ type SlotProps<T extends ProgressGroupItem> = (props: {
58
+ item: T;
59
+ index: number;
60
+ percent: number;
61
+ }) => VNode[];
62
+ export type ProgressGroupSlots<T extends ProgressGroupItem = ProgressGroupItem> = {
63
+ 'status'?: (props: {
64
+ percent: number;
65
+ }) => VNode[];
66
+ 'item'?: SlotProps<T>;
67
+ 'item-leading'?: SlotProps<T>;
68
+ 'item-label'?: SlotProps<T>;
69
+ 'item-trailing'?: SlotProps<T>;
70
+ } & DynamicSlots<T, 'leading' | 'label' | 'trailing', {
71
+ index: number;
72
+ percent: number;
73
+ }>;
74
+ declare const _default: typeof __VLS_export;
75
+ export default _default;
76
+ declare const __VLS_export: <T extends ProgressGroupItem>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
77
+ props: import("vue").PublicProps & __VLS_PrettifyLocal<ProgressGroupProps<T>> & (typeof globalThis extends {
78
+ __VLS_PROPS_FALLBACK: infer P;
79
+ } ? P : {});
80
+ expose: (exposed: {}) => void;
81
+ attrs: any;
82
+ slots: ProgressGroupSlots<T>;
83
+ emit: {};
84
+ }>) => import("vue").VNode & {
85
+ __ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
86
+ };
87
+ type __VLS_PrettifyLocal<T> = (T extends any ? {
88
+ [K in keyof T]: T[K];
89
+ } : {
90
+ [K in keyof T as K]: T[K];
91
+ }) & {};
@@ -2,6 +2,7 @@ import type { RadioGroupRootProps, RadioGroupRootEmits } from 'reka-ui';
2
2
  import type { VNode } from 'vue';
3
3
  import type { AppConfig } from '@nuxt/schema';
4
4
  import theme from '#build/ui/radio-group';
5
+ import type { IconProps } from './Icon.vue';
5
6
  import type { AcceptableValue, GetItemKeys, GetModelValue, GetModelValueEmits } from '../types/utils';
6
7
  import type { ComponentConfig } from '../types/tv';
7
8
  type RadioGroup = ComponentConfig<typeof theme, AppConfig, 'radioGroup'>;
@@ -11,8 +12,13 @@ export type RadioGroupItem = RadioGroupValue | {
11
12
  description?: string;
12
13
  disabled?: boolean;
13
14
  value?: RadioGroupValue;
15
+ /**
16
+ * The icon displayed above the label when `indicator` is `hidden`.
17
+ * @IconifyIcon
18
+ */
19
+ icon?: IconProps['name'];
14
20
  class?: any;
15
- ui?: Pick<RadioGroup['slots'], 'item' | 'container' | 'base' | 'indicator' | 'wrapper' | 'label' | 'description'>;
21
+ ui?: Pick<RadioGroup['slots'], 'item' | 'container' | 'base' | 'indicator' | 'wrapper' | 'label' | 'icon' | 'description'>;
16
22
  [key: string]: any;
17
23
  };
18
24
  export interface RadioGroupProps<T extends RadioGroupItem[] = RadioGroupItem[], VK extends GetItemKeys<T> = 'value'> extends Pick<RadioGroupRootProps, 'disabled' | 'loop' | 'name' | 'required'> {