windly-ui 1.0.9 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/module.json +1 -1
- package/dist/runtime/components/Alert.d.vue.ts +18 -0
- package/dist/runtime/components/Alert.vue +21 -3
- package/dist/runtime/components/Alert.vue.d.ts +18 -0
- package/dist/runtime/components/Avatar.d.vue.ts +5 -5
- package/dist/runtime/components/Avatar.vue.d.ts +5 -5
- package/dist/runtime/components/Badge.d.vue.ts +5 -5
- package/dist/runtime/components/Badge.vue.d.ts +5 -5
- package/dist/runtime/components/Breadcrumbs.vue +2 -2
- package/dist/runtime/components/Button.d.vue.ts +5 -5
- package/dist/runtime/components/Button.vue +5 -5
- package/dist/runtime/components/Button.vue.d.ts +5 -5
- package/dist/runtime/components/Card.d.vue.ts +27 -0
- package/dist/runtime/components/Card.vue +12 -8
- package/dist/runtime/components/Card.vue.d.ts +27 -0
- package/dist/runtime/components/Checkbox.d.vue.ts +14 -5
- package/dist/runtime/components/Checkbox.vue +11 -4
- package/dist/runtime/components/Checkbox.vue.d.ts +14 -5
- package/dist/runtime/components/Chip.d.vue.ts +5 -5
- package/dist/runtime/components/Chip.vue.d.ts +5 -5
- package/dist/runtime/components/CodeBlock.vue +50 -8
- package/dist/runtime/components/Date.d.vue.ts +22 -4
- package/dist/runtime/components/Date.vue +39 -15
- package/dist/runtime/components/Date.vue.d.ts +22 -4
- package/dist/runtime/components/Dialog.d.vue.ts +7 -9
- package/dist/runtime/components/Dialog.vue +23 -4
- package/dist/runtime/components/Dialog.vue.d.ts +7 -9
- package/dist/runtime/components/Divider.vue +1 -1
- package/dist/runtime/components/Dropdown.d.vue.ts +2 -2
- package/dist/runtime/components/Dropdown.vue +71 -23
- package/dist/runtime/components/Dropdown.vue.d.ts +2 -2
- package/dist/runtime/components/FileUploader.d.vue.ts +1 -1
- package/dist/runtime/components/FileUploader.vue +11 -9
- package/dist/runtime/components/FileUploader.vue.d.ts +1 -1
- package/dist/runtime/components/Image.d.vue.ts +11 -1
- package/dist/runtime/components/Image.vue +15 -0
- package/dist/runtime/components/Image.vue.d.ts +11 -1
- package/dist/runtime/components/Input.d.vue.ts +5 -1
- package/dist/runtime/components/Input.vue +53 -14
- package/dist/runtime/components/Input.vue.d.ts +5 -1
- package/dist/runtime/components/Radio.d.vue.ts +2 -2
- package/dist/runtime/components/Radio.vue +9 -7
- package/dist/runtime/components/Radio.vue.d.ts +2 -2
- package/dist/runtime/components/ScrollArea.vue +4 -0
- package/dist/runtime/components/Select.d.vue.ts +1 -1
- package/dist/runtime/components/Select.vue +11 -9
- package/dist/runtime/components/Select.vue.d.ts +1 -1
- package/dist/runtime/components/Tabs.d.vue.ts +8 -1
- package/dist/runtime/components/Tabs.vue +21 -6
- package/dist/runtime/components/Tabs.vue.d.ts +8 -1
- package/dist/runtime/components/Textarea.d.vue.ts +39 -185
- package/dist/runtime/components/Textarea.vue +95 -72
- package/dist/runtime/components/Textarea.vue.d.ts +39 -185
- package/dist/runtime/components/Toggle.d.vue.ts +9 -3
- package/dist/runtime/components/Toggle.vue +70 -122
- package/dist/runtime/components/Toggle.vue.d.ts +9 -3
- package/dist/runtime/components/Tooltip.vue +3 -0
- package/dist/runtime/components/uiProps.d.ts +2 -2
- package/dist/runtime/components/uiProps.js +2 -2
- package/dist/runtime/components/useUiClasses.d.ts +3 -1
- package/dist/runtime/components/useUiClasses.js +7 -5
- package/dist/runtime/docs/index.vue +163 -72
- package/package.json +1 -1
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
declare const _default: typeof __VLS_export;
|
|
2
2
|
export default _default;
|
|
3
|
-
declare const __VLS_export: __VLS_WithSlots<
|
|
4
|
-
type __VLS_WithSlots<T, S> = T & (new () => {
|
|
5
|
-
$slots: S;
|
|
6
|
-
});
|
|
7
|
-
declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
3
|
+
declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
8
4
|
isOpen: {
|
|
9
5
|
type: BooleanConstructor;
|
|
10
6
|
required: true;
|
|
@@ -57,7 +53,9 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
|
|
|
57
53
|
height: string;
|
|
58
54
|
width: string;
|
|
59
55
|
persistent: boolean;
|
|
60
|
-
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>, {
|
|
57
|
+
default?: (props: {}) => any;
|
|
58
|
+
}>;
|
|
59
|
+
type __VLS_WithSlots<T, S> = T & (new () => {
|
|
60
|
+
$slots: S;
|
|
61
|
+
});
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
const openStack = [];
|
|
3
|
+
let nextInstanceId = 0;
|
|
4
|
+
</script>
|
|
5
|
+
|
|
1
6
|
<script setup>
|
|
2
|
-
import { computed, watch, onBeforeUnmount } from "vue";
|
|
7
|
+
import { computed, watch, onBeforeUnmount, ref } from "vue";
|
|
3
8
|
import { useRouter } from "vue-router";
|
|
4
9
|
const props = defineProps({
|
|
5
10
|
isOpen: {
|
|
@@ -26,15 +31,26 @@ const props = defineProps({
|
|
|
26
31
|
});
|
|
27
32
|
const emit = defineEmits(["update:isOpen"]);
|
|
28
33
|
const router = useRouter();
|
|
34
|
+
const instanceId = ++nextInstanceId;
|
|
35
|
+
const zIndex = ref(50);
|
|
29
36
|
const close = () => emit("update:isOpen", false);
|
|
30
37
|
const handleOutsideClick = () => {
|
|
31
38
|
if (!props.persistent) close();
|
|
32
39
|
};
|
|
40
|
+
const isTopMost = () => openStack[openStack.length - 1] === instanceId;
|
|
33
41
|
const handleEscKey = (event) => {
|
|
34
|
-
if (event.key === "Escape" && !props.persistent) {
|
|
42
|
+
if (event.key === "Escape" && !props.persistent && isTopMost()) {
|
|
35
43
|
close();
|
|
36
44
|
}
|
|
37
45
|
};
|
|
46
|
+
const registerOpen = () => {
|
|
47
|
+
if (!openStack.includes(instanceId)) openStack.push(instanceId);
|
|
48
|
+
zIndex.value = 50 + openStack.indexOf(instanceId) * 10;
|
|
49
|
+
};
|
|
50
|
+
const registerClose = () => {
|
|
51
|
+
const index = openStack.indexOf(instanceId);
|
|
52
|
+
if (index !== -1) openStack.splice(index, 1);
|
|
53
|
+
};
|
|
38
54
|
const positionClasses = computed(() => {
|
|
39
55
|
switch (props.position) {
|
|
40
56
|
case "top":
|
|
@@ -53,8 +69,10 @@ watch(
|
|
|
53
69
|
() => props.isOpen,
|
|
54
70
|
(open) => {
|
|
55
71
|
if (open) {
|
|
72
|
+
registerOpen();
|
|
56
73
|
document.addEventListener("keydown", handleEscKey);
|
|
57
74
|
} else {
|
|
75
|
+
registerClose();
|
|
58
76
|
document.removeEventListener("keydown", handleEscKey);
|
|
59
77
|
}
|
|
60
78
|
}
|
|
@@ -65,14 +83,15 @@ const removeGuard = router.beforeEach((to, from, next) => {
|
|
|
65
83
|
});
|
|
66
84
|
onBeforeUnmount(() => {
|
|
67
85
|
document.removeEventListener("keydown", handleEscKey);
|
|
86
|
+
registerClose();
|
|
68
87
|
removeGuard?.();
|
|
69
88
|
});
|
|
70
89
|
</script>
|
|
71
90
|
|
|
72
91
|
<template>
|
|
73
92
|
<transition name="fade">
|
|
74
|
-
<div v-if="isOpen" class="fixed inset-0
|
|
75
|
-
@click.self="handleOutsideClick" :class="positionClasses">
|
|
93
|
+
<div v-if="isOpen" class="fixed inset-0 flex bg-black bg-opacity-60" role="dialog" aria-modal="true"
|
|
94
|
+
@click.self="handleOutsideClick" :class="positionClasses" :style="{ zIndex }">
|
|
76
95
|
<div :style="{ width: width, height: height }">
|
|
77
96
|
<slot />
|
|
78
97
|
</div>
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
declare const _default: typeof __VLS_export;
|
|
2
2
|
export default _default;
|
|
3
|
-
declare const __VLS_export: __VLS_WithSlots<
|
|
4
|
-
type __VLS_WithSlots<T, S> = T & (new () => {
|
|
5
|
-
$slots: S;
|
|
6
|
-
});
|
|
7
|
-
declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
3
|
+
declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
8
4
|
isOpen: {
|
|
9
5
|
type: BooleanConstructor;
|
|
10
6
|
required: true;
|
|
@@ -57,7 +53,9 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
|
|
|
57
53
|
height: string;
|
|
58
54
|
width: string;
|
|
59
55
|
persistent: boolean;
|
|
60
|
-
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>, {
|
|
57
|
+
default?: (props: {}) => any;
|
|
58
|
+
}>;
|
|
59
|
+
type __VLS_WithSlots<T, S> = T & (new () => {
|
|
60
|
+
$slots: S;
|
|
61
|
+
});
|
|
@@ -38,7 +38,7 @@ const dividerClass = computed(() => [
|
|
|
38
38
|
props.vertical ? "h-full border-l" : "w-full border-t",
|
|
39
39
|
props.type === "dashed" ? "border-dashed" : "",
|
|
40
40
|
props.type === "dotted" ? "border-dotted" : "",
|
|
41
|
-
props.inset ? "mx-4" : "",
|
|
41
|
+
props.inset ? props.vertical ? "my-4" : "mx-4" : "",
|
|
42
42
|
!isHex(props.color) ? `border-${props.color}` : ""
|
|
43
43
|
]);
|
|
44
44
|
</script>
|
|
@@ -9,11 +9,11 @@ type __VLS_Props = {
|
|
|
9
9
|
borderColor?: string;
|
|
10
10
|
flat?: boolean;
|
|
11
11
|
};
|
|
12
|
-
declare var __VLS_1: {},
|
|
12
|
+
declare var __VLS_1: {}, __VLS_15: {};
|
|
13
13
|
type __VLS_Slots = {} & {
|
|
14
14
|
trigger?: (props: typeof __VLS_1) => any;
|
|
15
15
|
} & {
|
|
16
|
-
menu?: (props: typeof
|
|
16
|
+
menu?: (props: typeof __VLS_15) => any;
|
|
17
17
|
};
|
|
18
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
19
|
flat: boolean;
|
|
@@ -4,6 +4,7 @@ defineOptions({
|
|
|
4
4
|
});
|
|
5
5
|
import {
|
|
6
6
|
computed,
|
|
7
|
+
nextTick,
|
|
7
8
|
onBeforeUnmount,
|
|
8
9
|
onMounted,
|
|
9
10
|
ref
|
|
@@ -19,34 +20,79 @@ const props = defineProps({
|
|
|
19
20
|
});
|
|
20
21
|
const isOpen = ref(false);
|
|
21
22
|
const dropdownRef = ref(null);
|
|
23
|
+
const menuRef = ref(null);
|
|
24
|
+
const menuPositionStyle = ref({});
|
|
22
25
|
const isHex = (val) => {
|
|
23
26
|
return val.startsWith("#") || val.startsWith("rgb") || val.startsWith("hsl");
|
|
24
27
|
};
|
|
25
|
-
|
|
28
|
+
function updatePosition() {
|
|
29
|
+
const trigger = dropdownRef.value;
|
|
30
|
+
if (!trigger) return;
|
|
31
|
+
const rect = trigger.getBoundingClientRect();
|
|
32
|
+
const viewportWidth = window.innerWidth;
|
|
33
|
+
const viewportHeight = window.innerHeight;
|
|
34
|
+
const gap = 8;
|
|
35
|
+
const style = {
|
|
36
|
+
position: "fixed",
|
|
37
|
+
zIndex: "9999"
|
|
38
|
+
};
|
|
39
|
+
if (props.align === "right") {
|
|
40
|
+
style.right = `${Math.max(viewportWidth - rect.right, 0)}px`;
|
|
41
|
+
} else {
|
|
42
|
+
style.left = `${Math.max(rect.left, 0)}px`;
|
|
43
|
+
}
|
|
44
|
+
const estimatedMenuHeight = menuRef.value?.offsetHeight ?? 0;
|
|
45
|
+
const fitsBelow = rect.bottom + gap + estimatedMenuHeight <= viewportHeight;
|
|
46
|
+
if (fitsBelow) {
|
|
47
|
+
style.top = `${rect.bottom + gap}px`;
|
|
48
|
+
} else {
|
|
49
|
+
style.bottom = `${Math.max(viewportHeight - rect.top + gap, 0)}px`;
|
|
50
|
+
}
|
|
51
|
+
menuPositionStyle.value = style;
|
|
52
|
+
}
|
|
53
|
+
const toggle = async () => {
|
|
26
54
|
isOpen.value = !isOpen.value;
|
|
55
|
+
if (isOpen.value) {
|
|
56
|
+
await nextTick();
|
|
57
|
+
updatePosition();
|
|
58
|
+
}
|
|
27
59
|
};
|
|
28
60
|
const close = () => {
|
|
29
61
|
isOpen.value = false;
|
|
30
62
|
};
|
|
31
63
|
const handleClickOutside = (event) => {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
)
|
|
64
|
+
const path = event.composedPath();
|
|
65
|
+
const clickedTrigger = dropdownRef.value && path.includes(dropdownRef.value);
|
|
66
|
+
const clickedMenu = menuRef.value && path.includes(menuRef.value);
|
|
67
|
+
if (!clickedTrigger && !clickedMenu) {
|
|
68
|
+
close();
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
const handleEscape = (event) => {
|
|
72
|
+
if (event.key === "Escape" && isOpen.value) {
|
|
36
73
|
close();
|
|
37
74
|
}
|
|
38
75
|
};
|
|
76
|
+
const handleReposition = () => {
|
|
77
|
+
if (isOpen.value) updatePosition();
|
|
78
|
+
};
|
|
39
79
|
onMounted(() => {
|
|
40
80
|
document.addEventListener(
|
|
41
81
|
"click",
|
|
42
82
|
handleClickOutside
|
|
43
83
|
);
|
|
84
|
+
document.addEventListener("keydown", handleEscape);
|
|
85
|
+
window.addEventListener("resize", handleReposition);
|
|
86
|
+
window.addEventListener("scroll", handleReposition, true);
|
|
44
87
|
});
|
|
45
88
|
onBeforeUnmount(() => {
|
|
46
89
|
document.removeEventListener(
|
|
47
90
|
"click",
|
|
48
91
|
handleClickOutside
|
|
49
92
|
);
|
|
93
|
+
document.removeEventListener("keydown", handleEscape);
|
|
94
|
+
window.removeEventListener("resize", handleReposition);
|
|
95
|
+
window.removeEventListener("scroll", handleReposition, true);
|
|
50
96
|
});
|
|
51
97
|
const roundedClass = computed(
|
|
52
98
|
() => {
|
|
@@ -62,8 +108,7 @@ const roundedClass = computed(
|
|
|
62
108
|
}
|
|
63
109
|
);
|
|
64
110
|
const dropdownClasses = computed(() => [
|
|
65
|
-
"
|
|
66
|
-
props.align === "right" ? "right-0" : "left-0",
|
|
111
|
+
"transition-all overflow-hidden",
|
|
67
112
|
props.width,
|
|
68
113
|
roundedClass.value,
|
|
69
114
|
props.bordered && !isHex(props.borderColor) ? `border border-${props.borderColor}` : "",
|
|
@@ -75,7 +120,7 @@ const dropdownStyle = computed(
|
|
|
75
120
|
if (props.bordered && isHex(props.borderColor)) {
|
|
76
121
|
style.border = `1px solid ${props.borderColor}`;
|
|
77
122
|
}
|
|
78
|
-
return style;
|
|
123
|
+
return { ...style, ...menuPositionStyle.value };
|
|
79
124
|
}
|
|
80
125
|
);
|
|
81
126
|
const menuClasses = computed(
|
|
@@ -93,32 +138,35 @@ const menuClasses = computed(
|
|
|
93
138
|
class="relative inline-block"
|
|
94
139
|
>
|
|
95
140
|
<!-- Trigger -->
|
|
96
|
-
<div @click="toggle">
|
|
141
|
+
<div aria-haspopup="true" :aria-expanded="isOpen" @click="toggle">
|
|
97
142
|
<slot name="trigger" />
|
|
98
143
|
</div>
|
|
99
144
|
|
|
100
145
|
<!-- Dropdown -->
|
|
101
|
-
<
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
146
|
+
<Teleport to="body">
|
|
147
|
+
<transition
|
|
148
|
+
name="fade-slide"
|
|
149
|
+
>
|
|
150
|
+
<div
|
|
151
|
+
v-if="isOpen"
|
|
152
|
+
ref="menuRef"
|
|
153
|
+
:class="
|
|
107
154
|
dropdownClasses
|
|
108
155
|
"
|
|
109
|
-
|
|
156
|
+
:style="
|
|
110
157
|
dropdownStyle
|
|
111
158
|
"
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
159
|
+
>
|
|
160
|
+
<div
|
|
161
|
+
:class="
|
|
115
162
|
menuClasses
|
|
116
163
|
"
|
|
117
|
-
|
|
118
|
-
|
|
164
|
+
>
|
|
165
|
+
<slot name="menu" />
|
|
166
|
+
</div>
|
|
119
167
|
</div>
|
|
120
|
-
</
|
|
121
|
-
</
|
|
168
|
+
</transition>
|
|
169
|
+
</Teleport>
|
|
122
170
|
</div>
|
|
123
171
|
</template>
|
|
124
172
|
|
|
@@ -9,11 +9,11 @@ type __VLS_Props = {
|
|
|
9
9
|
borderColor?: string;
|
|
10
10
|
flat?: boolean;
|
|
11
11
|
};
|
|
12
|
-
declare var __VLS_1: {},
|
|
12
|
+
declare var __VLS_1: {}, __VLS_15: {};
|
|
13
13
|
type __VLS_Slots = {} & {
|
|
14
14
|
trigger?: (props: typeof __VLS_1) => any;
|
|
15
15
|
} & {
|
|
16
|
-
menu?: (props: typeof
|
|
16
|
+
menu?: (props: typeof __VLS_15) => any;
|
|
17
17
|
};
|
|
18
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
19
|
flat: boolean;
|
|
@@ -47,8 +47,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
|
|
|
47
47
|
multiple: boolean;
|
|
48
48
|
width: string;
|
|
49
49
|
rounded: Rounded;
|
|
50
|
-
readonly: boolean;
|
|
51
50
|
hint: string;
|
|
51
|
+
readonly: boolean;
|
|
52
52
|
fileType: string;
|
|
53
53
|
outlined: boolean;
|
|
54
54
|
borderless: boolean;
|
|
@@ -35,6 +35,14 @@ const files = computed({
|
|
|
35
35
|
)
|
|
36
36
|
});
|
|
37
37
|
const isHex = (val) => val.startsWith("#");
|
|
38
|
+
function supportsWidthValue(value) {
|
|
39
|
+
if (typeof CSS !== "undefined" && typeof CSS.supports === "function") {
|
|
40
|
+
return CSS.supports("width", value);
|
|
41
|
+
}
|
|
42
|
+
return /^-?\d*\.?\d+(px|%|rem|em|vh|vw|vmin|vmax|ch|ex|cm|mm|in|pt|pc)$/.test(
|
|
43
|
+
value.trim()
|
|
44
|
+
) || /^(calc|var|min|max|clamp)\(/.test(value.trim());
|
|
45
|
+
}
|
|
38
46
|
const roundedMap = {
|
|
39
47
|
none: "rounded-none",
|
|
40
48
|
sm: "rounded-sm",
|
|
@@ -88,12 +96,9 @@ const computedClass = computed(() => {
|
|
|
88
96
|
classes += " text-base";
|
|
89
97
|
}
|
|
90
98
|
if (props.color && !isHex(props.color)) {
|
|
91
|
-
classes += ` border-${props.color} focus:ring
|
|
99
|
+
classes += ` border-${props.color} focus:ring-0 `;
|
|
92
100
|
}
|
|
93
|
-
if (props.width && !
|
|
94
|
-
"width",
|
|
95
|
-
props.width
|
|
96
|
-
)) {
|
|
101
|
+
if (props.width && !supportsWidthValue(props.width)) {
|
|
97
102
|
classes += ` w-${props.width}`;
|
|
98
103
|
}
|
|
99
104
|
if (!props.width) {
|
|
@@ -106,10 +111,7 @@ const computedClass = computed(() => {
|
|
|
106
111
|
});
|
|
107
112
|
const computedStyle = computed(() => {
|
|
108
113
|
const styles = {};
|
|
109
|
-
if (props.width &&
|
|
110
|
-
"width",
|
|
111
|
-
props.width
|
|
112
|
-
)) {
|
|
114
|
+
if (props.width && supportsWidthValue(props.width)) {
|
|
113
115
|
styles.width = props.width;
|
|
114
116
|
}
|
|
115
117
|
if (props.color && isHex(props.color)) {
|
|
@@ -47,8 +47,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
|
|
|
47
47
|
multiple: boolean;
|
|
48
48
|
width: string;
|
|
49
49
|
rounded: Rounded;
|
|
50
|
-
readonly: boolean;
|
|
51
50
|
hint: string;
|
|
51
|
+
readonly: boolean;
|
|
52
52
|
fileType: string;
|
|
53
53
|
outlined: boolean;
|
|
54
54
|
borderless: boolean;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
declare
|
|
1
|
+
declare var __VLS_1: {};
|
|
2
|
+
type __VLS_Slots = {} & {
|
|
3
|
+
overlay?: (props: typeof __VLS_1) => any;
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
2
6
|
src: {
|
|
3
7
|
type: StringConstructor;
|
|
4
8
|
required: true;
|
|
@@ -73,5 +77,11 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|
|
73
77
|
fit: string;
|
|
74
78
|
borderType: string;
|
|
75
79
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
80
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
76
81
|
declare const _default: typeof __VLS_export;
|
|
77
82
|
export default _default;
|
|
83
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
84
|
+
new (): {
|
|
85
|
+
$slots: S;
|
|
86
|
+
};
|
|
87
|
+
};
|
|
@@ -50,7 +50,22 @@ function onError() {
|
|
|
50
50
|
</script>
|
|
51
51
|
|
|
52
52
|
<template>
|
|
53
|
+
<div v-if="$slots.overlay" class="relative inline-block">
|
|
54
|
+
<img
|
|
55
|
+
:src="imageSrc"
|
|
56
|
+
:alt="alt"
|
|
57
|
+
:class="imageClass"
|
|
58
|
+
:style="imageStyle"
|
|
59
|
+
@error="onError"
|
|
60
|
+
v-bind="$attrs"
|
|
61
|
+
>
|
|
62
|
+
<div class="absolute inset-0 pointer-events-none">
|
|
63
|
+
<slot name="overlay" />
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
53
67
|
<img
|
|
68
|
+
v-else
|
|
54
69
|
:src="imageSrc"
|
|
55
70
|
:alt="alt"
|
|
56
71
|
:class="imageClass"
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
declare
|
|
1
|
+
declare var __VLS_1: {};
|
|
2
|
+
type __VLS_Slots = {} & {
|
|
3
|
+
overlay?: (props: typeof __VLS_1) => any;
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
2
6
|
src: {
|
|
3
7
|
type: StringConstructor;
|
|
4
8
|
required: true;
|
|
@@ -73,5 +77,11 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|
|
73
77
|
fit: string;
|
|
74
78
|
borderType: string;
|
|
75
79
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
80
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
76
81
|
declare const _default: typeof __VLS_export;
|
|
77
82
|
export default _default;
|
|
83
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
84
|
+
new (): {
|
|
85
|
+
$slots: S;
|
|
86
|
+
};
|
|
87
|
+
};
|
|
@@ -28,6 +28,8 @@ type __VLS_Props = {
|
|
|
28
28
|
readonly?: boolean;
|
|
29
29
|
autocomplete?: string;
|
|
30
30
|
validateOn?: ValidateOn;
|
|
31
|
+
error?: boolean;
|
|
32
|
+
errorMessage?: string;
|
|
31
33
|
};
|
|
32
34
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
33
35
|
"update:modelValue": (value: string) => any;
|
|
@@ -49,10 +51,12 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
|
|
|
49
51
|
width: string;
|
|
50
52
|
rounded: string;
|
|
51
53
|
placeholder: string;
|
|
52
|
-
readonly: boolean;
|
|
53
54
|
hint: string;
|
|
55
|
+
readonly: boolean;
|
|
56
|
+
error: boolean;
|
|
54
57
|
outlined: boolean;
|
|
55
58
|
borderless: boolean;
|
|
59
|
+
errorMessage: string;
|
|
56
60
|
case: "upper" | "lower" | "capitalize" | "normal" | null;
|
|
57
61
|
allow: ValidationRule[];
|
|
58
62
|
deny: ValidationRule[];
|
|
@@ -26,14 +26,16 @@ const props = defineProps({
|
|
|
26
26
|
disabled: { type: Boolean, required: false, default: false },
|
|
27
27
|
readonly: { type: Boolean, required: false, default: false },
|
|
28
28
|
autocomplete: { type: String, required: false, default: "off" },
|
|
29
|
-
validateOn: { type: String, required: false, default: "input" }
|
|
29
|
+
validateOn: { type: String, required: false, default: "input" },
|
|
30
|
+
error: { type: Boolean, required: false, default: false },
|
|
31
|
+
errorMessage: { type: String, required: false, default: "" }
|
|
30
32
|
});
|
|
31
33
|
const emit = defineEmits(["update:modelValue", "valid", "invalid"]);
|
|
32
34
|
const validationError = ref("");
|
|
33
35
|
const NATIVE_PATTERNS = {
|
|
34
36
|
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
|
35
37
|
url: /^(https?:\/\/)?([\w-]+\.)+[\w-]{2,}(\/\S*)?$/i,
|
|
36
|
-
tel: /^\+?[
|
|
38
|
+
tel: /^\+?[\d\s().-]{7,20}$/
|
|
37
39
|
};
|
|
38
40
|
const RULE_PATTERNS = {
|
|
39
41
|
alphabets: /^[A-Za-z]+$/,
|
|
@@ -49,6 +51,14 @@ const RULE_PATTERNS = {
|
|
|
49
51
|
uppercase: /^[A-Z]+$/,
|
|
50
52
|
lowercase: /^[a-z]+$/
|
|
51
53
|
};
|
|
54
|
+
const CHAR_CLASS_SOURCE = {
|
|
55
|
+
alphabets: "A-Za-z",
|
|
56
|
+
numbers: "0-9",
|
|
57
|
+
spaces: "\\s",
|
|
58
|
+
punctuation: `.,!?;:'"()\\-`,
|
|
59
|
+
uppercase: "A-Z",
|
|
60
|
+
lowercase: "a-z"
|
|
61
|
+
};
|
|
52
62
|
function validate(value) {
|
|
53
63
|
validationError.value = "";
|
|
54
64
|
if (props.required && !value.trim()) {
|
|
@@ -76,7 +86,17 @@ function validate(value) {
|
|
|
76
86
|
validationError.value = `Invalid ${props.type} format.`;
|
|
77
87
|
return false;
|
|
78
88
|
}
|
|
79
|
-
|
|
89
|
+
const allowCharRules = props.allow.filter((rule) => CHAR_CLASS_SOURCE[rule]);
|
|
90
|
+
const allowFormatRules = props.allow.filter((rule) => !CHAR_CLASS_SOURCE[rule]);
|
|
91
|
+
if (allowCharRules.length && value) {
|
|
92
|
+
const unionSource = allowCharRules.map((rule) => CHAR_CLASS_SOURCE[rule]).join("");
|
|
93
|
+
const unionRegex = new RegExp(`^[${unionSource}]*$`);
|
|
94
|
+
if (!unionRegex.test(value)) {
|
|
95
|
+
validationError.value = `Only ${allowCharRules.join(", ")} characters are allowed.`;
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
for (const rule of allowFormatRules) {
|
|
80
100
|
const regex = RULE_PATTERNS[rule];
|
|
81
101
|
if (regex && value && !regex.test(value)) {
|
|
82
102
|
validationError.value = `Failed validation: ${rule}`;
|
|
@@ -84,8 +104,17 @@ function validate(value) {
|
|
|
84
104
|
}
|
|
85
105
|
}
|
|
86
106
|
for (const rule of props.deny) {
|
|
107
|
+
const charSource = CHAR_CLASS_SOURCE[rule];
|
|
108
|
+
if (charSource) {
|
|
109
|
+
const presenceRegex = new RegExp(`[${charSource}]`);
|
|
110
|
+
if (value && presenceRegex.test(value)) {
|
|
111
|
+
validationError.value = `${rule} characters are not permitted.`;
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
87
116
|
const regex = RULE_PATTERNS[rule];
|
|
88
|
-
if (regex && regex.test(value)) {
|
|
117
|
+
if (regex && value && regex.test(value)) {
|
|
89
118
|
validationError.value = `Contains unsupported value: ${rule}`;
|
|
90
119
|
return false;
|
|
91
120
|
}
|
|
@@ -125,6 +154,16 @@ function handleBlur(event) {
|
|
|
125
154
|
runValidation(target.value);
|
|
126
155
|
}
|
|
127
156
|
const isHex = (val) => val.startsWith("#");
|
|
157
|
+
function supportsWidthValue(value) {
|
|
158
|
+
if (typeof CSS !== "undefined" && typeof CSS.supports === "function") {
|
|
159
|
+
return CSS.supports("width", value);
|
|
160
|
+
}
|
|
161
|
+
return /^-?\d*\.?\d+(px|%|rem|em|vh|vw|vmin|vmax|ch|ex|cm|mm|in|pt|pc)$/.test(
|
|
162
|
+
value.trim()
|
|
163
|
+
) || /^(calc|var|min|max|clamp)\(/.test(value.trim());
|
|
164
|
+
}
|
|
165
|
+
const hasError = computed(() => Boolean(props.error) || Boolean(validationError.value));
|
|
166
|
+
const errorText = computed(() => props.errorMessage || validationError.value);
|
|
128
167
|
const roundedMap = {
|
|
129
168
|
none: "rounded-none",
|
|
130
169
|
sm: "rounded-sm",
|
|
@@ -163,12 +202,12 @@ const computedClass = computed(() => {
|
|
|
163
202
|
} else if (props.case === "capitalize") {
|
|
164
203
|
classes += " capitalize";
|
|
165
204
|
}
|
|
166
|
-
if (
|
|
167
|
-
classes += " border-red-500";
|
|
205
|
+
if (hasError.value) {
|
|
206
|
+
classes += " border-red-500 focus:ring-0 ";
|
|
168
207
|
} else {
|
|
169
|
-
classes += ` ${props.color && !isHex(props.color) ? `border-${props.color} focus:ring
|
|
208
|
+
classes += ` ${props.color && !isHex(props.color) ? `border-${props.color} focus:ring-0 ` : ""}`;
|
|
170
209
|
}
|
|
171
|
-
if (props.width && !
|
|
210
|
+
if (props.width && !supportsWidthValue(props.width)) {
|
|
172
211
|
classes += `w-${props.width} `;
|
|
173
212
|
}
|
|
174
213
|
if (!props.width) classes += "w-full ";
|
|
@@ -178,7 +217,7 @@ const computedClass = computed(() => {
|
|
|
178
217
|
});
|
|
179
218
|
const computedStyle = computed(() => {
|
|
180
219
|
let styles = {};
|
|
181
|
-
if (props.width &&
|
|
220
|
+
if (props.width && supportsWidthValue(props.width)) {
|
|
182
221
|
styles["width"] = props.width;
|
|
183
222
|
}
|
|
184
223
|
if (props.color && isHex(props.color)) {
|
|
@@ -210,7 +249,7 @@ const inputAttrs = computed(() => {
|
|
|
210
249
|
:style="computedStyle"
|
|
211
250
|
@input="handleInput"
|
|
212
251
|
@blur="handleBlur"
|
|
213
|
-
v-bind="inputAttrs"
|
|
252
|
+
v-bind="inputAttrs"
|
|
214
253
|
/>
|
|
215
254
|
|
|
216
255
|
<label class="label" :class="color && !isHex(color) ? `text-${color}` : ''" :style="color ? { color: isHex(color) ? color : '' } : { color: '#9ca3af' }">
|
|
@@ -221,21 +260,21 @@ const inputAttrs = computed(() => {
|
|
|
221
260
|
<UIButton v-if="type === 'password'" :icon="visibility ? 'visibility_off' : 'visibility'" flat dense color="gray-400" @click="visibility = !visibility" />
|
|
222
261
|
</div>
|
|
223
262
|
<p
|
|
224
|
-
v-if="hint && !
|
|
263
|
+
v-if="hint && !hasError"
|
|
225
264
|
class="hint-text"
|
|
226
265
|
>
|
|
227
266
|
{{ hint }}
|
|
228
267
|
</p>
|
|
229
268
|
|
|
230
269
|
<p
|
|
231
|
-
v-if="
|
|
270
|
+
v-if="hasError"
|
|
232
271
|
class="error-text"
|
|
233
272
|
>
|
|
234
|
-
{{
|
|
273
|
+
{{ errorText }}
|
|
235
274
|
</p>
|
|
236
275
|
</div>
|
|
237
276
|
</template>
|
|
238
277
|
|
|
239
278
|
<style scoped>
|
|
240
|
-
.input-container{margin:20px 0 10px;position:relative}.input-container.dense{margin:10px 0}.input-container .input{padding:10px}.input-container.dense .input{padding:6px 10px}.input:focus{outline:none}.label{left:10px;padding:0 5px;pointer-events:none;position:absolute;top:50%;transform:translateY(-50%);transition:top .2s ease,font-size .2s ease,color .2s ease,transform .2s ease}.input-container:has(input[type=date]) .label,.input-container:has(input[type=datetime-local]) .label,.input-container:has(input[type=month]) .label,.input-container:has(input[type=time]) .label,.input-container:has(input[type=week]) .label{left:5px;top:-20%;transform:none;z-index:1}.input-container.input-lg.dense .input:-moz-placeholder+.label,.input:-moz-placeholder+.label{left:5px;top:-20px;transform:none;z-index:1}.input-container.input-lg.dense .input.has-value+.label,.input-container.input-lg.dense .input:focus+.label,.input-container.input-lg.dense .input:placeholder-shown+.label,.input.has-value+.label,.input:focus+.label,.input:placeholder-shown+.label{left:5px;top:-20px;transform:none;z-index:1}.input-container.dense .input:-moz-placeholder+.label{top:-10px}.input-container.dense .input.has-value+.label,.input-container.dense .input:focus+.label,.input-container.dense .input:placeholder-shown+.label{top:-10px}.input-container.input-lg .input:-moz-placeholder+.label{top:-24px}.input-container.input-lg .input.has-value+.label,.input-container.input-lg .input:focus+.label,.input-container.input-lg .input:placeholder-shown+.label{top:-24px}.input-container.input-sm .label{font-size:14px}.input-container.input-md .label{font-size:16px}.input-container.input-lg .label{font-size:18px}.input-container.input-sm .input:-moz-placeholder+.label{font-size:12px}.input-container.input-sm .input.has-value+.label,.input-container.input-sm .input:focus+.label,.input-container.input-sm .input:placeholder-shown+.label{font-size:12px}.input-container.input-md .input:-moz-placeholder+.label{font-size:14px}.input-container.input-md .input.has-value+.label,.input-container.input-md .input:focus+.label,.input-container.input-md .input:placeholder-shown+.label{font-size:14px}.input-container.input-lg .input:-moz-placeholder+.label{font-size:16px}.input-container.input-lg .input.has-value+.label,.input-container.input-lg .input:focus+.label,.input-container.input-lg .input:placeholder-shown+.label{font-size:16px}.hint-text{color:#6b7280;font-size:12px;margin-left:5px}.char-count{color:#6b7280}.char-count,.error-text{font-size:.875rem;margin-top:4px}.error-text{color:#ef4444}
|
|
279
|
+
.input-container{box-sizing:border-box;margin:20px 0 10px;min-width:0;position:relative;width:100%}.input-container.dense{margin:10px 0}.input-container .input{padding:10px}.input-container.dense .input{padding:6px 10px}.input:focus{outline:none}.label{left:10px;padding:0 5px;pointer-events:none;position:absolute;top:50%;transform:translateY(-50%);transition:top .2s ease,font-size .2s ease,color .2s ease,transform .2s ease}.input-container:has(input[type=date]) .label,.input-container:has(input[type=datetime-local]) .label,.input-container:has(input[type=month]) .label,.input-container:has(input[type=time]) .label,.input-container:has(input[type=week]) .label{left:5px;top:-20%;transform:none;z-index:1}.input-container.input-lg.dense .input:-moz-placeholder+.label,.input:-moz-placeholder+.label{left:5px;top:-20px;transform:none;z-index:1}.input-container.input-lg.dense .input.has-value+.label,.input-container.input-lg.dense .input:focus+.label,.input-container.input-lg.dense .input:placeholder-shown+.label,.input.has-value+.label,.input:focus+.label,.input:placeholder-shown+.label{left:5px;top:-20px;transform:none;z-index:1}.input-container.dense .input:-moz-placeholder+.label{top:-10px}.input-container.dense .input.has-value+.label,.input-container.dense .input:focus+.label,.input-container.dense .input:placeholder-shown+.label{top:-10px}.input-container.input-lg .input:-moz-placeholder+.label{top:-24px}.input-container.input-lg .input.has-value+.label,.input-container.input-lg .input:focus+.label,.input-container.input-lg .input:placeholder-shown+.label{top:-24px}.input-container.input-sm .label{font-size:14px}.input-container.input-md .label{font-size:16px}.input-container.input-lg .label{font-size:18px}.input-container.input-sm .input:-moz-placeholder+.label{font-size:12px}.input-container.input-sm .input.has-value+.label,.input-container.input-sm .input:focus+.label,.input-container.input-sm .input:placeholder-shown+.label{font-size:12px}.input-container.input-md .input:-moz-placeholder+.label{font-size:14px}.input-container.input-md .input.has-value+.label,.input-container.input-md .input:focus+.label,.input-container.input-md .input:placeholder-shown+.label{font-size:14px}.input-container.input-lg .input:-moz-placeholder+.label{font-size:16px}.input-container.input-lg .input.has-value+.label,.input-container.input-lg .input:focus+.label,.input-container.input-lg .input:placeholder-shown+.label{font-size:16px}.hint-text{color:#6b7280;font-size:12px;margin-left:5px;overflow-wrap:anywhere;word-break:break-word}.char-count{color:#6b7280}.char-count,.error-text{font-size:.875rem;margin-top:4px}.error-text{color:#ef4444;overflow-wrap:anywhere;word-break:break-word}
|
|
241
280
|
</style>
|
|
@@ -28,6 +28,8 @@ type __VLS_Props = {
|
|
|
28
28
|
readonly?: boolean;
|
|
29
29
|
autocomplete?: string;
|
|
30
30
|
validateOn?: ValidateOn;
|
|
31
|
+
error?: boolean;
|
|
32
|
+
errorMessage?: string;
|
|
31
33
|
};
|
|
32
34
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
33
35
|
"update:modelValue": (value: string) => any;
|
|
@@ -49,10 +51,12 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
|
|
|
49
51
|
width: string;
|
|
50
52
|
rounded: string;
|
|
51
53
|
placeholder: string;
|
|
52
|
-
readonly: boolean;
|
|
53
54
|
hint: string;
|
|
55
|
+
readonly: boolean;
|
|
56
|
+
error: boolean;
|
|
54
57
|
outlined: boolean;
|
|
55
58
|
borderless: boolean;
|
|
59
|
+
errorMessage: string;
|
|
56
60
|
case: "upper" | "lower" | "capitalize" | "normal" | null;
|
|
57
61
|
allow: ValidationRule[];
|
|
58
62
|
deny: ValidationRule[];
|