@brumaombra/ui-vintage 0.8.2 → 0.9.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 (152) hide show
  1. package/AGENTS.md +41 -0
  2. package/COMPONENTS.md +89 -18
  3. package/README.md +296 -262
  4. package/module.mjs +5 -11
  5. package/package.json +30 -12
  6. package/src/components/blog/BlogContentRenderer.vue +1 -1
  7. package/src/components/blog/BlogFAQSection.vue +1 -1
  8. package/src/components/blog/BlogHeaderSection.vue +93 -19
  9. package/src/components/blog/BlogInfoSection.vue +42 -37
  10. package/src/components/blog/BlogPostHeader.vue +181 -0
  11. package/src/components/blog/BlogProgressBar.vue +3 -5
  12. package/src/components/blog/BlogSectionTitle.vue +8 -6
  13. package/src/components/blog/BlogTagsSection.vue +16 -7
  14. package/src/components/blog/CategoryCard.vue +19 -23
  15. package/src/components/blog/HeaderCarousel.vue +17 -7
  16. package/src/components/blog/PostCard.vue +14 -21
  17. package/src/components/blog/SocialShareSidebar.vue +32 -15
  18. package/src/components/blog/TableOfContents.vue +64 -29
  19. package/src/components/blog/index.ts +1 -0
  20. package/src/components/card-grid/CardGrid.vue +3 -3
  21. package/src/components/chip/Chip.vue +1 -1
  22. package/src/components/confirm-dialog/ConfirmDialog.vue +6 -1
  23. package/src/components/confirm-dialog/confirm-dialog-state.ts +22 -22
  24. package/src/components/content/BlogList.vue +29 -52
  25. package/src/components/content/BlogTable.vue +26 -30
  26. package/src/components/content/CodeBlock.vue +99 -23
  27. package/src/components/content/Flow.vue +29 -35
  28. package/src/components/content/ProseHr.vue +6 -6
  29. package/src/components/content/ProsePre.vue +9 -4
  30. package/src/components/content/Terminal.vue +45 -21
  31. package/src/components/cookie-consent/CookieConsent.vue +1 -1
  32. package/src/components/dashboard-shell/DashboardShell.vue +24 -7
  33. package/src/components/empty-state-card/EmptyStateCard.vue +3 -3
  34. package/src/components/error-page/ErrorPage.vue +4 -4
  35. package/src/components/info-card/InfoCard.vue +4 -4
  36. package/src/components/landing/landing-footer/LandingFooter.vue +17 -12
  37. package/src/components/landing/landing-navbar/LandingNavbar.vue +22 -6
  38. package/src/components/landing/landing-shell/LandingShell.vue +2 -7
  39. package/src/components/language-flag/index.ts +1 -0
  40. package/src/components/language-selector/LanguageSelector.vue +19 -11
  41. package/src/components/load-more-button/LoadMoreButton.vue +9 -13
  42. package/src/components/loading-state-card/LoadingStateCard.vue +3 -3
  43. package/src/components/message-dialog/MessageDialog.vue +14 -12
  44. package/src/components/message-dialog/message-dialog-state.ts +1 -4
  45. package/src/components/message-toast/MessageToast.vue +28 -10
  46. package/src/components/message-toast/message-toast-state.ts +20 -0
  47. package/src/components/page-header/PageHeader.vue +1 -1
  48. package/src/components/progress-component/ProgressComponent.vue +7 -3
  49. package/src/components/single-value-card/SingleValueCard.vue +39 -40
  50. package/src/components/slider-form-component/SliderFormComponent.vue +1 -1
  51. package/src/components/stat-strip/StatStrip.vue +36 -0
  52. package/src/components/stat-strip/index.ts +2 -0
  53. package/src/components/switch-form-component/SwitchFormComponent.vue +5 -5
  54. package/src/components/text-link/TextLink.vue +1 -1
  55. package/src/components/theme-selector/ThemeSelector.vue +6 -3
  56. package/src/components/theme-selector/theme-transition.ts +15 -3
  57. package/src/components/ui/accordion/Accordion.vue +19 -19
  58. package/src/components/ui/alert/AlertTitle.vue +1 -1
  59. package/src/components/ui/alert/index.ts +1 -1
  60. package/src/components/ui/avatar/AvatarFallback.vue +2 -1
  61. package/src/components/ui/avatar/AvatarGroup.vue +13 -3
  62. package/src/components/ui/breadcrumb/BreadcrumbEllipsis.vue +3 -3
  63. package/src/components/ui/busy/Busy.vue +5 -7
  64. package/src/components/ui/button/Button.vue +4 -2
  65. package/src/components/ui/button/index.ts +1 -4
  66. package/src/components/ui/calendar/Calendar.vue +12 -7
  67. package/src/components/ui/calendar/CalendarCell.vue +1 -1
  68. package/src/components/ui/calendar/CalendarCellTrigger.vue +1 -1
  69. package/src/components/ui/calendar/CalendarHeadCell.vue +1 -1
  70. package/src/components/ui/calendar/CalendarNextButton.vue +2 -2
  71. package/src/components/ui/calendar/CalendarPrevButton.vue +2 -2
  72. package/src/components/ui/card/Card.vue +5 -15
  73. package/src/components/ui/checkbox/Checkbox.vue +5 -1
  74. package/src/components/ui/combobox/ComboboxInput.vue +5 -1
  75. package/src/components/ui/combobox/ComboboxItem.vue +1 -1
  76. package/src/components/ui/combobox/ComboboxSelect.vue +2 -1
  77. package/src/components/ui/combobox/ComboboxTrigger.vue +1 -1
  78. package/src/components/ui/command/CommandGroup.vue +17 -3
  79. package/src/components/ui/command/CommandInput.vue +2 -2
  80. package/src/components/ui/command/CommandList.vue +5 -1
  81. package/src/components/ui/data-table/DataTable.vue +13 -4
  82. package/src/components/ui/date-picker/DatePicker.vue +8 -2
  83. package/src/components/ui/date-time-picker/DateTimePicker.vue +5 -1
  84. package/src/components/ui/dialog/DialogContent.vue +1 -1
  85. package/src/components/ui/dialog/DialogScrollContent.vue +1 -1
  86. package/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +1 -1
  87. package/src/components/ui/dropdown-menu/DropdownMenuItem.vue +1 -1
  88. package/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +2 -2
  89. package/src/components/ui/dropdown-menu/classes.ts +2 -0
  90. package/src/components/ui/field/Field.vue +4 -0
  91. package/src/components/ui/field/FieldLabel.vue +6 -2
  92. package/src/components/ui/field/field-context.ts +62 -0
  93. package/src/components/ui/field/index.ts +1 -0
  94. package/src/components/ui/file-dropzone/FileDropzone.vue +14 -5
  95. package/src/components/ui/input/Input.vue +5 -1
  96. package/src/components/ui/native-select/NativeSelect.vue +7 -3
  97. package/src/components/ui/number-field/NumberField.vue +7 -2
  98. package/src/components/ui/pagination/PaginationEllipsis.vue +1 -1
  99. package/src/components/ui/pagination/PaginationFirst.vue +1 -1
  100. package/src/components/ui/pagination/PaginationItem.vue +1 -1
  101. package/src/components/ui/pagination/PaginationLast.vue +1 -1
  102. package/src/components/ui/pagination/PaginationNext.vue +1 -1
  103. package/src/components/ui/pagination/PaginationPrevious.vue +1 -1
  104. package/src/components/ui/pin-input/PinInputInput.vue +2 -2
  105. package/src/components/ui/progress/Progress.vue +2 -2
  106. package/src/components/ui/radio-group/RadioGroupCard.vue +1 -1
  107. package/src/components/ui/select/SelectItem.vue +2 -2
  108. package/src/components/ui/select/SelectItemContent.vue +1 -1
  109. package/src/components/ui/select/SelectScrollDownButton.vue +1 -1
  110. package/src/components/ui/select/SelectScrollUpButton.vue +1 -1
  111. package/src/components/ui/select/SelectTrigger.vue +6 -2
  112. package/src/components/ui/sidebar/Sidebar.vue +1 -1
  113. package/src/components/ui/sidebar/SidebarGroupAction.vue +1 -1
  114. package/src/components/ui/sidebar/SidebarGroupLabel.vue +1 -1
  115. package/src/components/ui/sidebar/SidebarInset.vue +1 -1
  116. package/src/components/ui/sidebar/SidebarMenuAction.vue +1 -1
  117. package/src/components/ui/sidebar/SidebarMenuBadge.vue +1 -1
  118. package/src/components/ui/sidebar/SidebarMenuSkeleton.vue +2 -2
  119. package/src/components/ui/sidebar/SidebarMenuSubButton.vue +1 -1
  120. package/src/components/ui/sidebar/SidebarProvider.vue +17 -6
  121. package/src/components/ui/sidebar/SidebarTrigger.vue +1 -1
  122. package/src/components/ui/sidebar/index.ts +2 -2
  123. package/src/components/ui/sidebar/utils.ts +1 -1
  124. package/src/components/ui/slider/Slider.vue +5 -4
  125. package/src/components/ui/spinner/Spinner.vue +3 -2
  126. package/src/components/ui/stepper/SimpleStepper.vue +1 -1
  127. package/src/components/ui/stepper/StepperDescription.vue +5 -3
  128. package/src/components/ui/stepper/StepperIndicator.vue +3 -3
  129. package/src/components/ui/stepper/StepperTitle.vue +5 -3
  130. package/src/components/ui/switch/Switch.vue +6 -2
  131. package/src/components/ui/tabs/TabsList.vue +1 -1
  132. package/src/components/ui/tabs/TabsTrigger.vue +1 -1
  133. package/src/components/ui/tags-input/TagsInputItemDelete.vue +1 -1
  134. package/src/components/ui/textarea/Textarea.vue +5 -1
  135. package/src/components/ui/time-picker/TimePicker.vue +2 -2
  136. package/src/components/ui/toggle-group/ToggleGroup.vue +1 -1
  137. package/src/components/ui/tooltip/TooltipContent.vue +1 -1
  138. package/src/i18n/de.json +16 -6
  139. package/src/i18n/en.json +16 -6
  140. package/src/i18n/es.json +16 -6
  141. package/src/i18n/fr.json +16 -6
  142. package/src/i18n/it.json +16 -6
  143. package/src/i18n/ja.json +16 -6
  144. package/src/i18n/pt.json +16 -6
  145. package/src/i18n/ru.json +16 -6
  146. package/src/i18n/zh.json +16 -6
  147. package/src/lib/color-tokens.ts +1 -1
  148. package/src/lib/i18n.ts +32 -4
  149. package/src/lib/utils.ts +21 -1
  150. package/src/runtime/ui-vintage-i18n-plugin.ts +11 -1
  151. package/src/styles.css +12 -16
  152. package/skills/ui-vintage/SKILL.md +0 -42
