@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.
- package/AGENTS.md +41 -0
- package/COMPONENTS.md +89 -18
- package/README.md +296 -262
- package/module.mjs +5 -11
- package/package.json +30 -12
- package/src/components/blog/BlogContentRenderer.vue +1 -1
- package/src/components/blog/BlogFAQSection.vue +1 -1
- package/src/components/blog/BlogHeaderSection.vue +93 -19
- package/src/components/blog/BlogInfoSection.vue +42 -37
- package/src/components/blog/BlogPostHeader.vue +181 -0
- package/src/components/blog/BlogProgressBar.vue +3 -5
- package/src/components/blog/BlogSectionTitle.vue +8 -6
- package/src/components/blog/BlogTagsSection.vue +16 -7
- package/src/components/blog/CategoryCard.vue +19 -23
- package/src/components/blog/HeaderCarousel.vue +17 -7
- package/src/components/blog/PostCard.vue +14 -21
- package/src/components/blog/SocialShareSidebar.vue +32 -15
- package/src/components/blog/TableOfContents.vue +64 -29
- package/src/components/blog/index.ts +1 -0
- package/src/components/card-grid/CardGrid.vue +3 -3
- package/src/components/chip/Chip.vue +1 -1
- package/src/components/confirm-dialog/ConfirmDialog.vue +6 -1
- package/src/components/confirm-dialog/confirm-dialog-state.ts +22 -22
- package/src/components/content/BlogList.vue +29 -52
- package/src/components/content/BlogTable.vue +26 -30
- package/src/components/content/CodeBlock.vue +99 -23
- package/src/components/content/Flow.vue +29 -35
- package/src/components/content/ProseHr.vue +6 -6
- package/src/components/content/ProsePre.vue +9 -4
- package/src/components/content/Terminal.vue +45 -21
- package/src/components/cookie-consent/CookieConsent.vue +1 -1
- package/src/components/dashboard-shell/DashboardShell.vue +24 -7
- package/src/components/empty-state-card/EmptyStateCard.vue +3 -3
- package/src/components/error-page/ErrorPage.vue +4 -4
- package/src/components/info-card/InfoCard.vue +4 -4
- package/src/components/landing/landing-footer/LandingFooter.vue +17 -12
- package/src/components/landing/landing-navbar/LandingNavbar.vue +22 -6
- package/src/components/landing/landing-shell/LandingShell.vue +2 -7
- package/src/components/language-flag/index.ts +1 -0
- package/src/components/language-selector/LanguageSelector.vue +19 -11
- package/src/components/load-more-button/LoadMoreButton.vue +9 -13
- package/src/components/loading-state-card/LoadingStateCard.vue +3 -3
- package/src/components/message-dialog/MessageDialog.vue +14 -12
- package/src/components/message-dialog/message-dialog-state.ts +1 -4
- package/src/components/message-toast/MessageToast.vue +28 -10
- package/src/components/message-toast/message-toast-state.ts +20 -0
- package/src/components/page-header/PageHeader.vue +1 -1
- package/src/components/progress-component/ProgressComponent.vue +7 -3
- package/src/components/single-value-card/SingleValueCard.vue +39 -40
- package/src/components/slider-form-component/SliderFormComponent.vue +1 -1
- package/src/components/stat-strip/StatStrip.vue +36 -0
- package/src/components/stat-strip/index.ts +2 -0
- package/src/components/switch-form-component/SwitchFormComponent.vue +5 -5
- package/src/components/text-link/TextLink.vue +1 -1
- package/src/components/theme-selector/ThemeSelector.vue +6 -3
- package/src/components/theme-selector/theme-transition.ts +15 -3
- package/src/components/ui/accordion/Accordion.vue +19 -19
- package/src/components/ui/alert/AlertTitle.vue +1 -1
- package/src/components/ui/alert/index.ts +1 -1
- package/src/components/ui/avatar/AvatarFallback.vue +2 -1
- package/src/components/ui/avatar/AvatarGroup.vue +13 -3
- package/src/components/ui/breadcrumb/BreadcrumbEllipsis.vue +3 -3
- package/src/components/ui/busy/Busy.vue +5 -7
- package/src/components/ui/button/Button.vue +4 -2
- package/src/components/ui/button/index.ts +1 -4
- package/src/components/ui/calendar/Calendar.vue +12 -7
- package/src/components/ui/calendar/CalendarCell.vue +1 -1
- package/src/components/ui/calendar/CalendarCellTrigger.vue +1 -1
- package/src/components/ui/calendar/CalendarHeadCell.vue +1 -1
- package/src/components/ui/calendar/CalendarNextButton.vue +2 -2
- package/src/components/ui/calendar/CalendarPrevButton.vue +2 -2
- package/src/components/ui/card/Card.vue +5 -15
- package/src/components/ui/checkbox/Checkbox.vue +5 -1
- package/src/components/ui/combobox/ComboboxInput.vue +5 -1
- package/src/components/ui/combobox/ComboboxItem.vue +1 -1
- package/src/components/ui/combobox/ComboboxSelect.vue +2 -1
- package/src/components/ui/combobox/ComboboxTrigger.vue +1 -1
- package/src/components/ui/command/CommandGroup.vue +17 -3
- package/src/components/ui/command/CommandInput.vue +2 -2
- package/src/components/ui/command/CommandList.vue +5 -1
- package/src/components/ui/data-table/DataTable.vue +13 -4
- package/src/components/ui/date-picker/DatePicker.vue +8 -2
- package/src/components/ui/date-time-picker/DateTimePicker.vue +5 -1
- package/src/components/ui/dialog/DialogContent.vue +1 -1
- package/src/components/ui/dialog/DialogScrollContent.vue +1 -1
- package/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +1 -1
- package/src/components/ui/dropdown-menu/DropdownMenuItem.vue +1 -1
- package/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +2 -2
- package/src/components/ui/dropdown-menu/classes.ts +2 -0
- package/src/components/ui/field/Field.vue +4 -0
- package/src/components/ui/field/FieldLabel.vue +6 -2
- package/src/components/ui/field/field-context.ts +62 -0
- package/src/components/ui/field/index.ts +1 -0
- package/src/components/ui/file-dropzone/FileDropzone.vue +14 -5
- package/src/components/ui/input/Input.vue +5 -1
- package/src/components/ui/native-select/NativeSelect.vue +7 -3
- package/src/components/ui/number-field/NumberField.vue +7 -2
- package/src/components/ui/pagination/PaginationEllipsis.vue +1 -1
- package/src/components/ui/pagination/PaginationFirst.vue +1 -1
- package/src/components/ui/pagination/PaginationItem.vue +1 -1
- package/src/components/ui/pagination/PaginationLast.vue +1 -1
- package/src/components/ui/pagination/PaginationNext.vue +1 -1
- package/src/components/ui/pagination/PaginationPrevious.vue +1 -1
- package/src/components/ui/pin-input/PinInputInput.vue +2 -2
- package/src/components/ui/progress/Progress.vue +2 -2
- package/src/components/ui/radio-group/RadioGroupCard.vue +1 -1
- package/src/components/ui/select/SelectItem.vue +2 -2
- package/src/components/ui/select/SelectItemContent.vue +1 -1
- package/src/components/ui/select/SelectScrollDownButton.vue +1 -1
- package/src/components/ui/select/SelectScrollUpButton.vue +1 -1
- package/src/components/ui/select/SelectTrigger.vue +6 -2
- package/src/components/ui/sidebar/Sidebar.vue +1 -1
- package/src/components/ui/sidebar/SidebarGroupAction.vue +1 -1
- package/src/components/ui/sidebar/SidebarGroupLabel.vue +1 -1
- package/src/components/ui/sidebar/SidebarInset.vue +1 -1
- package/src/components/ui/sidebar/SidebarMenuAction.vue +1 -1
- package/src/components/ui/sidebar/SidebarMenuBadge.vue +1 -1
- package/src/components/ui/sidebar/SidebarMenuSkeleton.vue +2 -2
- package/src/components/ui/sidebar/SidebarMenuSubButton.vue +1 -1
- package/src/components/ui/sidebar/SidebarProvider.vue +17 -6
- package/src/components/ui/sidebar/SidebarTrigger.vue +1 -1
- package/src/components/ui/sidebar/index.ts +2 -2
- package/src/components/ui/sidebar/utils.ts +1 -1
- package/src/components/ui/slider/Slider.vue +5 -4
- package/src/components/ui/spinner/Spinner.vue +3 -2
- package/src/components/ui/stepper/SimpleStepper.vue +1 -1
- package/src/components/ui/stepper/StepperDescription.vue +5 -3
- package/src/components/ui/stepper/StepperIndicator.vue +3 -3
- package/src/components/ui/stepper/StepperTitle.vue +5 -3
- package/src/components/ui/switch/Switch.vue +6 -2
- package/src/components/ui/tabs/TabsList.vue +1 -1
- package/src/components/ui/tabs/TabsTrigger.vue +1 -1
- package/src/components/ui/tags-input/TagsInputItemDelete.vue +1 -1
- package/src/components/ui/textarea/Textarea.vue +5 -1
- package/src/components/ui/time-picker/TimePicker.vue +2 -2
- package/src/components/ui/toggle-group/ToggleGroup.vue +1 -1
- package/src/components/ui/tooltip/TooltipContent.vue +1 -1
- package/src/i18n/de.json +16 -6
- package/src/i18n/en.json +16 -6
- package/src/i18n/es.json +16 -6
- package/src/i18n/fr.json +16 -6
- package/src/i18n/it.json +16 -6
- package/src/i18n/ja.json +16 -6
- package/src/i18n/pt.json +16 -6
- package/src/i18n/ru.json +16 -6
- package/src/i18n/zh.json +16 -6
- package/src/lib/color-tokens.ts +1 -1
- package/src/lib/i18n.ts +32 -4
- package/src/lib/utils.ts +21 -1
- package/src/runtime/ui-vintage-i18n-plugin.ts +11 -1
- package/src/styles.css +12 -16
- 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
|
|
64
|
-
|
|
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-
|
|
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
|
|
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"
|
|
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 {
|
|
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
|
|
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
|
|
32
|
-
<
|
|
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:
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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-
|
|
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
|
|
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-
|
|
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
|
|
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
|
|
45
|
-
'relative flex flex-col gap-4 sm:gap-6
|
|
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:
|
|
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
|
-
)"
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
">
|