cynx-lib 1.3.84 → 1.3.85

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.
Files changed (40) hide show
  1. package/gen.js +77 -77
  2. package/package.json +5 -1
  3. package/ui/components/Drawer.jsx +208 -208
  4. package/ui/components/Pagination.jsx +145 -145
  5. package/ui/components/PrivacyBlur.jsx +603 -603
  6. package/utils/index.cjs +5 -0
  7. package/utils/index.js +22 -20
  8. package/utils/utils/duration.js +139 -139
  9. package/utils/utils/oidc.js +214 -214
  10. package/utils/utils/realtime.js +1634 -1634
  11. package/utils/utils/session.cjs +15 -2
  12. package/utils/utils/session.js +67 -58
  13. package/utils/utils/session_manager.cjs +490 -0
  14. package/utils/utils/session_manager.js +549 -0
  15. package/utils/utils/storage.js +393 -393
  16. package/utils/utils/ui.js +111 -111
  17. package/vue/components/Badge.vue +79 -79
  18. package/vue/components/Button.vue +209 -209
  19. package/vue/components/Checkbox.vue +103 -103
  20. package/vue/components/CircularProgress.vue +383 -383
  21. package/vue/components/Divider.vue +94 -94
  22. package/vue/components/Drawer.vue +243 -243
  23. package/vue/components/InfoBadge.vue +223 -223
  24. package/vue/components/Input.vue +341 -341
  25. package/vue/components/LinearProgress.vue +268 -268
  26. package/vue/components/Modal.vue +226 -226
  27. package/vue/components/Pagination.vue +199 -199
  28. package/vue/components/Select.vue +713 -713
  29. package/vue/components/Sidebar.vue +955 -955
  30. package/vue/components/Skeleton.vue +91 -91
  31. package/vue/components/Spinner.vue +263 -263
  32. package/vue/components/StatusTag.vue +89 -89
  33. package/vue/components/TabBar.vue +176 -176
  34. package/vue/components/Table.vue +388 -388
  35. package/vue/components/TimePicker.vue +399 -399
  36. package/vue/components/Toggle.vue +115 -115
  37. package/vue/components/XTable.vue +119 -119
  38. package/vue/composables/useAutoSkeletonRows.ts +71 -71
  39. package/vue/index.d.ts +67 -67
  40. package/vue/index.js +86 -86
