cynx-lib 1.3.55 → 1.3.57
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/vue/components/Badge.vue +22 -15
- package/vue/components/Button.vue +18 -7
- package/vue/components/Modal.vue +226 -0
- package/vue/components/Spinner.vue +202 -85
- package/vue/components/Table.vue +12 -39
- package/vue/components/Toggle.vue +115 -0
- package/vue/index.d.ts +2 -0
- package/vue/index.js +6 -0
package/package.json
CHANGED
package/vue/components/Badge.vue
CHANGED
|
@@ -2,19 +2,20 @@
|
|
|
2
2
|
import { computed } from "vue";
|
|
3
3
|
|
|
4
4
|
const VARIANTS: Record<string, any> = {
|
|
5
|
-
ok: { bg: "rgba(39,174,96,.1)", color: "#16a34a", border: "rgba(39,174,96,.2)", dot: "#16a34a" },
|
|
6
|
-
err: { bg: "rgba(246,71,71,.1)", color: "#ef4444", border: "rgba(246,71,71,.2)", dot: "#ef4444" },
|
|
7
|
-
warn: { bg: "rgba(249,158,44,.1)", color: "#f59e0b", border: "rgba(249,158,44,.25)", dot: "#f59e0b" },
|
|
8
|
-
blue: { bg: "rgba(39,130,228,.1)", color: "#2563eb", border: "rgba(39,130,228,.2)", dot: "#2563eb" },
|
|
9
|
-
teal: { bg: "rgba(18,103,93,.1)", color: "#12675D", border: "rgba(18,103,93,.25)", dot: "#12675D" },
|
|
10
|
-
dim: { bg: "#
|
|
5
|
+
ok: { bg: "var(--success-bg, rgba(39,174,96,.1))", color: "var(--success, #16a34a)", border: "var(--sd, rgba(39,174,96,.2))", dot: "var(--success, #16a34a)" },
|
|
6
|
+
err: { bg: "var(--danger-bg, rgba(246,71,71,.1))", color: "var(--danger, #ef4444)", border: "var(--dd, rgba(246,71,71,.2))", dot: "var(--danger, #ef4444)" },
|
|
7
|
+
warn: { bg: "var(--accent-bg, rgba(249,158,44,.1))", color: "var(--accent, #f59e0b)", border: "var(--accent-border, rgba(249,158,44,.25))", dot: "var(--accent, #f59e0b)" },
|
|
8
|
+
blue: { bg: "var(--blue-bg, rgba(39,130,228,.1))", color: "var(--blue, #2563eb)", border: "var(--infd, rgba(39,130,228,.2))", dot: "var(--blue, #2563eb)" },
|
|
9
|
+
teal: { bg: "rgba(18,103,93,.1)", color: "var(--accent, #12675D)", border: "rgba(18,103,93,.25)", dot: "var(--accent, #12675D)" },
|
|
10
|
+
dim: { bg: "var(--grey-1, #f5f5f8)", color: "var(--grey-4, #64748b)", border: "var(--border, #e2e8f0)", dot: "var(--grey-4, #64748b)" },
|
|
11
|
+
ghost: { bg: "transparent", color: "var(--t2, #64748b)", border: "var(--bd, #e2e8f0)", dot: "var(--t3, #94a3b8)" },
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
const SIZE_MAP: Record<string, any> = {
|
|
14
|
-
xs: { padding: "1.5px 6px", fontSize: "9.5px", dotSize: 5 },
|
|
15
|
-
sm: { padding: "2px 8px", fontSize: "11px", dotSize: 6 },
|
|
16
|
-
md: { padding: "3px 9px", fontSize: "12px", dotSize: 6.5 },
|
|
17
|
-
lg: { padding: "4px 12px", fontSize: "13px", dotSize: 8 },
|
|
15
|
+
xs: { padding: "1.5px 6px", plainPadding: "1px 0", fontSize: "9.5px", dotSize: 5 },
|
|
16
|
+
sm: { padding: "2px 8px", plainPadding: "2px 0", fontSize: "11px", dotSize: 6 },
|
|
17
|
+
md: { padding: "3px 9px", plainPadding: "2px 0", fontSize: "12px", dotSize: 6.5 },
|
|
18
|
+
lg: { padding: "4px 12px", plainPadding: "3px 0", fontSize: "13px", dotSize: 8 },
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
const props = withDefaults(
|
|
@@ -23,30 +24,36 @@ const props = withDefaults(
|
|
|
23
24
|
size?: "xs" | "sm" | "md" | "lg";
|
|
24
25
|
dot?: boolean;
|
|
25
26
|
pill?: boolean;
|
|
27
|
+
plain?: boolean;
|
|
28
|
+
minimal?: boolean;
|
|
26
29
|
}>(),
|
|
27
30
|
{
|
|
28
31
|
variant: "dim",
|
|
29
32
|
size: "sm",
|
|
30
33
|
dot: false,
|
|
31
34
|
pill: true,
|
|
35
|
+
plain: false,
|
|
36
|
+
minimal: false,
|
|
32
37
|
}
|
|
33
38
|
);
|
|
34
39
|
|
|
35
40
|
const v = computed(() => VARIANTS[props.variant] || VARIANTS.dim);
|
|
36
41
|
const s = computed(() => SIZE_MAP[props.size] || SIZE_MAP.sm);
|
|
42
|
+
const isPlain = computed(() => props.plain || props.minimal);
|
|
37
43
|
</script>
|
|
38
44
|
|
|
39
45
|
<template>
|
|
40
46
|
<span
|
|
41
47
|
:class="[
|
|
42
|
-
'inline-flex items-center gap-1.5
|
|
43
|
-
|
|
48
|
+
'inline-flex items-center gap-1.5 select-none',
|
|
49
|
+
isPlain ? 'font-semibold' : 'font-bold leading-none',
|
|
50
|
+
isPlain ? '' : (pill ? 'rounded-full' : 'rounded-md')
|
|
44
51
|
]"
|
|
45
52
|
:style="{
|
|
46
|
-
background: v.bg,
|
|
53
|
+
background: isPlain ? 'transparent' : v.bg,
|
|
47
54
|
color: v.color,
|
|
48
|
-
border: `1px solid ${v.border}`,
|
|
49
|
-
padding: s.padding,
|
|
55
|
+
border: isPlain ? 'none' : `1px solid ${v.border}`,
|
|
56
|
+
padding: isPlain ? s.plainPadding : s.padding,
|
|
50
57
|
fontSize: s.fontSize,
|
|
51
58
|
}"
|
|
52
59
|
>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed } from "vue";
|
|
2
|
+
import { computed, useSlots } from "vue";
|
|
3
3
|
import Spinner from "./Spinner.vue";
|
|
4
4
|
|
|
5
5
|
const VARIANTS: Record<string, any> = {
|
|
@@ -60,12 +60,22 @@ const VARIANTS: Record<string, any> = {
|
|
|
60
60
|
hoverShadow: "0 2px 8px rgba(249,158,44,.25)",
|
|
61
61
|
},
|
|
62
62
|
ghost: {
|
|
63
|
-
background: "
|
|
63
|
+
background: "transparent",
|
|
64
64
|
color: "var(--t2, #797b8d)",
|
|
65
|
-
border: "1px solid
|
|
65
|
+
border: "1px solid transparent",
|
|
66
66
|
hoverColor: "var(--t1, #1a1a2e)",
|
|
67
67
|
hoverBg: "var(--grey-1, #f5f5f8)",
|
|
68
|
-
hoverBorder: "
|
|
68
|
+
hoverBorder: "transparent",
|
|
69
|
+
boxShadow: "none",
|
|
70
|
+
},
|
|
71
|
+
icon: {
|
|
72
|
+
background: "transparent",
|
|
73
|
+
color: "var(--t2, #797b8d)",
|
|
74
|
+
border: "1px solid transparent",
|
|
75
|
+
hoverColor: "var(--t1, #1a1a2e)",
|
|
76
|
+
hoverBg: "var(--grey-1, #f5f5f8)",
|
|
77
|
+
hoverBorder: "transparent",
|
|
78
|
+
boxShadow: "none",
|
|
69
79
|
},
|
|
70
80
|
outline: {
|
|
71
81
|
background: "transparent",
|
|
@@ -120,9 +130,10 @@ const emit = defineEmits<{
|
|
|
120
130
|
(e: "click", event: MouseEvent): void;
|
|
121
131
|
}>();
|
|
122
132
|
|
|
133
|
+
const slots = useSlots();
|
|
123
134
|
const v = computed(() => VARIANTS[props.variant] || VARIANTS.default);
|
|
124
135
|
const s = computed(() => SIZES[props.size] || SIZES.md);
|
|
125
|
-
const isIconOnly = computed(() => props.variant === "icon");
|
|
136
|
+
const isIconOnly = computed(() => props.variant === "icon" || (!slots.default && (Boolean(slots.icon) || Boolean(slots.iconRight))));
|
|
126
137
|
</script>
|
|
127
138
|
|
|
128
139
|
<template>
|
|
@@ -137,7 +148,7 @@ const isIconOnly = computed(() => props.variant === "icon");
|
|
|
137
148
|
whiteSpace: 'nowrap',
|
|
138
149
|
letterSpacing: '-.01em',
|
|
139
150
|
outline: 'none',
|
|
140
|
-
boxShadow: 'var(--shadow, 0 1px 3px rgba(20,22,41,.1))',
|
|
151
|
+
boxShadow: v.boxShadow !== undefined ? v.boxShadow : 'var(--shadow, 0 1px 3px rgba(20,22,41,.1))',
|
|
141
152
|
opacity: disabled ? 0.45 : 1,
|
|
142
153
|
pointerEvents: disabled ? 'none' : undefined,
|
|
143
154
|
background: v.background,
|
|
@@ -147,9 +158,9 @@ const isIconOnly = computed(() => props.variant === "icon");
|
|
|
147
158
|
fontSize: `${s.fontSize}px`,
|
|
148
159
|
height: `${s.height}px`,
|
|
149
160
|
width: isIconOnly ? `${s.height}px` : undefined,
|
|
161
|
+
minWidth: isIconOnly ? `${s.height}px` : undefined,
|
|
150
162
|
...(v.paddingLeft !== undefined && { paddingLeft: v.paddingLeft }),
|
|
151
163
|
...(v.paddingRight !== undefined && { paddingRight: v.paddingRight }),
|
|
152
|
-
...(v.boxShadow !== undefined && { boxShadow: v.boxShadow }),
|
|
153
164
|
}"
|
|
154
165
|
@click="emit('click', $event)"
|
|
155
166
|
>
|
|
@@ -0,0 +1,226 @@
|
|
|
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
|
+
maxWidth?: number | string;
|
|
11
|
+
maxHeight?: number | string;
|
|
12
|
+
showClose?: boolean;
|
|
13
|
+
bodyStyle?: Record<string, any>;
|
|
14
|
+
style?: Record<string, any>;
|
|
15
|
+
}>(),
|
|
16
|
+
{
|
|
17
|
+
modelValue: false,
|
|
18
|
+
open: false,
|
|
19
|
+
title: "",
|
|
20
|
+
maxWidth: 520,
|
|
21
|
+
showClose: true,
|
|
22
|
+
bodyStyle: () => ({}),
|
|
23
|
+
style: () => ({}),
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const emit = defineEmits<{
|
|
28
|
+
(e: "update:modelValue", value: boolean): void;
|
|
29
|
+
(e: "close"): void;
|
|
30
|
+
}>();
|
|
31
|
+
|
|
32
|
+
const isOpen = computed(() => props.modelValue || props.open);
|
|
33
|
+
const closing = ref(false);
|
|
34
|
+
|
|
35
|
+
const STYLES = `
|
|
36
|
+
@keyframes cynxModalFadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
37
|
+
@keyframes cynxModalFadeOut { from { opacity: 1; } to { opacity: 0; } }
|
|
38
|
+
@keyframes cynxModalCardIn { from { opacity: 0; transform: scale(.96) translateY(-8px); } to { opacity: 1; transform: scale(1) translateY(0); } }
|
|
39
|
+
@keyframes cynxModalCardOut { from { opacity: 1; transform: scale(1) translateY(0); } to { opacity: 0; transform: scale(.96) translateY(-8px); } }
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
let _id = "cynx-modal-styles";
|
|
43
|
+
function ensureStyles() {
|
|
44
|
+
if (typeof document === "undefined") return;
|
|
45
|
+
if (document.getElementById(_id)) return;
|
|
46
|
+
const tag = document.createElement("style");
|
|
47
|
+
tag.id = _id;
|
|
48
|
+
tag.textContent = STYLES;
|
|
49
|
+
document.head.appendChild(tag);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (typeof document !== "undefined") ensureStyles();
|
|
53
|
+
|
|
54
|
+
function close() {
|
|
55
|
+
if (closing.value) return;
|
|
56
|
+
closing.value = true;
|
|
57
|
+
setTimeout(() => {
|
|
58
|
+
closing.value = false;
|
|
59
|
+
emit("update:modelValue", false);
|
|
60
|
+
emit("close");
|
|
61
|
+
}, 260);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function handleKeydown(e: KeyboardEvent) {
|
|
65
|
+
if (e.key === "Escape" && isOpen.value) {
|
|
66
|
+
close();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
watch(
|
|
71
|
+
isOpen,
|
|
72
|
+
(val) => {
|
|
73
|
+
if (typeof document === "undefined") return;
|
|
74
|
+
if (val) {
|
|
75
|
+
document.body.style.overflow = "hidden";
|
|
76
|
+
window.addEventListener("keydown", handleKeydown);
|
|
77
|
+
} else {
|
|
78
|
+
document.body.style.overflow = "";
|
|
79
|
+
window.removeEventListener("keydown", handleKeydown);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
{ immediate: true }
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
onUnmounted(() => {
|
|
86
|
+
if (typeof document !== "undefined") {
|
|
87
|
+
document.body.style.overflow = "";
|
|
88
|
+
window.removeEventListener("keydown", handleKeydown);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const resolvedMaxWidth = computed(() => {
|
|
93
|
+
if (typeof props.maxWidth === "number") return `${props.maxWidth}px`;
|
|
94
|
+
return props.maxWidth;
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const resolvedMaxHeight = computed(() => {
|
|
98
|
+
if (props.maxHeight) {
|
|
99
|
+
return typeof props.maxHeight === "number" ? `${props.maxHeight}px` : props.maxHeight;
|
|
100
|
+
}
|
|
101
|
+
return "85vh";
|
|
102
|
+
});
|
|
103
|
+
</script>
|
|
104
|
+
|
|
105
|
+
<template>
|
|
106
|
+
<Teleport to="body">
|
|
107
|
+
<div
|
|
108
|
+
v-if="isOpen || closing"
|
|
109
|
+
class="cynx-modal-backdrop"
|
|
110
|
+
:style="{
|
|
111
|
+
position: 'fixed',
|
|
112
|
+
inset: 0,
|
|
113
|
+
background: 'rgba(20, 22, 41, .45)',
|
|
114
|
+
display: 'flex',
|
|
115
|
+
alignItems: 'center',
|
|
116
|
+
justifyContent: 'center',
|
|
117
|
+
zIndex: 8000,
|
|
118
|
+
padding: '20px',
|
|
119
|
+
animation: `${closing ? 'cynxModalFadeOut' : 'cynxModalFadeIn'} .26s ease forwards`,
|
|
120
|
+
}"
|
|
121
|
+
@mousedown.self="close"
|
|
122
|
+
>
|
|
123
|
+
<div
|
|
124
|
+
class="cynx-modal-card"
|
|
125
|
+
:style="{
|
|
126
|
+
background: 'var(--surface, #ffffff)',
|
|
127
|
+
border: '1px solid var(--border, #e2e8f0)',
|
|
128
|
+
borderRadius: '14px',
|
|
129
|
+
width: '100%',
|
|
130
|
+
maxWidth: resolvedMaxWidth,
|
|
131
|
+
maxHeight: resolvedMaxHeight,
|
|
132
|
+
boxShadow: 'var(--shadow-lg, 0 20px 60px rgba(0, 0, 0, .25))',
|
|
133
|
+
overflow: 'hidden',
|
|
134
|
+
display: 'flex',
|
|
135
|
+
flexDirection: 'column',
|
|
136
|
+
animation: `${closing ? 'cynxModalCardOut' : 'cynxModalCardIn'} .26s cubic-bezier(.4, 0, .2, 1) forwards`,
|
|
137
|
+
...style,
|
|
138
|
+
}"
|
|
139
|
+
@mousedown.stop
|
|
140
|
+
>
|
|
141
|
+
<!-- Modal Header -->
|
|
142
|
+
<div
|
|
143
|
+
class="cynx-modal-hdr"
|
|
144
|
+
:style="{
|
|
145
|
+
display: 'flex',
|
|
146
|
+
alignItems: 'center',
|
|
147
|
+
justifyContent: 'space-between',
|
|
148
|
+
padding: '15px 20px',
|
|
149
|
+
borderBottom: '1px solid var(--border-light, #ebebf0)',
|
|
150
|
+
borderRadius: '14px 14px 0 0',
|
|
151
|
+
flexShrink: 0,
|
|
152
|
+
}"
|
|
153
|
+
>
|
|
154
|
+
<slot name="header">
|
|
155
|
+
<span
|
|
156
|
+
class="cynx-modal-title"
|
|
157
|
+
:style="{
|
|
158
|
+
fontSize: '.95rem',
|
|
159
|
+
fontWeight: 700,
|
|
160
|
+
letterSpacing: '-.01em',
|
|
161
|
+
color: 'var(--t, var(--primary-text, #0f111c))',
|
|
162
|
+
}"
|
|
163
|
+
>
|
|
164
|
+
{{ title }}
|
|
165
|
+
</span>
|
|
166
|
+
</slot>
|
|
167
|
+
|
|
168
|
+
<button
|
|
169
|
+
v-if="showClose"
|
|
170
|
+
type="button"
|
|
171
|
+
class="cynx-modal-close"
|
|
172
|
+
:style="{
|
|
173
|
+
background: 'none',
|
|
174
|
+
border: 'none',
|
|
175
|
+
cursor: 'pointer',
|
|
176
|
+
padding: '4px',
|
|
177
|
+
borderRadius: '6px',
|
|
178
|
+
color: 'var(--muted, #797b8d)',
|
|
179
|
+
display: 'flex',
|
|
180
|
+
alignItems: 'center',
|
|
181
|
+
justifyContent: 'center',
|
|
182
|
+
transition: 'all .14s',
|
|
183
|
+
}"
|
|
184
|
+
@click="close"
|
|
185
|
+
>
|
|
186
|
+
<X :size="16" />
|
|
187
|
+
</button>
|
|
188
|
+
</div>
|
|
189
|
+
|
|
190
|
+
<!-- Modal Body -->
|
|
191
|
+
<div
|
|
192
|
+
class="cynx-modal-body custom-scrollbar"
|
|
193
|
+
:style="{
|
|
194
|
+
padding: '20px',
|
|
195
|
+
overflowX: 'hidden',
|
|
196
|
+
overflowY: 'auto',
|
|
197
|
+
flex: '1 1 auto',
|
|
198
|
+
minHeight: 0,
|
|
199
|
+
color: 'var(--t, var(--primary-text, #0f111c))',
|
|
200
|
+
...bodyStyle,
|
|
201
|
+
}"
|
|
202
|
+
>
|
|
203
|
+
<slot />
|
|
204
|
+
</div>
|
|
205
|
+
|
|
206
|
+
<!-- Modal Footer -->
|
|
207
|
+
<div
|
|
208
|
+
v-if="$slots.footer"
|
|
209
|
+
class="cynx-modal-footer"
|
|
210
|
+
:style="{
|
|
211
|
+
display: 'flex',
|
|
212
|
+
justifyContent: 'flex-end',
|
|
213
|
+
gap: '8px',
|
|
214
|
+
padding: '13px 20px',
|
|
215
|
+
borderTop: '1px solid var(--border-light, #ebebf0)',
|
|
216
|
+
background: 'var(--grey-1, #f5f5f8)',
|
|
217
|
+
borderRadius: '0 0 14px 14px',
|
|
218
|
+
flexShrink: 0,
|
|
219
|
+
}"
|
|
220
|
+
>
|
|
221
|
+
<slot name="footer" />
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
</Teleport>
|
|
226
|
+
</template>
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from "vue";
|
|
3
3
|
|
|
4
|
+
const STYLES = `.spin{animation:cx-spin .7s linear infinite}@keyframes cx-spin{to{transform:rotate(360deg)}}.v-progress-circular--indeterminate>svg{animation:progress-circular-rotate .8s linear infinite;transform-origin:center center;transition:all .2s ease-in-out}.v-progress-circular>svg{width:100%;height:100%;margin:auto;position:absolute;inset:0;z-index:0}@keyframes progress-circular-rotate{100%{transform:rotate(270deg)}}@keyframes progress-circular-dash{0%{stroke-dasharray:1,200;stroke-dashoffset:0}50%{stroke-dasharray:89,200;stroke-dashoffset:-35px}100%{stroke-dasharray:89,200;stroke-dashoffset:-124px}}.v-progress-circular--indeterminate .v-progress-circular__overlay{animation:progress-circular-dash 1.4s ease-in-out infinite;stroke-linecap:round}`;
|
|
5
|
+
|
|
6
|
+
const _id = "cynx-spinner-styles";
|
|
7
|
+
function ensureStyles() {
|
|
8
|
+
if (typeof document === "undefined") return;
|
|
9
|
+
if (document.getElementById(_id)) return;
|
|
10
|
+
const tag = document.createElement("style");
|
|
11
|
+
tag.id = _id;
|
|
12
|
+
tag.textContent = STYLES;
|
|
13
|
+
document.head.appendChild(tag);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (typeof document !== "undefined") ensureStyles();
|
|
17
|
+
|
|
4
18
|
const props = withDefaults(
|
|
5
19
|
defineProps<{
|
|
6
20
|
size?: number;
|
|
@@ -10,7 +24,7 @@ const props = withDefaults(
|
|
|
10
24
|
variant?: "default" | "classic";
|
|
11
25
|
}>(),
|
|
12
26
|
{
|
|
13
|
-
size:
|
|
27
|
+
size: undefined,
|
|
14
28
|
inline: false,
|
|
15
29
|
color: "currentColor",
|
|
16
30
|
fullPage: false,
|
|
@@ -18,56 +32,61 @@ const props = withDefaults(
|
|
|
18
32
|
}
|
|
19
33
|
);
|
|
20
34
|
|
|
21
|
-
|
|
35
|
+
ensureStyles();
|
|
36
|
+
|
|
37
|
+
const s = computed(() => {
|
|
38
|
+
if (props.size !== undefined && props.size !== null) {
|
|
39
|
+
return props.size;
|
|
40
|
+
}
|
|
41
|
+
return props.inline ? 14 : 20;
|
|
42
|
+
});
|
|
22
43
|
</script>
|
|
23
44
|
|
|
24
45
|
<template>
|
|
25
|
-
<!--
|
|
26
|
-
<
|
|
27
|
-
v-if="fullPage"
|
|
28
|
-
class="fixed inset-0 bg-[var(--bg,#f8fafc)] flex flex-col items-center justify-center gap-4 z-[9999]"
|
|
29
|
-
>
|
|
46
|
+
<!-- Classic Variant -->
|
|
47
|
+
<template v-if="variant === 'classic'">
|
|
30
48
|
<div
|
|
31
|
-
|
|
32
|
-
:style="{
|
|
49
|
+
v-if="fullPage"
|
|
50
|
+
:style="{
|
|
51
|
+
position: 'fixed',
|
|
52
|
+
inset: 0,
|
|
53
|
+
background: 'var(--bg, #f8fafc)',
|
|
54
|
+
display: 'flex',
|
|
55
|
+
flexDirection: 'column',
|
|
56
|
+
alignItems: 'center',
|
|
57
|
+
justifyContent: 'center',
|
|
58
|
+
gap: '16px',
|
|
59
|
+
zIndex: 9999,
|
|
60
|
+
}"
|
|
33
61
|
>
|
|
34
62
|
<svg
|
|
35
|
-
|
|
36
|
-
|
|
63
|
+
class="spin"
|
|
64
|
+
:width="s"
|
|
65
|
+
:height="s"
|
|
66
|
+
viewBox="0 0 24 24"
|
|
67
|
+
fill="none"
|
|
68
|
+
:stroke="color"
|
|
69
|
+
stroke-width="2.5"
|
|
70
|
+
stroke-linecap="round"
|
|
37
71
|
>
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
cx="50%"
|
|
41
|
-
cy="50%"
|
|
42
|
-
r="20"
|
|
43
|
-
stroke="var(--grey-2,#e0e0e0)"
|
|
44
|
-
stroke-width="2.42"
|
|
45
|
-
stroke-dasharray="125.66"
|
|
46
|
-
stroke-dashoffset="0"
|
|
47
|
-
/>
|
|
48
|
-
<circle
|
|
49
|
-
fill="transparent"
|
|
50
|
-
cx="50%"
|
|
51
|
-
cy="50%"
|
|
52
|
-
r="20"
|
|
53
|
-
:stroke="color === 'currentColor' ? 'var(--accent,#12675D)' : color"
|
|
54
|
-
stroke-width="2.42"
|
|
55
|
-
class="v-progress-circular__overlay"
|
|
72
|
+
<path
|
|
73
|
+
d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"
|
|
56
74
|
/>
|
|
57
75
|
</svg>
|
|
76
|
+
<div
|
|
77
|
+
:style="{
|
|
78
|
+
fontSize: '.75rem',
|
|
79
|
+
fontFamily: 'var(--mono, ui-monospace, monospace)',
|
|
80
|
+
color: 'var(--muted, #797b8d)',
|
|
81
|
+
}"
|
|
82
|
+
>
|
|
83
|
+
Loading...
|
|
84
|
+
</div>
|
|
58
85
|
</div>
|
|
59
|
-
<div class="text-xs font-mono text-[var(--muted,#797b8d)]">Loading...</div>
|
|
60
|
-
</div>
|
|
61
86
|
|
|
62
|
-
<!-- Inline / Normal Spinner -->
|
|
63
|
-
<div
|
|
64
|
-
v-else
|
|
65
|
-
:class="[inline ? 'inline-flex items-center' : 'flex items-center justify-center py-12']"
|
|
66
|
-
>
|
|
67
|
-
<!-- Classic spinner variant -->
|
|
68
87
|
<svg
|
|
69
|
-
v-if="
|
|
70
|
-
class="
|
|
88
|
+
v-else-if="inline"
|
|
89
|
+
class="spin"
|
|
71
90
|
:width="s"
|
|
72
91
|
:height="s"
|
|
73
92
|
viewBox="0 0 24 24"
|
|
@@ -81,66 +100,164 @@ const s = computed(() => (props.inline ? 14 : props.size));
|
|
|
81
100
|
/>
|
|
82
101
|
</svg>
|
|
83
102
|
|
|
84
|
-
<!-- Circular spinner variant (default) -->
|
|
85
103
|
<div
|
|
86
104
|
v-else
|
|
87
|
-
|
|
88
|
-
|
|
105
|
+
:style="{
|
|
106
|
+
display: 'flex',
|
|
107
|
+
alignItems: 'center',
|
|
108
|
+
justifyContent: 'center',
|
|
109
|
+
padding: '48px 0',
|
|
110
|
+
}"
|
|
111
|
+
>
|
|
112
|
+
<svg
|
|
113
|
+
class="spin"
|
|
114
|
+
:width="s"
|
|
115
|
+
:height="s"
|
|
116
|
+
viewBox="0 0 24 24"
|
|
117
|
+
fill="none"
|
|
118
|
+
:stroke="color"
|
|
119
|
+
stroke-width="2.5"
|
|
120
|
+
stroke-linecap="round"
|
|
121
|
+
>
|
|
122
|
+
<path
|
|
123
|
+
d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"
|
|
124
|
+
/>
|
|
125
|
+
</svg>
|
|
126
|
+
</div>
|
|
127
|
+
</template>
|
|
128
|
+
|
|
129
|
+
<!-- Circular Variant (Default) -->
|
|
130
|
+
<template v-else>
|
|
131
|
+
<!-- FullPage Mode -->
|
|
132
|
+
<div
|
|
133
|
+
v-if="fullPage"
|
|
134
|
+
:style="{
|
|
135
|
+
position: 'fixed',
|
|
136
|
+
inset: 0,
|
|
137
|
+
background: 'var(--bg, #f8fafc)',
|
|
138
|
+
display: 'flex',
|
|
139
|
+
flexDirection: 'column',
|
|
140
|
+
alignItems: 'center',
|
|
141
|
+
justifyContent: 'center',
|
|
142
|
+
gap: '16px',
|
|
143
|
+
zIndex: 9999,
|
|
144
|
+
}"
|
|
145
|
+
>
|
|
146
|
+
<div
|
|
147
|
+
class="v-progress-circular v-progress-circular--indeterminate"
|
|
148
|
+
:style="{ width: `${s}px`, height: `${s}px` }"
|
|
149
|
+
>
|
|
150
|
+
<svg
|
|
151
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
152
|
+
viewBox="0 0 42.42 42.42"
|
|
153
|
+
:style="{ width: `${s}px`, height: `${s}px`, transform: 'rotate(-90deg)' }"
|
|
154
|
+
>
|
|
155
|
+
<circle
|
|
156
|
+
fill="transparent"
|
|
157
|
+
cx="50%"
|
|
158
|
+
cy="50%"
|
|
159
|
+
r="20"
|
|
160
|
+
stroke="var(--grey-2, #e0e0e0)"
|
|
161
|
+
stroke-width="2.42"
|
|
162
|
+
stroke-dasharray="125.66"
|
|
163
|
+
stroke-dashoffset="0"
|
|
164
|
+
/>
|
|
165
|
+
<circle
|
|
166
|
+
class="v-progress-circular__overlay"
|
|
167
|
+
fill="transparent"
|
|
168
|
+
cx="50%"
|
|
169
|
+
cy="50%"
|
|
170
|
+
r="20"
|
|
171
|
+
:stroke="color"
|
|
172
|
+
stroke-width="2.42"
|
|
173
|
+
stroke-dasharray="125.66"
|
|
174
|
+
stroke-dashoffset="125.66"
|
|
175
|
+
/>
|
|
176
|
+
</svg>
|
|
177
|
+
</div>
|
|
178
|
+
<div
|
|
179
|
+
:style="{
|
|
180
|
+
fontSize: '.75rem',
|
|
181
|
+
fontFamily: 'var(--mono, ui-monospace, monospace)',
|
|
182
|
+
color: 'var(--muted, #797b8d)',
|
|
183
|
+
}"
|
|
184
|
+
>
|
|
185
|
+
Loading...
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
<!-- Inline Mode -->
|
|
190
|
+
<div
|
|
191
|
+
v-else-if="inline"
|
|
192
|
+
class="v-progress-circular v-progress-circular--indeterminate"
|
|
193
|
+
:style="{
|
|
194
|
+
width: `${s}px`,
|
|
195
|
+
height: `${s}px`,
|
|
196
|
+
position: 'relative',
|
|
197
|
+
display: 'inline-flex',
|
|
198
|
+
flexShrink: 0,
|
|
199
|
+
}"
|
|
89
200
|
>
|
|
90
201
|
<svg
|
|
91
202
|
viewBox="0 0 42.42 42.42"
|
|
92
|
-
|
|
203
|
+
:style="{ width: '100%', height: '100%', transform: 'rotate(-90deg)' }"
|
|
93
204
|
>
|
|
94
205
|
<circle
|
|
206
|
+
class="v-progress-circular__overlay"
|
|
95
207
|
fill="transparent"
|
|
96
208
|
cx="50%"
|
|
97
209
|
cy="50%"
|
|
98
210
|
r="20"
|
|
99
|
-
stroke="
|
|
211
|
+
:stroke="color"
|
|
100
212
|
stroke-width="2.42"
|
|
101
213
|
stroke-dasharray="125.66"
|
|
102
|
-
stroke-dashoffset="
|
|
103
|
-
/>
|
|
104
|
-
<circle
|
|
105
|
-
fill="transparent"
|
|
106
|
-
cx="50%"
|
|
107
|
-
cy="50%"
|
|
108
|
-
r="20"
|
|
109
|
-
:stroke="color === 'currentColor' ? 'var(--accent,#12675D)' : color"
|
|
110
|
-
stroke-width="2.42"
|
|
111
|
-
class="v-progress-circular__overlay"
|
|
214
|
+
stroke-dashoffset="125.66"
|
|
112
215
|
/>
|
|
113
216
|
</svg>
|
|
114
217
|
</div>
|
|
115
|
-
</div>
|
|
116
|
-
</template>
|
|
117
218
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
219
|
+
<!-- Normal Centered Mode -->
|
|
220
|
+
<div
|
|
221
|
+
v-else
|
|
222
|
+
:style="{
|
|
223
|
+
display: 'flex',
|
|
224
|
+
alignItems: 'center',
|
|
225
|
+
justifyContent: 'center',
|
|
226
|
+
padding: '48px 0',
|
|
227
|
+
}"
|
|
228
|
+
>
|
|
229
|
+
<div
|
|
230
|
+
class="v-progress-circular v-progress-circular--indeterminate"
|
|
231
|
+
:style="{ width: `${s}px`, height: `${s}px` }"
|
|
232
|
+
>
|
|
233
|
+
<svg
|
|
234
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
235
|
+
viewBox="0 0 42.42 42.42"
|
|
236
|
+
:style="{ width: `${s}px`, height: `${s}px`, transform: 'rotate(-90deg)' }"
|
|
237
|
+
>
|
|
238
|
+
<circle
|
|
239
|
+
fill="transparent"
|
|
240
|
+
cx="50%"
|
|
241
|
+
cy="50%"
|
|
242
|
+
r="20"
|
|
243
|
+
stroke="var(--grey-2, #e0e0e0)"
|
|
244
|
+
stroke-width="2.42"
|
|
245
|
+
stroke-dasharray="125.66"
|
|
246
|
+
stroke-dashoffset="0"
|
|
247
|
+
/>
|
|
248
|
+
<circle
|
|
249
|
+
class="v-progress-circular__overlay"
|
|
250
|
+
fill="transparent"
|
|
251
|
+
cx="50%"
|
|
252
|
+
cy="50%"
|
|
253
|
+
r="20"
|
|
254
|
+
:stroke="color"
|
|
255
|
+
stroke-width="2.42"
|
|
256
|
+
stroke-dasharray="125.66"
|
|
257
|
+
stroke-dashoffset="125.66"
|
|
258
|
+
/>
|
|
259
|
+
</svg>
|
|
260
|
+
</div>
|
|
261
|
+
</div>
|
|
262
|
+
</template>
|
|
263
|
+
</template>
|
package/vue/components/Table.vue
CHANGED
|
@@ -52,7 +52,7 @@ const props = withDefaults(
|
|
|
52
52
|
bordered: true,
|
|
53
53
|
skeletonRows: undefined,
|
|
54
54
|
autoSkeleton: true,
|
|
55
|
-
rowHeight:
|
|
55
|
+
rowHeight: 40,
|
|
56
56
|
}
|
|
57
57
|
);
|
|
58
58
|
|
|
@@ -62,18 +62,11 @@ const emit = defineEmits<{
|
|
|
62
62
|
}>();
|
|
63
63
|
|
|
64
64
|
const rootRef = ref<HTMLElement | null>(null);
|
|
65
|
-
const headerScrollRef = ref<HTMLElement | null>(null);
|
|
66
65
|
const bodyScrollRef = ref<HTMLElement | null>(null);
|
|
67
66
|
|
|
68
|
-
const handleBodyScroll = () => {
|
|
69
|
-
if (headerScrollRef.value && bodyScrollRef.value) {
|
|
70
|
-
headerScrollRef.value.scrollLeft = bodyScrollRef.value.scrollLeft;
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
|
|
74
67
|
const { rows: autoRows } = useAutoSkeletonRows(rootRef, {
|
|
75
68
|
rowHeight: props.rowHeight,
|
|
76
|
-
bottomBuffer:
|
|
69
|
+
bottomBuffer: 50,
|
|
77
70
|
});
|
|
78
71
|
|
|
79
72
|
const skeletonCount = computed(() => {
|
|
@@ -133,23 +126,23 @@ const getSkeletonWidth = (rIdx: number, cIdx: number, key: string) => {
|
|
|
133
126
|
]"
|
|
134
127
|
:style="height ? { height: formatWidth(height) } : undefined"
|
|
135
128
|
>
|
|
136
|
-
<!--
|
|
129
|
+
<!-- Scrollable Single Unified Table Container (100% Column Alignment) -->
|
|
137
130
|
<div
|
|
138
|
-
ref="
|
|
139
|
-
class="w-full overflow-
|
|
131
|
+
ref="bodyScrollRef"
|
|
132
|
+
class="w-full overflow-x-auto overflow-y-auto custom-scrollbar flex-1 relative"
|
|
140
133
|
>
|
|
141
134
|
<table class="w-full border-collapse text-left text-xs table-fixed">
|
|
142
135
|
<colgroup>
|
|
143
136
|
<col
|
|
144
137
|
v-for="col in columns"
|
|
145
|
-
:key="`
|
|
138
|
+
:key="`col-${col.key}`"
|
|
146
139
|
:style="{
|
|
147
140
|
width: formatWidth(col.width),
|
|
148
141
|
minWidth: formatWidth(col.minWidth || col.width || 100),
|
|
149
142
|
}"
|
|
150
143
|
/>
|
|
151
144
|
</colgroup>
|
|
152
|
-
<thead>
|
|
145
|
+
<thead class="sticky top-0 z-10 bg-[#f8fafc] dark:bg-[#16192e] border-b border-slate-200 dark:border-slate-800 shadow-[0_1px_0_0_rgba(226,232,240,1)] dark:shadow-[0_1px_0_0_rgba(30,41,59,1)]">
|
|
153
146
|
<tr>
|
|
154
147
|
<th
|
|
155
148
|
v-for="col in columns"
|
|
@@ -160,7 +153,7 @@ const getSkeletonWidth = (rIdx: number, cIdx: number, key: string) => {
|
|
|
160
153
|
textAlign: col.align || 'left',
|
|
161
154
|
}"
|
|
162
155
|
:class="[
|
|
163
|
-
'px-
|
|
156
|
+
'px-3.5 py-2.5 font-semibold text-[11px] uppercase tracking-wider text-slate-500 dark:text-slate-400 select-none transition-colors bg-[#f8fafc] dark:bg-[#16192e]',
|
|
164
157
|
col.sortable ? 'cursor-pointer hover:text-slate-900 dark:hover:text-white' : '',
|
|
165
158
|
col.headerClass || ''
|
|
166
159
|
]"
|
|
@@ -200,28 +193,8 @@ const getSkeletonWidth = (rIdx: number, cIdx: number, key: string) => {
|
|
|
200
193
|
</th>
|
|
201
194
|
</tr>
|
|
202
195
|
</thead>
|
|
203
|
-
</table>
|
|
204
|
-
</div>
|
|
205
|
-
|
|
206
|
-
<!-- Scrollable Body Container -->
|
|
207
|
-
<div
|
|
208
|
-
ref="bodyScrollRef"
|
|
209
|
-
class="w-full overflow-x-auto overflow-y-auto custom-scrollbar flex-1 relative"
|
|
210
|
-
@scroll="handleBodyScroll"
|
|
211
|
-
>
|
|
212
|
-
<table class="w-full border-collapse text-left text-xs table-fixed">
|
|
213
|
-
<colgroup>
|
|
214
|
-
<col
|
|
215
|
-
v-for="col in columns"
|
|
216
|
-
:key="`b-col-${col.key}`"
|
|
217
|
-
:style="{
|
|
218
|
-
width: formatWidth(col.width),
|
|
219
|
-
minWidth: formatWidth(col.minWidth || col.width || 100),
|
|
220
|
-
}"
|
|
221
|
-
/>
|
|
222
|
-
</colgroup>
|
|
223
196
|
<tbody class="divide-y divide-slate-100 dark:divide-slate-800/80">
|
|
224
|
-
<!-- Loading State (
|
|
197
|
+
<!-- Loading State (Real matching heights and paddings) -->
|
|
225
198
|
<template v-if="loading">
|
|
226
199
|
<tr
|
|
227
200
|
v-for="i in skeletonCount"
|
|
@@ -235,7 +208,7 @@ const getSkeletonWidth = (rIdx: number, cIdx: number, key: string) => {
|
|
|
235
208
|
textAlign: col.align || 'left',
|
|
236
209
|
height: `${rowHeight}px`,
|
|
237
210
|
}"
|
|
238
|
-
class="px-
|
|
211
|
+
class="px-3.5 py-2 align-middle"
|
|
239
212
|
>
|
|
240
213
|
<!-- Action button skeleton -->
|
|
241
214
|
<div v-if="col.key === 'actions'" class="flex items-center justify-center">
|
|
@@ -251,7 +224,7 @@ const getSkeletonWidth = (rIdx: number, cIdx: number, key: string) => {
|
|
|
251
224
|
<Skeleton
|
|
252
225
|
v-else
|
|
253
226
|
:width="getSkeletonWidth(i, cIdx, col.key)"
|
|
254
|
-
:height="
|
|
227
|
+
:height="14"
|
|
255
228
|
:radius="4"
|
|
256
229
|
/>
|
|
257
230
|
</td>
|
|
@@ -291,7 +264,7 @@ const getSkeletonWidth = (rIdx: number, cIdx: number, key: string) => {
|
|
|
291
264
|
height: `${rowHeight}px`,
|
|
292
265
|
}"
|
|
293
266
|
:class="[
|
|
294
|
-
'px-
|
|
267
|
+
'px-3.5 py-2 text-slate-700 dark:text-slate-200 text-xs font-normal align-middle',
|
|
295
268
|
col.cellClass || ''
|
|
296
269
|
]"
|
|
297
270
|
>
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
|
|
4
|
+
const SIZES: Record<string, { w: number; h: number; r: number; dot: number; off: number; on: number }> = {
|
|
5
|
+
sm: { w: 28, h: 16, r: 8, dot: 12, off: 2, on: 14 },
|
|
6
|
+
md: { w: 34, h: 18, r: 9, dot: 14, off: 2, on: 18 },
|
|
7
|
+
lg: { w: 44, h: 24, r: 12, dot: 18, off: 3, on: 23 },
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const COLORS: Record<string, string> = {
|
|
11
|
+
primary: "var(--accent, #12675D)",
|
|
12
|
+
success: "var(--success, var(--s, #27ae60))",
|
|
13
|
+
warning: "var(--warning, var(--w, #f59e0b))",
|
|
14
|
+
danger: "var(--danger, var(--d, #f64747))",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const OFF_COLOR = "var(--border, var(--bd, #d7d8e0))";
|
|
18
|
+
|
|
19
|
+
const props = withDefaults(
|
|
20
|
+
defineProps<{
|
|
21
|
+
modelValue?: boolean;
|
|
22
|
+
checked?: boolean;
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
size?: "sm" | "md" | "lg";
|
|
25
|
+
color?: "primary" | "success" | "warning" | "danger";
|
|
26
|
+
label?: string;
|
|
27
|
+
labelStyle?: Record<string, any>;
|
|
28
|
+
className?: string;
|
|
29
|
+
style?: Record<string, any>;
|
|
30
|
+
}>(),
|
|
31
|
+
{
|
|
32
|
+
modelValue: false,
|
|
33
|
+
checked: false,
|
|
34
|
+
disabled: false,
|
|
35
|
+
size: "md",
|
|
36
|
+
color: "primary",
|
|
37
|
+
label: "",
|
|
38
|
+
labelStyle: () => ({}),
|
|
39
|
+
className: "",
|
|
40
|
+
style: () => ({}),
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const emit = defineEmits<{
|
|
45
|
+
(e: "update:modelValue", value: boolean): void;
|
|
46
|
+
(e: "change", value: boolean): void;
|
|
47
|
+
}>();
|
|
48
|
+
|
|
49
|
+
const isChecked = computed(() => (props.modelValue !== undefined ? props.modelValue : props.checked));
|
|
50
|
+
const s = computed(() => SIZES[props.size] || SIZES.md);
|
|
51
|
+
const trackBg = computed(() => (isChecked.value ? (COLORS[props.color] || COLORS.primary) : OFF_COLOR));
|
|
52
|
+
|
|
53
|
+
const toggle = () => {
|
|
54
|
+
if (props.disabled) return;
|
|
55
|
+
const next = !isChecked.value;
|
|
56
|
+
emit("update:modelValue", next);
|
|
57
|
+
emit("change", next);
|
|
58
|
+
};
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<template>
|
|
62
|
+
<label
|
|
63
|
+
:class="['cynx-toggle-label', className]"
|
|
64
|
+
:style="{
|
|
65
|
+
display: 'inline-flex',
|
|
66
|
+
alignItems: 'center',
|
|
67
|
+
gap: '8px',
|
|
68
|
+
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
69
|
+
userSelect: 'none',
|
|
70
|
+
opacity: disabled ? 0.4 : 1,
|
|
71
|
+
...style,
|
|
72
|
+
}"
|
|
73
|
+
@click.prevent="toggle"
|
|
74
|
+
>
|
|
75
|
+
<input
|
|
76
|
+
type="checkbox"
|
|
77
|
+
:checked="isChecked"
|
|
78
|
+
:disabled="disabled"
|
|
79
|
+
style="position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none;"
|
|
80
|
+
/>
|
|
81
|
+
<span
|
|
82
|
+
class="cynx-toggle-track"
|
|
83
|
+
:style="{
|
|
84
|
+
position: 'relative',
|
|
85
|
+
width: `${s.w}px`,
|
|
86
|
+
height: `${s.h}px`,
|
|
87
|
+
borderRadius: `${s.r}px`,
|
|
88
|
+
background: trackBg,
|
|
89
|
+
flexShrink: 0,
|
|
90
|
+
transition: 'background .25s ease',
|
|
91
|
+
boxShadow: 'var(--shadow-sm, 0 1px 3px rgba(20,22,41,.1))',
|
|
92
|
+
}"
|
|
93
|
+
>
|
|
94
|
+
<span
|
|
95
|
+
class="cynx-toggle-dot"
|
|
96
|
+
:style="{
|
|
97
|
+
position: 'absolute',
|
|
98
|
+
top: '50%',
|
|
99
|
+
transform: 'translateY(-50%)',
|
|
100
|
+
left: `${isChecked ? s.on : s.off}px`,
|
|
101
|
+
width: `${s.dot}px`,
|
|
102
|
+
height: `${s.dot}px`,
|
|
103
|
+
background: 'var(--surface, #ffffff)',
|
|
104
|
+
borderRadius: '50%',
|
|
105
|
+
boxShadow: '0 1px 3px rgba(0,0,0,.25)',
|
|
106
|
+
transition: 'left .3s cubic-bezier(.34,1.56,.64,1), transform .2s ease',
|
|
107
|
+
willChange: 'left',
|
|
108
|
+
}"
|
|
109
|
+
/>
|
|
110
|
+
</span>
|
|
111
|
+
<span v-if="label || $slots.default" :style="labelStyle" class="cynx-toggle-text">
|
|
112
|
+
<slot>{{ label }}</slot>
|
|
113
|
+
</span>
|
|
114
|
+
</label>
|
|
115
|
+
</template>
|
package/vue/index.d.ts
CHANGED
|
@@ -50,6 +50,8 @@ export declare const Sidebar: DefineComponent<any, any, any>;
|
|
|
50
50
|
export declare const Table: DefineComponent<any, any, any>;
|
|
51
51
|
export declare const Pagination: DefineComponent<any, any, any>;
|
|
52
52
|
export declare const Skeleton: DefineComponent<any, any, any>;
|
|
53
|
+
export declare const Modal: DefineComponent<any, any, any>;
|
|
54
|
+
export declare const Toggle: DefineComponent<any, any, any>;
|
|
53
55
|
export declare const XTable: DefineComponent<any, any, any>;
|
|
54
56
|
export declare const XThead: DefineComponent<any, any, any>;
|
|
55
57
|
export declare const XTbody: DefineComponent<any, any, any>;
|
package/vue/index.js
CHANGED
|
@@ -10,6 +10,8 @@ import Sidebar from "./components/Sidebar.vue";
|
|
|
10
10
|
import Table from "./components/Table.vue";
|
|
11
11
|
import Pagination from "./components/Pagination.vue";
|
|
12
12
|
import Skeleton from "./components/Skeleton.vue";
|
|
13
|
+
import Modal from "./components/Modal.vue";
|
|
14
|
+
import Toggle from "./components/Toggle.vue";
|
|
13
15
|
import { XTable, XThead, XTbody, XTr, XTh, XTd } from "./components/XTable.vue";
|
|
14
16
|
import { useAutoSkeletonRows } from "./composables/useAutoSkeletonRows";
|
|
15
17
|
|
|
@@ -25,6 +27,8 @@ export {
|
|
|
25
27
|
Table,
|
|
26
28
|
Pagination,
|
|
27
29
|
Skeleton,
|
|
30
|
+
Modal,
|
|
31
|
+
Toggle,
|
|
28
32
|
XTable,
|
|
29
33
|
XThead,
|
|
30
34
|
XTbody,
|
|
@@ -46,6 +50,8 @@ export default {
|
|
|
46
50
|
Table,
|
|
47
51
|
Pagination,
|
|
48
52
|
Skeleton,
|
|
53
|
+
Modal,
|
|
54
|
+
Toggle,
|
|
49
55
|
XTable,
|
|
50
56
|
XThead,
|
|
51
57
|
XTbody,
|