@revojs/ui 0.2.10 → 0.2.12
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/dist/components/card.d.ts +0 -2
- package/dist/components/dialog.d.ts +1 -2
- package/dist/components/field.d.ts +2 -0
- package/dist/components/input.d.ts +2 -0
- package/dist/components/sheet.d.ts +1 -2
- package/dist/components/upload.d.ts +2 -0
- package/dist/module/index.d.ts +8 -0
- package/dist/module/index.js +16 -0
- package/dist/style.css +8 -0
- package/dist/vue/components/button.vue +9 -10
- package/dist/vue/components/card.vue +9 -15
- package/dist/vue/components/checkbox.vue +18 -4
- package/dist/vue/components/collection.vue +1 -1
- package/dist/vue/components/container.vue +6 -1
- package/dist/vue/components/counter.vue +7 -4
- package/dist/vue/components/dialog.vue +3 -3
- package/dist/vue/components/field.vue +12 -7
- package/dist/vue/components/input.vue +12 -5
- package/dist/vue/components/link.vue +1 -1
- package/dist/vue/components/popover.vue +8 -4
- package/dist/vue/components/progress.vue +2 -2
- package/dist/vue/components/radio.vue +19 -5
- package/dist/vue/components/range.vue +1 -1
- package/dist/vue/components/rating.vue +2 -2
- package/dist/vue/components/select.vue +24 -21
- package/dist/vue/components/separator.vue +2 -2
- package/dist/vue/components/sheet.vue +4 -4
- package/dist/vue/components/switch.vue +21 -7
- package/dist/vue/components/upload.vue +50 -18
- package/package.json +1 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { IconComponent } from "./icon";
|
|
2
|
+
export type InputText = "sm" | "md";
|
|
2
3
|
export interface InputProps {
|
|
3
4
|
modelValue?: string | number;
|
|
4
5
|
type?: string;
|
|
6
|
+
text?: InputText;
|
|
5
7
|
iconPrefix?: IconComponent;
|
|
6
8
|
iconSuffix?: IconComponent;
|
|
7
9
|
placeholder?: string;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export type UploadState = "idle" | "over";
|
|
2
|
+
export type UploadText = "sm" | "md";
|
|
2
3
|
export interface UploadProps {
|
|
3
4
|
modelValue?: Blob | File;
|
|
4
5
|
label?: string;
|
|
5
6
|
description?: string;
|
|
7
|
+
text?: UploadText;
|
|
6
8
|
accept?: string;
|
|
7
9
|
name?: string;
|
|
8
10
|
required?: boolean;
|
package/dist/module/index.d.ts
CHANGED
|
@@ -13,14 +13,22 @@ export interface UiTheme {
|
|
|
13
13
|
popover: TailwindColor;
|
|
14
14
|
popoverForeground: TailwindColor;
|
|
15
15
|
primary: TailwindColor;
|
|
16
|
+
primaryHover: TailwindColor;
|
|
17
|
+
primaryActive: TailwindColor;
|
|
16
18
|
primaryForeground: TailwindColor;
|
|
17
19
|
secondary: TailwindColor;
|
|
20
|
+
secondaryHover: TailwindColor;
|
|
21
|
+
secondaryActive: TailwindColor;
|
|
18
22
|
secondaryForeground: TailwindColor;
|
|
19
23
|
muted: TailwindColor;
|
|
20
24
|
mutedForeground: TailwindColor;
|
|
21
25
|
accent: TailwindColor;
|
|
22
26
|
accentForeground: TailwindColor;
|
|
27
|
+
ghostHover: TailwindColor;
|
|
28
|
+
ghostActive: TailwindColor;
|
|
23
29
|
destructive: TailwindColor;
|
|
30
|
+
destructiveHover: TailwindColor;
|
|
31
|
+
destructiveActive: TailwindColor;
|
|
24
32
|
destructiveForeground: TailwindColor;
|
|
25
33
|
border: TailwindColor;
|
|
26
34
|
input: TailwindColor;
|
package/dist/module/index.js
CHANGED
|
@@ -37,14 +37,22 @@ var module_default = defineModule({
|
|
|
37
37
|
popover: "white",
|
|
38
38
|
popoverForeground: "zinc-950",
|
|
39
39
|
primary: "zinc-950",
|
|
40
|
+
primaryHover: "zinc-900",
|
|
41
|
+
primaryActive: "zinc-800",
|
|
40
42
|
primaryForeground: "white",
|
|
41
43
|
secondary: "zinc-100",
|
|
44
|
+
secondaryHover: "zinc-200",
|
|
45
|
+
secondaryActive: "zinc-300",
|
|
42
46
|
secondaryForeground: "zinc-900",
|
|
43
47
|
muted: "zinc-100",
|
|
44
48
|
mutedForeground: "zinc-500",
|
|
45
49
|
accent: "zinc-100",
|
|
46
50
|
accentForeground: "zinc-900",
|
|
51
|
+
ghostHover: "zinc-100",
|
|
52
|
+
ghostActive: "zinc-200",
|
|
47
53
|
destructive: "red-500",
|
|
54
|
+
destructiveHover: "red-600",
|
|
55
|
+
destructiveActive: "red-700",
|
|
48
56
|
destructiveForeground: "zinc-50",
|
|
49
57
|
border: "zinc-200",
|
|
50
58
|
input: "zinc-200",
|
|
@@ -58,14 +66,22 @@ var module_default = defineModule({
|
|
|
58
66
|
popover: "zinc-950",
|
|
59
67
|
popoverForeground: "zinc-50",
|
|
60
68
|
primary: "white",
|
|
69
|
+
primaryHover: "zinc-100",
|
|
70
|
+
primaryActive: "zinc-200",
|
|
61
71
|
primaryForeground: "zinc-950",
|
|
62
72
|
secondary: "zinc-900",
|
|
73
|
+
secondaryHover: "zinc-800",
|
|
74
|
+
secondaryActive: "zinc-700",
|
|
63
75
|
secondaryForeground: "zinc-50",
|
|
64
76
|
muted: "zinc-900",
|
|
65
77
|
mutedForeground: "zinc-400",
|
|
66
78
|
accent: "zinc-900",
|
|
67
79
|
accentForeground: "zinc-50",
|
|
80
|
+
ghostHover: "zinc-900",
|
|
81
|
+
ghostActive: "zinc-800",
|
|
68
82
|
destructive: "red-500",
|
|
83
|
+
destructiveHover: "red-600",
|
|
84
|
+
destructiveActive: "red-700",
|
|
69
85
|
destructiveForeground: "zinc-50",
|
|
70
86
|
border: "zinc-700",
|
|
71
87
|
input: "zinc-700",
|
package/dist/style.css
CHANGED
|
@@ -10,14 +10,22 @@
|
|
|
10
10
|
--color-popover: var(--ui-popover);
|
|
11
11
|
--color-popover-foreground: var(--ui-popover-foreground);
|
|
12
12
|
--color-primary: var(--ui-primary);
|
|
13
|
+
--color-primary-hover: var(--ui-primary-hover);
|
|
14
|
+
--color-primary-active: var(--ui-primary-active);
|
|
13
15
|
--color-primary-foreground: var(--ui-primary-foreground);
|
|
14
16
|
--color-secondary: var(--ui-secondary);
|
|
17
|
+
--color-secondary-hover: var(--ui-secondary-hover);
|
|
18
|
+
--color-secondary-active: var(--ui-secondary-active);
|
|
15
19
|
--color-secondary-foreground: var(--ui-secondary-foreground);
|
|
16
20
|
--color-muted: var(--ui-muted);
|
|
17
21
|
--color-muted-foreground: var(--ui-muted-foreground);
|
|
18
22
|
--color-accent: var(--ui-accent);
|
|
19
23
|
--color-accent-foreground: var(--ui-accent-foreground);
|
|
24
|
+
--color-ghost-hover: var(--ui-ghost-hover);
|
|
25
|
+
--color-ghost-active: var(--ui-ghost-active);
|
|
20
26
|
--color-destructive: var(--ui-destructive);
|
|
27
|
+
--color-destructive-hover: var(--ui-destructive-hover);
|
|
28
|
+
--color-destructive-active: var(--ui-destructive-active);
|
|
21
29
|
--color-destructive-foreground: var(--ui-destructive-foreground);
|
|
22
30
|
--color-border: var(--ui-border);
|
|
23
31
|
--color-input: var(--ui-input);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<component
|
|
3
3
|
:is="is"
|
|
4
|
-
class="inline-flex cursor-pointer items-center justify-center gap-1 rounded-ui
|
|
4
|
+
class="inline-flex shrink-0 cursor-pointer items-center justify-center gap-1 truncate rounded-ui font-[inherit] no-underline outline-ring select-none focus-visible:outline-2 focus-visible:outline-offset-2 disabled:cursor-not-allowed disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50"
|
|
5
5
|
:class="[fonts[font], layouts[layout], sizes[size], texts[text], variants[variant]]"
|
|
6
6
|
:type="attributes.type ?? 'button'"
|
|
7
7
|
:disabled="disabled"
|
|
@@ -33,7 +33,7 @@ const {
|
|
|
33
33
|
iconPrefix,
|
|
34
34
|
iconSuffix,
|
|
35
35
|
label,
|
|
36
|
-
font = "
|
|
36
|
+
font = "semibold",
|
|
37
37
|
layout = "center",
|
|
38
38
|
size = "default",
|
|
39
39
|
text = "sm",
|
|
@@ -66,8 +66,8 @@ const labelLayouts: Record<ButtonLayout, string> = {
|
|
|
66
66
|
};
|
|
67
67
|
|
|
68
68
|
const sizes: Record<ButtonSize, string> = {
|
|
69
|
-
default: "
|
|
70
|
-
icon: "
|
|
69
|
+
default: "p-3",
|
|
70
|
+
icon: "p-3",
|
|
71
71
|
};
|
|
72
72
|
|
|
73
73
|
const texts: Record<ButtonText, string> = {
|
|
@@ -81,12 +81,11 @@ const iconSize: Record<ButtonText, number> = {
|
|
|
81
81
|
};
|
|
82
82
|
|
|
83
83
|
const variants: Record<ButtonVariant, string> = {
|
|
84
|
-
primary: "
|
|
85
|
-
secondary: "
|
|
84
|
+
primary: "bg-primary text-primary-foreground hover:bg-primary-hover active:bg-primary-active",
|
|
85
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary-hover active:bg-secondary-active",
|
|
86
86
|
outline:
|
|
87
|
-
"
|
|
88
|
-
ghost: "
|
|
89
|
-
destructive:
|
|
90
|
-
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/90 active:bg-destructive/80",
|
|
87
|
+
"bg-secondary text-secondary-foreground ring-1 ring-border ring-inset hover:bg-secondary-hover active:bg-secondary-active",
|
|
88
|
+
ghost: "bg-transparent text-foreground hover:bg-ghost-hover active:bg-ghost-active",
|
|
89
|
+
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive-hover active:bg-destructive-active",
|
|
91
90
|
};
|
|
92
91
|
</script>
|
|
@@ -1,33 +1,27 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<Stack is="section" gap="0" class="rounded-ui border border-border bg-card text-card-foreground
|
|
2
|
+
<Stack is="section" gap="0" class="w-full rounded-ui border border-border bg-card text-card-foreground">
|
|
3
3
|
<Stack
|
|
4
|
-
v-if="label ||
|
|
4
|
+
v-if="label || $slots.header || $slots.actions"
|
|
5
5
|
is="header"
|
|
6
6
|
direction="horizontal"
|
|
7
|
-
align="
|
|
7
|
+
align="center"
|
|
8
8
|
justify="between"
|
|
9
|
-
class="border-b border-border
|
|
9
|
+
class="shrink-0 border-b border-border"
|
|
10
10
|
>
|
|
11
11
|
<slot name="header">
|
|
12
|
-
<
|
|
13
|
-
<p v-if="label" class="text-xs font-semibold uppercase tracking-widest opacity-50">{{ label }}</p>
|
|
14
|
-
|
|
15
|
-
<h3 v-if="title" class="text-lg font-semibold leading-none">{{ title }}</h3>
|
|
16
|
-
|
|
17
|
-
<p v-if="description" class="text-sm opacity-70">{{ description }}</p>
|
|
18
|
-
</Stack>
|
|
12
|
+
<span v-if="label" class="min-w-0 flex-1 px-5 py-3 text-sm/5 font-normal"> {{ label }} </span>
|
|
19
13
|
</slot>
|
|
20
14
|
|
|
21
|
-
<Stack v-if="$slots.actions" direction="horizontal" align="center" class="shrink-0">
|
|
15
|
+
<Stack v-if="$slots.actions" direction="horizontal" align="center" class="ml-auto shrink-0">
|
|
22
16
|
<slot name="actions" />
|
|
23
17
|
</Stack>
|
|
24
18
|
</Stack>
|
|
25
19
|
|
|
26
|
-
<Stack gap="5" padding="5" class="flex-1">
|
|
20
|
+
<Stack gap="5" padding="5" class="min-h-0 flex-1">
|
|
27
21
|
<slot />
|
|
28
22
|
</Stack>
|
|
29
23
|
|
|
30
|
-
<Stack v-if="$slots.footer" is="footer" direction="horizontal" align="center"
|
|
24
|
+
<Stack v-if="$slots.footer" is="footer" direction="horizontal" align="center" class="shrink-0 px-5 pb-5">
|
|
31
25
|
<slot name="footer" />
|
|
32
26
|
</Stack>
|
|
33
27
|
</Stack>
|
|
@@ -37,5 +31,5 @@
|
|
|
37
31
|
import type { CardProps } from "../../components/card";
|
|
38
32
|
import Stack from "./stack.vue";
|
|
39
33
|
|
|
40
|
-
const { label
|
|
34
|
+
const { label } = defineProps<CardProps>();
|
|
41
35
|
</script>
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<Stack
|
|
2
|
+
<Stack
|
|
3
|
+
is="label"
|
|
4
|
+
direction="horizontal"
|
|
5
|
+
align="start"
|
|
6
|
+
gap="3"
|
|
7
|
+
class="group"
|
|
8
|
+
:class="interactionVariants[interaction]"
|
|
9
|
+
>
|
|
3
10
|
<input
|
|
4
11
|
class="peer sr-only"
|
|
5
12
|
type="checkbox"
|
|
@@ -12,8 +19,15 @@
|
|
|
12
19
|
|
|
13
20
|
<span
|
|
14
21
|
aria-hidden="true"
|
|
15
|
-
class="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-ui border
|
|
16
|
-
:class="
|
|
22
|
+
class="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-ui border peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-ring"
|
|
23
|
+
:class="[
|
|
24
|
+
modelValue ? 'border-transparent bg-primary' : 'border-input bg-background',
|
|
25
|
+
disabled
|
|
26
|
+
? ''
|
|
27
|
+
: modelValue
|
|
28
|
+
? 'group-hover:bg-primary-hover group-active:bg-primary-active'
|
|
29
|
+
: 'group-hover:bg-secondary-hover group-active:bg-secondary-active',
|
|
30
|
+
]"
|
|
17
31
|
>
|
|
18
32
|
<span
|
|
19
33
|
v-if="modelValue"
|
|
@@ -22,7 +36,7 @@
|
|
|
22
36
|
</span>
|
|
23
37
|
|
|
24
38
|
<Stack is="span" v-if="label || description || $slots.default" gap="0">
|
|
25
|
-
<span class="text-sm font-
|
|
39
|
+
<span class="text-sm font-normal"
|
|
26
40
|
><slot>{{ label }}</slot></span
|
|
27
41
|
>
|
|
28
42
|
<span v-if="description" class="text-sm opacity-60">{{ description }}</span>
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<Stack
|
|
2
|
+
<Stack
|
|
3
|
+
gap="5"
|
|
4
|
+
:padding="padding"
|
|
5
|
+
:class="sizes[size]"
|
|
6
|
+
class="mx-auto w-full overflow-y-scroll [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
|
7
|
+
>
|
|
3
8
|
<slot />
|
|
4
9
|
</Stack>
|
|
5
10
|
</template>
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Stack>
|
|
3
|
-
<span v-if="label" class="text-sm font-
|
|
3
|
+
<span v-if="label" class="text-sm font-normal">{{ label }}</span>
|
|
4
4
|
|
|
5
|
-
<div
|
|
6
|
-
class="grid h-10 grid-cols-[2.5rem_minmax(0,1fr)_2.5rem] items-center overflow-hidden rounded-ui border border-input"
|
|
7
|
-
>
|
|
5
|
+
<div class="relative grid grid-cols-[auto_minmax(0,1fr)_auto] items-center overflow-hidden rounded-ui">
|
|
8
6
|
<Button
|
|
9
7
|
class="text-lg"
|
|
10
8
|
size="icon"
|
|
@@ -26,6 +24,11 @@
|
|
|
26
24
|
aria-label="Increase"
|
|
27
25
|
@click="change(step)"
|
|
28
26
|
/>
|
|
27
|
+
|
|
28
|
+
<span
|
|
29
|
+
aria-hidden="true"
|
|
30
|
+
class="pointer-events-none absolute inset-0 rounded-ui ring-1 ring-input ring-inset"
|
|
31
|
+
/>
|
|
29
32
|
</div>
|
|
30
33
|
</Stack>
|
|
31
34
|
</template>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<dialog
|
|
3
3
|
ref="dialog"
|
|
4
|
-
class="m-
|
|
4
|
+
class="fixed inset-0 m-0 h-full max-h-none w-full max-w-none flex-col items-center overflow-y-auto border-0 bg-transparent p-5 backdrop:bg-black/50 open:flex [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
|
5
5
|
@close="$emit('update:modelValue', false)"
|
|
6
6
|
@click="$event.target === $event.currentTarget && ($event.currentTarget as HTMLDialogElement).close()"
|
|
7
7
|
>
|
|
8
|
-
<Card :
|
|
8
|
+
<Card :label="label" class="my-auto max-w-3xl shrink-0 shadow-2xl">
|
|
9
9
|
<template #actions>
|
|
10
10
|
<Button size="icon" variant="ghost" :icon-prefix="CloseIcon" :aria-label="closeLabel" @click="close" />
|
|
11
11
|
</template>
|
|
@@ -27,7 +27,7 @@ import Card from "./card.vue";
|
|
|
27
27
|
import { useFocusTrap } from "../utilities/use-focus-trap";
|
|
28
28
|
import { useScrollLock } from "../utilities/use-scroll-lock";
|
|
29
29
|
|
|
30
|
-
const { modelValue = false,
|
|
30
|
+
const { modelValue = false, label, closeLabel = "Close" } = defineProps<DialogProps>();
|
|
31
31
|
|
|
32
32
|
const emit = defineEmits<{
|
|
33
33
|
"update:modelValue": [value: boolean];
|
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<Stack is="fieldset" :disabled="disabled">
|
|
3
|
-
<legend v-if="label" class="mb-1
|
|
4
|
-
{{ label }}<span v-if="required" aria-hidden="true"> *</span>
|
|
2
|
+
<Stack is="fieldset" :disabled="disabled" :class="texts[text]">
|
|
3
|
+
<legend v-if="label" class="mb-1 font-normal">
|
|
4
|
+
{{ label }}<span v-if="required" aria-hidden="true" class="text-destructive"> *</span>
|
|
5
5
|
</legend>
|
|
6
6
|
|
|
7
7
|
<slot />
|
|
8
8
|
|
|
9
|
-
<p v-if="error" class="text-
|
|
9
|
+
<p v-if="error" class="text-destructive">
|
|
10
10
|
{{ error }}
|
|
11
11
|
</p>
|
|
12
|
-
<p v-else-if="description" class="
|
|
12
|
+
<p v-else-if="description" class="opacity-60">
|
|
13
13
|
{{ description }}
|
|
14
14
|
</p>
|
|
15
15
|
</Stack>
|
|
16
16
|
</template>
|
|
17
17
|
|
|
18
18
|
<script setup lang="ts">
|
|
19
|
-
import type { FieldProps } from "../../components/field";
|
|
19
|
+
import type { FieldProps, FieldText } from "../../components/field";
|
|
20
20
|
import Stack from "./stack.vue";
|
|
21
21
|
|
|
22
|
-
const { label, description, error, required = false, disabled = false } = defineProps<FieldProps>();
|
|
22
|
+
const { label, description, error, text = "sm", required = false, disabled = false } = defineProps<FieldProps>();
|
|
23
|
+
|
|
24
|
+
const texts: Record<FieldText, string> = {
|
|
25
|
+
sm: "text-sm",
|
|
26
|
+
md: "text-base",
|
|
27
|
+
};
|
|
23
28
|
</script>
|
|
@@ -3,15 +3,16 @@
|
|
|
3
3
|
is="span"
|
|
4
4
|
direction="horizontal"
|
|
5
5
|
align="center"
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
gap="0"
|
|
7
|
+
class="w-full cursor-text rounded-ui bg-secondary p-3 ring-1 ring-input ring-inset focus-within:ring-2 focus-within:ring-ring"
|
|
8
|
+
:class="[texts[text], interactionVariants[interaction]]"
|
|
8
9
|
>
|
|
9
10
|
<slot name="leading" />
|
|
10
11
|
|
|
11
12
|
<Icon v-if="iconPrefix" :is="iconPrefix" :size="iconSize" />
|
|
12
13
|
|
|
13
14
|
<input
|
|
14
|
-
class="w-full min-w-0 appearance-none bg-transparent
|
|
15
|
+
class="w-full min-w-0 appearance-none bg-transparent px-1 outline-none placeholder:opacity-50"
|
|
15
16
|
:value="modelValue"
|
|
16
17
|
:type="type"
|
|
17
18
|
:name="name"
|
|
@@ -32,13 +33,14 @@
|
|
|
32
33
|
<script setup lang="ts">
|
|
33
34
|
import { computed } from "vue";
|
|
34
35
|
import type { ControlInteraction } from "../../components/control.ts";
|
|
35
|
-
import type { InputProps } from "../../components/input";
|
|
36
|
+
import type { InputProps, InputText } from "../../components/input";
|
|
36
37
|
import Icon from "./icon.vue";
|
|
37
38
|
import Stack from "./stack.vue";
|
|
38
39
|
|
|
39
40
|
const {
|
|
40
41
|
modelValue,
|
|
41
42
|
type = "text",
|
|
43
|
+
text = "sm",
|
|
42
44
|
iconPrefix,
|
|
43
45
|
iconSuffix,
|
|
44
46
|
placeholder,
|
|
@@ -49,7 +51,12 @@ const {
|
|
|
49
51
|
disabled = false,
|
|
50
52
|
} = defineProps<InputProps>();
|
|
51
53
|
|
|
52
|
-
const iconSize = 20;
|
|
54
|
+
const iconSize = computed(() => (text === "sm" ? 20 : 24));
|
|
55
|
+
|
|
56
|
+
const texts: Record<InputText, string> = {
|
|
57
|
+
sm: "text-sm",
|
|
58
|
+
md: "text-base",
|
|
59
|
+
};
|
|
53
60
|
|
|
54
61
|
const emit = defineEmits<{
|
|
55
62
|
"update:modelValue": [value: string | number];
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div ref="container" class="relative
|
|
2
|
+
<div ref="container" class="relative w-full">
|
|
3
3
|
<Button variant="outline" :class="triggerClass" :layout="triggerLayout" :disabled="disabled" @click="toggle">
|
|
4
|
-
<slot name="trigger" :open="open" :close="close" :toggle="toggle">
|
|
4
|
+
<slot name="trigger" :open="open" :close="close" :toggle="toggle">
|
|
5
|
+
<span class="truncate px-1" :class="triggerLayout === 'center' ? '' : 'flex-1'">{{
|
|
6
|
+
triggerLabel
|
|
7
|
+
}}</span>
|
|
8
|
+
</slot>
|
|
5
9
|
</Button>
|
|
6
10
|
|
|
7
11
|
<div
|
|
8
12
|
v-if="isOpen"
|
|
9
|
-
class="absolute left-0 top-full z-10 mt-1 max-w-
|
|
13
|
+
class="absolute left-0 top-full z-10 mt-1 max-w-xs rounded-ui border border-border bg-popover text-popover-foreground shadow-xl"
|
|
10
14
|
:class="[contentClass, matchTriggerWidth ? 'w-full' : '']"
|
|
11
15
|
>
|
|
12
16
|
<slot :open="open" :close="close" :toggle="toggle" />
|
|
@@ -26,7 +30,7 @@ const {
|
|
|
26
30
|
triggerClass,
|
|
27
31
|
triggerLayout = "center",
|
|
28
32
|
contentClass = "flex flex-col gap-1 p-1",
|
|
29
|
-
matchTriggerWidth =
|
|
33
|
+
matchTriggerWidth = true,
|
|
30
34
|
disabled = false,
|
|
31
35
|
} = defineProps<PopoverProps>();
|
|
32
36
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Stack>
|
|
3
3
|
<Stack v-if="label" direction="horizontal" align="center" justify="between" gap="1" class="text-sm">
|
|
4
|
-
<span class="font-
|
|
4
|
+
<span class="font-normal">{{ label }}</span>
|
|
5
5
|
|
|
6
6
|
<span class="tabular-nums opacity-60">{{ Math.round(percent) }}%</span>
|
|
7
7
|
</Stack>
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
aria-valuemin="0"
|
|
17
17
|
:aria-valuemax="max"
|
|
18
18
|
>
|
|
19
|
-
<div class="h-full rounded-ui bg-primary
|
|
19
|
+
<div class="h-full rounded-ui bg-primary" :style="{ width: `${percent}%` }" />
|
|
20
20
|
</div>
|
|
21
21
|
</Stack>
|
|
22
22
|
</template>
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<Stack
|
|
2
|
+
<Stack
|
|
3
|
+
is="label"
|
|
4
|
+
direction="horizontal"
|
|
5
|
+
align="start"
|
|
6
|
+
gap="3"
|
|
7
|
+
class="group"
|
|
8
|
+
:class="interactionVariants[interaction]"
|
|
9
|
+
>
|
|
3
10
|
<input
|
|
4
11
|
class="peer sr-only"
|
|
5
12
|
type="radio"
|
|
@@ -13,14 +20,21 @@
|
|
|
13
20
|
|
|
14
21
|
<span
|
|
15
22
|
aria-hidden="true"
|
|
16
|
-
class="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-ui border
|
|
17
|
-
:class="
|
|
23
|
+
class="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-ui border peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-ring"
|
|
24
|
+
:class="[
|
|
25
|
+
modelValue === value ? 'border-primary bg-background' : 'border-input bg-background',
|
|
26
|
+
disabled ? '' : 'group-hover:bg-secondary-hover group-active:bg-secondary-active',
|
|
27
|
+
]"
|
|
18
28
|
>
|
|
19
|
-
<span
|
|
29
|
+
<span
|
|
30
|
+
v-if="modelValue === value"
|
|
31
|
+
class="size-2 rounded-ui bg-primary"
|
|
32
|
+
:class="disabled ? '' : 'group-hover:bg-secondary-foreground group-active:bg-secondary-foreground'"
|
|
33
|
+
/>
|
|
20
34
|
</span>
|
|
21
35
|
|
|
22
36
|
<Stack is="span" gap="0">
|
|
23
|
-
<span class="text-sm font-
|
|
37
|
+
<span class="text-sm font-normal">
|
|
24
38
|
<slot> {{ label }}</slot>
|
|
25
39
|
</span>
|
|
26
40
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Stack :class="interactionVariants[interaction]">
|
|
3
3
|
<Stack v-if="label" direction="horizontal" align="center" justify="between" gap="1" class="text-sm">
|
|
4
|
-
<span class="font-
|
|
4
|
+
<span class="font-normal"> {{ label }} </span>
|
|
5
5
|
|
|
6
6
|
<output class="tabular-nums opacity-60">
|
|
7
7
|
{{ modelValue }}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Stack>
|
|
3
|
-
<span v-if="label" class="text-sm font-
|
|
3
|
+
<span v-if="label" class="text-sm font-normal">{{ label }}</span>
|
|
4
4
|
|
|
5
5
|
<Stack direction="horizontal" align="center" gap="0" role="radiogroup" :aria-label="label">
|
|
6
6
|
<Button
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
:disabled="readonly || disabled"
|
|
17
17
|
@click="$emit('update:modelValue', value)"
|
|
18
18
|
>
|
|
19
|
-
<Icon :is="StarIcon" />
|
|
19
|
+
<Icon :is="StarIcon" :class="{ '[&_path]:fill-current': value <= modelValue }" />
|
|
20
20
|
</Button>
|
|
21
21
|
</Stack>
|
|
22
22
|
</Stack>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Stack>
|
|
3
|
-
<span v-if="label" class="text-sm font-
|
|
3
|
+
<span v-if="label" class="text-sm font-normal">
|
|
4
4
|
{{ label }}
|
|
5
5
|
</span>
|
|
6
6
|
|
|
@@ -10,22 +10,26 @@
|
|
|
10
10
|
<Stack
|
|
11
11
|
direction="horizontal"
|
|
12
12
|
align="center"
|
|
13
|
-
class="
|
|
13
|
+
class="w-full cursor-text rounded-ui bg-secondary p-3 text-sm font-semibold ring-1 ring-input ring-inset focus-within:ring-2 focus-within:ring-ring"
|
|
14
14
|
:class="disabled ? 'cursor-not-allowed opacity-50' : ''"
|
|
15
|
-
@click="
|
|
15
|
+
@click="
|
|
16
|
+
if (!disabled) {
|
|
17
|
+
open = true;
|
|
18
|
+
search?.focus();
|
|
19
|
+
}
|
|
20
|
+
"
|
|
16
21
|
>
|
|
17
22
|
<input
|
|
18
23
|
ref="search"
|
|
19
24
|
v-model="query"
|
|
20
25
|
role="combobox"
|
|
21
|
-
class="w-full min-w-0 bg-transparent
|
|
26
|
+
class="w-full min-w-0 bg-transparent px-1 outline-none placeholder:opacity-50"
|
|
22
27
|
:placeholder="placeholder ?? 'Select'"
|
|
23
28
|
:aria-expanded="open"
|
|
24
29
|
:aria-controls="listboxId"
|
|
25
30
|
:aria-activedescendant="activeId"
|
|
26
31
|
aria-autocomplete="list"
|
|
27
32
|
:disabled="disabled"
|
|
28
|
-
@focus="open = true"
|
|
29
33
|
@input="input"
|
|
30
34
|
@keydown.enter.prevent="confirm"
|
|
31
35
|
@keydown.escape="open = false"
|
|
@@ -33,24 +37,18 @@
|
|
|
33
37
|
@keydown.arrow-up.prevent="stepHighlight(-1)"
|
|
34
38
|
/>
|
|
35
39
|
|
|
36
|
-
<button
|
|
37
|
-
|
|
38
|
-
tabindex="-1"
|
|
39
|
-
class="shrink-0 opacity-60 transition-transform"
|
|
40
|
-
:class="open ? 'rotate-180' : ''"
|
|
41
|
-
:disabled="disabled"
|
|
42
|
-
@click.stop="open = !open"
|
|
43
|
-
>
|
|
44
|
-
<Icon :is="ArrowDownIcon" :size="iconSize" />
|
|
40
|
+
<button type="button" tabindex="-1" class="shrink-0" :disabled="disabled" @click.stop="open = !open">
|
|
41
|
+
<Icon :is="open ? ArrowUpIcon : ArrowDownIcon" :size="iconSize" />
|
|
45
42
|
</button>
|
|
46
43
|
</Stack>
|
|
47
44
|
|
|
48
|
-
<
|
|
45
|
+
<Stack
|
|
49
46
|
v-if="open"
|
|
50
47
|
:id="listboxId"
|
|
51
48
|
role="listbox"
|
|
52
49
|
:aria-label="label"
|
|
53
|
-
|
|
50
|
+
padding="1"
|
|
51
|
+
class="absolute inset-x-0 top-full z-10 mt-1 max-h-[min(16rem,50vh)] overflow-y-auto rounded-ui border border-border bg-popover text-popover-foreground shadow-xl [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
|
54
52
|
>
|
|
55
53
|
<Button
|
|
56
54
|
v-for="(option, index) in filtered"
|
|
@@ -66,7 +64,7 @@
|
|
|
66
64
|
>
|
|
67
65
|
<Stack direction="horizontal" align="center" justify="between" gap="1" class="w-full text-left">
|
|
68
66
|
<slot name="option" :option="option" :selected="option.value === modelValue">
|
|
69
|
-
{{ option.label }}
|
|
67
|
+
<span class="min-w-0 flex-1 truncate px-1">{{ option.label }}</span>
|
|
70
68
|
</slot>
|
|
71
69
|
|
|
72
70
|
<Icon v-if="option.value === modelValue" :is="TickIcon" :size="iconSize" />
|
|
@@ -76,13 +74,14 @@
|
|
|
76
74
|
<p v-if="filtered.length === 0" class="px-1 py-5 text-center text-sm opacity-60">
|
|
77
75
|
{{ emptyLabel }}
|
|
78
76
|
</p>
|
|
79
|
-
</
|
|
77
|
+
</Stack>
|
|
80
78
|
</div>
|
|
81
79
|
</Stack>
|
|
82
80
|
</template>
|
|
83
81
|
|
|
84
82
|
<script setup lang="ts">
|
|
85
83
|
import ArrowDownIcon from "~icons/hugeicons/arrow-down-01";
|
|
84
|
+
import ArrowUpIcon from "~icons/hugeicons/arrow-up-01";
|
|
86
85
|
import TickIcon from "~icons/hugeicons/tick-02";
|
|
87
86
|
import { computed, onUnmounted, ref, useId, useTemplateRef, watch } from "vue";
|
|
88
87
|
import type { SelectOption, SelectProps, SelectValue } from "../../components/select";
|
|
@@ -122,8 +121,10 @@ const selected = computed(() => options.find((option) => option.value === modelV
|
|
|
122
121
|
|
|
123
122
|
const query = ref(selected.value?.label ?? "");
|
|
124
123
|
|
|
124
|
+
const searching = ref(false);
|
|
125
|
+
|
|
125
126
|
const filtered = computed(() => {
|
|
126
|
-
const term = query.value.trim().toLowerCase();
|
|
127
|
+
const term = searching.value ? query.value.trim().toLowerCase() : "";
|
|
127
128
|
|
|
128
129
|
return term === "" ? options : options.filter((option) => option.label.toLowerCase().includes(term));
|
|
129
130
|
});
|
|
@@ -194,6 +195,8 @@ function confirm() {
|
|
|
194
195
|
}
|
|
195
196
|
|
|
196
197
|
function input() {
|
|
198
|
+
searching.value = true;
|
|
199
|
+
|
|
197
200
|
open.value = true;
|
|
198
201
|
|
|
199
202
|
highlighted.value = -1;
|
|
@@ -207,8 +210,6 @@ watch(selected, (value) => {
|
|
|
207
210
|
|
|
208
211
|
watch(open, (value) => {
|
|
209
212
|
if (value) {
|
|
210
|
-
query.value = "";
|
|
211
|
-
|
|
212
213
|
highlighted.value = filtered.value.findIndex((option) => option.value === modelValue);
|
|
213
214
|
|
|
214
215
|
document.addEventListener("pointerdown", handlePointerDown);
|
|
@@ -221,6 +222,8 @@ watch(open, (value) => {
|
|
|
221
222
|
|
|
222
223
|
highlighted.value = -1;
|
|
223
224
|
|
|
225
|
+
searching.value = false;
|
|
226
|
+
|
|
224
227
|
query.value = selected.value?.label ?? "";
|
|
225
228
|
}
|
|
226
229
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
-
class="shrink-0 bg-border"
|
|
3
|
+
class="shrink-0 self-center bg-border"
|
|
4
4
|
:class="variants[selected]"
|
|
5
5
|
:role="decorative ? 'none' : 'separator'"
|
|
6
6
|
:aria-orientation="decorative ? undefined : selected"
|
|
@@ -17,6 +17,6 @@ const selected = computed(() => variant ?? orientation);
|
|
|
17
17
|
|
|
18
18
|
const variants: Record<SeparatorOrientation, string> = {
|
|
19
19
|
horizontal: "h-px w-full",
|
|
20
|
-
vertical: "h-
|
|
20
|
+
vertical: "h-5 w-px",
|
|
21
21
|
};
|
|
22
22
|
</script>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<dialog
|
|
3
3
|
ref="sheet"
|
|
4
|
-
class="m-0 max-w-none border-0 bg-transparent p-0 backdrop:bg-black/50"
|
|
4
|
+
class="m-0 max-w-none border-0 bg-transparent p-0 backdrop:bg-black/50 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
|
5
5
|
:class="positions[side]"
|
|
6
6
|
@close="$emit('update:modelValue', false)"
|
|
7
7
|
@click="$event.target === $event.currentTarget && ($event.currentTarget as HTMLDialogElement).close()"
|
|
8
8
|
>
|
|
9
|
-
<Card :
|
|
9
|
+
<Card :label="label" :class="cardClasses[side]">
|
|
10
10
|
<template #actions>
|
|
11
11
|
<Button
|
|
12
12
|
size="icon"
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
/>
|
|
18
18
|
</template>
|
|
19
19
|
|
|
20
|
-
<div class="flex-1 overflow-auto">
|
|
20
|
+
<div class="min-h-0 flex-1 overflow-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
|
|
21
21
|
<slot />
|
|
22
22
|
</div>
|
|
23
23
|
|
|
@@ -37,7 +37,7 @@ import Card from "./card.vue";
|
|
|
37
37
|
import { useFocusTrap } from "../utilities/use-focus-trap";
|
|
38
38
|
import { useScrollLock } from "../utilities/use-scroll-lock";
|
|
39
39
|
|
|
40
|
-
const { modelValue = false, side = "right",
|
|
40
|
+
const { modelValue = false, side = "right", label, closeLabel = "Close" } = defineProps<SheetProps>();
|
|
41
41
|
|
|
42
42
|
defineEmits<{
|
|
43
43
|
"update:modelValue": [value: boolean];
|
|
@@ -1,21 +1,35 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<Stack
|
|
2
|
+
<Stack
|
|
3
|
+
is="label"
|
|
4
|
+
direction="horizontal"
|
|
5
|
+
align="center"
|
|
6
|
+
gap="3"
|
|
7
|
+
class="group"
|
|
8
|
+
:class="interactionVariants[interaction]"
|
|
9
|
+
>
|
|
3
10
|
<input v-if="name" type="hidden" :name="name" :value="modelValue ? 'on' : 'off'" />
|
|
4
11
|
|
|
5
12
|
<button
|
|
6
|
-
class="relative
|
|
7
|
-
:class="
|
|
13
|
+
class="relative flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-ui p-0.5 outline-ring focus-visible:outline-2 focus-visible:outline-offset-2 disabled:cursor-not-allowed"
|
|
14
|
+
:class="[
|
|
15
|
+
variants[state],
|
|
16
|
+
disabled
|
|
17
|
+
? ''
|
|
18
|
+
: modelValue
|
|
19
|
+
? 'group-hover:bg-primary-hover group-active:bg-primary-active'
|
|
20
|
+
: 'group-hover:bg-secondary-hover group-active:bg-secondary-active',
|
|
21
|
+
]"
|
|
8
22
|
type="button"
|
|
9
23
|
role="switch"
|
|
10
24
|
:aria-checked="modelValue"
|
|
11
25
|
:disabled="disabled"
|
|
12
26
|
@click="$emit('update:modelValue', !modelValue)"
|
|
13
27
|
>
|
|
14
|
-
<span class="block size-5 rounded-ui shadow
|
|
28
|
+
<span class="block size-5 rounded-ui shadow" :class="thumbVariants[state]" />
|
|
15
29
|
</button>
|
|
16
30
|
|
|
17
31
|
<Stack is="span" v-if="label || description || $slots.default" gap="0">
|
|
18
|
-
<span class="text-sm font-
|
|
32
|
+
<span class="text-sm font-normal">
|
|
19
33
|
<slot> {{ label }} </slot>
|
|
20
34
|
</span>
|
|
21
35
|
|
|
@@ -47,11 +61,11 @@ const interactionVariants: Record<ControlInteraction, string> = {
|
|
|
47
61
|
|
|
48
62
|
const thumbVariants: Record<SwitchState, string> = {
|
|
49
63
|
checked: "translate-x-5 bg-primary-foreground",
|
|
50
|
-
unchecked: "translate-x-
|
|
64
|
+
unchecked: "translate-x-0 bg-foreground",
|
|
51
65
|
};
|
|
52
66
|
|
|
53
67
|
const variants: Record<SwitchState, string> = {
|
|
54
68
|
checked: "bg-primary",
|
|
55
|
-
unchecked: "bg-input",
|
|
69
|
+
unchecked: "bg-background ring-1 ring-input ring-inset",
|
|
56
70
|
};
|
|
57
71
|
</script>
|
|
@@ -1,32 +1,42 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<Stack>
|
|
3
|
-
<span v-if="label" class="
|
|
4
|
-
{{ label }}<span v-if="required" aria-hidden="true"> *</span>
|
|
2
|
+
<Stack class="w-full" :class="texts[text]">
|
|
3
|
+
<span v-if="label" class="font-normal">
|
|
4
|
+
{{ label }}<span v-if="required" aria-hidden="true" class="text-destructive"> *</span>
|
|
5
5
|
</span>
|
|
6
6
|
|
|
7
7
|
<Stack
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
gap="3"
|
|
11
|
-
class="relative min-h-32 rounded-ui border border-input border-dashed p-5 text-center transition-colors"
|
|
8
|
+
gap="0"
|
|
9
|
+
class="relative rounded-ui"
|
|
12
10
|
:class="[variants[state], interactionVariants[interaction]]"
|
|
13
11
|
@dragenter.prevent="isOver = true"
|
|
14
12
|
@dragover.prevent="isOver = true"
|
|
15
13
|
@dragleave="leave"
|
|
16
14
|
@drop.prevent="drop"
|
|
17
15
|
>
|
|
18
|
-
<
|
|
19
|
-
<span class="
|
|
20
|
-
|
|
16
|
+
<Stack v-if="modelValue" direction="horizontal" align="center" justify="between" gap="0">
|
|
17
|
+
<span class="min-w-0 flex-1 p-3">
|
|
18
|
+
<span class="block truncate px-1">{{
|
|
19
|
+
"name" in modelValue ? modelValue.name : "File selected"
|
|
20
|
+
}}</span>
|
|
21
21
|
</span>
|
|
22
22
|
|
|
23
|
-
<Button
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
<Button
|
|
24
|
+
size="icon"
|
|
25
|
+
variant="secondary"
|
|
26
|
+
:text="text"
|
|
27
|
+
:icon-prefix="CloseIcon"
|
|
28
|
+
aria-label="Remove file"
|
|
29
|
+
:disabled="disabled"
|
|
30
|
+
@click="remove"
|
|
31
|
+
/>
|
|
32
|
+
</Stack>
|
|
33
|
+
<Stack v-else align="center" gap="3" padding="5" class="text-center">
|
|
34
|
+
<Icon :is="UploadIcon" :size="24" />
|
|
35
|
+
|
|
26
36
|
<span class="text-sm opacity-60"> Drag and drop a file here </span>
|
|
27
37
|
|
|
28
38
|
<Button label="Browse files" variant="outline" :disabled="disabled" @click="input?.click()" />
|
|
29
|
-
</
|
|
39
|
+
</Stack>
|
|
30
40
|
|
|
31
41
|
<input
|
|
32
42
|
ref="input"
|
|
@@ -38,20 +48,37 @@
|
|
|
38
48
|
:disabled="disabled"
|
|
39
49
|
@change="change"
|
|
40
50
|
/>
|
|
51
|
+
|
|
52
|
+
<span
|
|
53
|
+
aria-hidden="true"
|
|
54
|
+
class="pointer-events-none absolute inset-0 rounded-ui ring-1 ring-input ring-inset"
|
|
55
|
+
/>
|
|
41
56
|
</Stack>
|
|
42
57
|
|
|
43
|
-
<span v-if="description" class="
|
|
58
|
+
<span v-if="description" class="opacity-60">{{ description }}</span>
|
|
44
59
|
</Stack>
|
|
45
60
|
</template>
|
|
46
61
|
|
|
47
62
|
<script setup lang="ts">
|
|
63
|
+
import UploadIcon from "~icons/hugeicons/file-upload";
|
|
64
|
+
import CloseIcon from "~icons/hugeicons/multiplication-sign";
|
|
48
65
|
import { computed, ref, useTemplateRef } from "vue";
|
|
49
66
|
import type { ControlInteraction } from "../../components/control.ts";
|
|
50
|
-
import type { UploadProps, UploadState } from "../../components/upload";
|
|
67
|
+
import type { UploadProps, UploadState, UploadText } from "../../components/upload";
|
|
51
68
|
import Button from "./button.vue";
|
|
69
|
+
import Icon from "./icon.vue";
|
|
52
70
|
import Stack from "./stack.vue";
|
|
53
71
|
|
|
54
|
-
const {
|
|
72
|
+
const {
|
|
73
|
+
modelValue,
|
|
74
|
+
label,
|
|
75
|
+
description,
|
|
76
|
+
text = "sm",
|
|
77
|
+
accept,
|
|
78
|
+
name,
|
|
79
|
+
required = false,
|
|
80
|
+
disabled = false,
|
|
81
|
+
} = defineProps<UploadProps>();
|
|
55
82
|
|
|
56
83
|
const emit = defineEmits<{
|
|
57
84
|
"update:modelValue": [value?: Blob | File];
|
|
@@ -71,10 +98,15 @@ const interactionVariants: Record<ControlInteraction, string> = {
|
|
|
71
98
|
};
|
|
72
99
|
|
|
73
100
|
const variants: Record<UploadState, string> = {
|
|
74
|
-
idle: "bg-
|
|
101
|
+
idle: "bg-secondary",
|
|
75
102
|
over: "bg-accent",
|
|
76
103
|
};
|
|
77
104
|
|
|
105
|
+
const texts: Record<UploadText, string> = {
|
|
106
|
+
sm: "text-sm",
|
|
107
|
+
md: "text-base",
|
|
108
|
+
};
|
|
109
|
+
|
|
78
110
|
function change() {
|
|
79
111
|
const file = input.value?.files?.item(0);
|
|
80
112
|
|