@@ -38,6 +38,15 @@ function getChildren() {
38
38
  return flatten(slots.default?.() ?? []);
39
39
  }
40
40
 
41
+ // Overlap per size, kept clear of the initials so stacked fallbacks stay readable
42
+ const overlapClasses: Record<AvatarSize, string> = {
43
+ xs: '-space-x-1',
44
+ sm: '-space-x-1.5',
45
+ md: '-space-x-2',
46
+ lg: '-space-x-2.5',
47
+ xl: '-space-x-3'
48
+ };
49
+
41
50
  // Whether a max limit applies
42
51
  function hasMax() {
43
52
  return props.max !== undefined && props.max >= 0;
@@ -60,15 +69,16 @@ const VNodeRenderer = (renderProps: { node: VNode }) => renderProps.node;
60
69
 
61
70
  <template>
62
71
  <div data-slot="avatar-group" :class="cn(
63
- 'flex items-center -space-x-2 hover:-space-x-0.5',
64
- '*:ring-2 *:ring-background *:transition-[margin,translate] *:duration-380 *:ease-spring *:hover:-translate-y-0.5',
72
+ 'flex items-center',
73
+ overlapClasses[props.size],
74
+ '*:ring-2 *:ring-background',
65
75
  props.class
66
76
  )">
67
77
  <!-- Visible avatars -->
68
78
  <VNodeRenderer v-for="(node, index) in getVisible()" :key="node.key ?? index" :node="node" />
69
79
 
70
80
  <!-- Overflow bubble -->
71
- <span v-if="getOverflow() > 0" data-slot="avatar-group-overflow" :class="cn('relative inline-flex shrink-0 select-none items-center justify-center bg-surface font-bold text-muted-foreground', avatarSizeClasses[props.size], avatarShapeClasses[props.shape], avatarTextClasses[props.size])">
81
+ <span v-if="getOverflow() > 0" data-slot="avatar-group-overflow" :class="cn('relative inline-flex shrink-0 select-none items-center justify-center bg-surface font-semibold text-muted-foreground', avatarSizeClasses[props.size], avatarShapeClasses[props.shape], avatarTextClasses[props.size])">
72
82
  +{{ getOverflow() }}
