cynx-lib 1.3.66 → 1.3.67
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/package.json +1 -1
- package/ui/components/Drawer.jsx +208 -0
- package/ui/index.cjs +3 -0
- package/ui/index.js +2 -0
- package/vue/components/Drawer.vue +243 -0
- package/vue/components/Select.vue +10 -6
- package/vue/index.js +3 -0
package/package.json
CHANGED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import React, { useState, useEffect, useRef } from 'react';
|
|
2
|
+
import { createPortal } from 'react-dom';
|
|
3
|
+
import { X } from 'lucide-react';
|
|
4
|
+
|
|
5
|
+
const DRAWER_STYLES = `
|
|
6
|
+
@keyframes cynxDrawerFadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
7
|
+
@keyframes cynxDrawerFadeOut { from { opacity: 1; } to { opacity: 0; } }
|
|
8
|
+
@keyframes cynxDrawerSlideInRight { from { transform: translateX(100%); } to { transform: translateX(0); } }
|
|
9
|
+
@keyframes cynxDrawerSlideOutRight { from { transform: translateX(0); } to { transform: translateX(100%); } }
|
|
10
|
+
@keyframes cynxDrawerSlideInLeft { from { transform: translateX(-100%); } to { transform: translateX(0); } }
|
|
11
|
+
@keyframes cynxDrawerSlideOutLeft { from { transform: translateX(0); } to { transform: translateX(-100%); } }
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
let _drawer_id = 'cynx-drawer-styles';
|
|
15
|
+
function ensureDrawerStyles() {
|
|
16
|
+
if (typeof document === 'undefined') return;
|
|
17
|
+
if (document.getElementById(_drawer_id)) return;
|
|
18
|
+
const tag = document.createElement('style');
|
|
19
|
+
tag.id = _drawer_id;
|
|
20
|
+
tag.textContent = DRAWER_STYLES;
|
|
21
|
+
document.head.appendChild(tag);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function Drawer({
|
|
25
|
+
open = false,
|
|
26
|
+
onClose,
|
|
27
|
+
title = '',
|
|
28
|
+
description = '',
|
|
29
|
+
width = 560,
|
|
30
|
+
side = 'right',
|
|
31
|
+
showClose = true,
|
|
32
|
+
children,
|
|
33
|
+
footer,
|
|
34
|
+
header,
|
|
35
|
+
style = {},
|
|
36
|
+
bodyStyle = {},
|
|
37
|
+
}) {
|
|
38
|
+
const [closing, setClosing] = useState(false);
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
ensureDrawerStyles();
|
|
42
|
+
}, []);
|
|
43
|
+
|
|
44
|
+
const handleClose = () => {
|
|
45
|
+
if (closing) return;
|
|
46
|
+
setClosing(true);
|
|
47
|
+
setTimeout(() => {
|
|
48
|
+
setClosing(false);
|
|
49
|
+
onClose?.();
|
|
50
|
+
}, 260);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (typeof document === 'undefined') return;
|
|
55
|
+
const handleKeydown = (e) => {
|
|
56
|
+
if (e.key === 'Escape' && open) handleClose();
|
|
57
|
+
};
|
|
58
|
+
if (open) {
|
|
59
|
+
document.body.style.overflow = 'hidden';
|
|
60
|
+
window.addEventListener('keydown', handleKeydown);
|
|
61
|
+
} else {
|
|
62
|
+
document.body.style.overflow = '';
|
|
63
|
+
window.removeEventListener('keydown', handleKeydown);
|
|
64
|
+
}
|
|
65
|
+
return () => {
|
|
66
|
+
document.body.style.overflow = '';
|
|
67
|
+
window.removeEventListener('keydown', handleKeydown);
|
|
68
|
+
};
|
|
69
|
+
}, [open]);
|
|
70
|
+
|
|
71
|
+
if (!open && !closing) return null;
|
|
72
|
+
if (typeof document === 'undefined') return null;
|
|
73
|
+
|
|
74
|
+
const resolvedWidth = typeof width === 'number' ? `${width}px` : width;
|
|
75
|
+
const slideInAnim = side === 'left' ? 'cynxDrawerSlideInLeft' : 'cynxDrawerSlideInRight';
|
|
76
|
+
const slideOutAnim = side === 'left' ? 'cynxDrawerSlideOutLeft' : 'cynxDrawerSlideOutRight';
|
|
77
|
+
|
|
78
|
+
return createPortal(
|
|
79
|
+
<div
|
|
80
|
+
style={{
|
|
81
|
+
position: 'fixed',
|
|
82
|
+
inset: 0,
|
|
83
|
+
background: 'rgba(20, 22, 41, .45)',
|
|
84
|
+
zIndex: 8500,
|
|
85
|
+
display: 'flex',
|
|
86
|
+
justifyContent: side === 'left' ? 'flex-start' : 'flex-end',
|
|
87
|
+
animation: `${closing ? 'cynxDrawerFadeOut' : 'cynxDrawerFadeIn'} .26s ease forwards`,
|
|
88
|
+
}}
|
|
89
|
+
onMouseDown={(e) => {
|
|
90
|
+
if (e.target === e.currentTarget) handleClose();
|
|
91
|
+
}}
|
|
92
|
+
>
|
|
93
|
+
<div
|
|
94
|
+
style={{
|
|
95
|
+
background: 'var(--surface, #ffffff)',
|
|
96
|
+
borderLeft: side === 'right' ? '1px solid var(--border, var(--bd, #e2e8f0))' : 'none',
|
|
97
|
+
borderRight: side === 'left' ? '1px solid var(--border, var(--bd, #e2e8f0))' : 'none',
|
|
98
|
+
width: resolvedWidth,
|
|
99
|
+
maxWidth: '100vw',
|
|
100
|
+
height: '100%',
|
|
101
|
+
boxShadow: 'var(--shadow-lg, 0 20px 60px rgba(0, 0, 0, .25))',
|
|
102
|
+
display: 'flex',
|
|
103
|
+
flexDirection: 'column',
|
|
104
|
+
overflow: 'hidden',
|
|
105
|
+
animation: `${closing ? slideOutAnim : slideInAnim} .26s cubic-bezier(.16, 1, .3, 1) forwards`,
|
|
106
|
+
...style,
|
|
107
|
+
}}
|
|
108
|
+
onMouseDown={(e) => e.stopPropagation()}
|
|
109
|
+
>
|
|
110
|
+
{/* Header */}
|
|
111
|
+
<div
|
|
112
|
+
style={{
|
|
113
|
+
display: 'flex',
|
|
114
|
+
alignItems: 'center',
|
|
115
|
+
justifyContent: 'space-between',
|
|
116
|
+
padding: '16px 20px',
|
|
117
|
+
borderBottom: '1px solid var(--border-light, #ebebf0)',
|
|
118
|
+
flexShrink: 0,
|
|
119
|
+
}}
|
|
120
|
+
>
|
|
121
|
+
{header ? (
|
|
122
|
+
header
|
|
123
|
+
) : (
|
|
124
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
|
125
|
+
<span
|
|
126
|
+
style={{
|
|
127
|
+
fontSize: '1rem',
|
|
128
|
+
fontWeight: 700,
|
|
129
|
+
letterSpacing: '-.01em',
|
|
130
|
+
color: 'var(--t, var(--primary-text, #0f111c))',
|
|
131
|
+
}}
|
|
132
|
+
>
|
|
133
|
+
{{ title }}
|
|
134
|
+
</span>
|
|
135
|
+
{description && (
|
|
136
|
+
<span
|
|
137
|
+
style={{
|
|
138
|
+
fontSize: '.75rem',
|
|
139
|
+
color: 'var(--muted, #797b8d)',
|
|
140
|
+
}}
|
|
141
|
+
>
|
|
142
|
+
{description}
|
|
143
|
+
</span>
|
|
144
|
+
)}
|
|
145
|
+
</div>
|
|
146
|
+
)}
|
|
147
|
+
|
|
148
|
+
{showClose && (
|
|
149
|
+
<button
|
|
150
|
+
type="button"
|
|
151
|
+
style={{
|
|
152
|
+
background: 'none',
|
|
153
|
+
border: 'none',
|
|
154
|
+
cursor: 'pointer',
|
|
155
|
+
padding: '6px',
|
|
156
|
+
borderRadius: '6px',
|
|
157
|
+
color: 'var(--muted, #797b8d)',
|
|
158
|
+
display: 'flex',
|
|
159
|
+
alignItems: 'center',
|
|
160
|
+
justifyContent: 'center',
|
|
161
|
+
transition: 'all .14s',
|
|
162
|
+
}}
|
|
163
|
+
onClick={handleClose}
|
|
164
|
+
>
|
|
165
|
+
<X size={18} />
|
|
166
|
+
</button>
|
|
167
|
+
)}
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
{/* Body */}
|
|
171
|
+
<div
|
|
172
|
+
className="custom-scrollbar"
|
|
173
|
+
style={{
|
|
174
|
+
padding: '20px',
|
|
175
|
+
overflowX: 'hidden',
|
|
176
|
+
overflowY: 'auto',
|
|
177
|
+
flex: '1 1 auto',
|
|
178
|
+
minHeight: 0,
|
|
179
|
+
color: 'var(--t, var(--primary-text, #0f111c))',
|
|
180
|
+
...bodyStyle,
|
|
181
|
+
}}
|
|
182
|
+
>
|
|
183
|
+
{children}
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
{/* Footer */}
|
|
187
|
+
{footer && (
|
|
188
|
+
<div
|
|
189
|
+
style={{
|
|
190
|
+
display: 'flex',
|
|
191
|
+
justifyContent: 'flex-end',
|
|
192
|
+
gap: 8,
|
|
193
|
+
padding: '14px 20px',
|
|
194
|
+
borderTop: '1px solid var(--border-light, #ebebf0)',
|
|
195
|
+
background: 'var(--grey-1, #f5f5f8)',
|
|
196
|
+
flexShrink: 0,
|
|
197
|
+
}}
|
|
198
|
+
>
|
|
199
|
+
{footer}
|
|
200
|
+
</div>
|
|
201
|
+
)}
|
|
202
|
+
</div>
|
|
203
|
+
</div>,
|
|
204
|
+
document.body
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export default Drawer;
|
package/ui/index.cjs
CHANGED
|
@@ -41,6 +41,7 @@ __export(index_exports, {
|
|
|
41
41
|
ConfirmRoot: () => ConfirmRoot,
|
|
42
42
|
DatePicker: () => DatePicker,
|
|
43
43
|
Divider: () => Divider,
|
|
44
|
+
Drawer: () => Drawer,
|
|
44
45
|
Dropzone: () => Dropzone,
|
|
45
46
|
FilePicker: () => FilePicker,
|
|
46
47
|
HoverCard: () => HoverCard,
|
|
@@ -111,6 +112,7 @@ var import_PulseDot = __toESM(require("./components/PulseDot.jsx"), 1);
|
|
|
111
112
|
var import_CircularProgress = __toESM(require("./components/CircularProgress.jsx"), 1);
|
|
112
113
|
var import_Skeleton = __toESM(require("./components/Skeleton.jsx"), 1);
|
|
113
114
|
var import_Modal = require("./components/Modal.jsx");
|
|
115
|
+
var import_Drawer = require("./components/Drawer.jsx");
|
|
114
116
|
var import_Toast = require("./components/Toast.jsx");
|
|
115
117
|
var import_Confirm = require("./components/Confirm.jsx");
|
|
116
118
|
var import_Pagination = require("./components/Pagination.jsx");
|
|
@@ -157,6 +159,7 @@ const PulseDot = import_PulseDot.default;
|
|
|
157
159
|
const CircularProgress = import_CircularProgress.default;
|
|
158
160
|
const Skeleton = import_Skeleton.default;
|
|
159
161
|
const Modal = import_Modal.Modal;
|
|
162
|
+
const Drawer = import_Drawer.Drawer;
|
|
160
163
|
const Confirm = import_Modal.Confirm;
|
|
161
164
|
const toast = import_Toast.toast;
|
|
162
165
|
const useToast = import_Toast.useToast;
|
package/ui/index.js
CHANGED
|
@@ -21,6 +21,7 @@ import { default as _PulseDot } from './components/PulseDot.jsx';
|
|
|
21
21
|
import { default as _CircularProgress } from './components/CircularProgress.jsx';
|
|
22
22
|
import { default as _Skeleton } from './components/Skeleton.jsx';
|
|
23
23
|
import { Modal as _Modal, Confirm as _Confirm } from './components/Modal.jsx';
|
|
24
|
+
import { Drawer as _Drawer } from './components/Drawer.jsx';
|
|
24
25
|
import { toast as _toast, useToast as _useToast, Toast as _Toast } from './components/Toast.jsx';
|
|
25
26
|
import { ConfirmRoot as _ConfirmRoot, confirm as _confirm } from './components/Confirm.jsx';
|
|
26
27
|
import { Pagination as _Pagination } from './components/Pagination.jsx';
|
|
@@ -69,6 +70,7 @@ export const PulseDot = _PulseDot;
|
|
|
69
70
|
export const CircularProgress = _CircularProgress;
|
|
70
71
|
export const Skeleton = _Skeleton;
|
|
71
72
|
export const Modal = _Modal;
|
|
73
|
+
export const Drawer = _Drawer;
|
|
72
74
|
export const Confirm = _Confirm;
|
|
73
75
|
export const toast = _toast;
|
|
74
76
|
export const useToast = _useToast;
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref, watch, onUnmounted, computed } from "vue";
|
|
3
|
+
import { X } from "lucide-vue-next";
|
|
4
|
+
|
|
5
|
+
const props = withDefaults(
|
|
6
|
+
defineProps<{
|
|
7
|
+
modelValue?: boolean;
|
|
8
|
+
open?: boolean;
|
|
9
|
+
title?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
width?: number | string;
|
|
12
|
+
maxWidth?: number | string;
|
|
13
|
+
side?: "right" | "left";
|
|
14
|
+
showClose?: boolean;
|
|
15
|
+
bodyStyle?: Record<string, any>;
|
|
16
|
+
style?: Record<string, any>;
|
|
17
|
+
}>(),
|
|
18
|
+
{
|
|
19
|
+
modelValue: false,
|
|
20
|
+
open: false,
|
|
21
|
+
title: "",
|
|
22
|
+
description: "",
|
|
23
|
+
width: 560,
|
|
24
|
+
maxWidth: "100vw",
|
|
25
|
+
side: "right",
|
|
26
|
+
showClose: true,
|
|
27
|
+
bodyStyle: () => ({}),
|
|
28
|
+
style: () => ({}),
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const emit = defineEmits<{
|
|
33
|
+
(e: "update:modelValue", value: boolean): void;
|
|
34
|
+
(e: "update:open", value: boolean): void;
|
|
35
|
+
(e: "close"): void;
|
|
36
|
+
}>();
|
|
37
|
+
|
|
38
|
+
const isOpen = computed(() => props.modelValue || props.open);
|
|
39
|
+
const closing = ref(false);
|
|
40
|
+
|
|
41
|
+
const STYLES = `
|
|
42
|
+
@keyframes cynxDrawerFadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
43
|
+
@keyframes cynxDrawerFadeOut { from { opacity: 1; } to { opacity: 0; } }
|
|
44
|
+
@keyframes cynxDrawerSlideInRight { from { transform: translateX(100%); } to { transform: translateX(0); } }
|
|
45
|
+
@keyframes cynxDrawerSlideOutRight { from { transform: translateX(0); } to { transform: translateX(100%); } }
|
|
46
|
+
@keyframes cynxDrawerSlideInLeft { from { transform: translateX(-100%); } to { transform: translateX(0); } }
|
|
47
|
+
@keyframes cynxDrawerSlideOutLeft { from { transform: translateX(0); } to { transform: translateX(-100%); } }
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
let _id = "cynx-drawer-styles";
|
|
51
|
+
function ensureStyles() {
|
|
52
|
+
if (typeof document === "undefined") return;
|
|
53
|
+
if (document.getElementById(_id)) return;
|
|
54
|
+
const tag = document.createElement("style");
|
|
55
|
+
tag.id = _id;
|
|
56
|
+
tag.textContent = STYLES;
|
|
57
|
+
document.head.appendChild(tag);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (typeof document !== "undefined") ensureStyles();
|
|
61
|
+
|
|
62
|
+
function close() {
|
|
63
|
+
if (closing.value) return;
|
|
64
|
+
closing.value = true;
|
|
65
|
+
setTimeout(() => {
|
|
66
|
+
closing.value = false;
|
|
67
|
+
emit("update:modelValue", false);
|
|
68
|
+
emit("update:open", false);
|
|
69
|
+
emit("close");
|
|
70
|
+
}, 260);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function handleKeydown(e: KeyboardEvent) {
|
|
74
|
+
if (e.key === "Escape" && isOpen.value) {
|
|
75
|
+
close();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
watch(
|
|
80
|
+
isOpen,
|
|
81
|
+
(val) => {
|
|
82
|
+
if (typeof document === "undefined") return;
|
|
83
|
+
if (val) {
|
|
84
|
+
document.body.style.overflow = "hidden";
|
|
85
|
+
window.addEventListener("keydown", handleKeydown);
|
|
86
|
+
} else {
|
|
87
|
+
document.body.style.overflow = "";
|
|
88
|
+
window.removeEventListener("keydown", handleKeydown);
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
{ immediate: true }
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
onUnmounted(() => {
|
|
95
|
+
if (typeof document !== "undefined") {
|
|
96
|
+
document.body.style.overflow = "";
|
|
97
|
+
window.removeEventListener("keydown", handleKeydown);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const resolvedWidth = computed(() => {
|
|
102
|
+
if (typeof props.width === "number") return `${props.width}px`;
|
|
103
|
+
return props.width;
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const slideInAnim = computed(() =>
|
|
107
|
+
props.side === "left" ? "cynxDrawerSlideInLeft" : "cynxDrawerSlideInRight"
|
|
108
|
+
);
|
|
109
|
+
const slideOutAnim = computed(() =>
|
|
110
|
+
props.side === "left" ? "cynxDrawerSlideOutLeft" : "cynxDrawerSlideOutRight"
|
|
111
|
+
);
|
|
112
|
+
</script>
|
|
113
|
+
|
|
114
|
+
<template>
|
|
115
|
+
<Teleport to="body">
|
|
116
|
+
<div
|
|
117
|
+
v-if="isOpen || closing"
|
|
118
|
+
class="cynx-drawer-backdrop"
|
|
119
|
+
:style="{
|
|
120
|
+
position: 'fixed',
|
|
121
|
+
inset: 0,
|
|
122
|
+
background: 'rgba(20, 22, 41, .45)',
|
|
123
|
+
zIndex: 8500,
|
|
124
|
+
display: 'flex',
|
|
125
|
+
justifyContent: side === 'left' ? 'flex-start' : 'flex-end',
|
|
126
|
+
animation: `${closing ? 'cynxDrawerFadeOut' : 'cynxDrawerFadeIn'} .26s ease forwards`,
|
|
127
|
+
}"
|
|
128
|
+
@mousedown.self="close"
|
|
129
|
+
>
|
|
130
|
+
<div
|
|
131
|
+
class="cynx-drawer-panel"
|
|
132
|
+
:style="{
|
|
133
|
+
background: 'var(--surface, #ffffff)',
|
|
134
|
+
borderLeft: side === 'right' ? '1px solid var(--border, var(--bd, #e2e8f0))' : 'none',
|
|
135
|
+
borderRight: side === 'left' ? '1px solid var(--border, var(--bd, #e2e8f0))' : 'none',
|
|
136
|
+
width: resolvedWidth,
|
|
137
|
+
maxWidth: '100vw',
|
|
138
|
+
height: '100%',
|
|
139
|
+
boxShadow: 'var(--shadow-lg, 0 20px 60px rgba(0, 0, 0, .25))',
|
|
140
|
+
display: 'flex',
|
|
141
|
+
flexDirection: 'column',
|
|
142
|
+
overflow: 'hidden',
|
|
143
|
+
animation: `${closing ? slideOutAnim : slideInAnim} .26s cubic-bezier(.16, 1, .3, 1) forwards`,
|
|
144
|
+
...style,
|
|
145
|
+
}"
|
|
146
|
+
@mousedown.stop
|
|
147
|
+
>
|
|
148
|
+
<!-- Header -->
|
|
149
|
+
<div
|
|
150
|
+
class="cynx-drawer-hdr"
|
|
151
|
+
:style="{
|
|
152
|
+
display: 'flex',
|
|
153
|
+
alignItems: 'center',
|
|
154
|
+
justifyContent: 'space-between',
|
|
155
|
+
padding: '16px 20px',
|
|
156
|
+
borderBottom: '1px solid var(--border-light, #ebebf0)',
|
|
157
|
+
flexShrink: 0,
|
|
158
|
+
}"
|
|
159
|
+
>
|
|
160
|
+
<slot name="header">
|
|
161
|
+
<div style="display: flex; flex-direction: column; gap: 2px;">
|
|
162
|
+
<span
|
|
163
|
+
class="cynx-drawer-title"
|
|
164
|
+
:style="{
|
|
165
|
+
fontSize: '1rem',
|
|
166
|
+
fontWeight: 700,
|
|
167
|
+
letterSpacing: '-.01em',
|
|
168
|
+
color: 'var(--t, var(--primary-text, #0f111c))',
|
|
169
|
+
}"
|
|
170
|
+
>
|
|
171
|
+
{{ title }}
|
|
172
|
+
</span>
|
|
173
|
+
<span
|
|
174
|
+
v-if="description"
|
|
175
|
+
class="cynx-drawer-desc"
|
|
176
|
+
:style="{
|
|
177
|
+
fontSize: '.75rem',
|
|
178
|
+
color: 'var(--muted, #797b8d)',
|
|
179
|
+
}"
|
|
180
|
+
>
|
|
181
|
+
{{ description }}
|
|
182
|
+
</span>
|
|
183
|
+
</div>
|
|
184
|
+
</slot>
|
|
185
|
+
|
|
186
|
+
<button
|
|
187
|
+
v-if="showClose"
|
|
188
|
+
type="button"
|
|
189
|
+
class="cynx-drawer-close"
|
|
190
|
+
:style="{
|
|
191
|
+
background: 'none',
|
|
192
|
+
border: 'none',
|
|
193
|
+
cursor: 'pointer',
|
|
194
|
+
padding: '6px',
|
|
195
|
+
borderRadius: '6px',
|
|
196
|
+
color: 'var(--muted, #797b8d)',
|
|
197
|
+
display: 'flex',
|
|
198
|
+
alignItems: 'center',
|
|
199
|
+
justifyContent: 'center',
|
|
200
|
+
transition: 'all .14s',
|
|
201
|
+
}"
|
|
202
|
+
@click="close"
|
|
203
|
+
>
|
|
204
|
+
<X :size="18" />
|
|
205
|
+
</button>
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
<!-- Body -->
|
|
209
|
+
<div
|
|
210
|
+
class="cynx-drawer-body custom-scrollbar"
|
|
211
|
+
:style="{
|
|
212
|
+
padding: '20px',
|
|
213
|
+
overflowX: 'hidden',
|
|
214
|
+
overflowY: 'auto',
|
|
215
|
+
flex: '1 1 auto',
|
|
216
|
+
minHeight: 0,
|
|
217
|
+
color: 'var(--t, var(--primary-text, #0f111c))',
|
|
218
|
+
...bodyStyle,
|
|
219
|
+
}"
|
|
220
|
+
>
|
|
221
|
+
<slot />
|
|
222
|
+
</div>
|
|
223
|
+
|
|
224
|
+
<!-- Footer -->
|
|
225
|
+
<div
|
|
226
|
+
v-if="$slots.footer"
|
|
227
|
+
class="cynx-drawer-footer"
|
|
228
|
+
:style="{
|
|
229
|
+
display: 'flex',
|
|
230
|
+
justifyContent: 'flex-end',
|
|
231
|
+
gap: '8px',
|
|
232
|
+
padding: '14px 20px',
|
|
233
|
+
borderTop: '1px solid var(--border-light, #ebebf0)',
|
|
234
|
+
background: 'var(--grey-1, #f5f5f8)',
|
|
235
|
+
flexShrink: 0,
|
|
236
|
+
}"
|
|
237
|
+
>
|
|
238
|
+
<slot name="footer" />
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
</Teleport>
|
|
243
|
+
</template>
|
|
@@ -64,6 +64,7 @@ const props = withDefaults(
|
|
|
64
64
|
placeholder?: string;
|
|
65
65
|
disabled?: boolean;
|
|
66
66
|
multi?: boolean;
|
|
67
|
+
multiple?: boolean;
|
|
67
68
|
searchable?: boolean;
|
|
68
69
|
clearable?: boolean;
|
|
69
70
|
size?: "xs" | "sm" | "md" | "lg";
|
|
@@ -82,6 +83,7 @@ const props = withDefaults(
|
|
|
82
83
|
placeholder: "Select...",
|
|
83
84
|
disabled: false,
|
|
84
85
|
multi: false,
|
|
86
|
+
multiple: false,
|
|
85
87
|
searchable: false,
|
|
86
88
|
clearable: false,
|
|
87
89
|
size: "md",
|
|
@@ -118,9 +120,11 @@ const currentVal = computed(() => {
|
|
|
118
120
|
return props.modelValue !== undefined ? props.modelValue : props.value;
|
|
119
121
|
});
|
|
120
122
|
|
|
123
|
+
const isMulti = computed(() => Boolean(props.multi || props.multiple));
|
|
124
|
+
|
|
121
125
|
const selectedValues = computed<any[]>(() => {
|
|
122
126
|
const v = currentVal.value;
|
|
123
|
-
if (
|
|
127
|
+
if (isMulti.value) {
|
|
124
128
|
return Array.isArray(v) ? v : [];
|
|
125
129
|
}
|
|
126
130
|
return v !== undefined && v !== null && v !== "" ? [v] : [];
|
|
@@ -133,7 +137,7 @@ function getLabel(optVal: any) {
|
|
|
133
137
|
|
|
134
138
|
function isSelected(optVal: any) {
|
|
135
139
|
const v = currentVal.value;
|
|
136
|
-
return
|
|
140
|
+
return isMulti.value ? (Array.isArray(v) && v.includes(optVal)) : v === optVal;
|
|
137
141
|
}
|
|
138
142
|
|
|
139
143
|
const radiusVal = computed(() => {
|
|
@@ -279,7 +283,7 @@ function handleOpen() {
|
|
|
279
283
|
}
|
|
280
284
|
|
|
281
285
|
function toggleOption(optVal: any) {
|
|
282
|
-
if (
|
|
286
|
+
if (isMulti.value) {
|
|
283
287
|
const arr = Array.isArray(currentVal.value) ? currentVal.value : [];
|
|
284
288
|
const next = arr.includes(optVal)
|
|
285
289
|
? arr.filter((v: any) => v !== optVal)
|
|
@@ -304,7 +308,7 @@ function removeTag(e: MouseEvent, valToRemove: any) {
|
|
|
304
308
|
|
|
305
309
|
function handleClear(e: MouseEvent) {
|
|
306
310
|
e.stopPropagation();
|
|
307
|
-
const next =
|
|
311
|
+
const next = isMulti.value ? [] : "";
|
|
308
312
|
emit("update:modelValue", next);
|
|
309
313
|
emit("change", next);
|
|
310
314
|
emit("clear");
|
|
@@ -385,10 +389,10 @@ function handleInputKeyDown(e: KeyboardEvent) {
|
|
|
385
389
|
<slot
|
|
386
390
|
v-if="slots.label && selectedValues.length > 0"
|
|
387
391
|
name="label"
|
|
388
|
-
:value="
|
|
392
|
+
:value="isMulti ? selectedValues : selectedValues[0]"
|
|
389
393
|
/>
|
|
390
394
|
|
|
391
|
-
<template v-else-if="
|
|
395
|
+
<template v-else-if="isMulti && selectedValues.length > 0">
|
|
392
396
|
<span
|
|
393
397
|
v-if="showCount"
|
|
394
398
|
style="
|
package/vue/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import Table from "./components/Table.vue";
|
|
|
11
11
|
import Pagination from "./components/Pagination.vue";
|
|
12
12
|
import Skeleton from "./components/Skeleton.vue";
|
|
13
13
|
import Modal from "./components/Modal.vue";
|
|
14
|
+
import Drawer from "./components/Drawer.vue";
|
|
14
15
|
import Toggle from "./components/Toggle.vue";
|
|
15
16
|
import { XTable, XThead, XTbody, XTr, XTh, XTd } from "./components/XTable.vue";
|
|
16
17
|
import { useAutoSkeletonRows } from "./composables/useAutoSkeletonRows";
|
|
@@ -28,6 +29,7 @@ export {
|
|
|
28
29
|
Pagination,
|
|
29
30
|
Skeleton,
|
|
30
31
|
Modal,
|
|
32
|
+
Drawer,
|
|
31
33
|
Toggle,
|
|
32
34
|
XTable,
|
|
33
35
|
XThead,
|
|
@@ -51,6 +53,7 @@ export default {
|
|
|
51
53
|
Pagination,
|
|
52
54
|
Skeleton,
|
|
53
55
|
Modal,
|
|
56
|
+
Drawer,
|
|
54
57
|
Toggle,
|
|
55
58
|
XTable,
|
|
56
59
|
XThead,
|