@brumaombra/ui-vintage 0.9.6 → 0.9.8
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 +3 -1
- package/COMPONENTS.md +132 -15
- package/package.json +4 -2
- package/src/components/action-bar/ActionBar.vue +38 -0
- package/src/components/action-bar/index.ts +2 -0
- package/src/components/blog/BlogContentRenderer.vue +3 -14
- package/src/components/blog/BlogFAQSection.vue +1 -1
- package/src/components/blog/BlogIndexPage.vue +1 -1
- package/src/components/blog/BlogInfoSection.vue +4 -4
- package/src/components/blog/BlogPostHeader.vue +4 -5
- package/src/components/blog/BlogPostPage.vue +6 -2
- package/src/components/blog/BlogSectionTitle.vue +7 -21
- package/src/components/blog/BlogSummary.vue +31 -0
- package/src/components/blog/BlogTagsSection.vue +1 -1
- package/src/components/blog/CategoryCard.vue +1 -1
- package/src/components/blog/PostCard.vue +1 -1
- package/src/components/blog/blogPageUtils.ts +1 -0
- package/src/components/blog/index.ts +1 -0
- package/src/components/chip/Chip.vue +1 -1
- package/src/components/dashboard-shell/DashboardShell.vue +3 -3
- package/src/components/data-list/DataListText.vue +7 -4
- package/src/components/language-flag/flags/ChineseFlag.vue +1 -1
- package/src/components/language-flag/flags/EnglishFlag.vue +1 -1
- package/src/components/language-flag/flags/FrenchFlag.vue +1 -1
- package/src/components/language-flag/flags/GermanFlag.vue +1 -1
- package/src/components/language-flag/flags/ItalianFlag.vue +1 -1
- package/src/components/language-flag/flags/JapaneseFlag.vue +1 -1
- package/src/components/language-flag/flags/PortugueseFlag.vue +1 -1
- package/src/components/language-flag/flags/RussianFlag.vue +1 -1
- package/src/components/language-flag/flags/SpanishFlag.vue +1 -1
- package/src/components/language-selector/LanguageSelector.vue +2 -2
- package/src/components/section-title/SectionTitle.vue +47 -0
- package/src/components/section-title/index.ts +2 -0
- package/src/components/single-value-card/SingleValueCard.vue +1 -1
- package/src/components/ui/alert-dialog/AlertDialogAction.vue +6 -6
- package/src/components/ui/avatar/Avatar.vue +4 -2
- package/src/components/ui/avatar/AvatarFallback.vue +25 -5
- package/src/components/ui/badge/Badge.vue +21 -4
- package/src/components/ui/breadcrumb/BreadcrumbEllipsis.vue +1 -1
- package/src/components/ui/breadcrumb/BreadcrumbLink.vue +1 -1
- package/src/components/ui/button/Button.vue +18 -2
- package/src/components/ui/button/index.ts +4 -0
- package/src/components/ui/card/Card.vue +25 -1
- package/src/components/ui/card/CardMedia.vue +15 -0
- package/src/components/ui/card/CardTitle.vue +8 -3
- package/src/components/ui/card/index.ts +1 -0
- package/src/components/ui/checkbox/Checkbox.vue +1 -1
- package/src/components/ui/combobox/ComboboxSelect.vue +2 -2
- package/src/components/ui/combobox/ComboboxTrigger.vue +1 -1
- package/src/components/ui/data-table/DataTable.vue +1 -1
- package/src/components/ui/dialog/DialogContent.vue +7 -3
- package/src/components/ui/dialog/DialogScrollContent.vue +7 -3
- package/src/components/ui/dialog/index.ts +11 -1
- package/src/components/ui/file-dropzone/FileDropzone.vue +1 -1
- package/src/components/ui/input/Input.vue +42 -9
- package/src/components/ui/kbd/Kbd.vue +8 -4
- package/src/components/ui/number-field/NumberField.vue +2 -2
- package/src/components/ui/progress/Progress.vue +17 -5
- package/src/components/ui/select/SelectItem.vue +1 -1
- package/src/components/ui/select/SelectItemContent.vue +5 -3
- package/src/components/ui/skeleton/Skeleton.vue +7 -3
- package/src/components/ui/switch/Switch.vue +1 -1
- package/src/components/ui/table/TableHeader.vue +1 -1
- package/src/components/ui/table/TableRow.vue +11 -4
- package/src/components/ui/tabs/Tabs.vue +1 -1
- package/src/components/ui/tabs/TabsContent.vue +8 -5
- 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 +1 -1
- package/src/components/ui/toggle-group/ToggleGroup.vue +2 -2
- package/src/components/ui/toggle-group/ToggleGroupItem.vue +3 -1
- package/src/components/ui/toggle-group/index.ts +2 -2
- package/src/i18n/de.json +1 -0
- package/src/i18n/en.json +1 -0
- package/src/i18n/es.json +1 -0
- package/src/i18n/fr.json +1 -0
- package/src/i18n/it.json +1 -0
- package/src/i18n/ja.json +1 -0
- package/src/i18n/pt.json +1 -0
- package/src/i18n/ru.json +1 -0
- package/src/i18n/zh.json +1 -0
- package/src/lib/color-tokens.ts +6 -6
- package/src/styles.css +11 -6
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { HTMLAttributes } from 'vue';
|
|
3
|
+
import { HugeiconsIcon } from '@hugeicons/vue';
|
|
3
4
|
import { cn } from '../../../lib/utils';
|
|
4
|
-
import type { HeadingLevel } from '../../../lib/common-types';
|
|
5
|
+
import type { HeadingLevel, HugeiconsIconDefinition } from '../../../lib/common-types';
|
|
5
6
|
|
|
6
7
|
// Props
|
|
7
8
|
const props = withDefaults(defineProps<{
|
|
8
9
|
class?: HTMLAttributes['class'];
|
|
9
10
|
level?: HeadingLevel;
|
|
11
|
+
icon?: HugeiconsIconDefinition | null;
|
|
10
12
|
}>(), {
|
|
11
|
-
level: 3
|
|
13
|
+
level: 3,
|
|
14
|
+
icon: null
|
|
12
15
|
});
|
|
13
16
|
</script>
|
|
14
17
|
|
|
15
18
|
<template>
|
|
16
19
|
<component :is="`h${props.level}`"
|
|
17
20
|
data-slot="card-title"
|
|
18
|
-
:class="cn('leading-none font-semibold text-sm sm:text-base', props.class)">
|
|
21
|
+
:class="cn('leading-none font-semibold text-sm sm:text-base', props.icon && 'flex items-center gap-2', props.class)">
|
|
22
|
+
<!-- Leading icon -->
|
|
23
|
+
<HugeiconsIcon v-if="props.icon" :icon="props.icon" class="size-4 shrink-0" />
|
|
19
24
|
<slot />
|
|
20
25
|
</component>
|
|
21
26
|
</template>
|
|
@@ -5,4 +5,5 @@ export { default as CardText } from './CardText.vue';
|
|
|
5
5
|
export { default as CardDescription } from './CardDescription.vue';
|
|
6
6
|
export { default as CardFooter } from './CardFooter.vue';
|
|
7
7
|
export { default as CardHeader } from './CardHeader.vue';
|
|
8
|
+
export { default as CardMedia } from './CardMedia.vue';
|
|
8
9
|
export { default as CardTitle } from './CardTitle.vue';
|
|
@@ -23,7 +23,7 @@ const fieldControlId = useFieldControlId(() => props.id);
|
|
|
23
23
|
|
|
24
24
|
<template>
|
|
25
25
|
<CheckboxRoot v-slot="slotProps" data-slot="checkbox" v-bind="forwarded" :id="fieldControlId" :class="cn(
|
|
26
|
-
'group/checkbox peer relative inline-flex size-5 shrink-0 cursor-pointer items-center justify-center rounded
|
|
26
|
+
'group/checkbox peer relative inline-flex size-5 shrink-0 cursor-pointer items-center justify-center rounded border border-input bg-secondary text-primary-foreground shadow-elevated-sm outline-none',
|
|
27
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)]',
|
|
28
28
|
'hover:border-border-strong active:scale-[0.86] focus-visible:ring-[3px] focus-visible:ring-ring/45',
|
|
29
29
|
'data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary',
|
|
@@ -134,10 +134,10 @@ const handleKeydown = (event: KeyboardEvent) => {
|
|
|
134
134
|
<div class="flex min-w-0 flex-1 flex-wrap items-center gap-1.5">
|
|
135
135
|
<!-- Selected tags (multiple mode) -->
|
|
136
136
|
<TransitionGroup v-if="props.multiple" leave-active-class="transition-[opacity,scale] duration-150 ease-snappy" leave-to-class="scale-75 opacity-0">
|
|
137
|
-
<span v-for="option in selectedOptions" :key="option.value" data-slot="combobox-select-tag" class="inline-flex h-7 max-w-full animate-uv-pop items-center gap-1 rounded
|
|
137
|
+
<span v-for="option in selectedOptions" :key="option.value" data-slot="combobox-select-tag" class="inline-flex h-7 max-w-full animate-uv-pop items-center gap-1 rounded border border-border bg-surface pr-1 pl-2 text-[11px] font-semibold text-foreground sm:text-xs">
|
|
138
138
|
<HugeiconsIcon v-if="option.icon" :icon="option.icon" class="size-3" />
|
|
139
139
|
<span class="truncate">{{ option.label }}</span>
|
|
140
|
-
<button type="button" :disabled="props.disabled" :aria-label="`${t('uiVintage.buttons.remove')} ${option.label}`" class="flex size-5 cursor-pointer items-center justify-center rounded
|
|
140
|
+
<button type="button" :disabled="props.disabled" :aria-label="`${t('uiVintage.buttons.remove')} ${option.label}`" class="flex size-5 cursor-pointer items-center justify-center rounded text-muted-foreground outline-none transition-[color,background-color,scale] duration-150 ease-spring hover:bg-accent hover:text-destructive focus-visible:ring-[3px] focus-visible:ring-ring/45 active:scale-90 disabled:pointer-events-none" @click.stop="removeValue(option.value)">
|
|
141
141
|
<HugeiconsIcon :icon="Cancel01Icon" class="size-3" />
|
|
142
142
|
</button>
|
|
143
143
|
</span>
|
|
@@ -16,7 +16,7 @@ const forwardedProps = useForwardProps(delegatedProps);
|
|
|
16
16
|
|
|
17
17
|
<template>
|
|
18
18
|
<ComboboxTrigger data-slot="combobox-trigger" v-bind="forwardedProps" :class="cn(
|
|
19
|
-
'group/combobox-trigger -mr-1 flex size-7 shrink-0 cursor-pointer items-center justify-center rounded
|
|
19
|
+
'group/combobox-trigger -mr-1 flex size-7 shrink-0 cursor-pointer items-center justify-center rounded text-muted-foreground outline-none transition-colors duration-150 hover:bg-accent hover:text-foreground disabled:cursor-not-allowed disabled:opacity-60',
|
|
20
20
|
props.class
|
|
21
21
|
)
|
|
22
22
|
">
|
|
@@ -157,7 +157,7 @@ const handleRowKeydown = (row: T, event: KeyboardEvent) => {
|
|
|
157
157
|
<!-- Column headers -->
|
|
158
158
|
<th v-for="column in props.columns" :key="column.key" :style="column.width ? { width: column.width } : undefined" :aria-sort="props.sort?.key === column.key ? (props.sort.direction === 'asc' ? 'ascending' : 'descending') : undefined" :class="cn('h-11 px-4 text-xs font-semibold whitespace-nowrap text-muted-foreground', alignClasses[column.align ?? 'left'], column.headerClass)">
|
|
159
159
|
<slot :name="`header-${column.key}`" :column="column">
|
|
160
|
-
<button v-if="column.sortable" type="button" :class="cn('group/sort -mx-1.5 inline-flex cursor-pointer items-center gap-1.5 rounded
|
|
160
|
+
<button v-if="column.sortable" type="button" :class="cn('group/sort -mx-1.5 inline-flex cursor-pointer items-center gap-1.5 rounded px-1.5 py-1 outline-none transition-colors duration-150 hover:bg-accent hover:text-foreground focus-visible:ring-[3px] focus-visible:ring-ring/45', props.sort?.key === column.key && 'text-foreground', justifyClasses[column.align ?? 'left'])" @click="toggleSort(column)">
|
|
161
161
|
{{ column.label }}
|
|
162
162
|
<HugeiconsIcon :icon="ArrowUp01Icon" :class="cn('size-3.5 transition-[rotate,opacity,color] duration-300 ease-spring', props.sort?.key === column.key ? 'text-primary opacity-100' : 'opacity-0 group-hover/sort:opacity-50', props.sort?.key === column.key && props.sort.direction === 'desc' && 'rotate-180')" />
|
|
163
163
|
</button>
|
|
@@ -7,6 +7,7 @@ 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';
|
|
10
|
+
import { dialogSizeClasses, type DialogSize } from '.';
|
|
10
11
|
import { Button } from '../button';
|
|
11
12
|
import DialogOverlay from './DialogOverlay.vue';
|
|
12
13
|
|
|
@@ -18,8 +19,10 @@ defineOptions({
|
|
|
18
19
|
const props = withDefaults(defineProps<DialogContentProps & {
|
|
19
20
|
class?: HTMLAttributes['class'];
|
|
20
21
|
showCloseButton?: boolean;
|
|
22
|
+
size?: DialogSize;
|
|
21
23
|
}>(), {
|
|
22
|
-
showCloseButton: false
|
|
24
|
+
showCloseButton: false,
|
|
25
|
+
size: 'sm'
|
|
23
26
|
});
|
|
24
27
|
|
|
25
28
|
const { t } = useI18n();
|
|
@@ -27,7 +30,7 @@ const { t } = useI18n();
|
|
|
27
30
|
// Emits
|
|
28
31
|
const emits = defineEmits<DialogContentEmits>();
|
|
29
32
|
|
|
30
|
-
const delegatedProps = reactiveOmit(props, 'class');
|
|
33
|
+
const delegatedProps = reactiveOmit(props, 'class', 'size');
|
|
31
34
|
|
|
32
35
|
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|
33
36
|
</script>
|
|
@@ -36,7 +39,8 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|
|
36
39
|
<DialogPortal>
|
|
37
40
|
<DialogOverlay />
|
|
38
41
|
<DialogContent data-slot="dialog-content" v-bind="{ ...$attrs, ...forwarded }" :class="cn(
|
|
39
|
-
'uv-modal-motion bg-card text-card-foreground fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded border border-border p-6 shadow-elevated-xl outline-none
|
|
42
|
+
'uv-modal-motion bg-card text-card-foreground fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded border border-border p-6 shadow-elevated-xl outline-none',
|
|
43
|
+
dialogSizeClasses[props.size],
|
|
40
44
|
props.class
|
|
41
45
|
)
|
|
42
46
|
">
|
|
@@ -7,6 +7,7 @@ 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';
|
|
10
|
+
import { dialogSizeClasses, type DialogSize } from '.';
|
|
10
11
|
import { Button } from '../button';
|
|
11
12
|
|
|
12
13
|
defineOptions({
|
|
@@ -17,8 +18,10 @@ defineOptions({
|
|
|
17
18
|
const props = withDefaults(defineProps<DialogContentProps & {
|
|
18
19
|
class?: HTMLAttributes['class'];
|
|
19
20
|
showCloseButton?: boolean;
|
|
21
|
+
size?: DialogSize;
|
|
20
22
|
}>(), {
|
|
21
|
-
showCloseButton: false
|
|
23
|
+
showCloseButton: false,
|
|
24
|
+
size: 'sm'
|
|
22
25
|
});
|
|
23
26
|
|
|
24
27
|
const { t } = useI18n();
|
|
@@ -26,7 +29,7 @@ const { t } = useI18n();
|
|
|
26
29
|
// Emits
|
|
27
30
|
const emits = defineEmits<DialogContentEmits>();
|
|
28
31
|
|
|
29
|
-
const delegatedProps = reactiveOmit(props, 'class');
|
|
32
|
+
const delegatedProps = reactiveOmit(props, 'class', 'size');
|
|
30
33
|
|
|
31
34
|
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|
32
35
|
</script>
|
|
@@ -35,7 +38,8 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|
|
35
38
|
<DialogPortal>
|
|
36
39
|
<DialogOverlay class="uv-overlay-motion fixed inset-0 isolate z-50 grid place-items-center overflow-y-auto bg-black/40 backdrop-blur-[3px] dark:bg-black/60">
|
|
37
40
|
<DialogContent :class="cn(
|
|
38
|
-
'uv-modal-motion relative z-50 my-6 grid w-full max-w-[calc(100%-2rem)] gap-4 rounded border border-border bg-card p-6 text-card-foreground shadow-elevated-xl outline-none
|
|
41
|
+
'uv-modal-motion relative z-50 my-6 grid w-full max-w-[calc(100%-2rem)] gap-4 rounded border border-border bg-card p-6 text-card-foreground shadow-elevated-xl outline-none',
|
|
42
|
+
dialogSizeClasses[props.size],
|
|
39
43
|
props.class
|
|
40
44
|
)
|
|
41
45
|
" v-bind="{ ...$attrs, ...forwarded }" @pointer-down-outside="
|
|
@@ -7,4 +7,14 @@ export { default as DialogHeader } from './DialogHeader.vue';
|
|
|
7
7
|
export { default as DialogOverlay } from './DialogOverlay.vue';
|
|
8
8
|
export { default as DialogScrollContent } from './DialogScrollContent.vue';
|
|
9
9
|
export { default as DialogTitle } from './DialogTitle.vue';
|
|
10
|
-
export { default as DialogTrigger } from './DialogTrigger.vue';
|
|
10
|
+
export { default as DialogTrigger } from './DialogTrigger.vue';
|
|
11
|
+
|
|
12
|
+
export type DialogSize = 'sm' | 'md' | 'lg' | 'xl';
|
|
13
|
+
|
|
14
|
+
// Max width per dialog size (sm is the compact default)
|
|
15
|
+
export const dialogSizeClasses: Record<DialogSize, string> = {
|
|
16
|
+
sm: 'sm:max-w-106.25',
|
|
17
|
+
md: 'sm:max-w-md',
|
|
18
|
+
lg: 'sm:max-w-xl',
|
|
19
|
+
xl: 'sm:max-w-2xl'
|
|
20
|
+
};
|
|
@@ -221,7 +221,7 @@ onBeforeUnmount(() => clearTimeout(rejectTimer));
|
|
|
221
221
|
<div class="pb-2">
|
|
222
222
|
<!-- File row -->
|
|
223
223
|
<div data-slot="file-dropzone-item" class="flex items-center gap-3 rounded border border-border bg-card py-2 pr-2 pl-3 shadow-elevated-sm">
|
|
224
|
-
<span class="flex size-9 shrink-0 items-center justify-center rounded
|
|
224
|
+
<span class="flex size-9 shrink-0 items-center justify-center rounded bg-surface text-muted-foreground">
|
|
225
225
|
<HugeiconsIcon :icon="getFileIcon(file)" class="size-4.5" />
|
|
226
226
|
</span>
|
|
227
227
|
<div class="flex min-w-0 flex-1 flex-col">
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type
|
|
3
|
-
import { useVModel } from '@vueuse/core';
|
|
2
|
+
import { computed, ref, useSlots, type HTMLAttributes } from 'vue';
|
|
3
|
+
import { useElementSize, useVModel } from '@vueuse/core';
|
|
4
4
|
import { cn } from '../../../lib/utils';
|
|
5
5
|
import { useFieldControlId } from '../field/field-context';
|
|
6
6
|
|
|
7
|
+
defineOptions({
|
|
8
|
+
inheritAttrs: false
|
|
9
|
+
});
|
|
10
|
+
|
|
7
11
|
// Props
|
|
8
12
|
const props = defineProps<{
|
|
9
13
|
defaultValue?: string | number;
|
|
10
14
|
modelValue?: string | number;
|
|
15
|
+
size?: 'default' | 'sm';
|
|
11
16
|
class?: HTMLAttributes['class'];
|
|
12
17
|
}>();
|
|
13
18
|
|
|
@@ -23,14 +28,42 @@ const modelValue = useVModel(props, 'modelValue', emits, {
|
|
|
23
28
|
|
|
24
29
|
// Let a surrounding Field label point to this control
|
|
25
30
|
const fieldControlId = useFieldControlId();
|
|
31
|
+
|
|
32
|
+
// Field look, shared by the plain input and the one with a suffix
|
|
33
|
+
const inputClasses = computed(() => cn(
|
|
34
|
+
'uv-field w-full min-w-0 rounded border border-input bg-secondary px-4 py-3 text-xs font-semibold text-foreground shadow-elevated-sm outline-none sm:text-sm',
|
|
35
|
+
props.size === 'sm' ? 'h-10 py-2' : 'h-13',
|
|
36
|
+
'selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground placeholder:opacity-60 placeholder:transition-opacity focus-visible:placeholder:opacity-40',
|
|
37
|
+
'file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-semibold file:text-foreground',
|
|
38
|
+
'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-60 aria-invalid:animate-uv-shake',
|
|
39
|
+
props.class
|
|
40
|
+
));
|
|
41
|
+
|
|
42
|
+
// Optional prefix (like a search icon) and suffix (like a unit) inside the field: the text never runs under them
|
|
43
|
+
const slots = useSlots();
|
|
44
|
+
const prefixRef = ref<HTMLElement | null>(null);
|
|
45
|
+
const suffixRef = ref<HTMLElement | null>(null);
|
|
46
|
+
const { width: prefixWidth } = useElementSize(prefixRef);
|
|
47
|
+
const { width: suffixWidth } = useElementSize(suffixRef);
|
|
48
|
+
const affixPadding = computed(() => ({
|
|
49
|
+
...(slots.prefix && prefixWidth.value ? { paddingLeft: `calc(${prefixWidth.value}px + 1.5rem)` } : {}),
|
|
50
|
+
...(slots.suffix && suffixWidth.value ? { paddingRight: `calc(${suffixWidth.value}px + 1.75rem)` } : {})
|
|
51
|
+
}));
|
|
26
52
|
</script>
|
|
27
53
|
|
|
28
54
|
<template>
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
55
|
+
<div v-if="$slots.prefix || $slots.suffix" data-slot="input-group" class="relative w-full min-w-0">
|
|
56
|
+
<!-- Prefix -->
|
|
57
|
+
<span v-if="$slots.prefix" ref="prefixRef" data-slot="input-prefix" class="pointer-events-none absolute inset-y-0 left-3.5 flex items-center text-muted-foreground [&_svg:not([class*='size-'])]:size-4">
|
|
58
|
+
<slot name="prefix" />
|
|
59
|
+
</span>
|
|
60
|
+
|
|
61
|
+
<input :id="fieldControlId" v-model="modelValue" data-slot="input" v-bind="$attrs" :style="affixPadding" :class="inputClasses" />
|
|
62
|
+
|
|
63
|
+
<!-- Suffix -->
|
|
64
|
+
<span v-if="$slots.suffix" ref="suffixRef" data-slot="input-suffix" class="pointer-events-none absolute inset-y-0 right-4 flex items-center text-xs font-semibold text-muted-foreground sm:text-sm">
|
|
65
|
+
<slot name="suffix" />
|
|
66
|
+
</span>
|
|
67
|
+
</div>
|
|
68
|
+
<input v-else :id="fieldControlId" v-model="modelValue" data-slot="input" v-bind="$attrs" :class="inputClasses" />
|
|
36
69
|
</template>
|
|
@@ -2,17 +2,21 @@
|
|
|
2
2
|
import type { HTMLAttributes } from 'vue';
|
|
3
3
|
import { cn } from '../../../lib/utils';
|
|
4
4
|
|
|
5
|
-
// Props
|
|
6
|
-
const props = defineProps<{
|
|
5
|
+
// Props (lg is a big standalone key, for example to show a shortcut in a hint)
|
|
6
|
+
const props = withDefaults(defineProps<{
|
|
7
7
|
class?: HTMLAttributes['class'];
|
|
8
8
|
pressed?: boolean;
|
|
9
|
-
|
|
9
|
+
size?: 'default' | 'lg';
|
|
10
|
+
}>(), {
|
|
11
|
+
size: 'default'
|
|
12
|
+
});
|
|
10
13
|
</script>
|
|
11
14
|
|
|
12
15
|
<template>
|
|
13
16
|
<kbd data-slot="kbd" :data-pressed="props.pressed ? '' : undefined" :class="cn(
|
|
14
|
-
'inline-flex h-5 min-w-5 select-none items-center justify-center gap-1 rounded
|
|
17
|
+
'inline-flex h-5 min-w-5 select-none items-center justify-center gap-1 rounded border border-b-2 border-border bg-surface px-1.5 font-mono text-[11px] leading-none font-semibold text-muted-foreground shadow-elevated-sm transition-[translate,border-width,box-shadow] duration-100 ease-snappy [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-3',
|
|
15
18
|
'data-pressed:translate-y-px data-pressed:border-b data-pressed:shadow-none',
|
|
19
|
+
props.size === 'lg' && 'h-10 min-w-10 px-3 text-sm',
|
|
16
20
|
props.class
|
|
17
21
|
)
|
|
18
22
|
">
|
|
@@ -92,7 +92,7 @@ const fieldControlId = useFieldControlId(() => props.id);
|
|
|
92
92
|
)" @update:model-value="animateChange">
|
|
93
93
|
<!-- Decrement button -->
|
|
94
94
|
<NumberFieldDecrement data-slot="number-field-decrement" :aria-label="resolvedDecrementLabel" :class="cn(
|
|
95
|
-
'inline-flex size-10 shrink-0 cursor-pointer items-center justify-center rounded
|
|
95
|
+
'inline-flex size-10 shrink-0 cursor-pointer items-center justify-center rounded text-muted-foreground outline-none',
|
|
96
96
|
'[transition:scale_320ms_var(--ease-spring),background-color_150ms_var(--ease-snappy),color_150ms_var(--ease-snappy),opacity_150ms_var(--ease-snappy)]',
|
|
97
97
|
'hover:bg-accent hover:text-foreground active:scale-[0.84] data-pressed:scale-[0.84] focus-visible:ring-[3px] focus-visible:ring-ring/45',
|
|
98
98
|
'disabled:pointer-events-none disabled:opacity-35 data-disabled:pointer-events-none data-disabled:opacity-35'
|
|
@@ -119,7 +119,7 @@ const fieldControlId = useFieldControlId(() => props.id);
|
|
|
119
119
|
|
|
120
120
|
<!-- Increment button -->
|
|
121
121
|
<NumberFieldIncrement data-slot="number-field-increment" :aria-label="resolvedIncrementLabel" :class="cn(
|
|
122
|
-
'inline-flex size-10 shrink-0 cursor-pointer items-center justify-center rounded
|
|
122
|
+
'inline-flex size-10 shrink-0 cursor-pointer items-center justify-center rounded text-muted-foreground outline-none',
|
|
123
123
|
'[transition:scale_320ms_var(--ease-spring),background-color_150ms_var(--ease-snappy),color_150ms_var(--ease-snappy),opacity_150ms_var(--ease-snappy)]',
|
|
124
124
|
'hover:bg-accent hover:text-foreground active:scale-[0.84] data-pressed:scale-[0.84] focus-visible:ring-[3px] focus-visible:ring-ring/45',
|
|
125
125
|
'disabled:pointer-events-none disabled:opacity-35 data-disabled:pointer-events-none data-disabled:opacity-35'
|
|
@@ -6,16 +6,28 @@ import { reactiveOmit } from '@vueuse/core';
|
|
|
6
6
|
import { ProgressIndicator, ProgressRoot } from 'reka-ui';
|
|
7
7
|
import { cn } from '../../../lib/utils';
|
|
8
8
|
|
|
9
|
+
type ProgressColor = 'primary' | 'green' | 'red' | 'yellow';
|
|
10
|
+
|
|
9
11
|
// Props
|
|
10
12
|
const props = withDefaults(defineProps<ProgressRootProps & {
|
|
11
13
|
indeterminate?: boolean;
|
|
14
|
+
color?: ProgressColor;
|
|
12
15
|
class?: HTMLAttributes['class'];
|
|
13
16
|
}>(), {
|
|
14
17
|
modelValue: 0,
|
|
15
|
-
indeterminate: false
|
|
18
|
+
indeterminate: false,
|
|
19
|
+
color: 'primary'
|
|
16
20
|
});
|
|
17
21
|
|
|
18
|
-
const delegatedProps = reactiveOmit(props, 'class', 'indeterminate');
|
|
22
|
+
const delegatedProps = reactiveOmit(props, 'class', 'indeterminate', 'color');
|
|
23
|
+
|
|
24
|
+
// Fill color of the bar
|
|
25
|
+
const colorClasses: Record<ProgressColor, string> = {
|
|
26
|
+
primary: 'bg-primary',
|
|
27
|
+
green: 'bg-success',
|
|
28
|
+
red: 'bg-destructive',
|
|
29
|
+
yellow: 'bg-warning'
|
|
30
|
+
};
|
|
19
31
|
|
|
20
32
|
// Resolve the fill percentage against the configured max
|
|
21
33
|
const percentage = computed(() => {
|
|
@@ -27,13 +39,13 @@ const percentage = computed(() => {
|
|
|
27
39
|
|
|
28
40
|
<template>
|
|
29
41
|
<ProgressRoot data-slot="progress" v-bind="delegatedProps" :model-value="props.indeterminate ? null : props.modelValue" :class="cn(
|
|
30
|
-
'relative h-2 w-full overflow-hidden rounded
|
|
42
|
+
'relative h-2 w-full overflow-hidden rounded bg-muted',
|
|
31
43
|
props.class
|
|
32
44
|
)">
|
|
33
45
|
<!-- Indeterminate bar -->
|
|
34
|
-
<div v-if="props.indeterminate" data-slot="progress-indicator" class="absolute inset-y-0 left-0 w-full origin-left animate-uv-indeterminate rounded
|
|
46
|
+
<div v-if="props.indeterminate" data-slot="progress-indicator" :class="cn('absolute inset-y-0 left-0 w-full origin-left animate-uv-indeterminate rounded', colorClasses[props.color])" />
|
|
35
47
|
|
|
36
48
|
<!-- Determinate bar -->
|
|
37
|
-
<ProgressIndicator v-else data-slot="progress-indicator" class="relative h-full w-full flex-1 overflow-hidden rounded
|
|
49
|
+
<ProgressIndicator v-else data-slot="progress-indicator" :class="cn('relative h-full w-full flex-1 overflow-hidden rounded transition-transform duration-700 ease-out-expo', colorClasses[props.color])" :style="`transform: translateX(-${100 - percentage}%);`" />
|
|
38
50
|
</ProgressRoot>
|
|
39
51
|
</template>
|
|
@@ -16,7 +16,7 @@ const forwardedProps = useForwardProps(delegatedProps);
|
|
|
16
16
|
|
|
17
17
|
<template>
|
|
18
18
|
<SelectItem data-slot="select-item" v-bind="forwardedProps" :class="cn(
|
|
19
|
-
'focus:bg-accent focus:text-accent-foreground data-[state=checked]:text-foreground transition-colors duration-150 [&_svg:not([class*=\'text-\'])]:text-muted-foreground relative flex w-full cursor-pointer items-center gap-2 rounded
|
|
19
|
+
'focus:bg-accent focus:text-accent-foreground data-[state=checked]:text-foreground transition-colors duration-150 [&_svg:not([class*=\'text-\'])]:text-muted-foreground relative flex w-full cursor-pointer items-center gap-2 rounded py-2.5 pr-8 pl-2 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:cursor-not-allowed data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2',
|
|
20
20
|
props.class
|
|
21
21
|
)
|
|
22
22
|
">
|
|
@@ -18,9 +18,11 @@ const props = defineProps<{
|
|
|
18
18
|
|
|
19
19
|
<template>
|
|
20
20
|
<div data-slot="select-item-content" :class="cn('flex min-w-0 flex-1 items-center gap-2', props.class)">
|
|
21
|
-
<!--
|
|
22
|
-
<div v-if="props.icon" data-slot="select-item-content-icon" :class="cn('shrink-0 self-center', props.iconClass)">
|
|
23
|
-
<
|
|
21
|
+
<!-- Leading visual: the default slot (for example a flag), or the icon -->
|
|
22
|
+
<div v-if="$slots.default || props.icon" data-slot="select-item-content-icon" :class="cn('shrink-0 self-center', props.iconClass)">
|
|
23
|
+
<slot>
|
|
24
|
+
<HugeiconsIcon :icon="props.icon" class="block size-3 sm:size-4" />
|
|
25
|
+
</slot>
|
|
24
26
|
</div>
|
|
25
27
|
|
|
26
28
|
<!-- Text and description -->
|
|
@@ -3,14 +3,18 @@ import type { HTMLAttributes } from 'vue';
|
|
|
3
3
|
import { cn } from '../../../lib/utils';
|
|
4
4
|
|
|
5
5
|
// Props
|
|
6
|
-
const props = defineProps<{
|
|
6
|
+
const props = withDefaults(defineProps<{
|
|
7
|
+
circle?: boolean;
|
|
7
8
|
class?: HTMLAttributes['class'];
|
|
8
|
-
}>()
|
|
9
|
+
}>(), {
|
|
10
|
+
circle: false
|
|
11
|
+
});
|
|
9
12
|
</script>
|
|
10
13
|
|
|
11
14
|
<template>
|
|
12
15
|
<div data-slot="skeleton" :class="cn(
|
|
13
|
-
'relative overflow-hidden
|
|
16
|
+
'relative overflow-hidden border border-border bg-surface',
|
|
17
|
+
props.circle ? 'rounded-full' : 'rounded',
|
|
14
18
|
'after:absolute after:inset-0 after:animate-uv-shimmer after:bg-[linear-gradient(90deg,transparent,color-mix(in_oklab,var(--foreground)_6%,transparent),transparent)]',
|
|
15
19
|
props.class
|
|
16
20
|
)" />
|
|
@@ -29,7 +29,7 @@ const fieldControlId = useFieldControlId(() => props.id);
|
|
|
29
29
|
props.class
|
|
30
30
|
)">
|
|
31
31
|
<SwitchThumb data-slot="switch-thumb" :class="cn(
|
|
32
|
-
'pointer-events-none absolute left-1 top-1/2 flex h-4 w-4 -translate-y-1/2 items-center justify-center rounded
|
|
32
|
+
'pointer-events-none absolute left-1 top-1/2 flex h-4 w-4 -translate-y-1/2 items-center justify-center rounded bg-muted-foreground ring-0',
|
|
33
33
|
'[transition:translate_380ms_var(--ease-spring),width_220ms_var(--ease-spring),background-color_200ms]',
|
|
34
34
|
'group-active/switch:w-5 data-[state=checked]:translate-x-6 data-[state=checked]:bg-primary group-active/switch:data-[state=checked]:translate-x-5'
|
|
35
35
|
)">
|
|
@@ -9,7 +9,7 @@ const props = defineProps<{
|
|
|
9
9
|
</script>
|
|
10
10
|
|
|
11
11
|
<template>
|
|
12
|
-
<thead data-slot="table-header" :class="cn('[&_tr]:border-b [&_tr]:border-border', props.class)">
|
|
12
|
+
<thead data-slot="table-header" :class="cn('[&_tr]:border-b [&_tr]:border-border [&_tr]:hover:bg-transparent', props.class)">
|
|
13
13
|
<slot />
|
|
14
14
|
</thead>
|
|
15
15
|
</template>
|
|
@@ -2,15 +2,22 @@
|
|
|
2
2
|
import type { HTMLAttributes } from 'vue';
|
|
3
3
|
import { cn } from '../../../lib/utils';
|
|
4
4
|
|
|
5
|
-
// Props
|
|
6
|
-
const props = defineProps<{
|
|
5
|
+
// Props (static rows don't highlight on hover, interactive rows show a pointer for clickable rows; data-state="selected" highlights a row with a soft primary tint)
|
|
6
|
+
const props = withDefaults(defineProps<{
|
|
7
|
+
static?: boolean;
|
|
8
|
+
interactive?: boolean;
|
|
7
9
|
class?: HTMLAttributes['class'];
|
|
8
|
-
}>()
|
|
10
|
+
}>(), {
|
|
11
|
+
static: false,
|
|
12
|
+
interactive: false
|
|
13
|
+
});
|
|
9
14
|
</script>
|
|
10
15
|
|
|
11
16
|
<template>
|
|
12
17
|
<tr data-slot="table-row" :class="cn(
|
|
13
|
-
'
|
|
18
|
+
'data-[state=selected]:bg-primary/5 data-[state=selected]:hover:bg-primary/10 border-b border-border transition-colors',
|
|
19
|
+
!props.static && 'hover:bg-muted/50',
|
|
20
|
+
props.interactive && 'cursor-pointer',
|
|
14
21
|
props.class
|
|
15
22
|
)
|
|
16
23
|
">
|
|
@@ -18,7 +18,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|
|
18
18
|
</script>
|
|
19
19
|
|
|
20
20
|
<template>
|
|
21
|
-
<TabsRoot v-slot="slotProps" data-slot="tabs" v-bind="forwarded" :class="cn('flex flex-col gap-2', props.class)">
|
|
21
|
+
<TabsRoot v-slot="slotProps" data-slot="tabs" v-bind="forwarded" :class="cn('flex flex-col gap-2 data-[orientation=vertical]:gap-4 sm:data-[orientation=vertical]:flex-row', props.class)">
|
|
22
22
|
<slot v-bind="slotProps" />
|
|
23
23
|
</TabsRoot>
|
|
24
24
|
</template>
|
|
@@ -5,16 +5,19 @@ import { reactiveOmit } from '@vueuse/core';
|
|
|
5
5
|
import { TabsContent } from 'reka-ui';
|
|
6
6
|
import { cn } from '../../../lib/utils';
|
|
7
7
|
|
|
8
|
-
// Props
|
|
9
|
-
const props = defineProps<TabsContentProps & {
|
|
8
|
+
// Props (boxed frames the panel in a bordered surface)
|
|
9
|
+
const props = withDefaults(defineProps<TabsContentProps & {
|
|
10
|
+
variant?: 'plain' | 'boxed';
|
|
10
11
|
class?: HTMLAttributes['class'];
|
|
11
|
-
}>()
|
|
12
|
+
}>(), {
|
|
13
|
+
variant: 'plain'
|
|
14
|
+
});
|
|
12
15
|
|
|
13
|
-
const delegatedProps = reactiveOmit(props, 'class');
|
|
16
|
+
const delegatedProps = reactiveOmit(props, 'class', 'variant');
|
|
14
17
|
</script>
|
|
15
18
|
|
|
16
19
|
<template>
|
|
17
|
-
<TabsContent data-slot="tabs-content" :class="cn('flex-1 outline-none data-[state=active]:animate-uv-fade-up', props.class)" v-bind="delegatedProps">
|
|
20
|
+
<TabsContent data-slot="tabs-content" :class="cn('flex-1 outline-none data-[state=active]:animate-uv-fade-up', props.variant === 'boxed' && 'rounded border border-border bg-surface/50 p-4', props.class)" v-bind="delegatedProps">
|
|
18
21
|
<slot />
|
|
19
22
|
</TabsContent>
|
|
20
23
|
</template>
|
|
@@ -18,7 +18,7 @@ const forwardedProps = useForwardProps(delegatedProps);
|
|
|
18
18
|
<TabsTrigger data-slot="tabs-trigger" :class="cn(
|
|
19
19
|
'relative inline-flex min-h-9 flex-1 cursor-pointer items-center justify-center gap-1.5 rounded border border-transparent bg-transparent px-4 py-2 text-sm font-semibold whitespace-nowrap text-muted-foreground outline-none select-none',
|
|
20
20
|
'[transition:color_150ms,background-color_150ms,scale_300ms_var(--ease-spring)] hover:text-foreground active:scale-[0.97] focus-visible:ring-[3px] focus-visible:ring-ring/45 disabled:pointer-events-none disabled:opacity-50',
|
|
21
|
-
'data-[state=active]:text-primary data-[state=inactive]:hover:bg-accent/60',
|
|
21
|
+
'data-[state=active]:text-primary data-[state=inactive]:hover:bg-accent/60 group-aria-[orientation=vertical]/tabs-list:justify-start',
|
|
22
22
|
'[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4',
|
|
23
23
|
props.class
|
|
24
24
|
)" v-bind="forwardedProps">
|
|
@@ -18,7 +18,7 @@ const forwardedProps = useForwardProps(delegatedProps);
|
|
|
18
18
|
|
|
19
19
|
<template>
|
|
20
20
|
<TagsInputItemDelete data-slot="tags-input-item-delete" v-bind="forwardedProps" :class="cn(
|
|
21
|
-
'inline-flex size-5 shrink-0 cursor-pointer items-center justify-center rounded
|
|
21
|
+
'inline-flex size-5 shrink-0 cursor-pointer items-center justify-center rounded text-muted-foreground outline-none',
|
|
22
22
|
'[transition:rotate_320ms_var(--ease-spring),scale_320ms_var(--ease-spring),background-color_150ms_var(--ease-snappy),color_150ms_var(--ease-snappy)]',
|
|
23
23
|
'hover:rotate-90 hover:bg-accent hover:text-foreground active:scale-[0.8] focus-visible:ring-[3px] focus-visible:ring-ring/45',
|
|
24
24
|
'group-data-[state=active]/tag:text-primary data-disabled:pointer-events-none',
|
|
@@ -27,7 +27,7 @@ const fieldControlId = useFieldControlId();
|
|
|
27
27
|
|
|
28
28
|
<template>
|
|
29
29
|
<textarea :id="fieldControlId" v-model="modelValue" data-slot="textarea" :class="cn(
|
|
30
|
-
'uv-field flex field-sizing-content min-h-30 w-full min-w-0 rounded border border-input bg-secondary px-4 py-3 text-xs font-semibold text-foreground shadow-elevated-sm outline-none sm:text-sm',
|
|
30
|
+
'uv-field flex field-sizing-content min-h-30 w-full min-w-0 resize-none rounded border border-input bg-secondary px-4 py-3 text-xs font-semibold text-foreground shadow-elevated-sm outline-none sm:text-sm',
|
|
31
31
|
'selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground placeholder:opacity-60',
|
|
32
32
|
'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-60 aria-invalid:animate-uv-shake',
|
|
33
33
|
props.class
|
|
@@ -34,7 +34,7 @@ const isSingle = computed(() => {
|
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
// Share size and indicator mode with the items
|
|
37
|
-
provide(toggleGroupContextKey, { size: toRef(props, 'size'), indicator: isSingle });
|
|
37
|
+
provide(toggleGroupContextKey, { size: toRef(props, 'size'), indicator: isSingle, highlight: toRef(props, 'highlight') });
|
|
38
38
|
|
|
39
39
|
// Sliding indicator geometry
|
|
40
40
|
const rootRef = ref();
|
|
@@ -83,7 +83,7 @@ const indicatorStyle = computed<CSSProperties>(() => ({
|
|
|
83
83
|
<ToggleGroupRoot ref="rootRef" v-slot="slotProps" data-slot="toggle-group" :data-size="props.size" v-bind="forwarded" :class="cn('relative inline-flex w-fit items-center gap-1 rounded border border-border bg-card p-1 shadow-elevated-sm data-[orientation=vertical]:flex-col data-[orientation=vertical]:items-stretch', props.class)">
|
|
84
84
|
<!-- Sliding indicator behind the active item -->
|
|
85
85
|
<span v-if="isSingle" data-slot="toggle-group-indicator" aria-hidden="true" :style="indicatorStyle" :class="cn(
|
|
86
|
-
'pointer-events-none absolute top-0 left-0 z-0 rounded
|
|
86
|
+
'pointer-events-none absolute top-0 left-0 z-0 rounded border', // Same radius as the tab triggers, so every switch looks alike
|
|
87
87
|
animated && '[transition:transform_380ms_var(--ease-spring),width_380ms_var(--ease-spring),height_380ms_var(--ease-spring),opacity_180ms_var(--ease-snappy)]',
|
|
88
88
|
props.highlight ? 'border-primary/40 bg-primary/10' : 'border-border-strong bg-surface shadow-elevated-sm'
|
|
89
89
|
)" />
|
|
@@ -17,16 +17,18 @@ const props = defineProps<ToggleGroupItemProps & {
|
|
|
17
17
|
const delegatedProps = reactiveOmit(props, 'class', 'size');
|
|
18
18
|
const forwardedProps = useForwardProps(delegatedProps);
|
|
19
19
|
|
|
20
|
-
// Size
|
|
20
|
+
// Size, indicator mode and highlight come from the group
|
|
21
21
|
const context = inject(toggleGroupContextKey, null);
|
|
22
22
|
const size = computed(() => props.size ?? context?.size.value ?? 'default');
|
|
23
23
|
const hasIndicator = computed(() => context?.indicator.value ?? false);
|
|
24
|
+
const isHighlighted = computed(() => context?.highlight.value ?? false);
|
|
24
25
|
</script>
|
|
25
26
|
|
|
26
27
|
<template>
|
|
27
28
|
<ToggleGroupItem v-slot="slotProps" data-slot="toggle-group-item" :data-size="size" v-bind="forwardedProps" :class="cn(
|
|
28
29
|
toggleGroupItemVariants({ size }),
|
|
29
30
|
!hasIndicator && 'hover:bg-accent data-[state=on]:bg-accent',
|
|
31
|
+
isHighlighted && 'data-[state=on]:text-primary', // The highlighted group tints the active item like the active tab
|
|
30
32
|
props.class
|
|
31
33
|
)">
|
|
32
34
|
<slot v-bind="slotProps" />
|
|
@@ -45,7 +45,7 @@ export const toggleVariants: (props?: ToggleVariants & ClassProp) => string = cv
|
|
|
45
45
|
// Segmented control item (sits above the sliding indicator)
|
|
46
46
|
export const toggleGroupItemVariants: (props?: { size?: ToggleGroupSize | null } & ClassProp) => string = cva(
|
|
47
47
|
[
|
|
48
|
-
"relative z-10 inline-flex shrink-0 cursor-pointer select-none items-center justify-center gap-1.5 whitespace-nowrap rounded
|
|
48
|
+
"relative z-10 inline-flex shrink-0 cursor-pointer select-none items-center justify-center gap-1.5 whitespace-nowrap rounded text-xs font-semibold text-muted-foreground outline-none sm:text-sm",
|
|
49
49
|
"[transition:color_150ms,background-color_150ms,box-shadow_220ms,opacity_150ms,transform_380ms_var(--ease-spring)]",
|
|
50
50
|
"hover:text-foreground data-[state=on]:text-foreground active:scale-[0.96] active:duration-75 disabled:pointer-events-none disabled:opacity-50",
|
|
51
51
|
"focus-visible:ring-[3px] focus-visible:ring-ring/45",
|
|
@@ -66,4 +66,4 @@ export const toggleGroupItemVariants: (props?: { size?: ToggleGroupSize | null }
|
|
|
66
66
|
);
|
|
67
67
|
|
|
68
68
|
// Context shared by a toggle group with its items
|
|
69
|
-
export const toggleGroupContextKey: InjectionKey<{ size: Ref<ToggleGroupSize>; indicator: Ref<boolean> }> = Symbol('uv-toggle-group');
|
|
69
|
+
export const toggleGroupContextKey: InjectionKey<{ size: Ref<ToggleGroupSize>; indicator: Ref<boolean>; highlight: Ref<boolean> }> = Symbol('uv-toggle-group');
|
package/src/i18n/de.json
CHANGED
|
@@ -79,6 +79,7 @@
|
|
|
79
79
|
"readMore": "Weiterlesen",
|
|
80
80
|
"goToPost": "Zum Beitrag",
|
|
81
81
|
"new": "Neu",
|
|
82
|
+
"inShort": "Kurz gesagt",
|
|
82
83
|
"tableOfContents": "Inhaltsverzeichnis",
|
|
83
84
|
"tableOfContentsDescription": "Zu einem Abschnitt dieses Artikels springen",
|
|
84
85
|
"tableOfContentsSections": "{count} Abschnitt | {count} Abschnitte",
|
package/src/i18n/en.json
CHANGED
|
@@ -79,6 +79,7 @@
|
|
|
79
79
|
"readMore": "Read more",
|
|
80
80
|
"goToPost": "Go to post",
|
|
81
81
|
"new": "New",
|
|
82
|
+
"inShort": "In short",
|
|
82
83
|
"tableOfContents": "Table of contents",
|
|
83
84
|
"tableOfContentsDescription": "Jump to a section of this article",
|
|
84
85
|
"tableOfContentsSections": "{count} section | {count} sections",
|
package/src/i18n/es.json
CHANGED
|
@@ -79,6 +79,7 @@
|
|
|
79
79
|
"readMore": "Leer mas",
|
|
80
80
|
"goToPost": "Ir al articulo",
|
|
81
81
|
"new": "Nuevo",
|
|
82
|
+
"inShort": "En resumen",
|
|
82
83
|
"tableOfContents": "Tabla de contenidos",
|
|
83
84
|
"tableOfContentsDescription": "Ir a una seccion de este articulo",
|
|
84
85
|
"tableOfContentsSections": "{count} seccion | {count} secciones",
|
package/src/i18n/fr.json
CHANGED
|
@@ -79,6 +79,7 @@
|
|
|
79
79
|
"readMore": "Lire la suite",
|
|
80
80
|
"goToPost": "Aller a l'article",
|
|
81
81
|
"new": "Nouveau",
|
|
82
|
+
"inShort": "En bref",
|
|
82
83
|
"tableOfContents": "Table des matieres",
|
|
83
84
|
"tableOfContentsDescription": "Aller a une section de cet article",
|
|
84
85
|
"tableOfContentsSections": "{count} section | {count} sections",
|