73
83
  </span>
74
84
  </div>
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import type { HTMLAttributes } from 'vue';
3
- import { MoreHorizontalIcon } from '@hugeicons/core-free-icons';
3
+ import MoreHorizontalIcon from '@hugeicons/core-free-icons/MoreHorizontalIcon';
4
4
  import { HugeiconsIcon } from '@hugeicons/vue';
5
5
  import { useI18n } from 'vue-i18n';
6
6
  import { cn } from '../../../lib/utils';
@@ -14,10 +14,10 @@ const { t } = useI18n();
14
14
  </script>
15
15
 
16
16
  <template>
17
- <span data-slot="breadcrumb-ellipsis" role="presentation" aria-hidden="true" :class="cn('flex size-6 items-center justify-center rounded-sm transition-colors duration-150 hover:bg-accent hover:text-primary', props.class)">
17
+ <span data-slot="breadcrumb-ellipsis" role="presentation" :class="cn('flex size-6 items-center justify-center rounded-sm transition-colors duration-150 hover:bg-accent hover:text-primary', props.class)">
18
18
  <!-- Default slot content -->
19
19
  <slot>
20
- <HugeiconsIcon :icon="MoreHorizontalIcon" class="size-4" />
20
+ <HugeiconsIcon :icon="MoreHorizontalIcon" class="size-4" aria-hidden="true" />
21
21
  </slot>
22
22
 
23
23
  <!-- Screen reader text for accessibility -->
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, type HTMLAttributes } from 'vue';
3
- import { getUiVintageRuntimeMessage } from '../../../lib/i18n';
3
+ import { useUiVintageMessage } from '../../../lib/i18n';
4
4
  import { cn } from '../../../lib/utils';
5
5
  import { Spinner } from '../spinner';
6
6
 
@@ -14,7 +14,8 @@ const props = withDefaults(defineProps<{
14
14
  show: false
15
15
  });
16
16
 
17
- const resolvedLabel = computed(() => props.label || getUiVintageRuntimeMessage('uiVintage.common.loading.title', 'Loading...'));
17
+ const message = useUiVintageMessage();
18
+ const resolvedLabel = computed(() => props.label || message('uiVintage.common.loading.title', 'Loading...'));
18
19
  </script>
19
20
 
20
21
  <template>
