cynx-lib 1.3.83 → 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 (42) 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/Layout.jsx +37 -0
  5. package/ui/components/Pagination.jsx +145 -145
  6. package/ui/components/PrivacyBlur.jsx +603 -603
  7. package/ui/components/Sidebar.jsx +300 -134
  8. package/utils/index.cjs +5 -0
  9. package/utils/index.js +22 -20
  10. package/utils/utils/duration.js +139 -139
  11. package/utils/utils/oidc.js +214 -214
  12. package/utils/utils/realtime.js +1634 -1634
  13. package/utils/utils/session.cjs +15 -2
  14. package/utils/utils/session.js +67 -58
  15. package/utils/utils/session_manager.cjs +490 -0
  16. package/utils/utils/session_manager.js +549 -0
  17. package/utils/utils/storage.js +393 -393
  18. package/utils/utils/ui.js +111 -111
  19. package/vue/components/Badge.vue +79 -79
  20. package/vue/components/Button.vue +209 -209
  21. package/vue/components/Checkbox.vue +103 -103
  22. package/vue/components/CircularProgress.vue +383 -383
  23. package/vue/components/Divider.vue +94 -94
  24. package/vue/components/Drawer.vue +243 -243
  25. package/vue/components/InfoBadge.vue +223 -223
  26. package/vue/components/Input.vue +341 -341
  27. package/vue/components/LinearProgress.vue +268 -268
  28. package/vue/components/Modal.vue +226 -226
  29. package/vue/components/Pagination.vue +199 -199
  30. package/vue/components/Select.vue +713 -713
  31. package/vue/components/Sidebar.vue +955 -955
  32. package/vue/components/Skeleton.vue +91 -91
  33. package/vue/components/Spinner.vue +263 -263
  34. package/vue/components/StatusTag.vue +89 -89
  35. package/vue/components/TabBar.vue +176 -176
  36. package/vue/components/Table.vue +388 -388
  37. package/vue/components/TimePicker.vue +399 -399
  38. package/vue/components/Toggle.vue +115 -115
  39. package/vue/components/XTable.vue +119 -119
  40. package/vue/composables/useAutoSkeletonRows.ts +71 -71
  41. package/vue/index.d.ts +67 -67
  42. 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>