@@ -1,89 +1,89 @@
1
- <script setup lang="ts">
2
- import { computed } from "vue";
3
-
4
- const STATUS_COLORS: Record<string, string> = {
5
- DR: "var(--drive, #28C76F)",
6
- SB: "var(--break, #FFB200)",
7
- ON: "var(--shift, #0F60FF)",
8
- OFF: "var(--muted, #8B909A)",
9
- PC: "var(--muted, #8B909A)",
10
- YM: "var(--shift, #0F60FF)",
11
- };
12
-
13
- const props = withDefaults(
14
- defineProps<{
15
- status?: string | null;
16
- color?: string;
17
- width?: number | string;
18
- height?: number | string;
19
- radius?: number | string;
20
- plain?: boolean;
21
- }>(),
22
- {
23
- status: "",
24
- width: 36,
25
- height: 20,
26
- radius: 6,
27
- plain: false,
28
- }
29
- );
30
-
31
- const normalizedKey = computed(() =>
32
- (props.status || "").replace(/\s+/g, "_").toUpperCase().trim()
33
- );
34
-
35
- const isPrimaryStatus = computed(() => {
36
- if (props.color) return true;
37
- return Boolean(STATUS_COLORS[normalizedKey.value]);
38
- });
39
-
40
- const statusColor = computed(() => {
41
- if (props.color) return props.color;
42
- return STATUS_COLORS[normalizedKey.value] || "var(--muted, #8B909A)";
43
- });
44
-
45
- const isShortStatus = computed(() => (props.status || "").trim().length <= 3);
46
-
47
- const boxWidth = computed(() =>
48
- typeof props.width === "number" ? `${props.width}px` : props.width
49
- );
50
- const boxHeight = computed(() =>
51
- typeof props.height === "number" ? `${props.height}px` : props.height
52
- );
53
- const borderRadius = computed(() =>
54
- typeof props.radius === "number" ? `${props.radius}px` : props.radius
55
- );
56
- </script>
57
-
58
- <template>
59
- <span
60
- v-if="status && plain"
61
- class="inline-flex items-center text-[11.5px] font-bold select-none whitespace-nowrap tracking-wide"
62
- :style="{
63
- color: statusColor,
64
- }"
65
- >
66
- {{ status }}
67
- </span>
68
- <span
69
- v-else-if="status && isPrimaryStatus"
70
- class="inline-flex items-center justify-center text-[11px] font-bold text-white tracking-wide select-none shadow-sm flex-shrink-0"
71
- :style="{
72
- width: isShortStatus ? boxWidth : 'auto',
73
- minWidth: boxWidth,
74
- padding: isShortStatus ? '0' : '0 6px',
75
- height: boxHeight,
76
- borderRadius: borderRadius,
77
- backgroundColor: statusColor,
78
- }"
79
- >
80
- {{ status }}
81
- </span>
82
- <span
83
- v-else-if="status"
84
- class="text-[12px] font-semibold text-[var(--muted,#8B909A)] select-none truncate"
85
- >
86
- {{ status }}
87
- </span>
88
- <span v-else class="text-slate-400 select-none">-</span>
89
- </template>
1
+ <script setup lang="ts">
2
+ import { computed } from "vue";
3
+
4
+ const STATUS_COLORS: Record<string, string> = {
5
+ DR: "var(--drive, #28C76F)",
6
+ SB: "var(--break, #FFB200)",
7
+ ON: "var(--shift, #0F60FF)",
8
+ OFF: "var(--muted, #8B909A)",
9
+ PC: "var(--muted, #8B909A)",
10
+ YM: "var(--shift, #0F60FF)",
11
+ };
12
+
13
+ const props = withDefaults(
14
+ defineProps<{
15
+ status?: string | null;
16
+ color?: string;
17
+ width?: number | string;
18
+ height?: number | string;
19
+ radius?: number | string;
20
+ plain?: boolean;
21
+ }>(),
22
+ {
23
+ status: "",
24
+ width: 36,
25
+ height: 20,
26
+ radius: 6,
27
+ plain: false,
28
+ }
29
+ );
30
+
31
+ const normalizedKey = computed(() =>
32
+ (props.status || "").replace(/\s+/g, "_").toUpperCase().trim()
33
+ );
34
+
35
+ const isPrimaryStatus = computed(() => {
36
+ if (props.color) return true;
37
+ return Boolean(STATUS_COLORS[normalizedKey.value]);
38
+ });
39
+
40
+ const statusColor = computed(() => {
41
+ if (props.color) return props.color;
42
+ return STATUS_COLORS[normalizedKey.value] || "var(--muted, #8B909A)";
43
+ });
44
+
45
+ const isShortStatus = computed(() => (props.status || "").trim().length <= 3);
46
+
47
+ const boxWidth = computed(() =>
48
+ typeof props.width === "number" ? `${props.width}px` : props.width
49
+ );
50
+ const boxHeight = computed(() =>
51
+ typeof props.height === "number" ? `${props.height}px` : props.height
52
+ );
53
+ const borderRadius = computed(() =>
54
+ typeof props.radius === "number" ? `${props.radius}px` : props.radius
55
+ );
56
+ </script>
57
+
58
+ <template>
59
+ <span
60
+ v-if="status && plain"
61
+ class="inline-flex items-center text-[11.5px] font-bold select-none whitespace-nowrap tracking-wide"
62
+ :style="{
63
+ color: statusColor,
64
+ }"
65
+ >
66
+ {{ status }}
67
+ </span>
68
+ <span
69
+ v-else-if="status && isPrimaryStatus"
70
+ class="inline-flex items-center justify-center text-[11px] font-bold text-white tracking-wide select-none shadow-sm flex-shrink-0"
71
+ :style="{
72
+ width: isShortStatus ? boxWidth : 'auto',
73
+ minWidth: boxWidth,
74
+ padding: isShortStatus ? '0' : '0 6px',
75
+ height: boxHeight,
76
+ borderRadius: borderRadius,
77
+ backgroundColor: statusColor,
78
+ }"
79
+ >
80
+ {{ status }}
81
+ </span>
82
+ <span
83
+ v-else-if="status"
84
+ class="text-[12px] font-semibold text-[var(--muted,#8B909A)] select-none truncate"
85
+ >
86
+ {{ status }}
87
+ </span>
88
+ <span v-else class="text-slate-400 select-none">-</span>
89
+ </template>
@@ -1,176 +1,176 @@
1
- <script setup lang="ts">
2
- import { ref, watch, onMounted, nextTick } from "vue";
3
-
4
- export interface TabItem {
5
- id: string | number;
6
- label?: string;
7
- name?: string;
8
- icon?: any;
9
- badge?: string;
10
- count?: number;
11
- disabled?: boolean;
12
- }
13
-
14
- const props = withDefaults(
15
- defineProps<{
16
- tabs: TabItem[];
17
- modelValue?: string | number;
18
- active?: string | number;
19
- variant?: "underline" | "pill";
20
- padding?: string;
21
- capitalize?: boolean;
22
- }>(),
23
- {
24
- variant: "underline",
25
- capitalize: false,
26
- }
27
- );
28
-
29
- const emit = defineEmits<{
30
- (e: "update:modelValue", value: string | number): void;
31
- (e: "change", value: string | number): void;
32
- (e: "tab-click", tab: TabItem): void;
33
- }>();
34
-
35
- const barRef = ref<HTMLElement | null>(null);
36
- const indRef = ref<HTMLElement | null>(null);
37
- const currentActive = ref(props.modelValue ?? props.active ?? props.tabs?.[0]?.id);
38
-
39
- function updateIndicator() {
40
- if (props.variant !== "underline") return;
41
- nextTick(() => {
42
- const bar = barRef.value;
43
- const ind = indRef.value;
44
- if (!bar || !ind) return;
45
-
46
- const targetId = String(currentActive.value ?? "");
47
- const activeBtn =
48
- (bar.querySelector(`[data-tabid="${targetId}"]`) as HTMLElement) ||
49
- (bar.querySelector(`[data-active="true"]`) as HTMLElement);
50
-
51
- if (!activeBtn) return;
52
-
53
- const barRect = bar.getBoundingClientRect();
54
- const btnRect = activeBtn.getBoundingClientRect();
55
- const x = btnRect.left - barRect.left;
56
- const w = btnRect.width;
57
-
58
- if (w > 0) {
59
- ind.style.width = `${w}px`;
60
- ind.style.transform = `translateX(${x}px)`;
61
- ind.style.opacity = "1";
62
- }
63
- });
64
- }
65
-
66
- watch(
67
- () => [props.modelValue, props.active, props.tabs],
68
- ([newM, newA]) => {
69
- if (newM !== undefined && newM !== null) currentActive.value = newM;
70
- else if (newA !== undefined && newA !== null) currentActive.value = newA;
71
- else if (props.tabs?.length && (currentActive.value === undefined || currentActive.value === null)) {
72
- currentActive.value = props.tabs[0]?.id;
73
- }
74
- updateIndicator();
75
- },
76
- { deep: true, immediate: true }
77
- );
78
-
79
- onMounted(() => {
80
- updateIndicator();
81
- nextTick(() => {
82
- requestAnimationFrame(updateIndicator);
83
- setTimeout(updateIndicator, 50);
84
- setTimeout(updateIndicator, 200);
85
- });
86
- window.addEventListener("resize", updateIndicator);
87
- });
88
-
89
- const selectTab = (tab: TabItem) => {
90
- if (tab.disabled) return;
91
- currentActive.value = tab.id;
92
- emit("update:modelValue", tab.id);
93
- emit("change", tab.id);
94
- emit("tab-click", tab);
95
- updateIndicator();
96
- };
97
- </script>
98
-
99
- <template>
100
- <div
101
- ref="barRef"
102
- :class="[
103
- 'relative flex items-center',
104
- variant === 'underline' ? 'overflow-visible' : 'gap-1 bg-slate-100 dark:bg-slate-800 p-1 rounded-md'
105
- ]"
106
- >
107
- <!-- Underline Indicator (cynx-lib smooth bezier) -->
108
- <div
109
- v-if="variant === 'underline'"
110
- ref="indRef"
111
- class="pointer-events-none z-[2]"
112
- style="
113
- position: absolute;
114
- bottom: -1px;
115
- left: 0;
116
- height: 2px;
117
- width: 0px;
118
- opacity: 0;
119
- background: var(--accent, #12675D);
120
- border-radius: 2px 2px 0 0;
121
- transition: transform 0.22s cubic-bezier(0.4, 0, 0.2, 1), width 0.22s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.15s ease;
122
- "
123
- />
124
-
125
- <!-- Tab Items -->
126
- <button
127
- v-for="t in tabs"
128
- :key="t.id"
129
- type="button"
130
- :data-tabid="t.id"
131
- :data-active="String(currentActive) === String(t.id)"
132
- :disabled="t.disabled"
133
- :class="[
134
- 'inline-flex items-center gap-2 text-xs font-semibold whitespace-nowrap transition-colors relative z-[1] select-none',
135
- String(currentActive) === String(t.id)
136
- ? 'text-[#12675D] dark:text-teal-400 font-bold'
137
- : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white font-medium',
138
- t.disabled ? 'opacity-40 cursor-not-allowed' : 'cursor-pointer'
139
- ]"
140
- :style="{
141
- padding: padding || (variant === 'underline' ? '10px 16px' : '6px 12px'),
142
- border: 'none',
143
- background: 'none',
144
- textTransform: capitalize ? 'capitalize' : 'none',
145
- }"
146
- @click="selectTab(t)"
147
- >
148
- <slot name="tab" :tab="t" :is-active="String(currentActive) === String(t.id)">
149
- <component
150
- :is="t.icon"
151
- v-if="t.icon"
152
- class="h-4 w-4"
153
- :class="String(currentActive) === String(t.id) ? 'text-[#12675D] dark:text-teal-400' : 'text-slate-400'"
154
- />
155
- <span>{{ t.label || t.name || t.id }}</span>
156
- <span
157
- v-if="t.badge"
158
- class="px-1.5 py-0.5 rounded-full text-[9px] font-bold bg-[#12675D] text-white leading-none"
159
- >
160
- {{ t.badge }}
161
- </span>
162
- <span
163
- v-if="t.count != null && t.count > 0"
164
- :class="[
165
- 'px-1.5 py-0.5 rounded-full text-[10px] font-bold leading-none',
166
- String(currentActive) === String(t.id)
167
- ? 'bg-[#12675D]/10 text-[#12675D]'
168
- : 'bg-slate-100 text-slate-500'
169
- ]"
170
- >
171
- {{ t.count }}
172
- </span>
173
- </slot>
174
- </button>
175
- </div>
176
- </template>
1
+ <script setup lang="ts">
2
+ import { ref, watch, onMounted, nextTick } from "vue";
3
+
4
+ export interface TabItem {
5
+ id: string | number;
6
+ label?: string;
7
+ name?: string;
8
+ icon?: any;
9
+ badge?: string;
10
+ count?: number;
11
+ disabled?: boolean;
12
+ }
13
+
14
+ const props = withDefaults(
15
+ defineProps<{
16
+ tabs: TabItem[];
17
+ modelValue?: string | number;
18
+ active?: string | number;
19
+ variant?: "underline" | "pill";
20
+ padding?: string;
21
+ capitalize?: boolean;
22
+ }>(),
23
+ {
24
+ variant: "underline",
25
+ capitalize: false,
26
+ }
27
+ );
28
+
29
+ const emit = defineEmits<{
30
+ (e: "update:modelValue", value: string | number): void;
31
+ (e: "change", value: string | number): void;
32
+ (e: "tab-click", tab: TabItem): void;
33
+ }>();
34
+
35
+ const barRef = ref<HTMLElement | null>(null);
36
+ const indRef = ref<HTMLElement | null>(null);
37
+ const currentActive = ref(props.modelValue ?? props.active ?? props.tabs?.[0]?.id);
38
+
39
+ function updateIndicator() {
40
+ if (props.variant !== "underline") return;
41
+ nextTick(() => {
42
+ const bar = barRef.value;
43
+ const ind = indRef.value;
44
+ if (!bar || !ind) return;
45
+
46
+ const targetId = String(currentActive.value ?? "");
47
+ const activeBtn =
48
+ (bar.querySelector(`[data-tabid="${targetId}"]`) as HTMLElement) ||
49
+ (bar.querySelector(`[data-active="true"]`) as HTMLElement);
50
+
51
+ if (!activeBtn) return;
52
+
53
+ const barRect = bar.getBoundingClientRect();
54
+ const btnRect = activeBtn.getBoundingClientRect();
55
+ const x = btnRect.left - barRect.left;
56
+ const w = btnRect.width;
57
+
58
+ if (w > 0) {
59
+ ind.style.width = `${w}px`;
60
+ ind.style.transform = `translateX(${x}px)`;
61
+ ind.style.opacity = "1";
62
+ }
63
+ });
64
+ }
65
+
66
+ watch(
67
+ () => [props.modelValue, props.active, props.tabs],
68
+ ([newM, newA]) => {
69
+ if (newM !== undefined && newM !== null) currentActive.value = newM;
70
+ else if (newA !== undefined && newA !== null) currentActive.value = newA;
71
+ else if (props.tabs?.length && (currentActive.value === undefined || currentActive.value === null)) {
72
+ currentActive.value = props.tabs[0]?.id;
73
+ }
74
+ updateIndicator();
75
+ },
76
+ { deep: true, immediate: true }
77
+ );
78
+
79
+ onMounted(() => {
80
+ updateIndicator();
81
+ nextTick(() => {
82
+ requestAnimationFrame(updateIndicator);
83
+ setTimeout(updateIndicator, 50);
84
+ setTimeout(updateIndicator, 200);
85
+ });
86
+ window.addEventListener("resize", updateIndicator);
87
+ });
88
+
89
+ const selectTab = (tab: TabItem) => {
90
+ if (tab.disabled) return;
91
+ currentActive.value = tab.id;
92
+ emit("update:modelValue", tab.id);
93
+ emit("change", tab.id);
94
+ emit("tab-click", tab);
95
+ updateIndicator();
96
+ };
97
+ </script>
98
+
99
+ <template>
100
+ <div
101
+ ref="barRef"
102
+ :class="[
103
+ 'relative flex items-center',
104
+ variant === 'underline' ? 'overflow-visible' : 'gap-1 bg-slate-100 dark:bg-slate-800 p-1 rounded-md'
105
+ ]"
106
+ >
107
+ <!-- Underline Indicator (cynx-lib smooth bezier) -->
108
+ <div
109
+ v-if="variant === 'underline'"
110
+ ref="indRef"
111
+ class="pointer-events-none z-[2]"
112
+ style="
113
+ position: absolute;
114
+ bottom: -1px;
115
+ left: 0;
116
+ height: 2px;
117
+ width: 0px;
118
+ opacity: 0;
119
+ background: var(--accent, #12675D);
120
+ border-radius: 2px 2px 0 0;
121
+ transition: transform 0.22s cubic-bezier(0.4, 0, 0.2, 1), width 0.22s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.15s ease;
122
+ "
123
+ />
124
+
125
+ <!-- Tab Items -->
126
+ <button
127
+ v-for="t in tabs"
128
+ :key="t.id"
129
+ type="button"
130
+ :data-tabid="t.id"
131
+ :data-active="String(currentActive) === String(t.id)"
132
+ :disabled="t.disabled"
133
+ :class="[
134
+ 'inline-flex items-center gap-2 text-xs font-semibold whitespace-nowrap transition-colors relative z-[1] select-none',
135
+ String(currentActive) === String(t.id)
136
+ ? 'text-[#12675D] dark:text-teal-400 font-bold'
137
+ : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white font-medium',
138
+ t.disabled ? 'opacity-40 cursor-not-allowed' : 'cursor-pointer'
139
+ ]"
140
+ :style="{
141
+ padding: padding || (variant === 'underline' ? '10px 16px' : '6px 12px'),
142
+ border: 'none',
143
+ background: 'none',
144
+ textTransform: capitalize ? 'capitalize' : 'none',
145
+ }"
146
+ @click="selectTab(t)"
147
+ >
148
+ <slot name="tab" :tab="t" :is-active="String(currentActive) === String(t.id)">
149
+ <component
150
+ :is="t.icon"
151
+ v-if="t.icon"
152
+ class="h-4 w-4"
153
+ :class="String(currentActive) === String(t.id) ? 'text-[#12675D] dark:text-teal-400' : 'text-slate-400'"
154
+ />
155
+ <span>{{ t.label || t.name || t.id }}</span>
156
+ <span
157
+ v-if="t.badge"
158
+ class="px-1.5 py-0.5 rounded-full text-[9px] font-bold bg-[#12675D] text-white leading-none"
159
+ >
160
+ {{ t.badge }}
161
+ </span>
162
+ <span
163
+ v-if="t.count != null && t.count > 0"
164
+ :class="[
165
+ 'px-1.5 py-0.5 rounded-full text-[10px] font-bold leading-none',
166
+ String(currentActive) === String(t.id)
167
+ ? 'bg-[#12675D]/10 text-[#12675D]'
168
+ : 'bg-slate-100 text-slate-500'
169
+ ]"
170
+ >
171
+ {{ t.count }}
172
+ </span>
173
+ </slot>
174
+ </button>
175
+ </div>
176
+ </template>