@mptw/skylens-ui 1.5.0 → 1.5.2
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/AGENTS.md +59 -0
- package/CLAUDE.md +36 -0
- package/README.md +7 -7
- package/app/app.config.ts +11 -2
- package/app/app.vue +5 -0
- package/app/assets/css/layout.css +134 -0
- package/app/assets/css/main.css +189 -0
- package/app/components/SLAlertModal.vue +24 -16
- package/app/components/SLBreadcrumbs.vue +12 -8
- package/app/components/SLButton.vue +196 -163
- package/app/components/SLCalendarButton.vue +40 -34
- package/app/components/SLCalendarPopup.vue +79 -53
- package/app/components/SLCard.vue +57 -41
- package/app/components/SLChart.vue +6 -3
- package/app/components/SLCheckbox.vue +92 -49
- package/app/components/SLCircleButton.vue +25 -16
- package/app/components/SLCollapsableBlock.vue +44 -33
- package/app/components/SLCollapsableMulitPillSelect.vue +20 -15
- package/app/components/SLConfirmModal.vue +9 -6
- package/app/components/SLContextMenu.vue +88 -48
- package/app/components/SLDateInput.vue +109 -92
- package/app/components/SLDatePicker.vue +9 -7
- package/app/components/SLDistributor.vue +1 -1
- package/app/components/SLDownloadButton.vue +39 -29
- package/app/components/SLDraggable.vue +2 -2
- package/app/components/SLDropdown.vue +33 -25
- package/app/components/SLDropdownItem.vue +9 -5
- package/app/components/SLElementWithTitle.vue +12 -7
- package/app/components/SLEyeCheckbox.vue +44 -31
- package/app/components/SLFileInput.vue +23 -16
- package/app/components/SLGenderAgeSelect.vue +52 -36
- package/app/components/SLHourRangeInput.vue +45 -32
- package/app/components/SLIOSSwitch.vue +81 -60
- package/app/components/SLIcon.vue +4 -3
- package/app/components/SLIconButton.vue +31 -29
- package/app/components/SLInfoTip.vue +51 -40
- package/app/components/SLInsightSitePage.vue +12 -7
- package/app/components/SLLegend.vue +19 -12
- package/app/components/SLLinearProgressBar.vue +7 -20
- package/app/components/SLLink.vue +12 -8
- package/app/components/SLLoading.vue +34 -31
- package/app/components/SLMapChart.vue +5 -4
- package/app/components/SLModal.vue +90 -66
- package/app/components/SLMonthCalendarButton.vue +91 -45
- package/app/components/SLMonthPicker.vue +42 -37
- package/app/components/SLMultiEmailInput.vue +163 -113
- package/app/components/SLMultiSelect.vue +37 -26
- package/app/components/SLNoData.vue +16 -11
- package/app/components/SLNotification.vue +133 -129
- package/app/components/SLPageModal.vue +14 -9
- package/app/components/SLPagination.vue +45 -33
- package/app/components/SLPillCheckbox.vue +50 -42
- package/app/components/SLPillLabel.vue +43 -28
- package/app/components/SLPopupMenuButton.vue +26 -17
- package/app/components/SLProfileButton.vue +28 -20
- package/app/components/SLProjectShareItem.vue +30 -17
- package/app/components/SLRadioButton.vue +41 -33
- package/app/components/SLRadioButtonGroup.vue +28 -23
- package/app/components/SLRadioGroup.vue +90 -78
- package/app/components/SLRangeInput.vue +53 -38
- package/app/components/SLRegionsMapChart.vue +5 -4
- package/app/components/SLRightSider.vue +50 -40
- package/app/components/SLSearchInput.vue +24 -19
- package/app/components/SLSelect.vue +114 -87
- package/app/components/SLSelectAllToggle.vue +52 -37
- package/app/components/SLSidebarNav.vue +253 -193
- package/app/components/SLSidebarNavGroupItem.vue +13 -10
- package/app/components/SLSidebarNavGroupItemSection.vue +240 -179
- package/app/components/SLSidebarNavLinkItem.vue +131 -109
- package/app/components/SLSidebarNavSectionItem.vue +21 -18
- package/app/components/SLSitePage.vue +35 -24
- package/app/components/SLSkyInsightSitePage.vue +13 -8
- package/app/components/SLSortByDropdown.vue +35 -22
- package/app/components/SLSpinIcon.vue +6 -3
- package/app/components/SLStayRangeInput.vue +47 -36
- package/app/components/SLTab.vue +7 -2
- package/app/components/SLTable.vue +330 -313
- package/app/components/SLTableTooltip.vue +44 -30
- package/app/components/SLTabs.vue +333 -280
- package/app/components/SLTextInput.vue +162 -134
- package/app/components/SLTextarea.vue +40 -29
- package/app/components/SLToast.vue +19 -11
- package/app/components/SLToggleButton.vue +18 -11
- package/app/components/SLTwoLayerMultiSelect.vue +180 -85
- package/app/components/SLTwoLayerSelect.vue +47 -37
- package/app/components/SLTwoLayerSingleSelect.vue +52 -33
- package/app/components/SLWarnModal.vue +9 -7
- package/app/interface/commons.ts +7 -0
- package/app/utils/index.ts +213 -146
- package/eslint.config.js +3 -0
- package/nuxt.config.ts +17 -14
- package/package.json +9 -8
- package/skills-lock.json +59 -0
- package/tsconfig.json +1 -6
- package/.eslintrc.cjs +0 -4
- package/app/assets/css/layout.styl +0 -98
- package/app/assets/css/tailwind.css +0 -3
- package/app/pages/index.vue +0 -15
- package/interface/commons.ts +0 -7
- package/tailwind.config.js +0 -271
- /package/{interface → app/interface}/IDateRange.ts +0 -0
- /package/{interface → app/interface}/IHourRange.ts +0 -0
- /package/{interface → app/interface}/IInputOption.ts +0 -0
- /package/{interface → app/interface}/ILegendItem.ts +0 -0
- /package/{interface → app/interface}/IOrganization.ts +0 -0
- /package/{interface → app/interface}/IPopupMenuButton.ts +0 -0
- /package/{interface → app/interface}/IStayRange.ts +0 -0
- /package/{interface → app/interface}/ITableField.ts +0 -0
- /package/{interface → app/interface}/IWeb.ts +0 -0
- /package/{interface → app/interface}/NavConfigType.ts +0 -0
- /package/{models → app/models}/DateRange.ts +0 -0
package/app/utils/index.ts
CHANGED
|
@@ -14,32 +14,35 @@ import {
|
|
|
14
14
|
getYear,
|
|
15
15
|
format as formatDate,
|
|
16
16
|
getDaysInMonth,
|
|
17
|
-
} from
|
|
17
|
+
} from "date-fns";
|
|
18
18
|
import { sortBy, isEqual } from "lodash-es";
|
|
19
|
-
import { HanRegex } from
|
|
20
|
-
import type IDateRange from
|
|
21
|
-
import { ScreenSize } from
|
|
22
|
-
import ExcelJS from
|
|
23
|
-
import { useGtm } from
|
|
19
|
+
import { HanRegex } from "#imports";
|
|
20
|
+
import type IDateRange from "~ui/app/interface/IDateRange";
|
|
21
|
+
import { ScreenSize } from "~ui/app/interface/commons";
|
|
22
|
+
import ExcelJS from "exceljs";
|
|
23
|
+
import { useGtm } from "@gtm-support/vue-gtm";
|
|
24
24
|
import * as htmlToImage from "html-to-image";
|
|
25
25
|
|
|
26
26
|
export const getUID = () => {
|
|
27
27
|
return `uid-${Math.random().toString(36).substring(2)}`;
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
-
export const dateRangeToStatus = (
|
|
31
|
-
|
|
30
|
+
export const dateRangeToStatus = (
|
|
31
|
+
dateRange: IDateRange | null,
|
|
32
|
+
baseDate = new Date(),
|
|
33
|
+
) => {
|
|
34
|
+
if (!dateRange || !dateRange.start || !dateRange.end) return "custom";
|
|
32
35
|
|
|
33
36
|
const { start, end } = dateRange;
|
|
34
37
|
const diff = differenceInCalendarDays(end, start);
|
|
35
38
|
const isEndDateEqualBaseDate = differenceInCalendarDays(baseDate, end) === 0;
|
|
36
39
|
|
|
37
40
|
if (diff === 6 && isEndDateEqualBaseDate) {
|
|
38
|
-
return
|
|
41
|
+
return "last7Days";
|
|
39
42
|
} else if (diff === 29 && isEndDateEqualBaseDate) {
|
|
40
|
-
return
|
|
43
|
+
return "last30Days";
|
|
41
44
|
} else if (diff === 89 && isEndDateEqualBaseDate) {
|
|
42
|
-
return
|
|
45
|
+
return "last90Days";
|
|
43
46
|
} else if (
|
|
44
47
|
getYear(start) === getYear(end) &&
|
|
45
48
|
getMonth(start) === getMonth(end) &&
|
|
@@ -47,39 +50,45 @@ export const dateRangeToStatus = (dateRange: IDateRange | null, baseDate = new D
|
|
|
47
50
|
isLastDayOfMonth(end) &&
|
|
48
51
|
differenceInCalendarMonths(end, baseDate) === -1
|
|
49
52
|
) {
|
|
50
|
-
return
|
|
53
|
+
return "lastMonth";
|
|
51
54
|
}
|
|
52
55
|
|
|
53
|
-
return
|
|
56
|
+
return "custom";
|
|
54
57
|
};
|
|
55
58
|
|
|
56
|
-
export const statusToDateRange = (
|
|
59
|
+
export const statusToDateRange = (
|
|
60
|
+
status: string | null,
|
|
61
|
+
baseDate = new Date(),
|
|
62
|
+
) => {
|
|
57
63
|
const lastMonth = subMonths(baseDate, 1);
|
|
58
64
|
|
|
59
65
|
switch (status) {
|
|
60
|
-
case
|
|
66
|
+
case "last30Days":
|
|
61
67
|
return { start: subDays(baseDate, 29), end: baseDate };
|
|
62
|
-
case
|
|
68
|
+
case "last90Days":
|
|
63
69
|
return { start: subDays(baseDate, 89), end: baseDate };
|
|
64
|
-
case
|
|
70
|
+
case "lastMonth":
|
|
65
71
|
return { start: startOfMonth(lastMonth), end: endOfMonth(lastMonth) };
|
|
66
|
-
case
|
|
72
|
+
case "last7Days":
|
|
67
73
|
return { start: subDays(baseDate, 6), end: baseDate };
|
|
68
74
|
default:
|
|
69
75
|
return null;
|
|
70
76
|
}
|
|
71
77
|
};
|
|
72
78
|
|
|
73
|
-
export const isPreviousDateRange = (
|
|
79
|
+
export const isPreviousDateRange = (
|
|
80
|
+
leftDateRange: IDateRange | null,
|
|
81
|
+
rightDateRange: IDateRange | null,
|
|
82
|
+
) => {
|
|
74
83
|
if (!leftDateRange || !rightDateRange) {
|
|
75
|
-
return
|
|
84
|
+
return "custom";
|
|
76
85
|
}
|
|
77
86
|
|
|
78
87
|
const { start: leftStart, end: leftEnd } = leftDateRange;
|
|
79
88
|
const { start: rightStart, end: rightEnd } = rightDateRange;
|
|
80
89
|
|
|
81
90
|
if (!leftStart || !leftEnd || !rightStart || !rightEnd) {
|
|
82
|
-
return
|
|
91
|
+
return "custom";
|
|
83
92
|
}
|
|
84
93
|
|
|
85
94
|
const leftDiff = differenceInCalendarDays(leftEnd, leftStart);
|
|
@@ -95,8 +104,8 @@ export const isPreviousDateRange = (leftDateRange: IDateRange | null, rightDateR
|
|
|
95
104
|
isFirstDayOfMonth(rightStart) &&
|
|
96
105
|
isLastDayOfMonth(rightEnd) &&
|
|
97
106
|
differenceInCalendarMonths(leftStart, rightStart) === 1))
|
|
98
|
-
?
|
|
99
|
-
:
|
|
107
|
+
? "previous"
|
|
108
|
+
: "custom";
|
|
100
109
|
};
|
|
101
110
|
|
|
102
111
|
export const previousDateRange = (dateRange: IDateRange | null) => {
|
|
@@ -109,15 +118,15 @@ export const previousDateRange = (dateRange: IDateRange | null) => {
|
|
|
109
118
|
|
|
110
119
|
switch (status) {
|
|
111
120
|
// case 'yesterday':
|
|
112
|
-
case
|
|
113
|
-
case
|
|
114
|
-
case
|
|
115
|
-
case
|
|
121
|
+
case "last7Days":
|
|
122
|
+
case "last30Days":
|
|
123
|
+
case "last90Days":
|
|
124
|
+
case "custom":
|
|
116
125
|
return {
|
|
117
126
|
start: subDays(start, diff + 1),
|
|
118
127
|
end: subDays(end, diff + 1),
|
|
119
128
|
};
|
|
120
|
-
case
|
|
129
|
+
case "lastMonth":
|
|
121
130
|
return {
|
|
122
131
|
start: startOfMonth(lastMonth),
|
|
123
132
|
end: endOfMonth(lastMonth),
|
|
@@ -127,24 +136,27 @@ export const previousDateRange = (dateRange: IDateRange | null) => {
|
|
|
127
136
|
}
|
|
128
137
|
};
|
|
129
138
|
|
|
130
|
-
export const emailValidator = (value: string) =>
|
|
139
|
+
export const emailValidator = (value: string) =>
|
|
140
|
+
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
|
|
141
|
+
value,
|
|
142
|
+
);
|
|
131
143
|
|
|
132
144
|
export const hexToRgba = (hex: string, opacity: number) => {
|
|
133
145
|
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
134
146
|
return result
|
|
135
147
|
? `rgba(${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
148
|
+
result[3],
|
|
149
|
+
16,
|
|
150
|
+
)}, ${opacity})`
|
|
139
151
|
: `rgba(255, 255, 255, 0)`;
|
|
140
152
|
};
|
|
141
153
|
|
|
142
154
|
export const formatDateRange = (dateRange: IDateRange | null) => {
|
|
143
|
-
if (!dateRange) return
|
|
155
|
+
if (!dateRange) return "";
|
|
144
156
|
|
|
145
|
-
return `${formatDate(dateRange.start,
|
|
157
|
+
return `${formatDate(dateRange.start, "yyyy/MM/dd")} - ${formatDate(
|
|
146
158
|
dateRange.end,
|
|
147
|
-
|
|
159
|
+
"yyyy/MM/dd",
|
|
148
160
|
)}`;
|
|
149
161
|
};
|
|
150
162
|
|
|
@@ -158,7 +170,7 @@ export const passwordValidator = (value: string) =>
|
|
|
158
170
|
|
|
159
171
|
export const windowSize = () => {
|
|
160
172
|
const windowWidth = window.innerWidth;
|
|
161
|
-
let windowSize: ScreenSize = ScreenSize[
|
|
173
|
+
let windowSize: ScreenSize = ScreenSize["2xl"];
|
|
162
174
|
|
|
163
175
|
if (windowWidth < 1024) {
|
|
164
176
|
windowSize = ScreenSize.md;
|
|
@@ -167,13 +179,13 @@ export const windowSize = () => {
|
|
|
167
179
|
} else if (windowWidth < 1366) {
|
|
168
180
|
windowSize = ScreenSize.xl;
|
|
169
181
|
} else if (windowWidth < 1536) {
|
|
170
|
-
windowSize = ScreenSize[
|
|
182
|
+
windowSize = ScreenSize["1.5xl"];
|
|
171
183
|
}
|
|
172
184
|
|
|
173
185
|
return windowSize;
|
|
174
186
|
};
|
|
175
187
|
|
|
176
|
-
const getCharLength = (char =
|
|
188
|
+
const getCharLength = (char = "") => {
|
|
177
189
|
const matched = HanRegex.test(char);
|
|
178
190
|
return matched ? 2 : 1;
|
|
179
191
|
};
|
|
@@ -228,8 +240,8 @@ export const getBarChartGridTop = (maxValue: number, showLabel = true) => {
|
|
|
228
240
|
30 +
|
|
229
241
|
(showLabel
|
|
230
242
|
? Number(maxValue).toLocaleString().length *
|
|
231
|
-
|
|
232
|
-
|
|
243
|
+
8 *
|
|
244
|
+
Math.sin((30 / 180) * Math.PI)
|
|
233
245
|
: 0)
|
|
234
246
|
);
|
|
235
247
|
};
|
|
@@ -240,13 +252,16 @@ export const getBarChartGridLeft = (value: number, min: number) => {
|
|
|
240
252
|
return left;
|
|
241
253
|
};
|
|
242
254
|
|
|
243
|
-
export const getBarChartBarLabelHeight = (
|
|
255
|
+
export const getBarChartBarLabelHeight = (
|
|
256
|
+
maxValue: string | number,
|
|
257
|
+
showLabel = true,
|
|
258
|
+
) => {
|
|
244
259
|
return (
|
|
245
260
|
16 +
|
|
246
261
|
(showLabel
|
|
247
262
|
? Number(maxValue).toLocaleString().length *
|
|
248
|
-
|
|
249
|
-
|
|
263
|
+
8 *
|
|
264
|
+
Math.sin((30 / 180) * Math.PI)
|
|
250
265
|
: 0)
|
|
251
266
|
);
|
|
252
267
|
};
|
|
@@ -255,11 +270,14 @@ export const getChartLabelWidth = (maxValue: string | number) => {
|
|
|
255
270
|
return Number(maxValue).toLocaleString().length * 8;
|
|
256
271
|
};
|
|
257
272
|
|
|
258
|
-
export const getDayDatesMatchWeekDay = (
|
|
273
|
+
export const getDayDatesMatchWeekDay = (
|
|
274
|
+
dateRange1: IDateRange,
|
|
275
|
+
dateRange2: IDateRange | null,
|
|
276
|
+
) => {
|
|
259
277
|
const dayDates: {
|
|
260
|
-
date: string
|
|
261
|
-
day: number
|
|
262
|
-
weekDay: string
|
|
278
|
+
date: string;
|
|
279
|
+
day: number;
|
|
280
|
+
weekDay: string;
|
|
263
281
|
}[] = [];
|
|
264
282
|
|
|
265
283
|
if (!dateRange2) {
|
|
@@ -268,7 +286,7 @@ export const getDayDatesMatchWeekDay = (dateRange1: IDateRange, dateRange2: IDat
|
|
|
268
286
|
const day = addDays(dateRange1.start, i);
|
|
269
287
|
|
|
270
288
|
dayDates.push({
|
|
271
|
-
date: formatDate(day,
|
|
289
|
+
date: formatDate(day, "yyyy/MM/dd"),
|
|
272
290
|
day: getDay(day),
|
|
273
291
|
weekDay: translateDayToLocale(getDay(day)),
|
|
274
292
|
});
|
|
@@ -280,93 +298,102 @@ export const getDayDatesMatchWeekDay = (dateRange1: IDateRange, dateRange2: IDat
|
|
|
280
298
|
const weekDayOfRange2Start = getDay(dateRange2.start);
|
|
281
299
|
const weekDayDiff = weekDayOfRange1Start - weekDayOfRange2Start;
|
|
282
300
|
|
|
283
|
-
if (
|
|
284
|
-
|
|
285
|
-
|| (weekDayDiff < -3)
|
|
286
|
-
) {
|
|
287
|
-
const iteratorTimes = weekDayDiff > 0 ? weekDayDiff : (weekDayDiff + 7);
|
|
301
|
+
if ((weekDayDiff > 0 && weekDayDiff <= 3) || weekDayDiff < -3) {
|
|
302
|
+
const iteratorTimes = weekDayDiff > 0 ? weekDayDiff : weekDayDiff + 7;
|
|
288
303
|
for (let i = 0; i < iteratorTimes; i += 1) {
|
|
289
304
|
const day = addDays(dateRange2.start, i);
|
|
290
305
|
dayDates.push({
|
|
291
|
-
date: formatDate(day,
|
|
306
|
+
date: formatDate(day, "yyyy/MM/dd"),
|
|
292
307
|
day: getDay(day),
|
|
293
308
|
weekDay: translateDayToLocale(getDay(day)),
|
|
294
309
|
});
|
|
295
310
|
}
|
|
296
311
|
}
|
|
297
312
|
|
|
298
|
-
const daysOfLastMonth =
|
|
313
|
+
const daysOfLastMonth =
|
|
314
|
+
differenceInCalendarDays(dateRange1.end, dateRange1.start) + 1;
|
|
299
315
|
for (let i = 0; i < daysOfLastMonth; i += 1) {
|
|
300
316
|
const day = addDays(dateRange1.start, i);
|
|
301
317
|
dayDates.push({
|
|
302
|
-
date: formatDate(day,
|
|
318
|
+
date: formatDate(day, "yyyy/MM/dd"),
|
|
303
319
|
day: getDay(day),
|
|
304
320
|
weekDay: translateDayToLocale(getDay(day)),
|
|
305
321
|
});
|
|
306
322
|
}
|
|
307
323
|
|
|
308
|
-
const overMonth1Days =
|
|
324
|
+
const overMonth1Days =
|
|
325
|
+
getDate(dateRange2.end) -
|
|
326
|
+
getDate(dateRange1.end) -
|
|
327
|
+
(7 - Math.abs(weekDayDiff));
|
|
309
328
|
if (overMonth1Days > 0) {
|
|
310
329
|
for (let j = overMonth1Days; j > 0; j -= 1) {
|
|
311
330
|
const day = subDays(dateRange2.end, j);
|
|
312
331
|
dayDates.push({
|
|
313
|
-
date: formatDate(day,
|
|
332
|
+
date: formatDate(day, "yyyy/MM/dd"),
|
|
314
333
|
day: getDay(day),
|
|
315
334
|
weekDay: translateDayToLocale(getDay(day)),
|
|
316
335
|
});
|
|
317
336
|
}
|
|
318
337
|
}
|
|
319
338
|
|
|
320
|
-
|
|
321
339
|
return dayDates;
|
|
322
|
-
}
|
|
340
|
+
};
|
|
323
341
|
|
|
324
342
|
export const translateDayToLocale = (day: number) => {
|
|
325
343
|
switch (day) {
|
|
326
344
|
case 0:
|
|
327
|
-
return
|
|
345
|
+
return "日";
|
|
328
346
|
case 1:
|
|
329
|
-
return
|
|
347
|
+
return "一";
|
|
330
348
|
case 2:
|
|
331
|
-
return
|
|
349
|
+
return "二";
|
|
332
350
|
case 3:
|
|
333
|
-
return
|
|
351
|
+
return "三";
|
|
334
352
|
case 4:
|
|
335
|
-
return
|
|
353
|
+
return "四";
|
|
336
354
|
case 5:
|
|
337
|
-
return
|
|
355
|
+
return "五";
|
|
338
356
|
default:
|
|
339
|
-
return
|
|
357
|
+
return "六";
|
|
340
358
|
}
|
|
341
|
-
}
|
|
359
|
+
};
|
|
342
360
|
|
|
343
|
-
export const numberToLocaleString = (
|
|
361
|
+
export const numberToLocaleString = (
|
|
362
|
+
value: number | null,
|
|
363
|
+
{ NaNString = "" } = {},
|
|
364
|
+
toNumber = false,
|
|
365
|
+
) => {
|
|
344
366
|
if (value === null || isNaN(value)) {
|
|
345
|
-
return NaNString ||
|
|
367
|
+
return NaNString || "";
|
|
346
368
|
} else if (!toNumber) {
|
|
347
369
|
return Number(value).toLocaleString();
|
|
348
370
|
}
|
|
349
371
|
|
|
350
372
|
return value;
|
|
351
|
-
}
|
|
373
|
+
};
|
|
352
374
|
|
|
353
|
-
export const toFixed = (
|
|
375
|
+
export const toFixed = (
|
|
376
|
+
value: number | null,
|
|
377
|
+
fixedTo: number = 0,
|
|
378
|
+
{ NaNString = "", unsign = false, unit = "" } = {},
|
|
379
|
+
toNumber = false,
|
|
380
|
+
) => {
|
|
354
381
|
if (value === null || isNaN(value)) {
|
|
355
|
-
return NaNString ||
|
|
382
|
+
return NaNString || "";
|
|
356
383
|
} else if (toNumber) {
|
|
357
384
|
if (unsign) {
|
|
358
385
|
return Number(Number(Math.abs(value)).toFixed(fixedTo));
|
|
359
386
|
}
|
|
360
387
|
return Number(Number(value).toFixed(fixedTo));
|
|
361
388
|
} else {
|
|
362
|
-
let formatedValue =
|
|
389
|
+
let formatedValue = "";
|
|
363
390
|
if (unsign) {
|
|
364
391
|
formatedValue = Number(Math.abs(value)).toFixed(fixedTo);
|
|
365
392
|
} else {
|
|
366
393
|
formatedValue = Number(value).toFixed(fixedTo);
|
|
367
394
|
}
|
|
368
395
|
|
|
369
|
-
const valueParts = formatedValue.split(
|
|
396
|
+
const valueParts = formatedValue.split(".");
|
|
370
397
|
|
|
371
398
|
if (valueParts[1]) {
|
|
372
399
|
return `${Number(valueParts[0]).toLocaleString()}.${valueParts[1]}${unit}`;
|
|
@@ -374,17 +401,21 @@ export const toFixed = (value: number | null, fixedTo: number = 0, { NaNString =
|
|
|
374
401
|
|
|
375
402
|
return `${Number(valueParts[0]).toLocaleString()}${unit}`;
|
|
376
403
|
}
|
|
377
|
-
}
|
|
404
|
+
};
|
|
378
405
|
|
|
379
406
|
export const downloadURL = (data: string, fileName: string) => {
|
|
380
|
-
const downloadLink = document.createElement(
|
|
407
|
+
const downloadLink = document.createElement("a");
|
|
381
408
|
downloadLink.href = data;
|
|
382
409
|
downloadLink.download = fileName;
|
|
383
410
|
downloadLink.click();
|
|
384
411
|
downloadLink.remove();
|
|
385
412
|
};
|
|
386
413
|
|
|
387
|
-
export const downloadBlob = (
|
|
414
|
+
export const downloadBlob = (
|
|
415
|
+
data: ExcelJS.Buffer,
|
|
416
|
+
fileName: string,
|
|
417
|
+
mimeType: string,
|
|
418
|
+
) => {
|
|
388
419
|
const blob = new Blob([data], {
|
|
389
420
|
type: mimeType,
|
|
390
421
|
});
|
|
@@ -395,41 +426,52 @@ export const downloadBlob = (data: ExcelJS.Buffer, fileName: string, mimeType: s
|
|
|
395
426
|
}, 1000);
|
|
396
427
|
};
|
|
397
428
|
|
|
398
|
-
export const isDateRangesEqual = (
|
|
399
|
-
|
|
429
|
+
export const isDateRangesEqual = (
|
|
430
|
+
dateRange1: IDateRange,
|
|
431
|
+
dateRange2: IDateRange,
|
|
432
|
+
) => {
|
|
433
|
+
return (
|
|
434
|
+
getYear(dateRange1.start) === getYear(dateRange2.start) &&
|
|
400
435
|
getMonth(dateRange1.start) === getMonth(dateRange2.start) &&
|
|
401
436
|
getDate(dateRange1.start) === getDate(dateRange2.start) &&
|
|
402
437
|
getYear(dateRange1.end) === getYear(dateRange2.end) &&
|
|
403
438
|
getMonth(dateRange1.end) === getMonth(dateRange2.end) &&
|
|
404
|
-
getDate(dateRange1.end) === getDate(dateRange2.end)
|
|
405
|
-
|
|
439
|
+
getDate(dateRange1.end) === getDate(dateRange2.end)
|
|
440
|
+
);
|
|
441
|
+
};
|
|
406
442
|
|
|
407
|
-
export const gaev = ({
|
|
408
|
-
category
|
|
409
|
-
action
|
|
410
|
-
label
|
|
411
|
-
value
|
|
412
|
-
cb
|
|
443
|
+
export const gaev = ({
|
|
444
|
+
category,
|
|
445
|
+
action,
|
|
446
|
+
label,
|
|
447
|
+
value,
|
|
448
|
+
cb,
|
|
449
|
+
}: {
|
|
450
|
+
category?: string;
|
|
451
|
+
action?: string;
|
|
452
|
+
label?: string;
|
|
453
|
+
value?: number;
|
|
454
|
+
cb?: () => void;
|
|
413
455
|
} = {}) => {
|
|
414
456
|
const gtm = useGtm();
|
|
415
457
|
|
|
416
458
|
if (gtm?.enabled() && !!event) {
|
|
417
459
|
const data: {
|
|
418
|
-
event:
|
|
419
|
-
EventCategory?: string
|
|
420
|
-
EventAction?: string
|
|
421
|
-
EventLabel?: string
|
|
422
|
-
EventValue?: number
|
|
423
|
-
eventCallback?: () => void
|
|
460
|
+
event: "GAEV";
|
|
461
|
+
EventCategory?: string;
|
|
462
|
+
EventAction?: string;
|
|
463
|
+
EventLabel?: string;
|
|
464
|
+
EventValue?: number;
|
|
465
|
+
eventCallback?: () => void;
|
|
424
466
|
} = {
|
|
425
|
-
event:
|
|
467
|
+
event: "GAEV",
|
|
426
468
|
EventCategory: category,
|
|
427
469
|
EventAction: action,
|
|
428
470
|
EventLabel: label,
|
|
429
471
|
EventValue: value,
|
|
430
472
|
};
|
|
431
473
|
|
|
432
|
-
if (typeof cb ===
|
|
474
|
+
if (typeof cb === "function") {
|
|
433
475
|
data.eventCallback = cb;
|
|
434
476
|
}
|
|
435
477
|
|
|
@@ -437,32 +479,38 @@ export const gaev = ({ category, action, label, value, cb }: {
|
|
|
437
479
|
}
|
|
438
480
|
};
|
|
439
481
|
|
|
440
|
-
export const ga4ev = ({
|
|
441
|
-
category
|
|
442
|
-
action
|
|
443
|
-
label
|
|
444
|
-
value
|
|
445
|
-
cb
|
|
482
|
+
export const ga4ev = ({
|
|
483
|
+
category,
|
|
484
|
+
action,
|
|
485
|
+
label,
|
|
486
|
+
value,
|
|
487
|
+
cb,
|
|
488
|
+
}: {
|
|
489
|
+
category?: string;
|
|
490
|
+
action?: string;
|
|
491
|
+
label?: string;
|
|
492
|
+
value?: number;
|
|
493
|
+
cb?: () => void;
|
|
446
494
|
} = {}) => {
|
|
447
495
|
const gtm = useGtm();
|
|
448
496
|
|
|
449
497
|
if (gtm?.enabled() && !!event) {
|
|
450
498
|
const data: {
|
|
451
|
-
event:
|
|
452
|
-
EventCategory?: string
|
|
453
|
-
EventAction?: string
|
|
454
|
-
EventLabel?: string
|
|
455
|
-
EventValue?: number
|
|
456
|
-
eventCallback?: () => void
|
|
499
|
+
event: "GA4EV";
|
|
500
|
+
EventCategory?: string;
|
|
501
|
+
EventAction?: string;
|
|
502
|
+
EventLabel?: string;
|
|
503
|
+
EventValue?: number;
|
|
504
|
+
eventCallback?: () => void;
|
|
457
505
|
} = {
|
|
458
|
-
event:
|
|
506
|
+
event: "GA4EV",
|
|
459
507
|
EventCategory: category,
|
|
460
508
|
EventAction: action,
|
|
461
509
|
EventLabel: label,
|
|
462
510
|
EventValue: value,
|
|
463
511
|
};
|
|
464
512
|
|
|
465
|
-
if (typeof cb ===
|
|
513
|
+
if (typeof cb === "function") {
|
|
466
514
|
data.eventCallback = cb;
|
|
467
515
|
}
|
|
468
516
|
|
|
@@ -475,42 +523,47 @@ export const ga4ev = ({ category, action, label, value, cb }: {
|
|
|
475
523
|
eventParams.event_callback = cb;
|
|
476
524
|
}
|
|
477
525
|
|
|
478
|
-
window.dataLayer?.push(
|
|
526
|
+
window.dataLayer?.push("event", label, eventParams);
|
|
479
527
|
}
|
|
480
528
|
};
|
|
481
529
|
|
|
482
|
-
export const getWebsiteFavico = (
|
|
530
|
+
export const getWebsiteFavico = (
|
|
531
|
+
domain: string,
|
|
532
|
+
): Promise<{ domain: string; icoUrl: string }> => {
|
|
483
533
|
const runtimeConfig = useRuntimeConfig();
|
|
484
534
|
|
|
485
535
|
return new Promise((resolve) => {
|
|
486
536
|
const icoUrl = `${runtimeConfig.public.icoUrl}/api/4.0/domain_ico?domain=${domain}`;
|
|
487
|
-
const canvas = document.createElement(
|
|
537
|
+
const canvas = document.createElement("canvas");
|
|
488
538
|
canvas.width = 26;
|
|
489
539
|
canvas.height = 26;
|
|
490
|
-
const ctx = canvas.getContext(
|
|
540
|
+
const ctx = canvas.getContext("2d");
|
|
491
541
|
|
|
492
542
|
if (ctx) {
|
|
493
|
-
ctx.fillStyle =
|
|
543
|
+
ctx.fillStyle = "#FFFFFF";
|
|
494
544
|
ctx.fill();
|
|
495
545
|
|
|
496
546
|
ctx.lineWidth = 1;
|
|
497
|
-
ctx.strokeStyle =
|
|
547
|
+
ctx.strokeStyle = "#BDBDBD";
|
|
498
548
|
ctx.roundRect(1, 1, 24, 24, 6);
|
|
499
549
|
ctx.stroke();
|
|
500
550
|
}
|
|
501
551
|
|
|
502
552
|
const image = new Image();
|
|
503
|
-
image.setAttribute(
|
|
553
|
+
image.setAttribute("crossOrigin", "Anonymous");
|
|
504
554
|
image.onload = () => {
|
|
505
555
|
ctx?.drawImage(image, 0, 0, image.width, image.height, 6, 6, 14, 14);
|
|
506
556
|
|
|
507
|
-
resolve({ domain, icoUrl: canvas.toDataURL(
|
|
557
|
+
resolve({ domain, icoUrl: canvas.toDataURL("image/png", 1.0) });
|
|
508
558
|
};
|
|
509
559
|
image.src = icoUrl;
|
|
510
560
|
});
|
|
511
561
|
};
|
|
512
562
|
|
|
513
|
-
export const httpUrlValidate = (url: string) =>
|
|
563
|
+
export const httpUrlValidate = (url: string) =>
|
|
564
|
+
/^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/.test(
|
|
565
|
+
url,
|
|
566
|
+
);
|
|
514
567
|
export const httpUrlToHost = (url: string) => {
|
|
515
568
|
if (httpUrlValidate(url)) {
|
|
516
569
|
try {
|
|
@@ -522,19 +575,20 @@ export const httpUrlToHost = (url: string) => {
|
|
|
522
575
|
}
|
|
523
576
|
|
|
524
577
|
return url;
|
|
525
|
-
}
|
|
578
|
+
};
|
|
526
579
|
export const hostToHttpUrl = (host: string) => {
|
|
527
580
|
if (/^https?:\/\//.test(host)) {
|
|
528
581
|
return host;
|
|
529
582
|
}
|
|
530
583
|
|
|
531
584
|
return `https://${host}`;
|
|
532
|
-
}
|
|
585
|
+
};
|
|
533
586
|
|
|
534
587
|
export const isValidDate = (d: any) => {
|
|
535
588
|
if (Object.prototype.toString.call(d) === "[object Date]") {
|
|
536
589
|
// it is a date
|
|
537
|
-
if (isNaN(d)) {
|
|
590
|
+
if (isNaN(d)) {
|
|
591
|
+
// d.getTime() or d.valueOf() will also work
|
|
538
592
|
return false;
|
|
539
593
|
} else {
|
|
540
594
|
return true;
|
|
@@ -542,22 +596,29 @@ export const isValidDate = (d: any) => {
|
|
|
542
596
|
} else {
|
|
543
597
|
return false;
|
|
544
598
|
}
|
|
545
|
-
}
|
|
599
|
+
};
|
|
546
600
|
|
|
547
601
|
export const waitForTime = (ms: number) => {
|
|
548
|
-
return new Promise(r => setTimeout(r, ms));
|
|
549
|
-
}
|
|
602
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
603
|
+
};
|
|
550
604
|
|
|
551
605
|
export const waitForever = () => {
|
|
552
|
-
return new Promise(r => {
|
|
553
|
-
}
|
|
606
|
+
return new Promise((r) => {});
|
|
607
|
+
};
|
|
554
608
|
|
|
555
|
-
export const waitFor = async (
|
|
609
|
+
export const waitFor = async (
|
|
610
|
+
condition: () => boolean,
|
|
611
|
+
pollInterval = 50,
|
|
612
|
+
timeoutAfter: number,
|
|
613
|
+
) => {
|
|
556
614
|
const startTime = Date.now();
|
|
557
615
|
|
|
558
616
|
while (true) {
|
|
559
|
-
if (
|
|
560
|
-
|
|
617
|
+
if (
|
|
618
|
+
typeof timeoutAfter === "number" &&
|
|
619
|
+
Date.now() > startTime + timeoutAfter
|
|
620
|
+
) {
|
|
621
|
+
throw "Condition not met before timeout";
|
|
561
622
|
}
|
|
562
623
|
|
|
563
624
|
const result = await condition();
|
|
@@ -566,9 +627,9 @@ export const waitFor = async (condition: () => boolean, pollInterval = 50, timeo
|
|
|
566
627
|
return result;
|
|
567
628
|
}
|
|
568
629
|
|
|
569
|
-
await new Promise(r => setTimeout(r, pollInterval));
|
|
630
|
+
await new Promise((r) => setTimeout(r, pollInterval));
|
|
570
631
|
}
|
|
571
|
-
}
|
|
632
|
+
};
|
|
572
633
|
|
|
573
634
|
function loadImage(url: string) {
|
|
574
635
|
return new Promise<HTMLImageElement>((resolve) => {
|
|
@@ -598,7 +659,7 @@ async function addPadding(dataURL: string, padding: number) {
|
|
|
598
659
|
|
|
599
660
|
async function addDateRanges(
|
|
600
661
|
dataURL: string,
|
|
601
|
-
dateRanges: (IDateRange | null)[]
|
|
662
|
+
dateRanges: (IDateRange | null)[],
|
|
602
663
|
) {
|
|
603
664
|
const fontSize = 16;
|
|
604
665
|
const canvas = document.createElement("canvas");
|
|
@@ -649,7 +710,11 @@ async function addWatermark(dataURL: string, watermarkImg: string) {
|
|
|
649
710
|
// ctx.fillStyle = pattern;
|
|
650
711
|
// ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
651
712
|
|
|
652
|
-
ctx.drawImage(
|
|
713
|
+
ctx.drawImage(
|
|
714
|
+
watermark,
|
|
715
|
+
canvas.width - watermark.width - 12,
|
|
716
|
+
canvas.height - watermark.height - 12,
|
|
717
|
+
);
|
|
653
718
|
|
|
654
719
|
return canvas.toDataURL();
|
|
655
720
|
}
|
|
@@ -661,7 +726,7 @@ export const domToPngDataUrl = async (
|
|
|
661
726
|
padding: number = 0,
|
|
662
727
|
dateRanges: (IDateRange | null)[] = [],
|
|
663
728
|
watermark: boolean = true,
|
|
664
|
-
watermarkImg: string =
|
|
729
|
+
watermarkImg: string = "/assets/images/commons/watermark-2.svg",
|
|
665
730
|
) => {
|
|
666
731
|
if (!imageTarget) {
|
|
667
732
|
console.log("Can not find target of htmlToImage!");
|
|
@@ -670,18 +735,20 @@ export const domToPngDataUrl = async (
|
|
|
670
735
|
|
|
671
736
|
const filterOption = (node: HTMLElement) => {
|
|
672
737
|
return (
|
|
673
|
-
!(
|
|
674
|
-
node?.getAttribute && node.getAttribute("data-skip-download") === ""
|
|
675
|
-
) &&
|
|
738
|
+
!(node?.getAttribute && node.getAttribute("data-skip-download") === "") &&
|
|
676
739
|
(typeof customFilter !== "function" ||
|
|
677
740
|
(customFilter && customFilter(node)))
|
|
678
741
|
);
|
|
679
742
|
};
|
|
680
|
-
const styleOption = Object.assign(
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
743
|
+
const styleOption = Object.assign(
|
|
744
|
+
{},
|
|
745
|
+
{
|
|
746
|
+
marginTop: 0,
|
|
747
|
+
marginBottom: 9,
|
|
748
|
+
border: 0,
|
|
749
|
+
},
|
|
750
|
+
customStyle || {},
|
|
751
|
+
);
|
|
685
752
|
|
|
686
753
|
const options = {
|
|
687
754
|
pixelRatio: 1,
|
|
@@ -695,10 +762,10 @@ export const domToPngDataUrl = async (
|
|
|
695
762
|
dataURL = await addDateRanges(dataURL, [...dateRanges].reverse());
|
|
696
763
|
if (watermark) dataURL = await addWatermark(dataURL, watermarkImg);
|
|
697
764
|
return dataURL;
|
|
698
|
-
}
|
|
765
|
+
};
|
|
699
766
|
|
|
700
767
|
export const dataURLtoBlob = (dataurl: string) => {
|
|
701
|
-
const arr = dataurl.split(
|
|
768
|
+
const arr = dataurl.split(",");
|
|
702
769
|
const mime = arr[0].match(/:(.*?);/)[1];
|
|
703
770
|
const bstr = atob(arr[1]);
|
|
704
771
|
let n = bstr.length;
|
|
@@ -707,4 +774,4 @@ export const dataURLtoBlob = (dataurl: string) => {
|
|
|
707
774
|
u8arr[n] = bstr.charCodeAt(n);
|
|
708
775
|
}
|
|
709
776
|
return new Blob([u8arr], { type: mime });
|
|
710
|
-
}
|
|
777
|
+
};
|