Add JSDoc documentation and file headers to all source files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,18 @@
|
||||
// The Lucia project.
|
||||
// Copyright 2023-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
/** @module abbreviateNumber Duration abbreviation formatting. */
|
||||
|
||||
/**
|
||||
* 將數字轉換成簡寫的形式,設定 dhms 的數值
|
||||
* @param {number} totalSeconds 總秒數
|
||||
* @returns {string}
|
||||
* Converts a total number of seconds into a human-readable abbreviated
|
||||
* duration string using days, hours, minutes, and seconds.
|
||||
*
|
||||
* @param {number} totalSeconds - The total number of seconds to convert.
|
||||
* @returns {string} The abbreviated duration string (e.g. "2d 3h 15m 30s"),
|
||||
* or "0" if totalSeconds is zero.
|
||||
*/
|
||||
export default function abbreviateNumber(totalSeconds) {
|
||||
let seconds = 0;
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
// The Lucia project.
|
||||
// Copyright 2023-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
/** @module alertModal SweetAlert2 modal dialogs for user interactions. */
|
||||
|
||||
import Swal from 'sweetalert2';
|
||||
import { useAllMapDataStore } from '@/stores/allMapData';
|
||||
import { useConformanceStore } from '@/stores/conformance';
|
||||
@@ -18,8 +26,11 @@ const customClass = {
|
||||
cancelButton: '!inline-block !rounded-full !text-sm !font-medium !text-center !align-middle !transition-colors !duration-300 !px-5 !py-2 !w-[100px] !h-[40px] ',
|
||||
};
|
||||
/**
|
||||
* Map Saved
|
||||
* @param { function } addFilterId 後端 API
|
||||
* Shows a modal dialog to save a new filter with a user-provided name.
|
||||
*
|
||||
* @param {Function} addFilterId - Backend API function to create the filter.
|
||||
* @param {Function|null} [next=null] - Vue Router next() guard callback.
|
||||
* @returns {Promise<boolean>} True if the filter was saved, false otherwise.
|
||||
*/
|
||||
export async function saveFilter(addFilterId, next = null) {
|
||||
let fileName = '';
|
||||
@@ -67,8 +78,10 @@ export async function saveFilter(addFilterId, next = null) {
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Saved Success
|
||||
* @param { string } value File's name
|
||||
* Shows a timed success notification after a file has been saved.
|
||||
*
|
||||
* @param {string} value - The name of the saved file.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function savedSuccessfully(value) {
|
||||
value = value || '';
|
||||
@@ -83,11 +96,16 @@ export async function savedSuccessfully(value) {
|
||||
})
|
||||
};
|
||||
/**
|
||||
* leave Map page
|
||||
* @param { function } next 執行完函式後的步驟
|
||||
* @param { function } addFilterId 後端 API
|
||||
* @param { string } toPath route path
|
||||
* @param { function } logOut 登出函式
|
||||
* Prompts the user to save unsaved filter changes before leaving the
|
||||
* Map page. Handles confirm (save), cancel (discard), and backdrop
|
||||
* (stay) scenarios.
|
||||
*
|
||||
* @param {Function} next - Vue Router next() guard callback.
|
||||
* @param {Function} addFilterId - Backend API function to create the filter.
|
||||
* @param {string} toPath - The destination route path.
|
||||
* @param {Function} [logOut] - Optional logout function to call instead
|
||||
* of navigating.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function leaveFilter(next, addFilterId, toPath, logOut) {
|
||||
const allMapDataStore = useAllMapDataStore();
|
||||
@@ -140,8 +158,12 @@ export async function leaveFilter(next, addFilterId, toPath, logOut) {
|
||||
|
||||
};
|
||||
/**
|
||||
* Conformance Saved
|
||||
* @param { function } addConformanceCreateCheckId 後端 API
|
||||
* Shows a modal dialog to save a new conformance rule with a
|
||||
* user-provided name.
|
||||
*
|
||||
* @param {Function} addConformanceCreateCheckId - Backend API function
|
||||
* to create the conformance check.
|
||||
* @returns {Promise<boolean>} True if the rule was saved, false otherwise.
|
||||
*/
|
||||
export async function saveConformance(addConformanceCreateCheckId) {
|
||||
let fileName = '';
|
||||
@@ -179,11 +201,16 @@ export async function saveConformance(addConformanceCreateCheckId) {
|
||||
}
|
||||
}
|
||||
/**
|
||||
* leave Conformance page
|
||||
* @param { function } next 執行完函式後的步驟
|
||||
* @param { function } addConformanceCreateCheckId 後端 API
|
||||
* @param { string } toPath route path
|
||||
* @param { function } logOut 登出函式
|
||||
* Prompts the user to save unsaved conformance rule changes before
|
||||
* leaving the Conformance page. Delegates to helper functions for
|
||||
* confirm, cancel, and backdrop scenarios.
|
||||
*
|
||||
* @param {Function} next - Vue Router next() guard callback.
|
||||
* @param {Function} addConformanceCreateCheckId - Backend API function
|
||||
* to create the conformance check.
|
||||
* @param {string} toPath - The destination route path.
|
||||
* @param {Function} [logOut] - Optional logout function.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function leaveConformance(next, addConformanceCreateCheckId, toPath, logOut) {
|
||||
const conformanceStore = useConformanceStore();
|
||||
@@ -195,6 +222,10 @@ export async function leaveConformance(next, addConformanceCreateCheckId, toPath
|
||||
await handleDismiss(result.dismiss, conformanceStore, next, toPath, logOut);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Displays the "SAVE YOUR RULE?" confirmation dialog.
|
||||
* @returns {Promise<Object>} The SweetAlert2 result object.
|
||||
*/
|
||||
async function showConfirmationDialog() {
|
||||
return Swal.fire({
|
||||
title: 'SAVE YOUR RULE?',
|
||||
@@ -210,6 +241,12 @@ async function showConfirmationDialog() {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the confirmed save action for conformance rules.
|
||||
* @param {Object} conformanceStore - The conformance Pinia store.
|
||||
* @param {Function} addConformanceCreateCheckId - API function to create check.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function handleConfirmed(conformanceStore, addConformanceCreateCheckId) {
|
||||
if (conformanceStore.conformanceFilterCreateCheckId || conformanceStore.conformanceLogCreateCheckId) {
|
||||
await conformanceStore.updateConformance();
|
||||
@@ -221,6 +258,15 @@ async function handleConfirmed(conformanceStore, addConformanceCreateCheckId) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles dismiss actions (cancel or backdrop click) for conformance modals.
|
||||
* @param {string} dismissType - The SweetAlert2 dismiss reason.
|
||||
* @param {Object} conformanceStore - The conformance Pinia store.
|
||||
* @param {Function} next - Vue Router next() guard callback.
|
||||
* @param {string} toPath - The destination route path.
|
||||
* @param {Function} [logOut] - Optional logout function.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function handleDismiss(dismissType, conformanceStore, next, toPath, logOut) {
|
||||
switch (dismissType) {
|
||||
case 'cancel':
|
||||
@@ -237,15 +283,23 @@ async function handleDismiss(dismissType, conformanceStore, next, toPath, logOut
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets temporary conformance check IDs to null.
|
||||
* @param {Object} conformanceStore - The conformance Pinia store.
|
||||
*/
|
||||
function resetTempCheckId(conformanceStore) {
|
||||
conformanceStore.conformanceFilterTempCheckId = null;
|
||||
conformanceStore.conformanceLogTempCheckId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload failde First
|
||||
* @param { string } failureType 後端檔案錯誤類型
|
||||
* @param { string } failureMsg 後端檔案錯誤訊息
|
||||
* Shows an error modal for the first stage of upload validation failure.
|
||||
*
|
||||
* @param {string} failureType - The error type from the backend (e.g.
|
||||
* "encoding", "insufficient_columns", "empty", "name_suffix", "mime_type").
|
||||
* @param {string} failureMsg - The raw error message from the backend.
|
||||
* @param {number} [failureLoc] - The row number where the error occurred.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function uploadFailedFirst(failureType, failureMsg, failureLoc) {
|
||||
// msg: 'not in UTF-8' | 'insufficient columns' | 'the csv file is empty' | 'the filename does not ends with .csv' | 'not a CSV file'
|
||||
@@ -283,8 +337,12 @@ export async function uploadFailedFirst(failureType, failureMsg, failureLoc) {
|
||||
})
|
||||
};
|
||||
/**
|
||||
* Upload failde Second
|
||||
* @param { array } detail 後端回傳的失敗訊息
|
||||
* Shows an error modal for the second stage of upload validation failure,
|
||||
* listing individual row-level errors.
|
||||
*
|
||||
* @param {Array<{type: string, loc: Array, input: string}>} detail -
|
||||
* Array of error detail objects from the backend.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function uploadFailedSecond(detail) {
|
||||
let srt = '';
|
||||
@@ -341,8 +399,8 @@ export async function uploadFailedSecond(detail) {
|
||||
srt = '';
|
||||
};
|
||||
/**
|
||||
* Upload Success
|
||||
* @param { string } value File's name
|
||||
* Shows a timed success notification after a file upload completes.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function uploadSuccess() {
|
||||
await Swal.fire({
|
||||
@@ -355,8 +413,11 @@ export async function uploadSuccess() {
|
||||
})
|
||||
};
|
||||
/**
|
||||
* Confirm whether to upload the file
|
||||
* @param { object } fetchData 後端 API POST data
|
||||
* Shows a confirmation dialog before uploading a file. Proceeds with
|
||||
* the upload only if the user confirms.
|
||||
*
|
||||
* @param {Object} fetchData - The upload request payload for the backend.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function uploadConfirm(fetchData) {
|
||||
const filesStore = useFilesStore();
|
||||
@@ -379,7 +440,8 @@ export async function uploadConfirm(fetchData) {
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Upload loader
|
||||
* Shows a non-dismissable loading spinner during file upload.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function uploadloader() {
|
||||
await Swal.fire({
|
||||
@@ -390,11 +452,13 @@ export async function uploadloader() {
|
||||
})
|
||||
};
|
||||
/**
|
||||
* Rename Modal
|
||||
* @param { function } rename 後端 API
|
||||
* @param { string } type File 檔案類型
|
||||
* @param { number } id File ID
|
||||
* @param { string } baseName 改名前的檔名
|
||||
* Shows a modal dialog for renaming a file.
|
||||
*
|
||||
* @param {Function} rename - Backend API function to rename the file.
|
||||
* @param {string} type - The file type (e.g. "log", "filter").
|
||||
* @param {number} id - The file ID.
|
||||
* @param {string} baseName - The current file name.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function renameModal(rename, type, id, baseName) {
|
||||
const fileName = baseName;
|
||||
@@ -434,11 +498,13 @@ export async function renameModal(rename, type, id, baseName) {
|
||||
// 清空欄位 fileName = '';
|
||||
}
|
||||
/**
|
||||
* Delete File
|
||||
* @param { string } files 有關連的檔案
|
||||
* @param { string } type File 檔案類型
|
||||
* @param { number } id File ID
|
||||
* @param { string } name 原本的檔案
|
||||
* Shows a confirmation dialog for deleting a file and its dependents.
|
||||
*
|
||||
* @param {string} files - HTML string listing dependent files to be deleted.
|
||||
* @param {string} type - The file type (e.g. "log", "filter").
|
||||
* @param {number} id - The file ID.
|
||||
* @param {string} name - The file name.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function deleteFileModal(files, type, id, name) {
|
||||
const filesStore = useFilesStore();
|
||||
@@ -471,7 +537,8 @@ export async function deleteFileModal(files, type, id, name) {
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Delete Success
|
||||
* Shows a timed success notification after file deletion.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function deleteSuccess() {
|
||||
await Swal.fire({
|
||||
@@ -484,9 +551,13 @@ export async function deleteSuccess() {
|
||||
})
|
||||
};
|
||||
/**
|
||||
* Really deleted information
|
||||
* @param {string} files 被刪除的檔案 html
|
||||
* @param {array} reallyDeleteData 被刪除的檔案 data,未取得 recordId 而傳入
|
||||
* Shows an informational modal about files deleted by other users,
|
||||
* then records the deletions and refreshes the file list.
|
||||
*
|
||||
* @param {string} files - HTML string listing the deleted files.
|
||||
* @param {Array<{id: number}>} reallyDeleteData - Array of deleted file
|
||||
* objects with their IDs.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function reallyDeleteInformation(files, reallyDeleteData) {
|
||||
const filesStore = useFilesStore();
|
||||
@@ -513,8 +584,9 @@ export async function reallyDeleteInformation(files, reallyDeleteData) {
|
||||
}
|
||||
|
||||
/**
|
||||
* When user is leaving the acct mgmt page but hasn't finished editing,
|
||||
* we jump out this alert modal to remind her.
|
||||
* Shows a reminder modal when the user leaves the account management
|
||||
* page with unsaved edits.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function leaveAccountManagementToRemind(){
|
||||
const modalStore = useModalStore();
|
||||
|
||||
@@ -1,15 +1,30 @@
|
||||
// The Lucia project.
|
||||
// Copyright 2023-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
/** @module apiError Centralized API error handler with toast notifications. */
|
||||
|
||||
import { useLoadingStore } from '@/stores/loading';
|
||||
import {useToast} from 'vue-toast-notification';
|
||||
import 'vue-toast-notification/dist/theme-sugar.css';
|
||||
|
||||
// Delay loading and toast
|
||||
/**
|
||||
* Returns a promise that resolves after the specified milliseconds.
|
||||
* @param {number} [s=0] - The delay in milliseconds.
|
||||
* @returns {Promise<void>} A promise that resolves after the delay.
|
||||
*/
|
||||
const delay = (s = 0) => new Promise((resolve, reject) => setTimeout(resolve, s));
|
||||
|
||||
/**
|
||||
* API catch error function.
|
||||
* 401 errors are handled by the axios response interceptor in api/client.js.
|
||||
* @param {object} error 後端 ERROR
|
||||
* @param {string} toastMessage Toast 的提示文字
|
||||
* Handles API errors by showing a loading spinner followed by a toast
|
||||
* notification. 401 errors are handled by the axios response interceptor
|
||||
* in api/client.js.
|
||||
*
|
||||
* @param {Object} error - The error object from the API call.
|
||||
* @param {string} toastMessage - The message to display in the toast.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export default async function apiError(error, toastMessage) {
|
||||
const loading = useLoadingStore();
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
// The Lucia project.
|
||||
// Copyright 2023-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
/**
|
||||
* @module cytoscapeMap Cytoscape.js process map rendering with
|
||||
* interactive node/edge highlighting, tooltips, and position persistence.
|
||||
*/
|
||||
|
||||
import cytoscape from 'cytoscape';
|
||||
import spread from 'cytoscape-spread';
|
||||
import dagre from 'cytoscape-dagre';
|
||||
@@ -6,11 +17,20 @@ import cola from 'cytoscape-cola';
|
||||
import tippy from 'tippy.js';
|
||||
import 'tippy.js/dist/tippy.css';
|
||||
import { useMapPathStore } from '@/stores/mapPathStore';
|
||||
import { getTimeLabel } from '@/module/timeLabel.js'; // 時間格式轉換器
|
||||
import { getTimeLabel } from '@/module/timeLabel.js';
|
||||
import { useCytoscapeStore } from '@/stores/cytoscapeStore';
|
||||
import { SAVE_KEY_NAME } from '@/constants/constants.js';
|
||||
|
||||
const composeFreqTypeText = (baseText, dataLayerOption, optionValue) => { //sonar-qube
|
||||
/**
|
||||
* Composes display text for frequency-type data layer values.
|
||||
*
|
||||
* @param {string} baseText - The base label text prefix.
|
||||
* @param {string} dataLayerOption - The data layer option key
|
||||
* (e.g. "rel_freq" for relative frequency).
|
||||
* @param {number} optionValue - The numeric value to format.
|
||||
* @returns {string} The formatted text with the value appended.
|
||||
*/
|
||||
const composeFreqTypeText = (baseText, dataLayerOption, optionValue) => {
|
||||
let text = baseText;
|
||||
const textInt = dataLayerOption === 'rel_freq' ? baseText + optionValue * 100 + "%" : baseText + optionValue;
|
||||
const textFloat = dataLayerOption === 'rel_freq' ? baseText + (optionValue * 100).toFixed(2) + "%" : baseText + optionValue.toFixed(2);
|
||||
@@ -26,18 +46,23 @@ cytoscape.use(fcose);
|
||||
cytoscape.use(cola);
|
||||
|
||||
/**
|
||||
* @param {object} mapData processMapData | bpmnData,可選以上任一。
|
||||
* mapData:{
|
||||
* startId: 0,
|
||||
* endId: 1,
|
||||
* nodes: [],
|
||||
* edges: []
|
||||
* }
|
||||
* @param {string} dataLayerType DataLayer's type
|
||||
* @param {string} dataLayerOption DataLayer's options
|
||||
* @param {string} curve Curve's type
|
||||
* @param {string} graphId cytoscape's container
|
||||
* @return {cytoscape.Core} cy
|
||||
* Creates and configures a Cytoscape.js process map instance with
|
||||
* interactive features including node/edge highlighting, tooltips,
|
||||
* and position persistence via localStorage.
|
||||
*
|
||||
* @param {Object} mapData - The map data containing nodes and edges.
|
||||
* @param {number} mapData.startId - The start node ID.
|
||||
* @param {number} mapData.endId - The end node ID.
|
||||
* @param {Array} mapData.nodes - Array of node data objects.
|
||||
* @param {Array} mapData.edges - Array of edge data objects.
|
||||
* @param {string} dataLayerType - The data layer type ("freq" or "duration").
|
||||
* @param {string} dataLayerOption - The data layer option key
|
||||
* (e.g. "abs_freq", "rel_freq", "rel_duration").
|
||||
* @param {string} curveStyle - The edge curve style
|
||||
* ("unbundled-bezier" or "taxi").
|
||||
* @param {string} rank - The layout direction ("TB" or "LR").
|
||||
* @param {HTMLElement} graphId - The DOM container element for Cytoscape.
|
||||
* @returns {cytoscape.Core} The configured Cytoscape instance.
|
||||
*/
|
||||
export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, curveStyle, rank, graphId) {
|
||||
// 設定每個 node, edges 的顏色與樣式
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
// The Lucia project.
|
||||
// Copyright 2023-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
/**
|
||||
* @module cytoscapeMapTrace Cytoscape.js process map rendering for
|
||||
* individual trace visualization.
|
||||
*/
|
||||
|
||||
import cytoscape from 'cytoscape';
|
||||
import dagre from 'cytoscape-dagre';
|
||||
import tippy from 'tippy.js';
|
||||
@@ -6,19 +17,14 @@ import 'tippy.js/dist/tippy.css';
|
||||
cytoscape.use( dagre );
|
||||
|
||||
/**
|
||||
* draw processmap for trace
|
||||
* @param {array} nodes array of an object, it contains
|
||||
* data:{
|
||||
* backgroundColor: string
|
||||
* bordercolor: string
|
||||
* height: number
|
||||
* id: number
|
||||
* label: string
|
||||
* shape: string
|
||||
* width: number
|
||||
* }
|
||||
* @param {array} edges it's similar to nodes
|
||||
* @param {string} graphId graph Id
|
||||
* Creates a Cytoscape.js instance for rendering a single trace's
|
||||
* process map with left-to-right dagre layout and tooltips.
|
||||
*
|
||||
* @param {Array<Object>} nodes - Array of node data objects with
|
||||
* backgroundColor, bordercolor, height, id, label, shape, and width.
|
||||
* @param {Array<Object>} edges - Array of edge data objects.
|
||||
* @param {HTMLElement} graphId - The DOM container element for Cytoscape.
|
||||
* @returns {cytoscape.Core} The configured Cytoscape instance.
|
||||
*/
|
||||
export default function cytoscapeMapTrace(nodes, edges, graphId) {
|
||||
// create Cytoscape
|
||||
|
||||
@@ -1,25 +1,32 @@
|
||||
// sonar-qube replace regex method
|
||||
// The Lucia project.
|
||||
// Copyright 2023-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
/** @module numberLabel Number formatting with comma separators. */
|
||||
|
||||
/**
|
||||
* Formats an integer string by inserting commas every three digits.
|
||||
* @param {string} numberStr - A string of digits to format.
|
||||
* @returns {string} The formatted string with commas (e.g. "1,000,000").
|
||||
*/
|
||||
const formatNumberWithCommas = (numberStr) => {
|
||||
// 反轉字符串
|
||||
let reversedStr = numberStr.split('').reverse().join('');
|
||||
|
||||
// 將反轉後的字符串每 3 個字符為一組進行分割
|
||||
let groupedStr = reversedStr.match(/.{1,3}/g);
|
||||
|
||||
// 用逗號將這些組連接起來
|
||||
let joinedStr = groupedStr.join(',');
|
||||
|
||||
// 再次反轉回原來的順序
|
||||
let finalStr = joinedStr.split('').reverse().join('');
|
||||
|
||||
return finalStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 將數字轉換成帶有逗號格式(ex: 1000 -> 1,000)
|
||||
* 也可以改用原生 JS 方法 `.toLocaleString('en-US')`
|
||||
* @param {number} num 數字
|
||||
* @returns
|
||||
* Converts a number to a string with comma-separated thousands.
|
||||
*
|
||||
* Handles decimal numbers by formatting only the integer part.
|
||||
*
|
||||
* @param {number} num - The number to format.
|
||||
* @returns {string} The formatted number string (e.g. "1,234.56").
|
||||
*/
|
||||
export default function numberLabel(num) {
|
||||
let parts = num.toString().split('.');
|
||||
|
||||
@@ -1,14 +1,27 @@
|
||||
// The Lucia project.
|
||||
// Copyright 2023-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
/** @module setChartData Chart.js data transformation utilities. */
|
||||
|
||||
import getMoment from 'moment';
|
||||
|
||||
/**
|
||||
* 將後端的 chart data 加入最大、最小值,折線圖
|
||||
* @param {array} baseData 後端給的 10 個時間點
|
||||
* @param {number} xMax 2022-05-23T18:00:00
|
||||
* @param {number} xMin 2022-05-23T08:00:00
|
||||
* @param {boolean} isPercent 是否以百分比顯示
|
||||
* @param {number} yMax case
|
||||
* @param {number} yMin case
|
||||
* @returns {array} 可直接套入 chart.js 的 data
|
||||
* Extends backend chart data with extrapolated boundary points for
|
||||
* line charts. Prepends a calculated minimum and appends a calculated
|
||||
* maximum data point based on linear extrapolation.
|
||||
*
|
||||
* @param {Array<{x: number, y: number}>} baseData - The 10 data points
|
||||
* from the backend.
|
||||
* @param {number} xMax - The maximum x-axis timestamp.
|
||||
* @param {number} xMin - The minimum x-axis timestamp.
|
||||
* @param {boolean} isPercent - Whether values are percentages (0-1 range).
|
||||
* @param {number} yMax - The maximum y-axis value for clamping.
|
||||
* @param {number} yMin - The minimum y-axis value for clamping.
|
||||
* @returns {Array<{x: number, y: number}>} The extended data array
|
||||
* with boundary points.
|
||||
*/
|
||||
export function setLineChartData(baseData, xMax, xMin, isPercent, yMax, yMin) {
|
||||
// 將 baseData 轉換為包含 x 和 y 屬性的物件陣列
|
||||
@@ -35,11 +48,14 @@ export function setLineChartData(baseData, xMax, xMin, isPercent, yMax, yMin) {
|
||||
return data;
|
||||
};
|
||||
/**
|
||||
* 計算 y 軸最小值
|
||||
*
|
||||
* x 值為 0 ~ 11,
|
||||
* 將三的座標(ax, ay), (bx, by), (cx, cy)命名為 (a, b), (c, d), (e, f)
|
||||
* 最小值: (f - b)(c - a) = (e - a)(d - b),求 b = (ed - ad - fa - fc) / (e - c - a)
|
||||
* Extrapolates the Y-axis minimum boundary value using linear
|
||||
* interpolation from the first two data points.
|
||||
*
|
||||
* @param {Array<{x: number, y: number}>} baseData - The base data points.
|
||||
* @param {boolean} isPercent - Whether to clamp to 0-1 range.
|
||||
* @param {number} yMin - The minimum allowed Y value.
|
||||
* @param {number} yMax - The maximum allowed Y value.
|
||||
* @returns {number} The extrapolated and clamped Y minimum value.
|
||||
*/
|
||||
function calculateYMin(baseData, isPercent, yMin, yMax) {
|
||||
let a = 0;
|
||||
@@ -51,11 +67,14 @@ function calculateYMin(baseData, isPercent, yMin, yMax) {
|
||||
return clampValue(b, isPercent, yMin, yMax);
|
||||
};
|
||||
/**
|
||||
* 計算 y 軸最大值
|
||||
*
|
||||
* x 值為 9 ~ 11,
|
||||
* 將三的座標(ax, ay), (bx, by), (cx, cy)命名為 (a, b), (c, d), (e, f)
|
||||
* 最大值: (f - b)(e - c) = (f - d)(e - a),求 f = (be - bc -de + da) / (a - c)
|
||||
* Extrapolates the Y-axis maximum boundary value using linear
|
||||
* interpolation from the last two data points.
|
||||
*
|
||||
* @param {Array<{x: number, y: number}>} baseData - The base data points.
|
||||
* @param {boolean} isPercent - Whether to clamp to 0-1 range.
|
||||
* @param {number} yMin - The minimum allowed Y value.
|
||||
* @param {number} yMax - The maximum allowed Y value.
|
||||
* @returns {number} The extrapolated and clamped Y maximum value.
|
||||
*/
|
||||
function calculateYMax(baseData, isPercent, yMin, yMax) {
|
||||
let ma = 9;
|
||||
@@ -67,15 +86,14 @@ function calculateYMax(baseData, isPercent, yMin, yMax) {
|
||||
return clampValue(mf, isPercent, yMin, yMax);
|
||||
};
|
||||
/**
|
||||
* 將值限制在指定範圍內
|
||||
* 如果 isPercent 為 true,則將值限制在 0 到 1 之間
|
||||
* 否則,將值限制在 yMin 和 yMax 之間
|
||||
*
|
||||
* @param {number} value 需要被限制的值
|
||||
* @param {boolean} isPercent 如果為 true,表示值應該被限制在百分比範圍內(0 到 1)
|
||||
* @param {number} min 非百分比情況下的最小允許值(yMin)
|
||||
* @param {number} max 非百分比情況下的最大允許值(yMax)
|
||||
* @returns {number} 被限制在指定範圍內的值
|
||||
* Clamps a value within a specified range. If isPercent is true, the
|
||||
* value is clamped to [0, 1]; otherwise to [min, max].
|
||||
*
|
||||
* @param {number} value - The value to clamp.
|
||||
* @param {boolean} isPercent - Whether to use the percentage range [0, 1].
|
||||
* @param {number} min - The minimum bound (used when isPercent is false).
|
||||
* @param {number} max - The maximum bound (used when isPercent is false).
|
||||
* @returns {number} The clamped value.
|
||||
*/
|
||||
function clampValue(value, isPercent, min, max) {
|
||||
if (isPercent) {
|
||||
@@ -95,9 +113,13 @@ function clampValue(value, isPercent, min, max) {
|
||||
return value;
|
||||
};
|
||||
/**
|
||||
* 將後端的 chart data x 值轉換時間格式,長條圖
|
||||
* @param {array} baseData 後端給的 10 個時間點
|
||||
* @returns {array} 可直接套入 chart.js 的 data
|
||||
* Converts backend chart data timestamps to formatted date strings
|
||||
* for bar charts.
|
||||
*
|
||||
* @param {Array<{x: string, y: number}>} baseData - The data points from
|
||||
* the backend with ISO timestamp x values.
|
||||
* @returns {Array<{x: string, y: number}>} Data with x values formatted
|
||||
* as "YYYY/M/D hh:mm:ss".
|
||||
*/
|
||||
export function setBarChartData(baseData) {
|
||||
let data = baseData.map(i =>{
|
||||
@@ -109,11 +131,13 @@ export function setBarChartData(baseData) {
|
||||
return data
|
||||
};
|
||||
/**
|
||||
* 將一段時間均分成多段的時間點
|
||||
* @param {string} startTime 開始時間(ex: 434) 總秒數
|
||||
* @param {string} endTime 結束時間(ex: 259065) 總秒數
|
||||
* @param {number} amount 切分成多少段
|
||||
* @returns {array} 均分成多段的時間 array
|
||||
* Divides a time range into evenly spaced time points.
|
||||
*
|
||||
* @param {number} minTime - The start time in seconds.
|
||||
* @param {number} maxTime - The end time in seconds.
|
||||
* @param {number} amount - The number of time points to generate.
|
||||
* @returns {Array<number>} An array of evenly spaced, rounded time
|
||||
* values in seconds.
|
||||
*/
|
||||
export function timeRange(minTime, maxTime, amount) {
|
||||
// x 軸(時間軸)的範圍是最大-最小,從最小值按照 index 累加間距到最大值
|
||||
@@ -129,11 +153,13 @@ export function timeRange(minTime, maxTime, amount) {
|
||||
return timeRange;
|
||||
};
|
||||
/**
|
||||
* 將 y 軸的值分割成跟 x 時間軸一樣的等份,使用統計學 R 語言算出貝茲曲線攻勢
|
||||
* @param {array} data 切分成多少段
|
||||
* @param {number} yAmount 切分成多少段
|
||||
* @param {number} yMax y 最大值
|
||||
* @returns {array} 均分成多段的時間 array
|
||||
* Generates smooth Y-axis values using cubic Bezier interpolation
|
||||
* to produce evenly spaced ticks matching the X-axis divisions.
|
||||
*
|
||||
* @param {Array<{x: number, y: number}>} data - The source data points.
|
||||
* @param {number} yAmount - The desired number of Y-axis ticks.
|
||||
* @param {number} yMax - The maximum Y value (unused, kept for API).
|
||||
* @returns {Array<number>} An array of interpolated Y values.
|
||||
*/
|
||||
export function yTimeRange(data, yAmount, yMax) {
|
||||
const yRange = [];
|
||||
@@ -171,10 +197,11 @@ export function yTimeRange(data, yAmount, yMax) {
|
||||
return yRange;
|
||||
};
|
||||
/**
|
||||
* 找出選擇的時間點的 index
|
||||
* @param {array} data xVal
|
||||
* @param {number} xValue x 值
|
||||
* @returns {numver} x index
|
||||
* Finds the index of the closest value in an array to the given target.
|
||||
*
|
||||
* @param {Array<number>} data - The array of values to search.
|
||||
* @param {number} xValue - The target value to find the closest match for.
|
||||
* @returns {number} The index of the closest value in the array.
|
||||
*/
|
||||
export function getXIndex(data, xValue) {
|
||||
let closestIndex = xValue; // 假定第一个元素的索引是 0
|
||||
@@ -192,9 +219,11 @@ export function getXIndex(data, xValue) {
|
||||
return closestIndex;
|
||||
};
|
||||
/**
|
||||
* 有 dhms 表達的時間單位
|
||||
* @param {number} seconds 總秒數
|
||||
* @returns {string}
|
||||
* Formats a duration in seconds to a compact string with d/h/m/s units.
|
||||
*
|
||||
* @param {number} seconds - The total number of seconds.
|
||||
* @returns {string|null} The formatted string (e.g. "2d3h15m30s"),
|
||||
* or null if the input is NaN.
|
||||
*/
|
||||
export function formatTime(seconds) {
|
||||
if(!isNaN(seconds)) {
|
||||
@@ -221,9 +250,10 @@ export function formatTime(seconds) {
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 只顯示最大的兩個單位
|
||||
* @param {array} times 時間
|
||||
* @returns {array}
|
||||
* Truncates each time string to show only the two largest time units.
|
||||
*
|
||||
* @param {Array<string>} times - Array of duration strings (e.g. "2d3h15m30s").
|
||||
* @returns {Array<string>} Array of truncated strings (e.g. "2d 3h").
|
||||
*/
|
||||
export function formatMaxTwo(times) {
|
||||
const formattedTimes = [];
|
||||
|
||||
@@ -1,19 +1,29 @@
|
||||
// The Lucia project.
|
||||
// Copyright 2023-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
/** @module shortScaleNumber Short-scale number formatting. */
|
||||
|
||||
/**
|
||||
* 數量單位轉換,輸出 k m b t 數值。
|
||||
* @param {number} number 總秒數
|
||||
* @returns {string}
|
||||
* Converts a number to a short-scale abbreviated string with a suffix
|
||||
* (k for thousands, m for millions, b for billions, t for trillions).
|
||||
*
|
||||
* Values are rounded up to one decimal place.
|
||||
*
|
||||
* @param {number} number - The number to abbreviate.
|
||||
* @returns {string} The abbreviated string (e.g. "1.5k ", "2.3m ").
|
||||
*/
|
||||
export default function shortScaleNumber(number) {
|
||||
const abbreviations = ["", "k", "m", "b", "t"];
|
||||
let index = 0;
|
||||
let num = number;
|
||||
|
||||
// 確保在轉換數字時,不會超出索引範圍。如果 index 已經達到了最後一個單位縮寫(t,兆),那麼我們就不再進行轉換,以免超出數組的範圍。
|
||||
while (num >= 1000 && index < abbreviations.length - 1) {
|
||||
num /= 1000;
|
||||
index++;
|
||||
}
|
||||
// 使用 Math.ceil 來無條件進位
|
||||
num = Math.ceil(num * 10) / 10;
|
||||
return num + abbreviations[index] + " " ;
|
||||
}
|
||||
|
||||
@@ -1,43 +1,52 @@
|
||||
// The Lucia project.
|
||||
// Copyright 2023-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
/** @module sortNumEngZhtw Sorting for mixed numeric/text data. */
|
||||
|
||||
/**
|
||||
* 數字、英文、中文,排序
|
||||
* @param {array} data List
|
||||
* @returns
|
||||
* Sorts an array of mixed numeric and text values in place.
|
||||
*
|
||||
* Numbers come first (ascending), followed by text sorted by
|
||||
* Traditional Chinese locale (zh-Hant-TW).
|
||||
*
|
||||
* @param {Array<string|number>} data - The array to sort.
|
||||
* @returns {Array<string|number>} The sorted array (same reference).
|
||||
*/
|
||||
export function sortNumEngZhtw(data) {
|
||||
return data.sort((a, b) => {
|
||||
// 檢查兩個值是否都是數字
|
||||
const isANumber = !isNaN(parseFloat(a)) && isFinite(a);
|
||||
const isBNumber = !isNaN(parseFloat(b)) && isFinite(b);
|
||||
|
||||
// 如果兩個值都是數字,直接比較大小
|
||||
if (isANumber && isBNumber) return parseFloat(a) - parseFloat(b);
|
||||
|
||||
// 如果其中一個值是數字,將數字視為最小,排在前面
|
||||
if (isANumber) return -1;
|
||||
if (isBNumber) return 1;
|
||||
|
||||
// 其他情況下,使用 localeCompare 方法進行中文排序
|
||||
return a.localeCompare(b, 'zh-Hant-TW', { sensitivity: 'accent' });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 數字、英文、中文,給 Filter Table 排序
|
||||
* @param {string} a label
|
||||
* @param {string} b label
|
||||
* @returns
|
||||
* Comparator function for sorting mixed numeric and text values.
|
||||
*
|
||||
* Suitable for use as an Array.sort() callback. Numbers sort before
|
||||
* text, and text is sorted by Traditional Chinese locale.
|
||||
*
|
||||
* @param {string|number} a - First value to compare.
|
||||
* @param {string|number} b - Second value to compare.
|
||||
* @returns {number} Negative if a < b, positive if a > b, zero if equal.
|
||||
*/
|
||||
export function sortNumEngZhtwForFilter(a, b) {
|
||||
// 檢查兩個值是否都是數字
|
||||
const isANumber = !isNaN(parseFloat(a)) && isFinite(a);
|
||||
const isBNumber = !isNaN(parseFloat(b)) && isFinite(b);
|
||||
|
||||
// 如果兩個值都是數字,直接比較大小
|
||||
if (isANumber && isBNumber) return parseFloat(a) - parseFloat(b);
|
||||
|
||||
// 如果其中一個值是數字,將數字視為最小,排在前面
|
||||
if (isANumber) return -1;
|
||||
if (isBNumber) return 1;
|
||||
|
||||
// 其他情況下,使用 localeCompare 方法進行中文排序
|
||||
return a.localeCompare(b, 'zh-Hant-TW', { sensitivity: 'accent' });
|
||||
}
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
// The Lucia project.
|
||||
// Copyright 2023-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
/** @module timeLabel Time formatting and chart axis tick utilities. */
|
||||
|
||||
import moment from 'moment';
|
||||
|
||||
/** @constant {number} Number of decimal places for formatted time values. */
|
||||
const TOFIXED_DEICMAL = 1;
|
||||
|
||||
/**
|
||||
* 若想要有等差的Y軸刻度,則必須確保index是乘以一個共通的被乘數
|
||||
* 此被乘數就是 stepSize
|
||||
* @param {number} maxTimeInSecond
|
||||
* @returns {object} {resultStepSize, unitToUse}
|
||||
* Calculates the step size and time unit for evenly spaced Y-axis ticks.
|
||||
*
|
||||
* @param {number} maxTimeInSecond - The maximum time value in seconds.
|
||||
* @param {number} numOfParts - The number of equal parts to divide into.
|
||||
* @returns {{resultStepSize: number, unitToUse: string}} The step size
|
||||
* and the time unit character ("d", "h", "m", or "s").
|
||||
*/
|
||||
export const getStepSizeOfYTicks = (maxTimeInSecond, numOfParts) => {
|
||||
const {unitToUse, timeValue} = getTimeUnitAndValueToUse(maxTimeInSecond);
|
||||
@@ -16,9 +28,12 @@ export const getStepSizeOfYTicks = (maxTimeInSecond, numOfParts) => {
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert second to possibly day or hour or mins.
|
||||
* @param {number} secondToDecide
|
||||
* @returns {object} {unitToUse, timeValue} where timeValue is measured in unitToUse
|
||||
* Determines the most appropriate time unit for a given number of seconds
|
||||
* and converts the value to that unit.
|
||||
*
|
||||
* @param {number} secondToDecide - The time in seconds to evaluate.
|
||||
* @returns {{unitToUse: string, timeValue: number}} The chosen unit
|
||||
* character ("d", "h", "m", or "s") and the converted time value.
|
||||
*/
|
||||
const getTimeUnitAndValueToUse = (secondToDecide) => {
|
||||
const day = 24 * 60 * 60;
|
||||
@@ -52,12 +67,13 @@ const getTimeUnitAndValueToUse = (secondToDecide) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* For the display of PrimeVue chart.
|
||||
* According to the max time of the data given,
|
||||
* split the Y axis into equal part as ticks.
|
||||
* @param {number} stepSize stepSize of Y axis
|
||||
* @param {number} index index of current visiting tick on Y axis
|
||||
* @param {string} unitToUse time unit to display; "dhms" usually
|
||||
* Formats a Y-axis tick value by multiplying the step size by the
|
||||
* tick index and appending the time unit suffix.
|
||||
*
|
||||
* @param {number} stepSize - The step size between Y-axis ticks.
|
||||
* @param {number} index - The zero-based index of the current tick.
|
||||
* @param {string} unitToUse - The time unit suffix ("d", "h", "m", or "s").
|
||||
* @returns {string} The formatted tick label (e.g. "2.5h").
|
||||
*/
|
||||
export function getYTicksByIndex(stepSize, index, unitToUse){
|
||||
const rawStepsizeMultIndex = (stepSize * index).toString();
|
||||
@@ -67,10 +83,12 @@ export function getYTicksByIndex(stepSize, index, unitToUse){
|
||||
};
|
||||
|
||||
/**
|
||||
* 將秒數轉換成帶有時間單位的格式
|
||||
* @param {number} second 總秒數
|
||||
* @param {number} fixedNumber 小數點後幾位
|
||||
* @returns {string} 轉換完的格式(ex: 1 day, 6.8 hrs)
|
||||
* Converts seconds to a human-readable time string with full unit names.
|
||||
*
|
||||
* @param {number} second - The total number of seconds.
|
||||
* @param {number} [fixedNumber=0] - Number of decimal places.
|
||||
* @returns {string} The formatted string (e.g. "1 days", "6.8 hrs",
|
||||
* "30 mins", "5 sec").
|
||||
*/
|
||||
export function getTimeLabel(second, fixedNumber = 0) {
|
||||
const day = 24 * 60 * 60;
|
||||
@@ -99,15 +117,14 @@ export function getTimeLabel(second, fixedNumber = 0) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 將秒數轉換成帶有縮寫時間單位的格式
|
||||
* @param {number} second 總秒數
|
||||
* @param {number} fixedNumber 小數點後幾位
|
||||
* @returns {string} 轉換完的格式(ex: 1 d, 6.8 h)
|
||||
* 如果秒數(second)大於等於一天(86400 秒),返回以天為單位的時間,帶有 d 標誌。
|
||||
* 如果秒數小於一天但大於等於一小時(3600 秒),返回以小時為單位的時間,帶有 h 標誌。
|
||||
* 如果秒數小於一小時但大於等於一分鐘(60 秒),返回以分鐘為單位的時間,帶有 m 標誌。
|
||||
* 如果秒數等於 0 秒,返回 "0s"。
|
||||
* 如果秒數小於 60 秒但大於 0 秒,返回原始秒數,帶有 s 標誌。
|
||||
* Converts seconds to a compact time string with abbreviated unit suffixes.
|
||||
*
|
||||
* Automatically selects the most appropriate unit: "d" for days,
|
||||
* "h" for hours, "m" for minutes, or "s" for seconds.
|
||||
*
|
||||
* @param {number} second - The total number of seconds.
|
||||
* @param {number} [fixedNumber=0] - Number of decimal places.
|
||||
* @returns {string} The formatted string (e.g. "1d", "6.8h", "30m", "5s").
|
||||
*/
|
||||
export function simpleTimeLabel(second, fixedNumber = 0) {
|
||||
const day = 24 * 60 * 60;
|
||||
@@ -132,14 +149,16 @@ export function simpleTimeLabel(second, fixedNumber = 0) {
|
||||
return second + "s";
|
||||
}
|
||||
/**
|
||||
* 考慮到每包資料內的時間範圍有的大有的小,
|
||||
* 需要根據不同時間範圍級別的資料刻劃不同的Y軸座標,
|
||||
* 因此需要根據最大的時間值來決定Y軸刻度怎麼切分。
|
||||
* 跟隨最大值的時間單位,將秒數轉換成帶有縮寫時間單位的格式
|
||||
* @param {number} second 要轉換單位的秒數
|
||||
* @param {number} max 該 data 中的最大值
|
||||
* @param {number} fixedNumber 小數點後幾位
|
||||
* @returns {string} 轉換完的格式(ex: 1 d, 6.8 h)
|
||||
* Converts seconds to a time string using the same unit as the maximum
|
||||
* value in the dataset, ensuring consistent Y-axis labels.
|
||||
*
|
||||
* The unit is determined by the max value: days if max > 1 day,
|
||||
* hours if max > 1 hour, minutes if max > 1 minute, else seconds.
|
||||
*
|
||||
* @param {number} second - The time value in seconds to format.
|
||||
* @param {number} max - The maximum time value in the dataset (seconds).
|
||||
* @param {number} [fixedNumber=0] - Number of decimal places.
|
||||
* @returns {string} The formatted string (e.g. "1.5d", "6.8h").
|
||||
*/
|
||||
export function followTimeLabel(second, max, fixedNumber = 0) {
|
||||
const day = 24 * 60 * 60;
|
||||
@@ -192,15 +211,15 @@ export function followTimeLabel(second, max, fixedNumber = 0) {
|
||||
|
||||
|
||||
/**
|
||||
* Select an appropriate time format based on the time difference.
|
||||
* 根據時間差距選擇適合的時間格式
|
||||
* 舉例 月: 2022/06
|
||||
* 舉例 日: 06/06
|
||||
* 舉例 時: 03/05 12:00
|
||||
* 舉例 分: 03/05 12:15
|
||||
* 舉例 秒: 09:05:32
|
||||
* @param {Array<number>} timestamps - An array of timestamps.
|
||||
* @returns {string} - The suitable time format string.
|
||||
* Selects an appropriate moment.js date format string based on the
|
||||
* difference between the minimum and maximum timestamps.
|
||||
*
|
||||
* Returns "HH:mm:ss" for sub-minute ranges, "MM/DD HH:mm" for
|
||||
* sub-day ranges, and "YYYY/MM/DD" for longer ranges.
|
||||
*
|
||||
* @param {number} minTimeStamp - The minimum timestamp in seconds.
|
||||
* @param {number} maxTimeStamp - The maximum timestamp in seconds.
|
||||
* @returns {string} A moment.js format string.
|
||||
*/
|
||||
export const setTimeStringFormatBaseOnTimeDifference = (minTimeStamp, maxTimeStamp) => {
|
||||
const timeDifferenceInSeconds = maxTimeStamp - minTimeStamp;
|
||||
@@ -222,11 +241,13 @@ export const setTimeStringFormatBaseOnTimeDifference = (minTimeStamp, maxTimeSta
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts an array of Unix timestamps to formatted date strings based on the
|
||||
* specified format for use as axis ticks.
|
||||
* 根據指定的格式將 Unix 時間戳數組轉換為軸標籤的格式化日期字符串。
|
||||
* @param {Array<number>} timeStampArr
|
||||
* @param {string} timeFormat For example, 'MM/DD'
|
||||
* Converts an array of Unix timestamps to formatted date strings for
|
||||
* use as chart axis tick labels.
|
||||
*
|
||||
* @param {Array<number>} timeStampArr - Array of Unix timestamps.
|
||||
* @param {string} timeFormat - A moment.js format string (e.g. "MM/DD").
|
||||
* @returns {Array<string>|undefined} Array of formatted date strings,
|
||||
* or undefined if timeStampArr is falsy.
|
||||
*/
|
||||
export const mapTimestampToAxisTicksByFormat = (timeStampArr, timeFormat) => {
|
||||
if (timeStampArr) {
|
||||
|
||||
Reference in New Issue
Block a user