windly-ui 1.0.2 → 1.0.3
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/{ui-library/dist → dist}/module.json +2 -2
- package/dist/module.mjs +26 -0
- package/dist/runtime/components/Accordion.d.vue.ts +39 -0
- package/dist/runtime/components/Accordion.vue +92 -0
- package/dist/runtime/components/Accordion.vue.d.ts +39 -0
- package/dist/runtime/components/Avatar.d.vue.ts +141 -0
- package/dist/runtime/components/Avatar.vue +72 -0
- package/dist/runtime/components/Avatar.vue.d.ts +141 -0
- package/dist/runtime/components/Badge.d.vue.ts +133 -0
- package/dist/runtime/components/Badge.vue +99 -0
- package/dist/runtime/components/Badge.vue.d.ts +133 -0
- package/dist/runtime/components/Breadcrumbs.d.vue.ts +83 -0
- package/dist/runtime/components/Breadcrumbs.vue +116 -0
- package/dist/runtime/components/Breadcrumbs.vue.d.ts +83 -0
- package/dist/runtime/components/Button.d.vue.ts +199 -0
- package/dist/runtime/components/Button.vue +125 -0
- package/dist/runtime/components/Button.vue.d.ts +199 -0
- package/dist/runtime/components/Card.d.vue.ts +83 -0
- package/dist/runtime/components/Card.vue +71 -0
- package/dist/runtime/components/Card.vue.d.ts +83 -0
- package/dist/runtime/components/Checkbox.d.vue.ts +190 -0
- package/dist/runtime/components/Checkbox.vue +125 -0
- package/dist/runtime/components/Checkbox.vue.d.ts +190 -0
- package/dist/runtime/components/CodeBlock.d.vue.ts +16 -0
- package/dist/runtime/components/CodeBlock.vue +75 -0
- package/dist/runtime/components/CodeBlock.vue.d.ts +16 -0
- package/dist/runtime/components/ColorPicker.d.vue.ts +91 -0
- package/dist/runtime/components/ColorPicker.vue +244 -0
- package/dist/runtime/components/ColorPicker.vue.d.ts +91 -0
- package/dist/runtime/components/Date.d.vue.ts +137 -0
- package/dist/runtime/components/Date.vue +201 -0
- package/dist/runtime/components/Date.vue.d.ts +137 -0
- package/dist/runtime/components/Dialog.d.vue.ts +103 -0
- package/dist/runtime/components/Dialog.vue +152 -0
- package/dist/runtime/components/Dialog.vue.d.ts +103 -0
- package/dist/runtime/components/Divider.d.vue.ts +51 -0
- package/dist/runtime/components/Divider.vue +52 -0
- package/dist/runtime/components/Divider.vue.d.ts +51 -0
- package/dist/runtime/components/Drawer.d.vue.ts +30 -0
- package/dist/runtime/components/Drawer.vue +43 -0
- package/dist/runtime/components/Drawer.vue.d.ts +30 -0
- package/dist/runtime/components/Dropdown.d.vue.ts +34 -0
- package/dist/runtime/components/Dropdown.vue +127 -0
- package/dist/runtime/components/Dropdown.vue.d.ts +34 -0
- package/dist/runtime/components/FileUploader.d.vue.ts +58 -0
- package/dist/runtime/components/FileUploader.vue +202 -0
- package/dist/runtime/components/FileUploader.vue.d.ts +58 -0
- package/dist/runtime/components/Icon.d.vue.ts +41 -0
- package/dist/runtime/components/Icon.vue +66 -0
- package/dist/runtime/components/Icon.vue.d.ts +41 -0
- package/dist/runtime/components/Image.d.vue.ts +77 -0
- package/dist/runtime/components/Image.vue +61 -0
- package/dist/runtime/components/Image.vue.d.ts +77 -0
- package/dist/runtime/components/Input.d.vue.ts +66 -0
- package/dist/runtime/components/Input.vue +233 -0
- package/dist/runtime/components/Input.vue.d.ts +66 -0
- package/dist/runtime/components/Radio.d.vue.ts +41 -0
- package/dist/runtime/components/Radio.vue +222 -0
- package/dist/runtime/components/Radio.vue.d.ts +41 -0
- package/dist/runtime/components/ScrollArea.d.vue.ts +79 -0
- package/dist/runtime/components/ScrollArea.vue +36 -0
- package/dist/runtime/components/ScrollArea.vue.d.ts +79 -0
- package/dist/runtime/components/Select.d.vue.ts +43 -0
- package/dist/runtime/components/Select.vue +200 -0
- package/dist/runtime/components/Select.vue.d.ts +43 -0
- package/dist/runtime/components/Stepper.d.vue.ts +46 -0
- package/dist/runtime/components/Stepper.vue +218 -0
- package/dist/runtime/components/Stepper.vue.d.ts +46 -0
- package/dist/runtime/components/TabPanel.d.vue.ts +17 -0
- package/dist/runtime/components/TabPanel.vue +21 -0
- package/dist/runtime/components/TabPanel.vue.d.ts +17 -0
- package/dist/runtime/components/Table.d.vue.ts +40 -0
- package/dist/runtime/components/Table.vue +92 -0
- package/dist/runtime/components/Table.vue.d.ts +40 -0
- package/dist/runtime/components/Tabs.d.vue.ts +20 -0
- package/dist/runtime/components/Tabs.vue +62 -0
- package/dist/runtime/components/Tabs.vue.d.ts +20 -0
- package/dist/runtime/components/Textarea.d.vue.ts +208 -0
- package/dist/runtime/components/Textarea.vue +185 -0
- package/dist/runtime/components/Textarea.vue.d.ts +208 -0
- package/dist/runtime/components/Toggle.d.vue.ts +36 -0
- package/dist/runtime/components/Toggle.vue +173 -0
- package/dist/runtime/components/Toggle.vue.d.ts +36 -0
- package/dist/runtime/components/Tooltip.d.vue.ts +63 -0
- package/dist/runtime/components/Tooltip.vue +144 -0
- package/dist/runtime/components/Tooltip.vue.d.ts +63 -0
- package/dist/runtime/components/uiProps.d.ts +38 -0
- package/dist/runtime/components/uiProps.js +11 -0
- package/dist/runtime/components/useUiClasses.d.ts +43 -0
- package/dist/runtime/components/useUiClasses.js +124 -0
- package/dist/runtime/composables/plugin.d.ts +2 -0
- package/dist/runtime/composables/plugin.js +3 -0
- package/dist/runtime/tailwind-color.d.ts +2 -0
- package/dist/runtime/tailwind-color.js +5859 -0
- package/package.json +12 -46
- package/LICENSE +0 -21
- package/README.md +0 -232
- package/ui-library/dist/module.mjs +0 -22
- /package/{ui-library/dist → dist}/module.d.mts +0 -0
- /package/{ui-library/dist → dist}/types.d.mts +0 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
defineOptions({
|
|
3
|
+
name: "UIDrawer"
|
|
4
|
+
});
|
|
5
|
+
defineProps({
|
|
6
|
+
isOpen: { type: Boolean, required: true },
|
|
7
|
+
direction: { type: String, required: false, default: "right" },
|
|
8
|
+
background: { type: String, required: false, default: "bg-white dark:bg-gray-900" },
|
|
9
|
+
width: { type: String, required: false, default: "w-80" },
|
|
10
|
+
padding: { type: String, required: false, default: "p-6" }
|
|
11
|
+
});
|
|
12
|
+
const emit = defineEmits(["close"]);
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<div
|
|
17
|
+
class="fixed inset-0 z-40 bg-black/40 transition-opacity duration-300"
|
|
18
|
+
:class="[
|
|
19
|
+
isOpen ? 'opacity-100 visible' : 'opacity-0 invisible'
|
|
20
|
+
]"
|
|
21
|
+
@click="emit('close')"
|
|
22
|
+
/>
|
|
23
|
+
|
|
24
|
+
<div
|
|
25
|
+
class="fixed top-0 bottom-0 z-50 shadow-2xl transition-transform duration-500 ease-in-out"
|
|
26
|
+
:class="[
|
|
27
|
+
width,
|
|
28
|
+
padding,
|
|
29
|
+
background,
|
|
30
|
+
direction === 'left' ? 'left-0' : 'right-0',
|
|
31
|
+
direction === 'left' ? isOpen ? 'translate-x-0' : '-translate-x-full' : isOpen ? 'translate-x-0' : 'translate-x-full'
|
|
32
|
+
]"
|
|
33
|
+
>
|
|
34
|
+
<button
|
|
35
|
+
class="absolute top-4 right-4 text-xl font-bold"
|
|
36
|
+
@click="emit('close')"
|
|
37
|
+
>
|
|
38
|
+
✕
|
|
39
|
+
</button>
|
|
40
|
+
|
|
41
|
+
<slot />
|
|
42
|
+
</div>
|
|
43
|
+
</template>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
type DrawerDirection = 'left' | 'right';
|
|
2
|
+
interface Props {
|
|
3
|
+
isOpen: boolean;
|
|
4
|
+
direction?: DrawerDirection;
|
|
5
|
+
background?: string;
|
|
6
|
+
width?: string;
|
|
7
|
+
padding?: string;
|
|
8
|
+
}
|
|
9
|
+
declare var __VLS_1: {};
|
|
10
|
+
type __VLS_Slots = {} & {
|
|
11
|
+
default?: (props: typeof __VLS_1) => any;
|
|
12
|
+
};
|
|
13
|
+
declare const __VLS_base: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
14
|
+
close: () => any;
|
|
15
|
+
}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
|
|
16
|
+
onClose?: (() => any) | undefined;
|
|
17
|
+
}>, {
|
|
18
|
+
width: string;
|
|
19
|
+
background: string;
|
|
20
|
+
direction: DrawerDirection;
|
|
21
|
+
padding: string;
|
|
22
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
23
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
24
|
+
declare const _default: typeof __VLS_export;
|
|
25
|
+
export default _default;
|
|
26
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
27
|
+
new (): {
|
|
28
|
+
$slots: S;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
type DropdownAlign = 'left' | 'right';
|
|
2
|
+
type Rounded = 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full';
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
align?: DropdownAlign;
|
|
5
|
+
width?: string;
|
|
6
|
+
menuBgClass?: string;
|
|
7
|
+
rounded?: Rounded;
|
|
8
|
+
bordered?: boolean;
|
|
9
|
+
borderColor?: string;
|
|
10
|
+
flat?: boolean;
|
|
11
|
+
};
|
|
12
|
+
declare var __VLS_1: {}, __VLS_9: {};
|
|
13
|
+
type __VLS_Slots = {} & {
|
|
14
|
+
trigger?: (props: typeof __VLS_1) => any;
|
|
15
|
+
} & {
|
|
16
|
+
menu?: (props: typeof __VLS_9) => any;
|
|
17
|
+
};
|
|
18
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
19
|
+
rounded: Rounded;
|
|
20
|
+
borderColor: string;
|
|
21
|
+
align: DropdownAlign;
|
|
22
|
+
flat: boolean;
|
|
23
|
+
bordered: boolean;
|
|
24
|
+
width: string;
|
|
25
|
+
menuBgClass: string;
|
|
26
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
27
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
28
|
+
declare const _default: typeof __VLS_export;
|
|
29
|
+
export default _default;
|
|
30
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
31
|
+
new (): {
|
|
32
|
+
$slots: S;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
defineOptions({
|
|
3
|
+
name: "UIDropdown"
|
|
4
|
+
});
|
|
5
|
+
import {
|
|
6
|
+
computed,
|
|
7
|
+
onBeforeUnmount,
|
|
8
|
+
onMounted,
|
|
9
|
+
ref
|
|
10
|
+
} from "vue";
|
|
11
|
+
const props = defineProps({
|
|
12
|
+
align: { type: String, required: false, default: "left" },
|
|
13
|
+
width: { type: String, required: false, default: "w-48" },
|
|
14
|
+
menuBgClass: { type: String, required: false, default: "bg-white dark:bg-gray-800" },
|
|
15
|
+
rounded: { type: String, required: false, default: "xl" },
|
|
16
|
+
bordered: { type: Boolean, required: false, default: false },
|
|
17
|
+
borderColor: { type: String, required: false, default: "gray-200" },
|
|
18
|
+
flat: { type: Boolean, required: false, default: false }
|
|
19
|
+
});
|
|
20
|
+
const isOpen = ref(false);
|
|
21
|
+
const dropdownRef = ref(null);
|
|
22
|
+
const isHex = (val) => {
|
|
23
|
+
return val.startsWith("#") || val.startsWith("rgb") || val.startsWith("hsl");
|
|
24
|
+
};
|
|
25
|
+
const toggle = () => {
|
|
26
|
+
isOpen.value = !isOpen.value;
|
|
27
|
+
};
|
|
28
|
+
const close = () => {
|
|
29
|
+
isOpen.value = false;
|
|
30
|
+
};
|
|
31
|
+
const handleClickOutside = (event) => {
|
|
32
|
+
const target = event.target;
|
|
33
|
+
if (dropdownRef.value && !dropdownRef.value.contains(
|
|
34
|
+
target
|
|
35
|
+
)) {
|
|
36
|
+
close();
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
onMounted(() => {
|
|
40
|
+
document.addEventListener(
|
|
41
|
+
"click",
|
|
42
|
+
handleClickOutside
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
onBeforeUnmount(() => {
|
|
46
|
+
document.removeEventListener(
|
|
47
|
+
"click",
|
|
48
|
+
handleClickOutside
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
const roundedClass = computed(
|
|
52
|
+
() => {
|
|
53
|
+
const map = {
|
|
54
|
+
none: "rounded-none",
|
|
55
|
+
sm: "rounded-sm",
|
|
56
|
+
md: "rounded-md",
|
|
57
|
+
lg: "rounded-lg",
|
|
58
|
+
xl: "rounded-xl",
|
|
59
|
+
full: "rounded-full"
|
|
60
|
+
};
|
|
61
|
+
return map[props.rounded];
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
const dropdownClasses = computed(() => [
|
|
65
|
+
"absolute mt-2 z-50 transition-all overflow-hidden",
|
|
66
|
+
props.align === "right" ? "right-0" : "left-0",
|
|
67
|
+
props.width,
|
|
68
|
+
roundedClass.value,
|
|
69
|
+
props.bordered && !isHex(props.borderColor) ? `border border-${props.borderColor}` : "",
|
|
70
|
+
props.flat ? "shadow-none" : "shadow-lg"
|
|
71
|
+
]);
|
|
72
|
+
const dropdownStyle = computed(
|
|
73
|
+
() => {
|
|
74
|
+
const style = {};
|
|
75
|
+
if (props.bordered && isHex(props.borderColor)) {
|
|
76
|
+
style.border = `1px solid ${props.borderColor}`;
|
|
77
|
+
}
|
|
78
|
+
return style;
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
const menuClasses = computed(
|
|
82
|
+
() => [
|
|
83
|
+
"p-2",
|
|
84
|
+
roundedClass.value,
|
|
85
|
+
props.menuBgClass
|
|
86
|
+
]
|
|
87
|
+
);
|
|
88
|
+
</script>
|
|
89
|
+
|
|
90
|
+
<template>
|
|
91
|
+
<div
|
|
92
|
+
ref="dropdownRef"
|
|
93
|
+
class="relative inline-block"
|
|
94
|
+
>
|
|
95
|
+
<!-- Trigger -->
|
|
96
|
+
<div @click="toggle">
|
|
97
|
+
<slot name="trigger" />
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<!-- Dropdown -->
|
|
101
|
+
<transition
|
|
102
|
+
name="fade-slide"
|
|
103
|
+
>
|
|
104
|
+
<div
|
|
105
|
+
v-if="isOpen"
|
|
106
|
+
:class="
|
|
107
|
+
dropdownClasses
|
|
108
|
+
"
|
|
109
|
+
:style="
|
|
110
|
+
dropdownStyle
|
|
111
|
+
"
|
|
112
|
+
>
|
|
113
|
+
<div
|
|
114
|
+
:class="
|
|
115
|
+
menuClasses
|
|
116
|
+
"
|
|
117
|
+
>
|
|
118
|
+
<slot name="menu" />
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</transition>
|
|
122
|
+
</div>
|
|
123
|
+
</template>
|
|
124
|
+
|
|
125
|
+
<style scoped>
|
|
126
|
+
.fade-slide-enter-active,.fade-slide-leave-active{transition:all .2s ease}.fade-slide-enter-from{opacity:0;transform:translateY(-8px)}.fade-slide-enter-to,.fade-slide-leave-from{opacity:1;transform:translateY(0)}.fade-slide-leave-to{opacity:0;transform:translateY(-8px)}
|
|
127
|
+
</style>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
type DropdownAlign = 'left' | 'right';
|
|
2
|
+
type Rounded = 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full';
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
align?: DropdownAlign;
|
|
5
|
+
width?: string;
|
|
6
|
+
menuBgClass?: string;
|
|
7
|
+
rounded?: Rounded;
|
|
8
|
+
bordered?: boolean;
|
|
9
|
+
borderColor?: string;
|
|
10
|
+
flat?: boolean;
|
|
11
|
+
};
|
|
12
|
+
declare var __VLS_1: {}, __VLS_9: {};
|
|
13
|
+
type __VLS_Slots = {} & {
|
|
14
|
+
trigger?: (props: typeof __VLS_1) => any;
|
|
15
|
+
} & {
|
|
16
|
+
menu?: (props: typeof __VLS_9) => any;
|
|
17
|
+
};
|
|
18
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
19
|
+
rounded: Rounded;
|
|
20
|
+
borderColor: string;
|
|
21
|
+
align: DropdownAlign;
|
|
22
|
+
flat: boolean;
|
|
23
|
+
bordered: boolean;
|
|
24
|
+
width: string;
|
|
25
|
+
menuBgClass: string;
|
|
26
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
27
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
28
|
+
declare const _default: typeof __VLS_export;
|
|
29
|
+
export default _default;
|
|
30
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
31
|
+
new (): {
|
|
32
|
+
$slots: S;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
interface UploadedFile {
|
|
2
|
+
file: File;
|
|
3
|
+
name: string;
|
|
4
|
+
size: number;
|
|
5
|
+
type: string;
|
|
6
|
+
}
|
|
7
|
+
type Separator = 'solid' | 'dashed' | 'dotted';
|
|
8
|
+
type Rounded = 'none' | 'sm' | 'md' | 'lg' | 'xl';
|
|
9
|
+
type __VLS_Props = {
|
|
10
|
+
modelValue?: UploadedFile[];
|
|
11
|
+
header?: string;
|
|
12
|
+
subHeader?: string;
|
|
13
|
+
fileType?: string;
|
|
14
|
+
multiple?: boolean;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
bordered?: boolean;
|
|
17
|
+
flat?: boolean;
|
|
18
|
+
rounded?: Rounded;
|
|
19
|
+
borderColor?: string;
|
|
20
|
+
bgColor?: string;
|
|
21
|
+
separator?: Separator;
|
|
22
|
+
dense?: boolean;
|
|
23
|
+
fileRemoverIcon?: string;
|
|
24
|
+
fileSizeLimitKb?: number;
|
|
25
|
+
};
|
|
26
|
+
declare var __VLS_1: {};
|
|
27
|
+
type __VLS_Slots = {} & {
|
|
28
|
+
content?: (props: typeof __VLS_1) => any;
|
|
29
|
+
};
|
|
30
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
31
|
+
"update:modelValue": (value: UploadedFile[]) => any;
|
|
32
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
33
|
+
"onUpdate:modelValue"?: ((value: UploadedFile[]) => any) | undefined;
|
|
34
|
+
}>, {
|
|
35
|
+
modelValue: UploadedFile[];
|
|
36
|
+
disabled: boolean;
|
|
37
|
+
dense: boolean;
|
|
38
|
+
header: string;
|
|
39
|
+
rounded: Rounded;
|
|
40
|
+
borderColor: string;
|
|
41
|
+
separator: Separator;
|
|
42
|
+
flat: boolean;
|
|
43
|
+
bordered: boolean;
|
|
44
|
+
subHeader: string;
|
|
45
|
+
fileType: string;
|
|
46
|
+
multiple: boolean;
|
|
47
|
+
bgColor: string;
|
|
48
|
+
fileRemoverIcon: string;
|
|
49
|
+
fileSizeLimitKb: number;
|
|
50
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
51
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
52
|
+
declare const _default: typeof __VLS_export;
|
|
53
|
+
export default _default;
|
|
54
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
55
|
+
new (): {
|
|
56
|
+
$slots: S;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed, ref } from "vue";
|
|
3
|
+
defineOptions({
|
|
4
|
+
name: "UIFileUploader"
|
|
5
|
+
});
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
modelValue: { type: Array, required: false, default: () => [] },
|
|
8
|
+
header: { type: String, required: false, default: "Upload Files" },
|
|
9
|
+
subHeader: { type: String, required: false, default: "Supports document uploads" },
|
|
10
|
+
fileType: { type: String, required: false, default: "*" },
|
|
11
|
+
multiple: { type: Boolean, required: false, default: true },
|
|
12
|
+
disabled: { type: Boolean, required: false, default: false },
|
|
13
|
+
bordered: { type: Boolean, required: false, default: true },
|
|
14
|
+
flat: { type: Boolean, required: false, default: false },
|
|
15
|
+
rounded: { type: String, required: false, default: "xl" },
|
|
16
|
+
borderColor: { type: String, required: false, default: "gray-300" },
|
|
17
|
+
bgColor: { type: String, required: false, default: "white" },
|
|
18
|
+
separator: { type: String, required: false, default: "dashed" },
|
|
19
|
+
dense: { type: Boolean, required: false, default: false },
|
|
20
|
+
fileRemoverIcon: { type: String, required: false, default: "delete" },
|
|
21
|
+
fileSizeLimitKb: { type: Number, required: false, default: 2048 }
|
|
22
|
+
});
|
|
23
|
+
const emit = defineEmits(["update:modelValue"]);
|
|
24
|
+
const fileInput = ref(
|
|
25
|
+
null
|
|
26
|
+
);
|
|
27
|
+
const errorMessage = ref("");
|
|
28
|
+
const files = computed({
|
|
29
|
+
get: () => props.modelValue,
|
|
30
|
+
set: (value) => emit(
|
|
31
|
+
"update:modelValue",
|
|
32
|
+
value
|
|
33
|
+
)
|
|
34
|
+
});
|
|
35
|
+
const isHex = (val) => val.startsWith("#");
|
|
36
|
+
const roundedClasses = computed(
|
|
37
|
+
() => {
|
|
38
|
+
const map = {
|
|
39
|
+
none: "",
|
|
40
|
+
sm: "rounded-sm",
|
|
41
|
+
md: "rounded-md",
|
|
42
|
+
lg: "rounded-lg",
|
|
43
|
+
xl: "rounded-xl"
|
|
44
|
+
};
|
|
45
|
+
return map[props.rounded];
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
const separatorClasses = computed(
|
|
49
|
+
() => {
|
|
50
|
+
switch (props.separator) {
|
|
51
|
+
case "solid":
|
|
52
|
+
return "border-solid";
|
|
53
|
+
case "dotted":
|
|
54
|
+
return "border-dotted";
|
|
55
|
+
case "dashed":
|
|
56
|
+
default:
|
|
57
|
+
return "border-dashed";
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
const uploaderClasses = computed(
|
|
62
|
+
() => [
|
|
63
|
+
"border-2 transition-all duration-300 flex flex-col items-center justify-center cursor-pointer",
|
|
64
|
+
roundedClasses.value,
|
|
65
|
+
separatorClasses.value,
|
|
66
|
+
props.bordered ? !isHex(props.borderColor) ? `border-${props.borderColor}` : "" : "border-transparent",
|
|
67
|
+
props.flat ? "shadow-none" : "shadow-md",
|
|
68
|
+
!isHex(props.bgColor) ? `bg-${props.bgColor}` : "",
|
|
69
|
+
props.dense ? "p-4" : "p-8",
|
|
70
|
+
props.disabled ? "opacity-50 pointer-events-none" : "hover:shadow-lg"
|
|
71
|
+
]
|
|
72
|
+
);
|
|
73
|
+
const uploaderStyle = computed(
|
|
74
|
+
() => {
|
|
75
|
+
const style = {};
|
|
76
|
+
if (isHex(props.borderColor)) {
|
|
77
|
+
style.borderColor = props.borderColor;
|
|
78
|
+
}
|
|
79
|
+
if (isHex(props.bgColor)) {
|
|
80
|
+
style.backgroundColor = props.bgColor;
|
|
81
|
+
}
|
|
82
|
+
return style;
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
const triggerFileInput = () => {
|
|
86
|
+
fileInput.value?.click();
|
|
87
|
+
};
|
|
88
|
+
const createFileObject = (file) => ({
|
|
89
|
+
file,
|
|
90
|
+
name: file.name,
|
|
91
|
+
size: file.size,
|
|
92
|
+
type: file.type
|
|
93
|
+
});
|
|
94
|
+
const handleFiles = (event) => {
|
|
95
|
+
const selectedFiles = "dataTransfer" in event && event.dataTransfer ? Array.from(
|
|
96
|
+
event.dataTransfer.files
|
|
97
|
+
) : Array.from(
|
|
98
|
+
event.target.files || []
|
|
99
|
+
);
|
|
100
|
+
const validFiles = selectedFiles.filter((file) => {
|
|
101
|
+
const fileSizeInKb = file.size / 1024;
|
|
102
|
+
if (fileSizeInKb > props.fileSizeLimitKb) {
|
|
103
|
+
errorMessage.value = `File "${file.name}" exceeds ${props.fileSizeLimitKb} KB`;
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
return true;
|
|
107
|
+
});
|
|
108
|
+
const mapped = validFiles.map(
|
|
109
|
+
createFileObject
|
|
110
|
+
);
|
|
111
|
+
files.value = props.multiple ? [
|
|
112
|
+
...files.value,
|
|
113
|
+
...mapped
|
|
114
|
+
] : mapped;
|
|
115
|
+
if (mapped.length > 0) {
|
|
116
|
+
errorMessage.value = "";
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const removeFile = (index) => {
|
|
120
|
+
files.value = files.value.filter(
|
|
121
|
+
(_, i) => i !== index
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
</script>
|
|
125
|
+
|
|
126
|
+
<template>
|
|
127
|
+
<div class="max-w-lg mx-auto">
|
|
128
|
+
<!-- Header -->
|
|
129
|
+
<div class="mb-6 text-center">
|
|
130
|
+
<h2 class="text-2xl font-bold text-gray-800 dark:text-white">
|
|
131
|
+
{{ header }}
|
|
132
|
+
</h2>
|
|
133
|
+
|
|
134
|
+
<p class="text-sm text-gray-500">
|
|
135
|
+
{{ subHeader }}
|
|
136
|
+
</p>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
<!-- Hidden Input -->
|
|
140
|
+
<input ref="fileInput" type="file" class="hidden" :accept="fileType" :multiple="multiple" @change="handleFiles">
|
|
141
|
+
|
|
142
|
+
<!-- Dropzone -->
|
|
143
|
+
<div :class="uploaderClasses" :style="uploaderStyle" @click="triggerFileInput" @dragover.prevent @drop.prevent="
|
|
144
|
+
handleFiles($event)
|
|
145
|
+
">
|
|
146
|
+
<slot name="content">
|
|
147
|
+
<p class="text-gray-600 mb-4">
|
|
148
|
+
{{
|
|
149
|
+
multiple ? "Drag & drop files here" : "Drag & drop file here"
|
|
150
|
+
}}
|
|
151
|
+
</p>
|
|
152
|
+
|
|
153
|
+
<button type="button" class="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
|
|
154
|
+
{{
|
|
155
|
+
multiple ? "Browse Files" : "Browse File"
|
|
156
|
+
}}
|
|
157
|
+
</button>
|
|
158
|
+
</slot>
|
|
159
|
+
</div>
|
|
160
|
+
|
|
161
|
+
<!-- Uploaded Files -->
|
|
162
|
+
<div v-if="files.length" class="mt-6">
|
|
163
|
+
<h3 class="text-lg font-semibold mb-4">
|
|
164
|
+
Uploaded Files
|
|
165
|
+
</h3>
|
|
166
|
+
|
|
167
|
+
<ul class="space-y-2">
|
|
168
|
+
<li v-for="(file, index) in files" :key="index"
|
|
169
|
+
class="flex items-center justify-between p-4 rounded-lg border border-gray-200 bg-gray-50">
|
|
170
|
+
<div class="flex items-center gap-3">
|
|
171
|
+
<span class="material-icons text-blue-500">
|
|
172
|
+
description
|
|
173
|
+
</span>
|
|
174
|
+
|
|
175
|
+
<span>
|
|
176
|
+
{{ file.name }}
|
|
177
|
+
</span>
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<button type="button" class="text-red-500 hover:text-red-700" @click.stop="
|
|
181
|
+
removeFile(index)
|
|
182
|
+
">
|
|
183
|
+
<span class="material-icons">
|
|
184
|
+
{{
|
|
185
|
+
fileRemoverIcon
|
|
186
|
+
}}
|
|
187
|
+
</span>
|
|
188
|
+
</button>
|
|
189
|
+
</li>
|
|
190
|
+
</ul>
|
|
191
|
+
</div>
|
|
192
|
+
|
|
193
|
+
<!-- Error -->
|
|
194
|
+
<div v-if="errorMessage" class="mt-4 text-sm text-red-500">
|
|
195
|
+
{{ errorMessage }}
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
</template>
|
|
199
|
+
|
|
200
|
+
<style scoped>
|
|
201
|
+
.material-icons{font-size:20px}
|
|
202
|
+
</style>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
interface UploadedFile {
|
|
2
|
+
file: File;
|
|
3
|
+
name: string;
|
|
4
|
+
size: number;
|
|
5
|
+
type: string;
|
|
6
|
+
}
|
|
7
|
+
type Separator = 'solid' | 'dashed' | 'dotted';
|
|
8
|
+
type Rounded = 'none' | 'sm' | 'md' | 'lg' | 'xl';
|
|
9
|
+
type __VLS_Props = {
|
|
10
|
+
modelValue?: UploadedFile[];
|
|
11
|
+
header?: string;
|
|
12
|
+
subHeader?: string;
|
|
13
|
+
fileType?: string;
|
|
14
|
+
multiple?: boolean;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
bordered?: boolean;
|
|
17
|
+
flat?: boolean;
|
|
18
|
+
rounded?: Rounded;
|
|
19
|
+
borderColor?: string;
|
|
20
|
+
bgColor?: string;
|
|
21
|
+
separator?: Separator;
|
|
22
|
+
dense?: boolean;
|
|
23
|
+
fileRemoverIcon?: string;
|
|
24
|
+
fileSizeLimitKb?: number;
|
|
25
|
+
};
|
|
26
|
+
declare var __VLS_1: {};
|
|
27
|
+
type __VLS_Slots = {} & {
|
|
28
|
+
content?: (props: typeof __VLS_1) => any;
|
|
29
|
+
};
|
|
30
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
31
|
+
"update:modelValue": (value: UploadedFile[]) => any;
|
|
32
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
33
|
+
"onUpdate:modelValue"?: ((value: UploadedFile[]) => any) | undefined;
|
|
34
|
+
}>, {
|
|
35
|
+
modelValue: UploadedFile[];
|
|
36
|
+
disabled: boolean;
|
|
37
|
+
dense: boolean;
|
|
38
|
+
header: string;
|
|
39
|
+
rounded: Rounded;
|
|
40
|
+
borderColor: string;
|
|
41
|
+
separator: Separator;
|
|
42
|
+
flat: boolean;
|
|
43
|
+
bordered: boolean;
|
|
44
|
+
subHeader: string;
|
|
45
|
+
fileType: string;
|
|
46
|
+
multiple: boolean;
|
|
47
|
+
bgColor: string;
|
|
48
|
+
fileRemoverIcon: string;
|
|
49
|
+
fileSizeLimitKb: number;
|
|
50
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
51
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
52
|
+
declare const _default: typeof __VLS_export;
|
|
53
|
+
export default _default;
|
|
54
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
55
|
+
new (): {
|
|
56
|
+
$slots: S;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
2
|
+
name: {
|
|
3
|
+
type: StringConstructor;
|
|
4
|
+
required: true;
|
|
5
|
+
};
|
|
6
|
+
library: {
|
|
7
|
+
type: StringConstructor;
|
|
8
|
+
default: string;
|
|
9
|
+
};
|
|
10
|
+
color: {
|
|
11
|
+
type: StringConstructor;
|
|
12
|
+
default: string;
|
|
13
|
+
};
|
|
14
|
+
size: {
|
|
15
|
+
type: StringConstructor;
|
|
16
|
+
default: string;
|
|
17
|
+
};
|
|
18
|
+
}>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
19
|
+
name: {
|
|
20
|
+
type: StringConstructor;
|
|
21
|
+
required: true;
|
|
22
|
+
};
|
|
23
|
+
library: {
|
|
24
|
+
type: StringConstructor;
|
|
25
|
+
default: string;
|
|
26
|
+
};
|
|
27
|
+
color: {
|
|
28
|
+
type: StringConstructor;
|
|
29
|
+
default: string;
|
|
30
|
+
};
|
|
31
|
+
size: {
|
|
32
|
+
type: StringConstructor;
|
|
33
|
+
default: string;
|
|
34
|
+
};
|
|
35
|
+
}>> & Readonly<{}>, {
|
|
36
|
+
color: string;
|
|
37
|
+
size: string;
|
|
38
|
+
library: string;
|
|
39
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
40
|
+
declare const _default: typeof __VLS_export;
|
|
41
|
+
export default _default;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
defineOptions({
|
|
3
|
+
inheritAttrs: false
|
|
4
|
+
});
|
|
5
|
+
import { computed } from "vue";
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
name: { type: String, required: true },
|
|
8
|
+
library: {
|
|
9
|
+
type: String,
|
|
10
|
+
default: "material"
|
|
11
|
+
},
|
|
12
|
+
color: {
|
|
13
|
+
type: String,
|
|
14
|
+
default: ""
|
|
15
|
+
},
|
|
16
|
+
size: {
|
|
17
|
+
type: String,
|
|
18
|
+
default: "md"
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
const isHex = (val) => val.startsWith("#");
|
|
22
|
+
const sizeMap = {
|
|
23
|
+
xs: "text-xs",
|
|
24
|
+
sm: "text-sm",
|
|
25
|
+
md: "text-base",
|
|
26
|
+
lg: "text-xl",
|
|
27
|
+
xl: "text-3xl"
|
|
28
|
+
};
|
|
29
|
+
const iconStyle = computed(() => {
|
|
30
|
+
const style = {};
|
|
31
|
+
if (isHex(props.color)) {
|
|
32
|
+
style.color = props.color;
|
|
33
|
+
}
|
|
34
|
+
return style;
|
|
35
|
+
});
|
|
36
|
+
const iconClass = computed(() => {
|
|
37
|
+
const classes = [
|
|
38
|
+
sizeMap[props.size]
|
|
39
|
+
];
|
|
40
|
+
if (!isHex(props.color) && props.color) {
|
|
41
|
+
classes.push(`text-${props.color}`);
|
|
42
|
+
}
|
|
43
|
+
switch (props.library) {
|
|
44
|
+
case "material":
|
|
45
|
+
classes.push("material-icons");
|
|
46
|
+
break;
|
|
47
|
+
case "material-symbols":
|
|
48
|
+
classes.push("material-symbols-outlined");
|
|
49
|
+
break;
|
|
50
|
+
case "fontawesome":
|
|
51
|
+
classes.push(props.name);
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
return classes;
|
|
55
|
+
});
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<template>
|
|
59
|
+
<i
|
|
60
|
+
:class="iconClass"
|
|
61
|
+
:style="iconStyle"
|
|
62
|
+
v-bind="$attrs"
|
|
63
|
+
>
|
|
64
|
+
{{ library.includes("material") ? name : "" }}
|
|
65
|
+
</i>
|
|
66
|
+
</template>
|