Translate all Chinese comments and strings to English

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 20:03:19 +08:00
parent 7d5918837b
commit 1d621bf304
57 changed files with 499 additions and 499 deletions

View File

@@ -34,12 +34,12 @@ 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);
// 判斷是否為整數,若非整數要取小數點後面兩個值。
// Check if the value is an integer; if not, round to 2 decimal places.
text = Math.trunc(optionValue) === optionValue ? textInt : textFloat;
return text;
};
// 註冊布局演算法
// Register layout algorithms
cytoscape.use(dagre);
cytoscape.use(spread);
cytoscape.use(fcose);
@@ -65,7 +65,7 @@ cytoscape.use(cola);
* @returns {cytoscape.Core} The configured Cytoscape instance.
*/
export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, curveStyle, rank, graphId) {
// 設定每個 node, edges 的顏色與樣式
// Set the color and style for each node and edge
let nodes = mapData.nodes;
let edges = mapData.edges;
@@ -73,15 +73,15 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
let cy = cytoscape({
container: graphId,
elements: {
nodes: nodes, //nodes, // 節點的資料
edges: edges, //edges, // 關係線的資料
nodes: nodes, // Node data
edges: edges, // Edge data
},
layout: {
name: 'dagre',
rankDir: rank, // 直向 TB | 橫向 LR, 'cytoscape-dagre' 套件裡的變數
rankDir: rank, // Vertical TB | Horizontal LR, variable from 'cytoscape-dagre' plugin
},
style: [
// 點擊 node 後改變的樣式
// Style changes when a node is selected
{
selector: 'node:selected',
style: {
@@ -89,27 +89,27 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
'border-width': '3',
},
},
// node 節點的樣式
// Node styling
{
selector: 'node',
style: {
'label':
function (node) { // 節點要顯示的文字
// node.data(this.dataLayerType+"."+this.dataLayerOption) 為原先陣列 node.data.key.value
function (node) { // Text to display on the node
// node.data(this.dataLayerType+"."+this.dataLayerOption) accesses the original array value at node.data.key.value
let optionValue = node.data(`${dataLayerType}.${dataLayerOption}`);
let text = '';
const STRING_LIMIT = 8;
if (node.data('label').length > STRING_LIMIT) {
// 若文字超過 STRING_LIMIT長度則字尾巴要加「...」style 要換兩行(\n 換行符號)
// 使用 data() 是因為在 cytoscape 中從陣列轉為 function
// If text exceeds STRING_LIMIT, append "..." and add line breaks (\n)
// Using data() because Cytoscape converts array data to function calls
text = `${node.data('label').substr(0, STRING_LIMIT)}...\n\n`;
} else { // 補空白直到撐寬label的寬度這是為了統一所有label的寬度
} else { // Pad with spaces to match the label width for consistent sizing
text = `${node.data('label').padEnd(STRING_LIMIT, ' ')}\n\n`
}
// element activity 歸類在 default所以要先判斷 node 是否為 activity 才裝入文字。
// 可使用 parseInt(整數) parseFloat(浮點數) 將字串轉為數字
// Relative 要轉為百分比 %
// In elements, activity is categorized as default, so check if the node is an activity before adding text.
// Use parseInt (integer) or parseFloat (float) to convert strings to numbers.
// Relative values need to be converted to percentages (%)
if (node.data('type') === 'activity') {
let textDurRel;
let timeLabelInt;
@@ -120,14 +120,14 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
case 'freq': // Frequency
text = composeFreqTypeText(text, dataLayerOption, optionValue);
break;
case 'duration': // Duration 除了 Relative 為百分比 % ,其他要轉變時間單位。
case 'duration': // Duration: Relative is percentage %, others need time unit conversion.
// Relative %
textDurRel = text + (optionValue * 100).toFixed(2) + "%";
// Timelabel
timeLabelInt = text + getTimeLabel(optionValue);
timeLabelFloat = text + getTimeLabel(optionValue.toFixed(2));
// 判斷是否為整數,若非整數要取小數點後面兩個值。
// Check if the value is an integer; if not, round to 2 decimal places.
textTimeLabel = Math.trunc(optionValue) === optionValue ? timeLabelInt : timeLabelFloat;
text = dataLayerOption === 'rel_duration' ? textDurRel : textTimeLabel;
@@ -145,12 +145,12 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
return node.data('type') === 'activity' ? '1' : '2';
},
'background-image': 'data(nodeImageUrl)',
'background-opacity': 'data(backgroundOpacity)', // 透明背景
'border-opacity': 'data(borderOpacity)', // 透明邊框
'background-opacity': 'data(backgroundOpacity)', // Transparent background
'border-opacity': 'data(borderOpacity)', // Transparent border
'shape': 'data(shape)',
'text-wrap': 'wrap',
'text-max-width': 'data(width)', // 在 div 內換行
'text-overflow-wrap': 'anywhere', // 在 div 內換行
'text-max-width': 'data(width)', // Wrap text within the node
'text-overflow-wrap': 'anywhere', // Allow wrapping at any position
'text-margin-x': function (node) {
return node.data('type') === 'activity' ? -5 : 0;
},
@@ -173,11 +173,11 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
},
},
},
// edge 關係線的樣式
// Edge styling
{
selector: 'edge',
style: {
'content': function (edge) { // 關係線顯示的文字
'content': function (edge) { // Text displayed on the edge
let optionValue = edge.data(`${dataLayerType}.${dataLayerOption}`);
let result = '';
let edgeInt;
@@ -193,11 +193,11 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
edgeInt = dataLayerOption === 'rel_freq' ? optionValue * 100 + "%" : optionValue;
edgeFloat = dataLayerOption === 'rel_freq' ? (optionValue * 100).toFixed(2) + "%" : optionValue.toFixed(2);
// 判斷是否為整數,若非整數要取小數點後面兩個值。
// Check if the value is an integer; if not, round to 2 decimal places.
result = Math.trunc(optionValue) === optionValue ? edgeInt : edgeFloat;
break;
case 'duration': // Duration 除了 Relative 為百分比 % ,其他要轉變時間單位。
case 'duration': // Duration: Relative is percentage %, others need time unit conversion.
// Relative %
edgeDurRel = (optionValue * 100).toFixed(2) + "%";
// Timelabel
@@ -211,10 +211,10 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
return result;
},
'curve-style': curveStyle, // unbundled-bezier | taxi
'overlay-opacity': 0, // overlay-opacity設置為0移除灰色陰影
'target-arrow-shape': 'triangle', // 指向目標的箭頭形狀: 三角形
'overlay-opacity': 0, // Set overlay-opacity to 0 to remove the gray shadow
'target-arrow-shape': 'triangle', // Arrow shape pointing to target: triangle
'color': 'gray', //#0066cc
//'control-point-step-size':100, // 從點到點的垂直線,指定貝茲取線邊緣間的距離
//'control-point-step-size':100, // Distance between Bezier curve control points
'width': 'data(lineWidth)',
'line-style': 'data(edgeStyle)',
"text-margin-y": "0.7rem",
@@ -237,30 +237,30 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
'overlay-padding': '5px',
},
}, {
selector: 'edge[source = target]', // 選擇 self-loop 的邊
selector: 'edge[source = target]', // Select self-loop edges
style: {
'loop-direction': '0deg', // 控制 loop 的方向
'loop-sweep': '-60deg', // 控制 loop 的弧度,這裡可以調整弧度以改變大小
'control-point-step-size': 50 // 控制 loop 的半徑大小,增加這個值可以增大 loop
'loop-direction': '0deg', // Control the loop direction
'loop-sweep': '-60deg', // Control the loop arc; adjust to change size
'control-point-step-size': 50 // Control the loop radius; increase to enlarge the loop
}
},
],
});
// 按下線條,線條及線條上數字有光暈效果
// When an edge is clicked, apply glow effect to the edge and its label
cy.on('tap', 'edge', function (event) {
cy.edges().removeClass('highlight-edge');
event.target.addClass('highlight-edge');
});
// 按下節點光暈效果與鄰邊光暈效果
// When a node is clicked, apply glow effect to the node and adjacent edges
cy.on('tap, mousedown', 'node', function (event) {
useMapPathStore().onNodeClickHighlightEdges(event.target);
});
// 按下線段光暈效果與兩端點光暈效果
// When an edge is clicked, apply glow effect to the edge and both endpoint nodes
cy.on('tap, mousedown', 'edge', function (event) {
useMapPathStore().onEdgeClickHighlightNodes(event.target);
});
@@ -288,12 +288,12 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
const cytoscapeStore = useCytoscapeStore();
cy.ready(() => {
cytoscapeStore.loadPositionsFromStorage(rank);
// 判斷localStorage是否儲存過拜訪資訊
// 若曾經儲存過拜訪後的座標位置則restore位置來渲染出來
// Check if localStorage has previously saved visit data.
// If saved node positions exist, restore them for rendering.
if (localStorage.getItem(SAVE_KEY_NAME) && JSON.parse(localStorage.getItem(SAVE_KEY_NAME))) {
const allGraphsRemembered = JSON.parse(localStorage.getItem(SAVE_KEY_NAME));
const currentGraphNodesRemembered =
allGraphsRemembered[cytoscapeStore.currentGraphId] ? allGraphsRemembered[cytoscapeStore.currentGraphId][rank] : null; // 可能是undefined
allGraphsRemembered[cytoscapeStore.currentGraphId] ? allGraphsRemembered[cytoscapeStore.currentGraphId][rank] : null; // May be undefined
if (currentGraphNodesRemembered) {
currentGraphNodesRemembered.forEach(nodeRemembered => {
const nodeToDecide = cy.getElementById(nodeRemembered.id);
@@ -303,14 +303,14 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
});
}
}
//存下此刻剛進入畫面時當前所有節點的座標位置
// Save the current positions of all nodes when the view is first entered
const allNodes = cy.nodes();
allNodes.forEach(nodeFirstlySave => {
cytoscapeStore.saveNodePosition(nodeFirstlySave.id(), nodeFirstlySave.position(), rank);
});
// 在改變節點位置後,盡可能地記錄節點線條的位置情報
// rank 代表現在使用者切換的是水平方向還是垂直方向模式
// After node positions change, save the updated positions.
// rank represents whether the user is in horizontal or vertical layout mode.
cy.on('dragfree', 'node', (event) => {
const nodeToSave = event.target;
cytoscapeStore.saveNodePosition(nodeToSave.id(), nodeToSave.position(), rank);