cynx-lib 1.3.66 → 1.3.68

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.
@@ -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 (props.multi) {
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 props.multi ? (Array.isArray(v) && v.includes(optVal)) : v === optVal;
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 (props.multi) {
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 = props.multi ? [] : "";
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="multi ? selectedValues : selectedValues[0]"
392
+ :value="isMulti ? selectedValues : selectedValues[0]"
389
393
  />
390
394
 
391
- <template v-else-if="multi && selectedValues.length > 0">
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,7 +11,9 @@ 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";
16
+ import DatePicker from "./components/DatePicker.vue";
15
17
  import { XTable, XThead, XTbody, XTr, XTh, XTd } from "./components/XTable.vue";
16
18
  import { useAutoSkeletonRows } from "./composables/useAutoSkeletonRows";
17
19
 
@@ -28,7 +30,9 @@ export {
28
30
  Pagination,
29
31
  Skeleton,
30
32
  Modal,
33
+ Drawer,
31
34
  Toggle,
35
+ DatePicker,
32
36
  XTable,
33
37
  XThead,
34
38
  XTbody,
@@ -51,7 +55,9 @@ export default {
51
55
  Pagination,
52
56
  Skeleton,
53
57
  Modal,
58
+ Drawer,
54
59
  Toggle,
60
+ DatePicker,
55
61
  XTable,
56
62
  XThead,
57
63
  XTbody,