@@ -28,11 +29,8 @@ const resolvedLabel = computed(() => props.label || getUiVintageRuntimeMessage('
28
29
  props.class,
29
30
  )">
30
31
  <slot>
31
- <!-- Spinner with a soft glow -->
32
- <div class="relative flex items-center justify-center">
33
- <span class="absolute size-14 rounded-full bg-primary/15 blur-md" />
34
- <Spinner data-slot="busy-spinner" size="xl" class="text-primary" />
35
- </div>
32
+ <!-- Spinner (decorative: the container already announces the label) -->
33
+ <Spinner data-slot="busy-spinner" size="xl" aria-hidden="true" class="text-primary" />
36
34
 
37
35
  <!-- Label -->
38
36
  <span v-if="resolvedLabel" data-slot="busy-label" class="text-xs font-semibold text-foreground sm:text-sm">
@@ -12,15 +12,17 @@ const props = withDefaults(defineProps<PrimitiveProps & {
12
12
  variant?: ButtonVariants['variant'];
13
13
  size?: ButtonVariants['size'];
14
14
  loading?: boolean;
15
+ disabled?: boolean;
15
16
  class?: HTMLAttributes['class'];
16
17
  }>(), {
17
18
  as: 'button',
18
- loading: false
19
+ loading: false,
20
+ disabled: false
19
21
  });
20
22
  </script>
21
23
 
22
24
  <template>
23
- <Primitive data-slot="button" :data-variant="variant" :data-size="size" :data-loading="props.loading ? '' : undefined" :aria-busy="props.loading || undefined" :disabled="props.loading || undefined" :as="as" :as-child="asChild" :class="cn(buttonVariants({ variant, size }), props.class)">
25
+ <Primitive data-slot="button" :data-variant="variant" :data-size="size" :data-loading="props.loading ? '' : undefined" :aria-busy="props.loading || undefined" :disabled="props.disabled || props.loading || undefined" :as="as" :as-child="asChild" :class="cn(buttonVariants({ variant, size }), props.class)">
24
26
  <!-- As-child buttons delegate rendering to the slotted element -->
25
27
  <slot v-if="asChild" />
26
28
 
@@ -28,9 +28,6 @@ export type ButtonVariants = {
28
28
  | undefined;
29
29
  };
30
30
 
31
- // Sheen sweep that crosses solid buttons on hover
32
- const sheenClasses = "before:pointer-events-none before:absolute before:inset-0 before:-z-10 before:-translate-x-full before:bg-[linear-gradient(105deg,transparent_35%,rgb(255_255_255/0.38)_50%,transparent_65%)] before:transition-transform before:duration-700 before:ease-out-expo hover:before:translate-x-full";
33
-
34
31
  export const buttonVariants: (props?: ButtonVariants & ClassProp) => string = cva(
35
32
  [
36
33
  "relative isolate inline-flex shrink-0 cursor-pointer select-none items-center justify-center gap-2 overflow-hidden whitespace-nowrap rounded text-xs font-semibold outline-none sm:text-sm",
@@ -43,7 +40,7 @@ export const buttonVariants: (props?: ButtonVariants & ClassProp) => string = cv
43
40
  {
44
41
  variants: {
45
42
  variant: {
46
- primary: `${getButtonVariantClasses('primary')} ${sheenClasses}`,
43
+ primary: getButtonVariantClasses('primary'),
47
44
  secondary: getButtonVariantClasses('secondary'),
48
45
  ghost: getButtonVariantClasses('ghost'),
49
46
  link: getButtonVariantClasses('link'),
@@ -6,7 +6,8 @@ import { getLocalTimeZone, today } from '@internationalized/date';
6
6
  import { createReusableTemplate, reactiveOmit, useVModel } from '@vueuse/core';
7
7
  import { CalendarRoot, useDateFormatter, useForwardPropsEmits } from 'reka-ui';
8
8
  import { createYear, createYearRange, toDate } from 'reka-ui/date';
9
- import { computed, toRaw } from 'vue';
9
+ import { computed, toRaw, watch } from 'vue';
10
+ import { useI18n } from 'vue-i18n';
10
11
  import { cn } from '../../../lib/utils';
11
12
  import { NativeSelect, NativeSelectOption } from '../native-select';
12
13
  import {
@@ -43,7 +44,11 @@ const placeholder = useVModel(props, 'placeholder', emits, {
43
44
  defaultValue: props.defaultPlaceholder ?? today(getLocalTimeZone()),
44
45
  }) as Ref<DateValue>;
45
46
 
46
- const formatter = useDateFormatter(props.locale ?? 'en');
47
+ // Follow the app language unless a locale is passed explicitly
48
+ const { t, locale: appLocale } = useI18n();
49
+ const resolvedLocale = computed(() => props.locale || appLocale.value || 'en');
50
+ const formatter = useDateFormatter(resolvedLocale.value);
51
+ watch(resolvedLocale, value => formatter.setLocale(value));
47
52
 
48
53
  const yearRange = computed(() => {
49
54
  return (
@@ -81,11 +86,11 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
81
86
  <template>
82
87
  <DefineMonthTemplate v-slot="{ date }">
83
88
  <div class="**:data-[slot=native-select-icon]:right-1">
84
- <div class="relative h-10 rounded border border-(--border-light) bg-(--button-secondary-light) transition-all duration-300 ease-in-out hover:border-(--border-hover-light) focus-within:border-(--border-hover-light) dark:border-(--border-dark) dark:bg-(--button-secondary-dark) dark:hover:border-(--border-hover-dark) dark:focus-within:border-(--border-hover-dark)">
89
+ <div class="relative h-10 rounded border border-border bg-secondary transition-all duration-300 ease-in-out hover:border-border-strong focus-within:border-border-strong">
85
90
  <div class="absolute inset-0 z-10 flex h-full items-center text-sm pl-2 pointer-events-none">
86
91
  {{ formatter.custom(toDate(date), { month: 'short' }) }}
87
92
  </div>
88
- <NativeSelect class="relative z-20 h-full min-h-0 w-23 rounded-none border-0 bg-transparent pl-2 pr-6 text-xs font-semibold text-transparent shadow-none transition-all duration-300 ease-in-out outline-none ring-0 focus-visible:border-0 focus-visible:ring-0 dark:bg-transparent sm:text-sm" :model-value="date.month" @change="
93
+ <NativeSelect :aria-label="t('uiVintage.datePicker.month')" class="relative z-20 h-full min-h-0 w-23 rounded-none border-0 bg-transparent pl-2 pr-6 text-xs font-semibold text-transparent shadow-none transition-all duration-300 ease-in-out outline-none ring-0 focus-visible:border-0 focus-visible:ring-0 dark:bg-transparent sm:text-sm" :model-value="date.month" @change="
89
94
  (e: Event) => {
90
95
  placeholder = placeholder.set({
91
96
  month: Number((e?.target as any)?.value),
@@ -104,11 +109,11 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
104
109
 
105
110
  <DefineYearTemplate v-slot="{ date }">
106
111
  <div class="**:data-[slot=native-select-icon]:right-1">
107
- <div class="relative h-10 rounded border border-(--border-light) bg-(--button-secondary-light) transition-all duration-300 ease-in-out hover:border-(--border-hover-light) focus-within:border-(--border-hover-light) dark:border-(--border-dark) dark:bg-(--button-secondary-dark) dark:hover:border-(--border-hover-dark) dark:focus-within:border-(--border-hover-dark)">
112
+ <div class="relative h-10 rounded border border-border bg-secondary transition-all duration-300 ease-in-out hover:border-border-strong focus-within:border-border-strong">
108
113
  <div class="absolute inset-0 z-10 flex h-full items-center text-sm pl-2 pointer-events-none">
109
114
  {{ formatter.custom(toDate(date), { year: 'numeric' }) }}
110
115
  </div>
111
- <NativeSelect class="relative z-20 h-full min-h-0 w-24 rounded-none border-0 bg-transparent pl-2 pr-6 text-xs font-semibold text-transparent shadow-none transition-all duration-300 ease-in-out outline-none ring-0 focus-visible:border-0 focus-visible:ring-0 dark:bg-transparent sm:text-sm" :model-value="date.year" @change="
116
+ <NativeSelect :aria-label="t('uiVintage.datePicker.year')" class="relative z-20 h-full min-h-0 w-24 rounded-none border-0 bg-transparent pl-2 pr-6 text-xs font-semibold text-transparent shadow-none transition-all duration-300 ease-in-out outline-none ring-0 focus-visible:border-0 focus-visible:ring-0 dark:bg-transparent sm:text-sm" :model-value="date.year" @change="
112
117
  (e: Event) => {
113
118
  placeholder = placeholder.set({
114
119
  year: Number((e?.target as any)?.value),
@@ -125,7 +130,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
125
130
  </div>
126
131
  </DefineYearTemplate>
127
132
 
128
- <CalendarRoot v-slot="{ grid, weekDays, date }" v-bind="forwarded" v-model:placeholder="placeholder" data-slot="calendar" :class="cn('p-3', props.class)">
133
+ <CalendarRoot v-slot="{ grid, weekDays, date }" v-bind="forwarded" v-model:placeholder="placeholder" :locale="resolvedLocale" data-slot="calendar" :class="cn('p-3', props.class)">
129
134
  <CalendarHeader class="pt-0">
130
135
  <nav class="flex items-center gap-1 absolute top-0 inset-x-0 justify-between">
131
136
  <CalendarPrevButton>
@@ -15,7 +15,7 @@ const forwardedProps = useForwardProps(delegatedProps);
15
15
 
16
16
  <template>
17
17
  <CalendarCell data-slot="calendar-cell" :class="cn(
18
- 'relative p-0 text-center text-sm focus-within:relative focus-within:z-20 flex-1 [&:has([data-selected])]:rounded-md',
18
+ 'relative p-0 text-center text-sm focus-within:relative focus-within:z-20 flex-1 [&:has([data-selected])]:rounded',
19
19
  props.class,
20
20
  )
21
21
  " v-bind="forwardedProps">
@@ -26,7 +26,7 @@ const forwardedProps = useForwardProps(delegatedProps);
26
26
  // Disabled
27
27
  'data-disabled:text-muted-foreground data-disabled:opacity-50',
28
28
  // Unavailable
29
- 'data-unavailable:text-destructive-foreground data-unavailable:line-through',
29
+ 'data-unavailable:text-destructive data-unavailable:line-through',
30
30
  // Outside months
31
31
  'data-outside-view:text-muted-foreground',
32
32
  props.class,
@@ -15,7 +15,7 @@ const forwardedProps = useForwardProps(delegatedProps);
15
15
 
16
16
  <template>
17
17
  <CalendarHeadCell data-slot="calendar-head-cell" :class="cn(
18
- 'text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem]',
18
+ 'text-muted-foreground rounded flex-1 font-normal text-[0.8rem]',
19
19
  props.class,
20
20
  )
21
21
  " v-bind="forwardedProps">
@@ -2,7 +2,7 @@
2
2
  import type { CalendarNextProps } from 'reka-ui';
3
3
  import type { HTMLAttributes } from 'vue';
4
4
  import { reactiveOmit } from '@vueuse/core';
5
- import { ArrowRight01Icon } from '@hugeicons/core-free-icons';
5
+ import ArrowRight01Icon from '@hugeicons/core-free-icons/ArrowRight01Icon';
6
6
  import { HugeiconsIcon } from '@hugeicons/vue';
7
7
  import { CalendarNext, useForwardProps } from 'reka-ui';
8
8
  import { cn } from '../../../lib/utils';
@@ -22,7 +22,7 @@ const forwardedProps = useForwardProps(delegatedProps);
22
22
  :class="
23
23
  cn(
24
24
  buttonVariants({ variant: 'secondary' }),
25
- 'size-7 min-h-10 bg-transparent p-0 text-(--text-secondary-light) hover:text-(--text-primary-light) dark:text-(--text-secondary-dark) dark:hover:text-(--text-primary-dark)',
25
+ 'size-7 min-h-10 bg-transparent p-0 text-muted-foreground hover:text-foreground',
26
26
  props.class,
27
27
  )
28
28
  "
@@ -2,7 +2,7 @@
2
2
  import type { CalendarPrevProps } from 'reka-ui';
3
3
  import type { HTMLAttributes } from 'vue';
4
4
  import { reactiveOmit } from '@vueuse/core';
5
- import { ArrowLeft01Icon } from '@hugeicons/core-free-icons';
5
+ import ArrowLeft01Icon from '@hugeicons/core-free-icons/ArrowLeft01Icon';
6
6
  import { HugeiconsIcon } from '@hugeicons/vue';
7
7
  import { CalendarPrev, useForwardProps } from 'reka-ui';
8
8
  import { cn } from '../../../lib/utils';
@@ -22,7 +22,7 @@ const forwardedProps = useForwardProps(delegatedProps);
22
22
  :class="
23
23
  cn(
24
24
  buttonVariants({ variant: 'secondary' }),
25
- 'size-7 min-h-10 bg-transparent p-0 text-(--text-secondary-light) hover:text-(--text-primary-light) dark:text-(--text-secondary-dark) dark:hover:text-(--text-primary-dark)',
25
+ 'size-7 min-h-10 bg-transparent p-0 text-muted-foreground hover:text-foreground',
26
26
  props.class,
27
27
  )
28
28
  "
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { computed, ref } from 'vue';
2
+ import { computed } from 'vue';
3
3
  import type { HTMLAttributes } from 'vue';
4
4
  import {
5
5
  getSurfaceToneClasses,
@@ -18,8 +18,6 @@ const props = withDefaults(defineProps<{
18
18
  interactive: false
19
19
  });
20
20
 
21
- const cardRef = ref<HTMLElement | null>(null);
22
-
23
21
  // Get the appropriate classes
24
22
  const colorClasses = computed(() => {
25
23
  // If color is not specified or is "default", use the default card styles
@@ -30,23 +28,15 @@ const colorClasses = computed(() => {
30
28
  // For other colors, use the corresponding surface tone classes
31
29
  return getSurfaceToneClasses(props.color);
32
30
  });
33
-
34
- // Track the pointer so interactive cards can render a spotlight under the cursor
35
- const handlePointerMove = (event: PointerEvent) => {
36
- if (!props.interactive || !cardRef.value) return;
37
- const rect = cardRef.value.getBoundingClientRect();
38
- cardRef.value.style.setProperty('--uv-spot-x', `${event.clientX - rect.left}px`);
39
- cardRef.value.style.setProperty('--uv-spot-y', `${event.clientY - rect.top}px`);
40
- };
41
31
  </script>
42
32
 
43
33
  <template>
44
- <div ref="cardRef" data-slot="card" :data-interactive="props.interactive ? '' : undefined" :class="cn(
45
- 'relative flex flex-col gap-4 sm:gap-6! rounded py-6 transition-[border-color,box-shadow,background-color,translate] duration-300 ease-out-expo',
34
+ <div data-slot="card" :data-interactive="props.interactive ? '' : undefined" :class="cn(
35
+ 'relative flex flex-col gap-4 sm:gap-6 rounded py-6 transition-[border-color,box-shadow,background-color,translate] duration-300 ease-out-expo',
46
36
  colorClasses,
47
- props.interactive && 'cursor-pointer hover:-translate-y-0.5 hover:border-primary/25 hover:shadow-elevated-lg active:translate-y-0 active:duration-100 before:pointer-events-none before:absolute before:inset-0 before:rounded-[inherit] before:opacity-0 before:transition-opacity before:duration-300 before:bg-[radial-gradient(360px_circle_at_var(--uv-spot-x,50%)_var(--uv-spot-y,50%),color-mix(in_oklab,var(--primary)_9%,transparent),transparent_70%)] hover:before:opacity-100',
37
+ props.interactive && 'cursor-pointer hover:-translate-y-0.5 hover:shadow-elevated-md active:translate-y-0 active:duration-100',
48
38
  props.class,
49
- )" @pointermove="handlePointerMove">
39
+ )">
50
40
  <slot />
51
41
  </div>
52
42
  </template>
@@ -4,6 +4,7 @@ import type { HTMLAttributes } from 'vue';
4
4
  import { reactiveOmit } from '@vueuse/core';
5
5
  import { CheckboxIndicator, CheckboxRoot, useForwardPropsEmits } from 'reka-ui';
6
6
  import { cn } from '../../../lib/utils';
7
+ import { useFieldControlId } from '../field/field-context';
7
8
 
8
9
  // Props
9
10
  const props = defineProps<CheckboxRootProps & {
@@ -15,10 +16,13 @@ const emits = defineEmits<CheckboxRootEmits>();
15
16
 
16
17
  const delegatedProps = reactiveOmit(props, 'class');
17
18
  const forwarded = useForwardPropsEmits(delegatedProps, emits);
19
+
20
+ // Let a surrounding Field label point to this control
21
+ const fieldControlId = useFieldControlId(() => props.id);
18
22
  </script>
19
23
 
20
24
  <template>
21
- <CheckboxRoot v-slot="slotProps" data-slot="checkbox" v-bind="forwarded" :class="cn(
25
+ <CheckboxRoot v-slot="slotProps" data-slot="checkbox" v-bind="forwarded" :id="fieldControlId" :class="cn(
22
26
  'group/checkbox peer relative inline-flex size-5 shrink-0 cursor-pointer items-center justify-center rounded-sm border border-input bg-secondary text-primary-foreground shadow-elevated-sm outline-none',
23
27
  '[transition:scale_320ms_var(--ease-spring),background-color_150ms_var(--ease-snappy),border-color_150ms_var(--ease-snappy),box-shadow_150ms_var(--ease-snappy)]',
24
28
  'hover:border-border-strong active:scale-[0.86] focus-visible:ring-[3px] focus-visible:ring-ring/45',
@@ -4,6 +4,7 @@ import type { HTMLAttributes } from 'vue';
4
4
  import { reactiveOmit } from '@vueuse/core';
5
5
  import { ComboboxInput, useForwardPropsEmits } from 'reka-ui';
6
6
  import { cn } from '../../../lib/utils';
7
+ import { useFieldControlId } from '../field/field-context';
7
8
 
8
9
  defineOptions({
9
10
  inheritAttrs: false
@@ -17,10 +18,13 @@ const emits = defineEmits<ComboboxInputEmits>();
17
18
 
18
19
  const delegatedProps = reactiveOmit(props, 'class');
19
20
  const forwarded = useForwardPropsEmits(delegatedProps, emits);
21
+
22
+ // Let a surrounding Field label point to this control
23
+ const fieldControlId = useFieldControlId();
20
24
  </script>
21
25
 
22
26
  <template>
23
- <ComboboxInput data-slot="combobox-input" v-bind="{ ...$attrs, ...forwarded }" :class="cn(
27
+ <ComboboxInput :id="fieldControlId" data-slot="combobox-input" v-bind="{ ...$attrs, ...forwarded }" :class="cn(
24
28
  'h-9 min-w-16 flex-1 truncate bg-transparent font-semibold text-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed',
25
29
  props.class
26
30
  )
@@ -2,7 +2,7 @@
2
2
  import type { ComboboxItemEmits, ComboboxItemProps } from 'reka-ui';
3
3
  import type { HTMLAttributes } from 'vue';
4
4
  import { reactiveOmit } from '@vueuse/core';
5
- import { Tick02Icon } from '@hugeicons/core-free-icons';
5
+ import Tick02Icon from '@hugeicons/core-free-icons/Tick02Icon';
6
6
  import { HugeiconsIcon } from '@hugeicons/vue';
7
7
  import { ComboboxItem, ComboboxItemIndicator, useForwardPropsEmits } from 'reka-ui';
8
8
  import { cn } from '../../../lib/utils';
@@ -2,7 +2,8 @@
2
2
  import type { HTMLAttributes } from 'vue';
3
3
  import type { AcceptableValue } from 'reka-ui';
4
4
  import { computed, ref } from 'vue';
5
- import { Cancel01Icon, Search01Icon } from '@hugeicons/core-free-icons';
5
+ import Cancel01Icon from '@hugeicons/core-free-icons/Cancel01Icon';
6
+ import Search01Icon from '@hugeicons/core-free-icons/Search01Icon';
6
7
  import { HugeiconsIcon } from '@hugeicons/vue';
7
8
  import { useI18n } from 'vue-i18n';
8
9
  import { cn } from '../../../lib/utils';
@@ -2,7 +2,7 @@
2
2
  import type { ComboboxTriggerProps } from 'reka-ui';
3
3
  import type { HTMLAttributes } from 'vue';
4
4
  import { reactiveOmit } from '@vueuse/core';
5
- import { ArrowDown01Icon } from '@hugeicons/core-free-icons';
5
+ import ArrowDown01Icon from '@hugeicons/core-free-icons/ArrowDown01Icon';
6
6
  import { HugeiconsIcon } from '@hugeicons/vue';
7
7
  import { ComboboxTrigger, useForwardProps } from 'reka-ui';
8
8
  import { cn } from '../../../lib/utils';
@@ -11,10 +11,24 @@ import { provideCommandGroupContext, useCommand } from '.';
11
11
  const props = defineProps<ListboxGroupProps & {
12
12
  class?: HTMLAttributes['class'];
13
13
  heading?: string;
14
+ stagger?: boolean;
14
15
  }>();
15
16
 
16
- // Omit local class from delegated props
17
- const delegatedProps = reactiveOmit(props, 'class');
17
+ // Omit local props from delegated props
18
+ const delegatedProps = reactiveOmit(props, 'class', 'stagger');
19
+
20
+ // Items float in one after another when the group mounts (same timing as the dropdown menu)
21
+ const staggerClass = [
22
+ '[&>*]:animate-[uv-float-in_240ms_var(--ease-out-expo)_both] [&>*]:[--uv-float-offset:-3px]',
23
+ '[&>*:nth-child(2)]:[animation-delay:16ms]',
24
+ '[&>*:nth-child(3)]:[animation-delay:32ms]',
25
+ '[&>*:nth-child(4)]:[animation-delay:48ms]',
26
+ '[&>*:nth-child(5)]:[animation-delay:64ms]',
27
+ '[&>*:nth-child(6)]:[animation-delay:80ms]',
28
+ '[&>*:nth-child(7)]:[animation-delay:96ms]',
29
+ '[&>*:nth-child(8)]:[animation-delay:112ms]',
30
+ '[&>*:nth-child(n+9)]:[animation-delay:128ms]'
31
+ ].join(' ');
18
32
 
19
33
  const { allGroups, filterState } = useCommand();
20
34
  const id = useId();
@@ -37,7 +51,7 @@ onUnmounted(() => {
37
51
  <ListboxGroupLabel v-if="heading" data-slot="command-group-heading" class="border-b border-border px-3 py-2 text-[11px] font-semibold uppercase tracking-[0.16em] text-muted-foreground">
38
52
  {{ heading }}
39
53
  </ListboxGroupLabel>
40
- <div class="p-2">
54
+ <div :class="cn('p-2', props.stagger && staggerClass)">
41
55
  <slot />
42
56
  </div>
43
57
  </ListboxGroup>
@@ -2,7 +2,7 @@
2
2
  import type { ListboxFilterProps } from 'reka-ui';
3
3
  import type { HTMLAttributes } from 'vue';
4
4
  import { reactiveOmit } from '@vueuse/core';
5
- import { Search01Icon } from '@hugeicons/core-free-icons';
5
+ import Search01Icon from '@hugeicons/core-free-icons/Search01Icon';
6
6
  import { HugeiconsIcon } from '@hugeicons/vue';
7
7
  import { ListboxFilter, useForwardProps } from 'reka-ui';
8
8
  import { cn } from '../../../lib/utils';
@@ -27,7 +27,7 @@ const { filterState } = useCommand();
27
27
  <div data-slot="command-input-wrapper" class="flex h-12 items-center gap-3 border-b border-border px-4">
28
28
  <HugeiconsIcon :icon="Search01Icon" class="size-4 shrink-0 text-muted-foreground" />
29
29
  <ListboxFilter v-bind="{ ...forwardedProps, ...$attrs }" v-model="filterState.search" data-slot="command-input" auto-focus :class="cn(
30
- 'placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50',
30
+ 'placeholder:text-muted-foreground flex h-10 w-full rounded bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50',
31
31
  props.class,
32
32
  )
33
33
  " />
@@ -3,6 +3,7 @@ import type { ListboxContentProps } from 'reka-ui';
3
3
  import type { HTMLAttributes } from 'vue';
4
4
  import { reactiveOmit } from '@vueuse/core';
5
5
  import { ListboxContent, useForwardProps } from 'reka-ui';
6
+ import { useI18n } from 'vue-i18n';
6
7
  import { cn } from '../../../lib/utils';
7
8
 
8
9
  // Props
@@ -13,10 +14,13 @@ const delegatedProps = reactiveOmit(props, 'class');
13
14
 
14
15
  // Forward props
15
16
  const forwarded = useForwardProps(delegatedProps);
17
+
18
+ // Name the list of results for screen readers (an aria-label passed by the app wins)
19
+ const { t } = useI18n();
16
20
  </script>
17
21
 
18
22
  <template>
19
- <ListboxContent data-slot="command-list" v-bind="forwarded" :class="cn(
23
+ <ListboxContent data-slot="command-list" :aria-label="t('uiVintage.commandDialog.title')" v-bind="forwarded" :class="cn(
20
24
  'max-h-75 scroll-py-1 overflow-x-hidden overflow-y-auto',
21
25
  props.class,
22
26
  )
@@ -2,7 +2,8 @@
2
2
  import type { HTMLAttributes } from 'vue';
3
3
  import { computed } from 'vue';
4
4
  import { useI18n } from 'vue-i18n';
5
- import { ArrowUp01Icon, PackageIcon } from '@hugeicons/core-free-icons';
5
+ import ArrowUp01Icon from '@hugeicons/core-free-icons/ArrowUp01Icon';
6
+ import PackageIcon from '@hugeicons/core-free-icons/PackageIcon';
6
7
  import { HugeiconsIcon } from '@hugeicons/vue';
7
8
  import { cn } from '../../../lib/utils';
8
9
  import { Checkbox } from '../checkbox';
@@ -41,7 +42,7 @@ const props = withDefaults(defineProps<{
41
42
  const emits = defineEmits<{
42
43
  'update:selected': [value: PropertyKey[]];
43
44
  'update:sort': [value: DataTableSort | null];
44
- 'row-click': [row: T, event: MouseEvent];
45
+ 'row-click': [row: T, event: MouseEvent | KeyboardEvent];
45
46
  }>();
46
47
 
47
48
  // Slots
@@ -132,6 +133,14 @@ const handleRowClick = (row: T, event: MouseEvent) => {
132
133
  if ((event.target as HTMLElement).closest('button, a, input, [role=checkbox]')) return;
133
134
  emits('row-click', row, event);
134
135
  };
136
+
137
+ // Let keyboard users activate a focused clickable row with Enter or Space
138
+ const handleRowKeydown = (row: T, event: KeyboardEvent) => {
139
+ if (!props.rowClickable || event.target !== event.currentTarget) return;
140
+ if (event.key !== 'Enter' && event.key !== ' ') return;
141
+ event.preventDefault();
142
+ emits('row-click', row, event);
143
+ };
135
144
  </script>
136
145
 
137
146
  <template>
@@ -178,7 +187,7 @@ const handleRowClick = (row: T, event: MouseEvent) => {
178
187
  <td :colspan="columnCount" class="px-4 py-12">
179
188
  <slot name="empty">
180
189
  <div class="flex flex-col items-center gap-2 text-center text-muted-foreground">
181
- <HugeiconsIcon :icon="PackageIcon" class="size-8 animate-uv-float opacity-40" />
190
+ <HugeiconsIcon :icon="PackageIcon" class="size-8 opacity-40" />
182
191
  <span class="text-xs sm:text-sm">{{ resolvedEmptyText }}</span>
183
192
  </div>
184
193
  </slot>
@@ -188,7 +197,7 @@ const handleRowClick = (row: T, event: MouseEvent) => {
188
197
 
189
198
  <!-- Rows (animated when sorted, added, or removed) -->
190
199
  <TransitionGroup v-else tag="tbody" name="uv-data-table-row">
191
- <tr v-for="(row, index) in sortedRows" :key="getRowKey(row)" :data-state="selectedSet.has(getRowKey(row)) ? 'selected' : undefined" :class="cn('border-b border-border transition-colors duration-150 last:border-b-0 hover:bg-surface/70 data-[state=selected]:bg-primary/5', props.rowClickable && 'cursor-pointer')" @click="handleRowClick(row, $event)">
200
+ <tr v-for="(row, index) in sortedRows" :key="getRowKey(row)" :data-state="selectedSet.has(getRowKey(row)) ? 'selected' : undefined" :class="cn('border-b border-border transition-colors duration-150 last:border-b-0 hover:bg-surface/70 data-[state=selected]:bg-primary/5', props.rowClickable && 'cursor-pointer focus-visible:bg-surface/70 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring')" :tabindex="props.rowClickable ? 0 : undefined" @click="handleRowClick(row, $event)" @keydown="handleRowKeydown(row, $event)">
192
201
  <!-- Row selection -->
193
202
  <td v-if="props.selectable" class="px-4 py-3">
194
203
  <Checkbox :model-value="selectedSet.has(getRowKey(row))" :aria-label="t('uiVintage.dataTable.selectRow')" @update:model-value="toggleRow(row)" />
@@ -1,12 +1,13 @@
1
1
  <script setup lang="ts">
2
2
  import type { HTMLAttributes } from 'vue';
3
3
  import { DateFormatter, getLocalTimeZone, today, } from '@internationalized/date';
4
- import { Calendar03Icon } from '@hugeicons/core-free-icons';
4
+ import Calendar03Icon from '@hugeicons/core-free-icons/Calendar03Icon';
5
5
  import { HugeiconsIcon } from '@hugeicons/vue';
6
6
  import type { DateValue } from 'reka-ui';
7
7
  import { computed, ref } from 'vue';
8
8
  import { useI18n } from 'vue-i18n';
9
9
  import { cn } from '../../../lib/utils';
10
+ import { useFieldControlId, useFieldLabelId } from '../field/field-context';
10
11
  import { Button } from '../button';
11
12
  import { Calendar } from '../calendar';
12
13
  import { Popover, PopoverContent, PopoverTrigger } from '../popover';
@@ -52,13 +53,18 @@ const defaultPlaceholder = computed(
52
53
  const formatter = computed(() => new DateFormatter(locale.value || 'en-US', {
53
54
  dateStyle: 'long',
54
55
  }));
56
+
57
+ // Let a surrounding Field label point to the trigger, and name it "label + selected date" so the value is not lost
58
+ const fieldControlId = useFieldControlId();
59
+ const fieldLabelId = useFieldLabelId();
60
+ const triggerLabelledBy = computed(() => fieldLabelId.value && fieldControlId.value ? `${fieldLabelId.value} ${fieldControlId.value}` : undefined);
55
61
  </script>
56
62
 
57
63
  <template>
58
64
  <Popover v-slot="{ close }">
59
65
  <!-- Date picker trigger -->
60
66
  <PopoverTrigger as-child>
61
- <Button variant="secondary" :disabled="disabled" :class="cn('h-13 w-60 justify-start text-left font-normal enabled:active:scale-100', !date && 'text-muted-foreground', props.class,)">
67
+ <Button :id="fieldControlId" :aria-labelledby="triggerLabelledBy" variant="secondary" :disabled="disabled" :class="cn('h-13 w-60 justify-start text-left font-normal enabled:active:scale-100', !date && 'text-muted-foreground', props.class,)">
62
68
  <HugeiconsIcon :icon="Calendar03Icon" class="size-4" />
63
69
  {{ date ? formatter.format(date.toDate(getLocalTimeZone())) : resolvedPlaceholder }}
64
70
  </Button>
@@ -3,6 +3,7 @@ import type { HTMLAttributes } from 'vue';
3
3
  import { fromDate, getLocalTimeZone, toCalendarDate, } from '@internationalized/date';
4
4
  import type { DateValue } from 'reka-ui';
5
5
  import { computed, ref } from 'vue';
6
+ import { useI18n } from 'vue-i18n';
6
7
  import { cn } from '../../../lib/utils';
7
8
  import { DatePicker } from '../date-picker';
8
9
  import { TimePicker } from '../time-picker';
@@ -154,6 +155,9 @@ const maxTimeValue = computed(() => {
154
155
  ? toTimeString(props.maxDate)
155
156
  : undefined;
156
157
  });
158
+
159
+ // The time input sits next to the date button, which takes the field label
160
+ const { t } = useI18n();
157
161
  </script>
158
162
 
159
163
  <template>
@@ -163,7 +167,7 @@ const maxTimeValue = computed(() => {
163
167
  </div>
164
168
 
165
169
  <div>
166
- <TimePicker v-model="timeValue" :disabled="props.disabled" :step="props.step" :min="minTimeValue" :max="maxTimeValue" :class="cn('w-full', props.timePickerClass)" />
170
+ <TimePicker v-model="timeValue" :aria-label="t('uiVintage.datePicker.time')" :disabled="props.disabled" :step="props.step" :min="minTimeValue" :max="maxTimeValue" :class="cn('w-full', props.timePickerClass)" />
167
171
  </div>
168
172
  </div>
169
173
  </template>
@@ -3,7 +3,7 @@ import type { DialogContentEmits, DialogContentProps } from 'reka-ui';
3
3
  import type { HTMLAttributes } from 'vue';
4
4
  import { reactiveOmit } from '@vueuse/core';
5
5
  import { DialogClose, DialogContent, DialogPortal, useForwardPropsEmits } from 'reka-ui';
6
- import { Cancel01Icon } from '@hugeicons/core-free-icons';
6
+ import Cancel01Icon from '@hugeicons/core-free-icons/Cancel01Icon';
7
7
  import { HugeiconsIcon } from '@hugeicons/vue';
8
8
  import { useI18n } from 'vue-i18n';
9
9
  import { cn } from '../../../lib/utils';
@@ -3,7 +3,7 @@ import type { DialogContentEmits, DialogContentProps } from 'reka-ui';
3
3
  import type { HTMLAttributes } from 'vue';
4
4
  import { reactiveOmit } from '@vueuse/core';
5
5
  import { DialogClose, DialogContent, DialogOverlay, DialogPortal, useForwardPropsEmits } from 'reka-ui';
6
- import { Cancel01Icon } from '@hugeicons/core-free-icons';
6
+ import Cancel01Icon from '@hugeicons/core-free-icons/Cancel01Icon';
7
7
  import { HugeiconsIcon } from '@hugeicons/vue';
8
8
  import { useI18n } from 'vue-i18n';
9
9
  import { cn } from '../../../lib/utils';
@@ -2,7 +2,7 @@
2
2
  import type { DropdownMenuCheckboxItemEmits, DropdownMenuCheckboxItemProps } from 'reka-ui';
3
3
  import type { HTMLAttributes } from 'vue';
4
4
  import { reactiveOmit } from '@vueuse/core';
5
- import { Tick02Icon } from '@hugeicons/core-free-icons';
5
+ import Tick02Icon from '@hugeicons/core-free-icons/Tick02Icon';
6
6
  import { HugeiconsIcon } from '@hugeicons/vue';
7
7
  import { DropdownMenuCheckboxItem, DropdownMenuItemIndicator, useForwardPropsEmits } from 'reka-ui';
8
8
  import { cn } from '../../../lib/utils';
@@ -25,7 +25,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
25
25
  <template>
26
26
  <DropdownMenuItem data-slot="dropdown-menu-item" :data-inset="props.inset ? '' : undefined" :data-variant="props.variant" v-bind="forwarded" :class="cn(
27
27
  dropdownMenuItemClass,
28
- props.variant === 'destructive' && 'text-destructive data-highlighted:bg-destructive/10 data-highlighted:text-destructive',
28
+ props.variant === 'destructive' && 'text-destructive before:bg-destructive data-highlighted:bg-destructive/10 data-highlighted:text-destructive',
29
29
  props.class
30
30
  )
31
31
  ">