ツールの紹介
- htmlファイルで完結しているため、単純にブラウザ起動で使える
- 「csvインポート」でcsvデータを読み取る
※小規模なら手作業でもできるが、大規模なら設計書や仕様書からこうした情報を収集するツールが別途必要そう - 主な編集操作は以下の通り
3-1. 機能単位でパネルになっていて移動できる。
3-2. 機能名部分をクリックすると項目部分の折り畳みと展開を切り替えられる
3-3. 項目名のチェックボックスをクリックすると関連のある項目を探索し全てチェック状態にする
3-4. 矢印の描画は基本的には項目対項目。関連先に該当項目が見つからない場合は機能名に線をつなげるようにした。
3-5. コメント機能。パネルに右クリックでコメントを付けることができる。
3-6.描画モード。
無効:線を描画しない。
対象指定:描画対象に指定したパネルのみ描画する。パネル左上にチェックボックスが出てくる。
全体:すべての線を描画する。
3-7.コピー。機能名とクラス名部分のアイコンで名称をコピー。
3-8.詳細表示。虫眼鏡アイコンで、パネルのすべての情報を表示する。
3-9.選択ツール。
画面下部の「△」を押下で展開する。
各種フィルタや、項目をまたがる検索が可能。
一覧に出力した情報に対しては一部操作の一括実施が可能。
Goボタンでは、対象のパネル位置まで移動する。
- 「編集エクスポート」ではパネルの配置やチェック状態を保存した結果をjson形式で出力する
- 「編集インポート」では上記エクスポートを取り込める。
少しずつ機能を追加したため、かなり冗長なコードになってしまった。
余力があったら改善したい。
データ(csv)
機能種別,クラス名,機能名,項目種別,項目名,リソースの機能種別,リソース,取得元項目,引渡し
画面,顧客管理系画面,顧客検索画面,-,顧客ID,-,,,
画面,顧客管理系画面,顧客検索画面,-,姓,-,,,
画面,顧客管理系画面,顧客検索画面,-,名,-,,,
画面,顧客管理系画面,顧客検索画面,-,電話番号,-,,,
画面,顧客管理系画面,顧客詳細画面,-,顧客ID,画面,顧客検索画面,顧客ID,
画面,顧客管理系画面,顧客詳細画面,-,顧客ID,API,顧客取得,,顧客ID
画面,顧客管理系画面,顧客詳細画面,-,あああ,API,顧客取得,,ああ
画面,顧客管理系画面,顧客詳細画面,-,あああ,画面,顧客検索画面,ああああ,
画面,顧客管理系画面,顧客詳細画面,-,姓,API,顧客取得,姓,
画面,顧客管理系画面,顧客詳細画面,-,名,API,顧客取得,名,
画面,顧客管理系画面,顧客詳細画面,-,電話番号,API,顧客取得,電話番号,
画面,顧客管理系画面,顧客詳細画面,-,生年月日,API,顧客取得,生年月日,
画面,顧客管理系画面,顧客詳細画面,-,住所,API,顧客取得,住所,
API,顧客情報API,顧客取得,入力,顧客ID,,,,
API,顧客情報API,顧客取得,出力,姓,テーブル,顧客情報,姓,
API,顧客情報API,顧客取得,出力,名,テーブル,顧客情報,名,
API,顧客情報API,顧客取得,出力,電話番号,テーブル,顧客情報,電話番号,
API,顧客情報API,顧客取得,出力,生年月日,テーブル,顧客情報,生年月日,
API,顧客情報API,顧客取得,出力,住所,テーブル,顧客情報,住所,
テーブル,オブジェクト,顧客情報,-,姓,-,-,-,-
テーブル,オブジェクト,顧客情報,-,名,-,-,-,-
テーブル,オブジェクト,顧客情報,-,電話番号,-,-,-,-
テーブル,オブジェクト,顧客情報,-,生年月日,-,-,-,-
テーブル,オブジェクト,顧客情報1,-,住所,-,-,-,-
テーブル,オブジェクト,顧客情報2,-,住所,-,-,-,-
テーブル,オブジェクト,顧客情報3,-,住所,-,-,-,-
コード
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>機能依存関係図</title>
<style>
body {
margin: 0;
overflow: auto;
}
.panel {
border: 1px solid #ccc;
margin: 10px;
padding: 10px;
width: 300px;
position: absolute;
cursor: move;
}
.panel-header {
display: flex;
justify-content: space-between;
align-items: center;
font-weight: bold;
background-color: #f1f1f1;
padding: 5px;
}
.panel-class {
font-size: 0.8em;
color: #555;
margin-top: 5px;
}
.toggle-button {
cursor: pointer;
background-color: transparent;
border: none;
font-size: 1.2em;
}
.panel-content {
display: none;
padding: 5px;
}
.control-button, .import-button, .export-button, .import-json-button, .comment-toggle-button {
margin: 10px;
padding: 10px;
cursor: pointer;
color: white;
border: none;
border-radius: 5px;
z-index: 1000;
}
.control-button {
background-color: #4CAF50;
}
.import-button {
background-color: #2196F3;
}
.export-button, .import-json-button {
background-color: #FFA500;
}
.comment-toggle-button {
background-color: #808080;
}
.screen-panel {
background-color: #add8e6;
}
.api-panel {
background-color: #f08080;
}
.batch-panel {
background-color: #ffcc99;
}
.table-panel {
background-color: #90ee90;
}
.external-panel {
background-color: #dda0dd;
}
.chart-container {
position: relative;
display: flex;
justify-content: space-around;
align-items: flex-start;
transform-origin: 0 0;
width: 10000px;
height: 10000px;
border-top: 2px solid #000;
}
.arrow {
position: absolute;
stroke: #000;
fill: none;
marker-end: url(#arrowhead);
}
.highlighted-item {
color: red;
}
.highlighted-arrow {
stroke: red;
}
.header-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
background-color: #fff;
z-index: 1000;
display: flex;
align-items: center;
padding: 10px;
border-bottom: 2px solid #000;
}
.input-field {
margin-left: 10px;
padding: 5px;
}
.comment-bubble {
position: absolute;
background-color: #fff;
border: 1px solid #ccc;
padding: 5px;
display: none;
z-index: 1001;
}
.toggle-active {
background-color: #008000;
}
.clipboard-icon {
margin-left: 5px;
cursor: pointer;
font-size: 1em;
}
.selection-box {
position: absolute;
border: 2px dashed red;
background-color: rgba(173, 216, 230, 0.3);
pointer-events: none;
}
.bookMarked-panel {
border: 5px solid rgb(255, 179, 0);
}
.selected-panel {
border: 6px solid red;
}
* テーブルヘッダの固定 */
thead {
position: sticky;
top: 0;
background-color: #fff;
z-index: 1;
}
/* 詳細ポップアップのスクロール領域 */
#detailPopup div {
max-height: 300px; /* 最大高さ */
overflow-y: auto; /* コンテンツがはみ出たらスクロール */
}
</style>
</head>
<body>
<div class="header-container">
<div>
<button id="toggleAll" class="control-button">すべて展開/閉じる</button>
<button id="importCsv" class="import-button">CSVインポート</button>
<button id="exportJson" class="export-button">編集エクスポート</button>
<button id="importJson" class="import-json-button">編集インポート</button>
<button id="toggleComments" class="comment-toggle-button">コメント表示</button>
<label for="drawModeSelect">描画モード</label>
<select id="drawModeSelect" class="input-field">
<option value="無効">無効</option>
<option value="全体">全体</option>
<option value="対象指定">対象指定</option>
</select>
<label for="widthInput">描画領域X(px)</label>
<input type="number" id="widthInput" class="input-field" value="10000">
<label for="heightInput">描画領域Y(px)</label>
<input type="number" id="heightInput" class="input-field" value="10000">
<button id="setDimensions" class="input-field">描画領域設定</button>
</div>
</div>
<div id="contents">
<div id="chart" class="chart-container"></div>
<svg id="arrows" width="100%" height="100%" style="position:absolute; top:0; left:0; pointer-events:none;">
<defs>
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto" markerUnits="strokeWidth">
<polygon points="0 0, 10 3.5, 0 7" />
</marker>
</defs>
</svg>
</div>
<!-- 詳細表示用ポップアップ -->
<div id="detailPopup" style="display: none; position: absolute; width: 600px; max-height: 400px; background-color: #fff; border: 1px solid #ccc; padding: 10px; z-index: 1002;">
<button id="closeDetailPopup" style="position: absolute; top: 5px; right: 5px;">閉じる</button>
<h3 id="popupScreenName">機能名: </h3>
<h4 id="popupClassName">クラス名: </h4>
<div style="max-height: 300px; overflow-y: auto;">
<table border="1" style="width: 100%; border-collapse: collapse;">
<thead style="position: sticky; top: 0; background-color: #fff; z-index: 1;">
<tr>
<th>項目種別</th>
<th>項目名</th>
<th>リソースの機能種別</th>
<th>リソース</th>
<th>取得元項目</th>
<th>引渡し</th>
</tr>
</thead>
<tbody id="popupTableBody">
<!-- 内容はJavaScriptで動的に挿入 -->
</tbody>
</table>
</div>
</div>
<!-- フッターとして△ボタンを追加 -->
<div id="footer" style="position: fixed; bottom: 0; left: 0; width: 100%; background-color: #f1f1f1; text-align: center; padding: 5px; border-top: 1px solid #ccc;">
<button id="toggleSelectionTool" style="font-size: 20px;">△</button>
</div>
<!-- 選択ツールのUI -->
<div id="selectionTool" style="display: none; position: fixed; bottom: 0; left: 0; width: 100%; background-color: #fff; border-top: 1px solid #ccc; z-index: 1001; height: 300px; overflow: hidden;">
<div id="selectionToolHeader" style="background-color: #ccc; cursor: ns-resize; padding: 5px; text-align: center;">
<button id="closeSelectionTool">▽</button>
</div>
<!-- モード選択 -->
<div >
<label for="modeToggle">モード:</label>
<select id="modeToggle">
<option value="panel">パネル</option>
<option value="item">項目</option>
</select>
<button id="filterButton" onclick="doFilter()" style="background-color: #4CAF50; color: white; padding: 1px;">フィルタ</button>
<label for="modeToggle">まとめて操作:</label>
<button id="filterButton" onclick="doSelectAllOn()" style="background-color: #d01919; color: white; padding: 1px;">選択on</button>
<button id="filterButton" onclick="doSelectAllOff()" style="background-color: #d01919a4; color: white; padding: 1px;">選択off</button>
<button id="filterButton" onclick="doEnableLineAllOn()" style="background-color: #194dd0; color: white; padding: 1px;">線描画on</button>
<button id="filterButton" onclick="doEnableLineAllOff()" style="background-color: #194dd0ab; color: white; padding: 1px;">線描画off</button>
<button id="filterButton" onclick="doVisibleAllOn()" style="background-color: #19d02e; color: white; padding: 1px;">非表示on</button>
<button id="filterButton" onclick="doVisibleAllOFF()" style="background-color: #19d02e9d; color: white; padding: 1px;">非表示off</button>
<button id="filterButton" onclick="doBookMarkAllOn()" style="background-color: #e2890c; color: white; padding: 1px;">★on</button>
<button id="filterButton" onclick="doBookMarkAllOff()" style="background-color: #e2b00c; color: white; padding: 1px;">★off</button>
</div>
<!-- 横断検索部分 -->
<div>
<label for="crossSearch">横断検索:</label>
<input type="text" id="crossSearch" style="width: 200px;">
<input type="checkbox" id="filterFeatureType">機能種別
<input type="checkbox" id="filterClassName">クラス名
<input type="checkbox" id="filterFeatureName">機能名
<input type="checkbox" id="filterItemType" class="toggle-tool-item"><span class="toggle-tool-item">項目種別</span>
<input type="checkbox" id="filterItemName" class="toggle-tool-item"><span class="toggle-tool-item">項目名</span>
<input type="checkbox" id="filterResourceType" class="toggle-tool-item"><span class="toggle-tool-item">リソースの機能種別</span>
<input type="checkbox" id="filterResource" class="toggle-tool-item"><span class="toggle-tool-item">リソース</span>
<input type="checkbox" id="filterSourceItem" class="toggle-tool-item"><span class="toggle-tool-item">取得元項目</span>
<input type="checkbox" id="filterHandoff" class="toggle-tool-item"><span class="toggle-tool-item">引渡し</span>
<!-- <button id="searchButton" style="background-color: #2196F3; color: white; padding: 1px;">検索</button> -->
</div>
<!-- フィルタ部分: 横に並べたテキストボックス -->
<div>
<label for="filterTextFeatureType">フィルタ:</label>
<input type="text" id="filterTextFeatureType" style="width: 130px;" placeholder="機能種別">
<input type="text" id="filterTextClassName" style="width: 130px;" placeholder="クラス名">
<input type="text" id="filterTextFeatureName" style="width: 130px;" placeholder="機能名">
<input type="text" id="filterTextItemType" style="width: 130px;" placeholder="項目種別" class="toggle-column">
<input type="text" id="filterTextItemName" style="width: 130px;" placeholder="項目名" class="toggle-column">
<input type="text" id="filterTextResourceType" style="width: 130px;" placeholder="リソースの機能種別" class="toggle-column">
<input type="text" id="filterTextResource" style="width: 130px;" placeholder="リソース" class="toggle-column">
<input type="text" id="filterTextSourceItem" style="width: 130px;" placeholder="取得元項目" class="toggle-column">
<input type="text" id="filterTextHandoff" style="width: 130px;" placeholder="引渡し" class="toggle-column">
<label>選択:</label>
<select id="filterSelected">
<option value="none">--</option>
<option value="false">□</option>
<option value="true">☑</option>
</select>
<label>線描画:</label>
<select id="filterEnableLine">
<option value="none">--</option>
<option value="false">□</option>
<option value="true">☑</option>
</select>
<label>非表示:</label>
<select id="filterHidden">
<option value="none">--</option>
<option value="false">□</option>
<option value="true">☑</option>
</select>
<label>★:</label>
<select id="filterBookMark">
<option value="none">--</option>
<option value="false">□</option>
<option value="true">☑</option>
</select>
</div>
<!-- テーブル表示部分 -->
<div style="overflow-y: auto; height: calc(100% - 120px);">
<table border="1" style="width: 100%; border-collapse: collapse;">
<thead style="position: sticky; top: 0; background-color: #fff; z-index: 1;">
<tr id="panelModeHeader">
<th>機能種別</th>
<th>クラス名</th>
<th>機能名</th>
<th class="toggle-column">項目種別</th>
<th class="toggle-column">項目名</th>
<th class="toggle-column">リソースの機能種別</th>
<th class="toggle-column">リソース</th>
<th class="toggle-column">取得元項目</th>
<th class="toggle-column">引渡し</th>
<th>選択</th>
<th>線描画</th>
<th>非表示</th>
<th>★</th>
<th>アクション</th>
</tr>
</thead>
<tbody id="selectionTableBody"></tbody>
</table>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<script>
var screenData = [];
var comments = {};
var arrowCache = [];
var drawMode = '無効';
var selectedPanels = new Set();
var selectionBox = null;
var selectionStartX = 0;
var selectionStartY = 0;
document.getElementById('importCsv').addEventListener('click', () => {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.csv';
input.onchange = (event) => {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
const text = e.target.result;
screenData = parseCsv(text);
renderChart();
requestAnimationFrame(drawArrows);
};
reader.readAsText(file);
}
};
input.click();
});
document.getElementById('setDimensions').addEventListener('click', () => {
const width = document.getElementById('widthInput').value;
const height = document.getElementById('heightInput').value;
const chart = document.getElementById('chart');
chart.style.width = `${width}px`;
chart.style.height = `${height}px`;
});
document.getElementById('exportJson').addEventListener('click', () => {
const blob = new Blob([JSON.stringify({screenData : screenData, groupedData : groupedData,})], { type: 'application/json' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = 'exported_content.json';
link.click();
});
document.getElementById('importJson').addEventListener('click', () => {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.json';
input.onchange = (event) => {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
const state = JSON.parse(e.target.result);
renderChart(state);
requestAnimationFrame(drawArrows);
};
reader.readAsText(file);
}
};
input.click();
});
document.getElementById('toggleComments').addEventListener('click', (e) => {
const commentBubbles = document.querySelectorAll('.comment-bubble');
const shouldShow = e.target.classList.toggle('toggle-active');
e.target.textContent = shouldShow ? 'コメント非表示' : 'コメント表示';
commentBubbles.forEach(bubble => {
bubble.style.display = shouldShow ? 'block' : 'none';
});
});
function parseCsv(csvText) {
const rows = csvText.split('\n');
const headers = rows[0].split(',');
return rows.slice(1).map(row => {
const values = row.split(',');
return headers.reduce((acc, header, index) => {
acc[header.trim()] = values[index]?.trim() || '';
return acc;
}, {});
});
}
var groupedData;
var panelPositions = {};
var itemPositions = {};
const chartDiv = document.getElementById('chart');
function renderChart(state = null) {
chartDiv.innerHTML = '';
panelPositions = {};
itemPositions = {};
screenData = state ? state.screenData : screenData;
groupedData = state ? state.groupedData : screenData.reduce((acc, row) => {
if (!acc[row.機能名]) {
acc[row.機能名] = {
type: row.機能種別,
screenName: row.機能名,
className: row.クラス名,
isSelected:false,
isEnableLine:false,
isHidden:false,
isPanelOpen:false,
positionX:0,
positionY:0,
bookMark:false,
comment:"",
items: []
};
}
acc[row.機能名].items.push({
type: row.機能種別,
screenName: row.機能名,
itemType: row.項目種別,
itemName: row.項目名,
sourceScreen: row.リソース,
sourceItem: row.取得元項目,
resourceType: row.リソースの機能種別,
handoff: row.引渡し,
isSelected:false
});
return acc;
}, {});
const data = Object.values(groupedData);
data.forEach((screen, index) => {
const panel = document.createElement('div');
panel.className = `panel ${screen.type === '画面' ? 'screen-panel' : screen.type === 'API' ? 'api-panel' : screen.type === 'バッチ' ? 'batch-panel' : screen.type === '外部機能' ? 'external-panel' : 'table-panel'}`;
panel.style.top = state ? groupedData[screen.screenName].positionY : `${index * 150 + 150}px`;
panel.style.left = state ? groupedData[screen.screenName].positionX : `${index % 2 === 0 ? 100 : 500}px`;
if(!state){
groupedData[screen.screenName].positionX = panel.style.left;
groupedData[screen.screenName].positionY = panel.style.top;
}
if(screen.isSelected){
panel.classList.add('selected-panel');
selectedPanels.add(panel);
}
if(screen.bookMark){
panel.classList.add('bookMarked-panel');
}
const panelHeader = document.createElement('div');
panelHeader.className = 'panel-header';
const headerTitle = document.createElement('span');
headerTitle.textContent = screen.screenName;
const copyIconScreenName = document.createElement('span');
copyIconScreenName.className = 'clipboard-icon';
copyIconScreenName.textContent = '📋';
copyIconScreenName.addEventListener('click', () => {
navigator.clipboard.writeText(screen.screenName);
});
const detailIconScreenName = document.createElement('span');
detailIconScreenName.className = 'detail-icon';
detailIconScreenName.textContent = '🔍';
detailIconScreenName.addEventListener('click', () => {
showDetailPopup(screen, panel);
});
const classLabel = document.createElement('div');
classLabel.className = 'panel-class';
classLabel.textContent = screen.className;
const copyIconClassName = document.createElement('span');
copyIconClassName.className = 'clipboard-icon';
copyIconClassName.textContent = '📋';
copyIconClassName.addEventListener('click', () => {
navigator.clipboard.writeText(screen.className);
});
classLabel.appendChild(copyIconClassName);
const toggleButton = document.createElement('button');
toggleButton.className = 'toggle-button';
toggleButton.textContent = '△';
toggleButton.addEventListener('click', (e) => {
const content = panel.querySelector('.panel-content');
content.style.display = content.style.display === 'none' ? 'block' : 'none';
toggleButton.textContent = content.style.display === 'none' ? '△' : '▽';
screen.isPanelOpen = content.style.display === 'none' ? false : true;
requestAnimationFrame(drawArrows);
});
const headerCheckbox = document.createElement('input');
headerCheckbox.type = 'checkbox';
headerCheckbox.style.marginRight = '5px';
headerCheckbox.style.display = drawMode === '対象指定' ? 'block' : 'none';
headerCheckbox.addEventListener('change', () => {
screen.isEnableLine = headerCheckbox.checked ? true :false;
requestAnimationFrame(drawArrows);
});
panelHeader.appendChild(headerCheckbox);
panelHeader.appendChild(headerTitle);
panelHeader.appendChild(copyIconScreenName);
panelHeader.appendChild(detailIconScreenName);
panelHeader.appendChild(toggleButton);
const commentBubble = document.createElement('div');
commentBubble.className = 'comment-bubble';
commentBubble.contentEditable = true;
commentBubble.style.display = 'none';
commentBubble.textContent = groupedData[screen.screenName].comment;
groupedData[screen.screenName].comment = commentBubble.textContent;
panelHeader.addEventListener('contextmenu', (e) => {
e.preventDefault();
commentBubble.style.top = e.clientY - commentBubble.getBoundingClientRect().top;
commentBubble.style.left = e.clientX - commentBubble.getBoundingClientRect().left;
commentBubble.style.display = 'block';
commentBubble.focus();
});
commentBubble.addEventListener('blur', () => {
groupedData[screen.screenName].comment = commentBubble.textContent;
});
const panelContent = document.createElement('div');
panelContent.className = 'panel-content';
panelContent.style.display = groupedData[screen.screenName].isPanelOpen ? 'block' : 'none';
screen.items.forEach(item => {
if (!itemPositions[`${screen.screenName}-${item.itemName}`]) {
const itemDiv = document.createElement('div');
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.style.marginRight = '5px';
itemDiv.appendChild(checkbox);
itemDiv.appendChild(document.createTextNode(item.itemName));
itemDiv.dataset.screenName = screen.screenName;
itemDiv.dataset.itemName = item.itemName;
panelContent.appendChild(itemDiv);
checkbox.checked = item.isSelected;
itemPositions[`${screen.screenName}-${item.itemName}`] = itemDiv;
checkbox.addEventListener('change', () => {
if (checkbox.checked) {
itemDiv.classList.add('highlighted-item');
item.isSelected = true;
} else {
itemDiv.classList.remove('highlighted-item');
item.isSelected = false
}
});
itemDiv.addEventListener('contextmenu', (e) => {
e.preventDefault();
commentBubble.style.top = e.clientY - commentBubble.getBoundingClientRect().top;
commentBubble.style.left = e.clientX - commentBubble.getBoundingClientRect().left;
commentBubble.style.display = 'block';
commentBubble.focus();
});
}
});
panel.appendChild(panelHeader);
panel.appendChild(classLabel);
panel.appendChild(panelContent);
panel.appendChild(commentBubble);
chartDiv.appendChild(panel);
panelPositions[screen.screenName] = panel;
panel.addEventListener('mousedown', (e) => {
if (e.button !== 0 || e.target.tagName === 'INPUT') return;
panel.dataset.moving = true;
// パネルの初期位置とマウスの初期位置を記録する
let initialMouseX = e.clientX;
let initialMouseY = e.clientY;
let initialPositions = [];
if (selectedPanels.has(panel)) {
selectedPanels.forEach(selectedPanel => {
const rect = selectedPanel.getBoundingClientRect();
initialPositions.push({
panel: selectedPanel,
startX: rect.left + window.pageXOffset,
startY: rect.top + window.pageYOffset
});
});
} else {
initialPositions.push({
panel: panel,
startX: panel.getBoundingClientRect().left + window.pageXOffset,
startY: panel.getBoundingClientRect().top + window.pageYOffset
});
}
function moveAt(currentMouseX, currentMouseY) {
// マウスの移動量を計算
const deltaX = currentMouseX - initialMouseX;
const deltaY = currentMouseY - initialMouseY;
// 初期位置に移動量を加算してパネルの位置を更新
initialPositions.forEach(({ panel, startX, startY }) => {
panel.style.left = `${startX + deltaX}px`;
panel.style.top = `${startY + deltaY}px`;
groupedData[panel.querySelector('.panel-header span').textContent].positionX = panel.style.left;
groupedData[panel.querySelector('.panel-header span').textContent].positionY = panel.style.top;
});
requestAnimationFrame(drawArrows);
}
function onMouseMove(event) {
panel.classList.add('dragging');
moveAt(event.clientX, event.clientY);
}
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', function onMouseUp() {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
panel.classList.remove('dragging');
delete panel.dataset.moving;
});
});
panel.ondragstart = function() {
return false;
};
panel.addEventListener('click', (e) => {
if (e.ctrlKey) {
if (selectedPanels.has(panel)) {
selectedPanels.delete(panel);
panel.classList.remove('selected-panel');
screen.isSelected = false;
} else {
selectedPanels.add(panel);
panel.classList.add('selected-panel');
screen.isSelected = true;
}
}
});
});
}
renderChart();
function drawArrows() {
const svg = document.getElementById('arrows');
svg.innerHTML = '<defs><marker id="arrowhead" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto" markerUnits="strokeWidth"><polygon points="0 0, 10 3.5, 0 7" /></marker></defs>';
// 無効モードの場合は描画しない
if (drawMode === '無効') {
return;
}
let maxX = 0;
let maxY = 0;
arrowCache = [];
screenData.forEach(row => {
if (
(row.リソース && row.取得元項目 && row.リソース !== row.機能名) ||
(row.引渡し && row.リソース)
) {
const sourceItem = row.取得元項目
? itemPositions[`${row.リソース}-${row.取得元項目}`]
: itemPositions[`${row.機能名}-${row.項目名}`];
const targetItem = row.取得元項目
? itemPositions[`${row.機能名}-${row.項目名}`]
: itemPositions[`${row.リソース}-${row.引渡し}`];
const sourcePanel = row.取得元項目
? panelPositions[row.リソース]
: panelPositions[row.機能名];
const targetPanel = row.取得元項目
? panelPositions[row.機能名]
: panelPositions[row.リソース];
if (sourcePanel && targetPanel) {
let sourceX, sourceY, targetX, targetY;
const sourceRect = sourceItem ? sourceItem.getBoundingClientRect() : sourcePanel.getBoundingClientRect();
const targetRect = targetItem ? targetItem.getBoundingClientRect() : targetPanel.getBoundingClientRect();
const sourceIsVisible = sourceItem ? sourceItem.closest('.panel-content').style.display === 'block' : false;
const targetIsVisible = targetItem ? targetItem.closest('.panel-content').style.display === 'block' : false;
const sourcePanelName = sourcePanel.querySelector('.panel-header span').textContent;
const targetPanelName = targetPanel.querySelector('.panel-header span').textContent;
if(groupedData[sourcePanelName].isHidden || groupedData[targetPanelName].isHidden) return;
if (drawMode === '対象指定') {
const isSourceChecked = sourcePanel.querySelector('.panel-header input[type="checkbox"]').checked;
const isTargetChecked = targetPanel.querySelector('.panel-header input[type="checkbox"]').checked;
if (!(isSourceChecked || isTargetChecked)) {
return;
}
}
if (sourceIsVisible) {
sourceX = sourceRect.right + window.pageXOffset;
sourceY = sourceRect.top + sourceRect.height / 2 + window.pageYOffset;
} else {
const sourcePanelRect = sourcePanel.getBoundingClientRect();
sourceX = sourcePanelRect.right + window.pageXOffset;
sourceY = sourcePanelRect.top + 20 + window.pageYOffset;
}
if (targetIsVisible) {
targetX = targetRect.left + window.pageXOffset;
targetY = targetRect.top + targetRect.height / 2 + window.pageYOffset;
} else {
const targetPanelRect = targetPanel.getBoundingClientRect();
targetX = targetPanelRect.left + window.pageXOffset;
targetY = targetPanelRect.top + 20 + window.pageYOffset;
}
if (sourceX < targetX) {
sourceX = sourceIsVisible ? sourceRect.right + window.pageXOffset : sourcePanel.getBoundingClientRect().right + window.pageXOffset;
targetX = targetIsVisible ? targetRect.left + window.pageXOffset : targetPanel.getBoundingClientRect().left + window.pageXOffset;
} else {
sourceX = sourceIsVisible ? sourceRect.left + window.pageXOffset : sourcePanel.getBoundingClientRect().left + window.pageXOffset;
targetX = targetIsVisible ? targetRect.right + window.pageXOffset : targetPanel.getBoundingClientRect().right + window.pageXOffset;
}
arrowCache.push({ sourceX, sourceY, targetX, targetY, sourceItem, targetItem });
}
}
});
arrowCache.forEach(({ sourceX, sourceY, targetX, targetY, sourceItem, targetItem }) => {
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
line.setAttribute('x1', sourceX);
line.setAttribute('y1', sourceY);
line.setAttribute('x2', targetX);
line.setAttribute('y2', targetY);
line.setAttribute('class', 'arrow');
if (
(sourceItem && sourceItem.classList.contains('highlighted-item')) ||
(targetItem && targetItem.classList.contains('highlighted-item'))
) {
line.classList.add('highlighted-arrow');
}
svg.appendChild(line);
maxX = Math.max(maxX, sourceX, targetX);
maxY = Math.max(maxY, sourceY, targetY);
});
svg.setAttribute('width', maxX + 50);
svg.setAttribute('height', maxY + 50);
}
requestAnimationFrame(drawArrows);
const toggleAllButton = document.getElementById('toggleAll');
toggleAllButton.addEventListener('click', () => {
const panels = document.querySelectorAll('.panel-content');
const allExpanded = Array.from(panels).every(panel => panel.style.display === 'block');
panels.forEach(panel => {
panel.style.display = allExpanded ? 'none' : 'block';
const toggleButton = panel.closest('.panel').querySelector('.toggle-button');
toggleButton.textContent = allExpanded ? '△' : '▽';
});
requestAnimationFrame(drawArrows);
});
window.addEventListener('resize', () => {
requestAnimationFrame(drawArrows);
});
chartDiv.addEventListener('mousedown', (e) => {
if (e.target !== chartDiv) return;
// ページ全体のテキスト選択を無効化
document.body.style.userSelect = 'none';
selectionStartX = e.clientX;
selectionStartY = e.clientY;
selectionBox = document.createElement('div');
selectionBox.className = 'selection-box';
selectionBox.style.left = `${selectionStartX + window.pageXOffset}px`;
selectionBox.style.top = `${selectionStartY + window.pageYOffset}px`;
document.body.appendChild(selectionBox);
function onMouseMove(event) {
const currentX = event.clientX;
const currentY = event.clientY;
selectionBox.style.left = `${Math.min(selectionStartX, currentX) + window.pageXOffset}px`;
selectionBox.style.top = `${Math.min(selectionStartY, currentY) + window.pageYOffset}px`;
selectionBox.style.width = `${Math.abs(currentX - selectionStartX)}px`;
selectionBox.style.height = `${Math.abs(currentY - selectionStartY)}px`;
}
function onMouseUp(event) {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
// テキスト選択を再び有効化
document.body.style.userSelect = '';
const selectionRect = selectionBox.getBoundingClientRect();
document.body.removeChild(selectionBox);
selectionBox = null;
const panels = document.querySelectorAll('.panel');
panels.forEach(panel => {
const panelRect = panel.getBoundingClientRect();
if (
panelRect.left < selectionRect.right &&
panelRect.right > selectionRect.left &&
panelRect.top < selectionRect.bottom &&
panelRect.bottom > selectionRect.top
) {
if (selectedPanels.has(panel)) {
// 選択を解除する
selectedPanels.delete(panel);
panel.classList.remove('selected-panel');
groupedData[panel.querySelector('.panel-header span').textContent].isSelected = false;
} else {
// 新たに選択する
selectedPanels.add(panel);
panel.classList.add('selected-panel');
groupedData[panel.querySelector('.panel-header span').textContent].isSelected = true;
}
}
});
}
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
});
// 描画モードのプルダウンメニューに変更に対応
const drawModeSelect = document.getElementById('drawModeSelect');
drawModeSelect.addEventListener('change', (e) => {
drawMode = e.target.value;
const panels = document.querySelectorAll('.panel');
panels.forEach(panel => {
const checkbox = panel.querySelector('.panel-header input[type="checkbox"]');
if (drawMode === '対象指定') {
// 対象指定モードに切り替えた際に、以前のチェック状態を復元
checkbox.checked = groupedData[panel.querySelector('.panel-header span').textContent].isEnableLine;
checkbox.style.display = 'block';
} else {
// チェック状態を保存して非表示にする
checkbox.checked = groupedData[panel.querySelector('.panel-header span').textContent].isEnableLine = checkbox.checked;
checkbox.checked = false;
checkbox.style.display = 'none';
}
});
requestAnimationFrame(drawArrows);
});
// 詳細表示UIを表示し、内容を動的に設定
function showDetailPopup(screen, panel) {
const detailPopup = document.getElementById('detailPopup');
const popupScreenName = document.getElementById('popupScreenName');
const popupClassName = document.getElementById('popupClassName');
const popupTableBody = document.getElementById('popupTableBody');
// 機能名とクラス名を設定
popupScreenName.textContent = `機能名: ${screen.screenName}`;
popupClassName.textContent = `クラス名: ${screen.className}`;
// テーブルの内容をクリアしてから新しいデータを追加
popupTableBody.innerHTML = screen.items.map(item => `
<tr>
<td>${item.itemType}</td>
<td>${item.itemName}</td>
<td>${item.resourceType}</td>
<td>${item.sourceScreen}</td>
<td>${item.sourceItem}</td>
<td>${item.handoff}</td>
</tr>
`).join('');
// ポップアップの位置をクリックしたパネルの右隣に設定
const panelRect = panel.getBoundingClientRect();
detailPopup.style.top = `${panelRect.top + window.pageYOffset}px`;
detailPopup.style.left = `${panelRect.right + 10 + window.pageXOffset}px`;
// ポップアップを表示
detailPopup.style.display = 'block';
}
// 閉じるボタンのクリックイベントを追加
document.getElementById('closeDetailPopup').addEventListener('click', () => {
document.getElementById('detailPopup').style.display = 'none';
});
const footer = document.getElementById('footer');
const selectionTool = document.getElementById('selectionTool');
const toggleSelectionToolButton = document.getElementById('toggleSelectionTool');
const closeSelectionToolButton = document.getElementById('closeSelectionTool');
const selectionToolHeader = document.getElementById('selectionToolHeader');
const modeToggle = document.getElementById('modeToggle');
const panelModeHeader = document.getElementById('panelModeHeader');
const itemModeHeader = document.getElementById('itemModeHeader');
const selectionTableBody = document.getElementById('selectionTableBody');
// フッターの「△」ボタンで選択ツールを表示
toggleSelectionToolButton.addEventListener('click', () => {
selectionTool.style.display = 'block';
toggleSelectionToolButton.style.display = 'none';
});
// 「▽」ボタンで選択ツールを閉じる
closeSelectionToolButton.addEventListener('click', () => {
selectionTool.style.display = 'none';
toggleSelectionToolButton.style.display = 'inline';
});
// 選択ツールのヘッダー部分で高さを調整可能にする
let isResizing = false;
let lastY = 0;
selectionToolHeader.addEventListener('mousedown', (e) => {
isResizing = true;
lastY = e.clientY;
});
document.addEventListener('mousemove', (e) => {
if (!isResizing) return;
const deltaY = e.clientY - lastY;
const newHeight = selectionTool.offsetHeight - deltaY;
if (newHeight >= 100 && newHeight <= 600) { // 最小・最大高さの制限
selectionTool.style.height = `${newHeight}px`;
lastY = e.clientY;
}
});
document.addEventListener('mouseup', () => {
isResizing = false;
});
// モード切り替えイベント
let selectToolMode = 'panel';
modeToggle.addEventListener('change', (e) => {
const toggleColumns = document.querySelectorAll('.toggle-column');
const toggleToolItem = document.querySelectorAll('.toggle-tool-item');
if (e.target.value === 'item') {
toggleColumns.forEach(col => col.style.display = 'table-cell'); // 項目モードでは表示
toggleToolItem.forEach(col => col.style.display = ''); // 項目モードでは表示
selectToolMode = 'item';
renderTableBody('item');
} else {
toggleColumns.forEach(col => col.style.display = 'none'); // パネルモードでは非表示
toggleToolItem.forEach(col => col.style.display = 'none'); // パネルモードでは非表示
selectToolMode = 'panel';
renderTableBody('panel');
}
});
// テーブル内容の動的生成
function renderTableBody(mode) {
selectionTableBody.innerHTML = ''; // テーブル内容をクリア
let rowCount = 0;
let rows = [];
const LIMIT_ROW_COUNT = 500;//性能対策のため500で描画を打ち切る
const groupedDataArr = Object.values(groupedData);
//検索条件の情報収集
let crossSearchText = document.getElementById("crossSearch").value;
let searchFeatureType = document.getElementById("filterFeatureType").checked;
let searchClassName = document.getElementById("filterClassName").checked;
let searchFeatureName = document.getElementById("filterFeatureName").checked;
let searchItemType = document.getElementById("filterItemType").checked;
let searchItemName = document.getElementById("filterItemName").checked;
let searchResourceType = document.getElementById("filterResourceType").checked;
let searchResource = document.getElementById("filterResource").checked;
let searchSourceItem = document.getElementById("filterSourceItem").checked;
let searchHandoff = document.getElementById("filterHandoff").checked;
let searchDefine = {
crossSearchText:!crossSearchText ? null : new RegExp(crossSearchText),
searchFeatureType:searchFeatureType,
searchClassName:searchClassName,
searchFeatureName:searchFeatureName,
searchItemType:searchItemType,
searchItemName:searchItemName,
searchResourceType:searchResourceType,
searchResource:searchResource,
searchSourceItem:searchSourceItem,
searchHandoff:searchHandoff
};
//フィルタの条件収集
let filterTextFeatureType = document.getElementById("filterTextFeatureType").value;
let filterTextClassName = document.getElementById("filterTextClassName").value;
let filterTextFeatureName = document.getElementById("filterTextFeatureName").value;
let filterTextItemType = document.getElementById("filterTextItemType").value;
let filterTextItemName = document.getElementById("filterTextItemName").value;
let filterTextResourceType = document.getElementById("filterTextResourceType").value;
let filterTextResource = document.getElementById("filterTextResource").value;
let filterTextSourceItem = document.getElementById("filterTextSourceItem").value;
let filterTextHandoff = document.getElementById("filterTextHandoff").value;
let filterSelected = document.getElementById("filterSelected").value;
let filterEnableLine = document.getElementById("filterEnableLine").value;
let filterHidden = document.getElementById("filterHidden").value;
let filterBookMark = document.getElementById("filterBookMark").value;
let filterDefine = {
filterTextFeatureType:new RegExp(filterTextFeatureType),
filterTextClassName:new RegExp(filterTextClassName),
filterTextFeatureName:new RegExp(filterTextFeatureName),
filterTextItemType:new RegExp(filterTextItemType),
filterTextItemName:new RegExp(filterTextItemName),
filterTextResourceType:new RegExp(filterTextResourceType),
filterTextResource:new RegExp(filterTextResource),
filterTextSourceItem:new RegExp(filterTextSourceItem),
filterTextHandoff:new RegExp(filterTextHandoff),
filterSelected:filterSelected,
filterEnableLine:filterEnableLine,
filterHidden:filterHidden,
filterBookMark:filterBookMark
};
for(let i = 0; i < groupedDataArr.length; i++){
let innerHtmlScreen = "";
let innerHtmlItem = "";
let innerHtmTool = "";
innerHtmlScreen = `
<td>${groupedDataArr[i].type}</td>
<td>${groupedDataArr[i].className}</td>
<td>${groupedDataArr[i].screenName}</td>
`;
if(mode === 'item'){
let matchList=[];
for(let j = 0; j < groupedDataArr[i].items.length; j++){
innerHtmlItem = `
<td>${groupedDataArr[i].items[j].itemType}</td>
<td>${groupedDataArr[i].items[j].itemName}</td>
<td>${groupedDataArr[i].items[j].resourceType}</td>
<td>${groupedDataArr[i].items[j].sourceScreen}</td>
<td>${groupedDataArr[i].items[j].sourceItem}</td>
<td>${groupedDataArr[i].items[j].handoff}</td>
`;
if(!searchAndFiter4Item(groupedDataArr[i],groupedDataArr[i].items[j],searchDefine,filterDefine)){
continue;
}else{
matchList.push(innerHtmlItem);
}
}
innerHtmTool = `
<td rowspan="${matchList.length}"><input type="checkbox" class="select-checkbox" ${groupedDataArr[i].isSelected ? 'checked' : ''}></td>
<td rowspan="${matchList.length}"><input type="checkbox" class="draw-checkbox" ${groupedDataArr[i].isEnableLine ? 'checked' : ''}></td>
<td rowspan="${matchList.length}"><input type="checkbox" class="hide-checkbox" ${groupedDataArr[i].isHidden ? 'checked' : ''}></td>
<td rowspan="${matchList.length}"><input type="checkbox" class="bookMark-checkbox" ${groupedDataArr[i].bookMark ? 'checked' : ''}></td>
<td rowspan="${matchList.length}"><button class="go-button">Go</button></td>
`;
for(let j = 0; j < matchList.length;j++){
const row = document.createElement('tr');
row.innerHTML = innerHtmlScreen + matchList[j] + (j === 0 ? innerHtmTool: '');
rows.push({screenName:groupedDataArr[i].screenName,row:row});
rowCount++;
}
}else{
innerHtmTool = `
<td><input type="checkbox" class="select-checkbox" ${groupedDataArr[i].isSelected ? 'checked' : ''}></td>
<td><input type="checkbox" class="draw-checkbox" ${groupedDataArr[i].isEnableLine ? 'checked' : ''}></td>
<td><input type="checkbox" class="hide-checkbox" ${groupedDataArr[i].isHidden ? 'checked' : ''}></td>
<td><input type="checkbox" class="bookMark-checkbox" ${groupedDataArr[i].bookMark ? 'checked' : ''}></td>
<td><button class="go-button">Go</button></td>
`;
if(!searchAndFiter4Panel(groupedDataArr[i],searchDefine,filterDefine)){ continue; };
const row = document.createElement('tr');
row.innerHTML = innerHtmlScreen + innerHtmTool;
rows.push({screenName:groupedDataArr[i].screenName,row:row});
rowCount++;
}
if(rowCount >= LIMIT_ROW_COUNT){
const row = document.createElement('tr');
row.innerHTML = `<td colspan="${mode==='panel'?8:14}">表示上限:${LIMIT_ROW_COUNT}件を超えています。フィルタ条件を見直してください。</td>`;
rows.push({screenName:'',row:row});
break;
}
}
rows.forEach(row =>{
selectionTableBody.appendChild(row.row);
// Goボタンの動作を追加
const goButton = row.row.querySelector('.go-button');
if(!goButton){return}
goButton.addEventListener('click', () => {
scrollToPanel(row.screenName);
});
// パネルの選択や線描画とチェックボックスの連動
const selectCheckbox = row.row.querySelector('.select-checkbox');
selectCheckbox.addEventListener('change', () => {
togglePanelSelection(row.screenName, selectCheckbox.checked);
});
const drawCheckbox = row.row.querySelector('.draw-checkbox');
drawCheckbox.addEventListener('change', () => {
toggleDrawPanel(row.screenName, drawCheckbox.checked);
});
const hideCheckbox = row.row.querySelector('.hide-checkbox');
hideCheckbox.addEventListener('change', () => {
toggleHidePanel(row.screenName, hideCheckbox.checked);
});
const bookMarkCheckbox = row.row.querySelector('.bookMark-checkbox');
bookMarkCheckbox.addEventListener('change', () => {
toggleBookMark(row.screenName, bookMarkCheckbox.checked);
});
});
}
//除外対象ならfalseを変える
function searchAndFiter4Panel(screen,searchDefine,filterDefine){
let searchResult = true;
let filterResult = true;
//チェックボックスのいずれにもチェックがないっていない場合
if(!searchDefine.searchFeatureType && !searchDefine.searchClassName && !searchDefine.searchFeatureName){
if(
(!searchDefine.crossSearchText //検索条件の入力がないまたは、チェックがついているいずれかで条件に合う場合
|| searchDefine.crossSearchText.test(screen.type)
|| searchDefine.crossSearchText.test(screen.className)
|| searchDefine.crossSearchText.test(screen.screenName)
)
){
searchResult = true;
}else{
searchResult = false;
}
}else{//チェックボックスのいずれかにチェックが入っていた場合
if(!searchDefine.crossSearchText
|| (searchDefine.searchFeatureType && searchDefine.crossSearchText.test(screen.type))
|| (searchDefine.searchClassName && searchDefine.crossSearchText.test(screen.className))
|| (searchDefine.searchFeatureName && searchDefine.crossSearchText.test(screen.screenName))
){
searchResult = true;
}else{
searchResult = false;
}
}
//フィルタ側の判定
if(
(!filterDefine.filterTextFeatureType || filterDefine.filterTextFeatureType.test(screen.type))
&& (!filterDefine.filterTextClassName || filterDefine.filterTextClassName.test(screen.className))
&& (!filterDefine.filterTextFeatureName || filterDefine.filterTextFeatureName.test(screen.screenName))
&& (filterDefine.filterSelected ==='none' || ((filterDefine.filterSelected ==='true' && screen.isSelected) ||filterDefine.filterSelected ==='false' && !screen.isSelected))
&& (filterDefine.filterEnableLine ==='none' || ((filterDefine.filterEnableLine ==='true' && screen.isEnableLine) ||filterDefine.filterEnableLine ==='false' && !screen.isEnableLine))
&& (filterDefine.filterHidden ==='none' || ((filterDefine.filterHidden ==='true' && screen.isHidden) ||filterDefine.filterHidden ==='false' && !screen.isHidden))
&& (filterDefine.filterBookMark ==='none' || ((filterDefine.filterBookMark ==='true' && screen.bookMark) ||filterDefine.filterBookMark ==='false' && !screen.bookMark))
)
return searchResult && filterResult;
}
function searchAndFiter4Item(screen,item,searchDefine,filterDefine){
let searchResult = true;
let filterResult = true;
//チェックボックスのいずれにもチェックがないっていない場合
if(!searchDefine.searchFeatureType && !searchDefine.searchClassName && !searchDefine.searchFeatureName
&& !searchDefine.searchItemType && !searchDefine.searchItemName && !searchDefine.searchResourceType
&& !searchDefine.searchResource && !searchDefine.searchSourceItem && !searchDefine.searchHandoff
){
if(
(!searchDefine.crossSearchText //検索条件の入力がないまたは、チェックがついているいずれかで条件に合う場合
|| searchDefine.crossSearchText.test(screen.type)
|| searchDefine.crossSearchText.test(screen.className)
|| searchDefine.crossSearchText.test(screen.screenName)
|| searchDefine.crossSearchText.test(item.itemType)
|| searchDefine.crossSearchText.test(item.itemName)
|| searchDefine.crossSearchText.test(item.sourceScreen)
|| searchDefine.crossSearchText.test(item.sourceItem)
|| searchDefine.crossSearchText.test(item.resourceType)
|| searchDefine.crossSearchText.test(item.handoff)
)
){
searchResult = true;
}else{
searchResult = false;
}
}else{//チェックボックスのいずれかにチェックが入っていた場合
if(!searchDefine.crossSearchText
|| (searchDefine.searchFeatureType && searchDefine.crossSearchText.test(screen.type))
|| (searchDefine.searchClassName && searchDefine.crossSearchText.test(screen.className))
|| (searchDefine.searchFeatureName && searchDefine.crossSearchText.test(screen.screenName))
|| (searchDefine.searchItemType && searchDefine.crossSearchText.test(item.itemType))
|| (searchDefine.searchItemName && searchDefine.crossSearchText.test(item.itemName))
|| (searchDefine.searchResourceType && searchDefine.crossSearchText.test(item.resourceType))
|| (searchDefine.searchResource && searchDefine.crossSearchText.test(item.sourceScreen)
|| (searchDefine.searchSourceItem && searchDefine.crossSearchText.test(item.sourceItem)))
|| (searchDefine.searchHandoff && searchDefine.crossSearchText.test(item.handoff))
){
searchResult = true;
}else{
searchResult = false;
}
}
//フィルタ側の判定
if(
(!filterDefine.filterTextFeatureType || filterDefine.filterTextFeatureType.test(screen.type))
&& (!filterDefine.filterTextClassName || filterDefine.filterTextClassName.test(screen.className))
&& (!filterDefine.filterTextFeatureName || filterDefine.filterTextFeatureName.test(screen.screenName))
&& (filterDefine.filterSelected ==='none' || ((filterDefine.filterSelected ==='true' && screen.isSelected) ||filterDefine.filterSelected ==='false' && !screen.isSelected))
&& (filterDefine.filterEnableLine ==='none' || ((filterDefine.filterEnableLine ==='true' && screen.isEnableLine) ||filterDefine.filterEnableLine ==='false' && !screen.isEnableLine))
&& (filterDefine.filterHidden ==='none' || ((filterDefine.filterHidden ==='true' && screen.isHidden) ||filterDefine.filterHidden ==='false' && !screen.isHidden))
&& (filterDefine.filterBookMark ==='none' || ((filterDefine.filterBookMark ==='true' && screen.bookMark) ||filterDefine.filterBookMark ==='false' && !screen.bookMark))
&& (!filterDefine.filterTextItemType || filterDefine.filterTextItemType.test(item.itemType))
&& (!filterDefine.filterTextItemName || filterDefine.filterTextItemName.test(item.itemName))
&& (!filterDefine.filterTextResourceType || filterDefine.filterTextResourceType.test(item.resourceType))
&& (!filterDefine.filterTextResource || filterDefine.filterTextResource.test(item.sourceScreen))
&& (!filterDefine.filterTextSourceItem || filterDefine.filterTextSourceItem.test(item.sourceItem))
&& (!filterDefine.filterTextHandoff || filterDefine.filterTextHandoff.test(item.handoff))
)
return searchResult && filterResult;
}
// パネルの選択をトグル
function togglePanelSelection(panelName, isSelected) {
const panel = panelPositions[panelName];
if (isSelected) {
panel.classList.add('selected-panel');
selectedPanels.add(panel);
groupedData[panelName].isSelected = true;
} else {
panel.classList.remove('selected-panel');
selectedPanels.delete(panel);
groupedData[panelName].isSelected = false;
}
}
// パネルの線描画状態をトグル
function toggleDrawPanel(panelName, isDrawing) {
const panel = panelPositions[panelName];
const checkbox = panel.querySelector('.panel-header input[type="checkbox"]');
checkbox.checked = isDrawing;
groupedData[panelName].isEnableLine = isDrawing;
requestAnimationFrame(drawArrows);
}
// パネルの表示/非表示をトグル
function toggleHidePanel(panelName, isHidden) {
const panel = panelPositions[panelName];
panel.style.display = isHidden ? 'none' : 'block';
groupedData[panelName].isHidden = isHidden;
requestAnimationFrame(drawArrows);
}
// ブックマークの状態をトグル
function toggleBookMark(panelName, isBookMark) {
const panel = panelPositions[panelName];
if(isBookMark){
panel.classList.add('bookMarked-panel');
}else{
panel.classList.remove('bookMarked-panel');
}
groupedData[panelName].bookMark = isBookMark;
requestAnimationFrame(drawArrows);
}
// Goボタン押下時に該当パネルをスクロールして強調表示
function scrollToPanel(panelName) {
const panel = panelPositions[panelName];
panel.scrollIntoView({ behavior: 'smooth', block: 'center' });
// パネルの強調表示を3秒間行う
panel.style.border = '3px solid blue';
setTimeout(() => {
panel.style.border = '';
}, 3000);
}
// 初期表示はパネルモードで、該当列は非表示に
document.querySelectorAll('.toggle-column').forEach(col => col.style.display = 'none');
document.querySelectorAll('.toggle-tool-item').forEach(col => col.style.display = 'none');
function doFilter(){
renderTableBody(selectToolMode);
}
// 全ての選択をONにする
function doSelectAllOn() {
const checkboxes = document.querySelectorAll('#selectionTableBody .select-checkbox');
checkboxes.forEach(checkbox => {
checkbox.checked = true;
const screenName = checkbox.closest('tr').querySelector('td:nth-child(3)').textContent;
togglePanelSelection(screenName, true); // パネルの選択状態も反映
});
}
// 全ての選択をOFFにする
function doSelectAllOff() {
const checkboxes = document.querySelectorAll('#selectionTableBody .select-checkbox');
checkboxes.forEach(checkbox => {
checkbox.checked = false;
const screenName = checkbox.closest('tr').querySelector('td:nth-child(3)').textContent;
togglePanelSelection(screenName, false); // パネルの選択状態も反映
});
}
// 全ての線描画をONにする
function doEnableLineAllOn() {
const checkboxes = document.querySelectorAll('#selectionTableBody .draw-checkbox');
checkboxes.forEach(checkbox => {
checkbox.checked = true;
const screenName = checkbox.closest('tr').querySelector('td:nth-child(3)').textContent;
toggleDrawPanel(screenName, true); // 線描画状態をONに
});
}
// 全ての線描画をOFFにする
function doEnableLineAllOff() {
const checkboxes = document.querySelectorAll('#selectionTableBody .draw-checkbox');
checkboxes.forEach(checkbox => {
checkbox.checked = false;
const screenName = checkbox.closest('tr').querySelector('td:nth-child(3)').textContent;
toggleDrawPanel(screenName, false); // 線描画状態をOFFに
});
}
// 全ての表示をONにする
function doVisibleAllOn() {
const checkboxes = document.querySelectorAll('#selectionTableBody .hide-checkbox');
checkboxes.forEach(checkbox => {
checkbox.checked = true;
const screenName = checkbox.closest('tr').querySelector('td:nth-child(3)').textContent;
toggleHidePanel(screenName, true); // ブックマークをONに
});
}
// 全ての表示をOFFにする
function doVisibleAllOFF() {
const checkboxes = document.querySelectorAll('#selectionTableBody .hide-checkbox');
checkboxes.forEach(checkbox => {
checkbox.checked = false; // 非表示チェックをOFFにして表示する
const screenName = checkbox.closest('tr').querySelector('td:nth-child(3)').textContent;
toggleHidePanel(screenName, false); // パネルを表示
});
}
// 全てのブックマークをONにする
function doBookMarkAllOn() {
const checkboxes = document.querySelectorAll('#selectionTableBody .bookMark-checkbox');
checkboxes.forEach(checkbox => {
checkbox.checked = true; // 非表示チェックをONにして非表示にする
const screenName = checkbox.closest('tr').querySelector('td:nth-child(3)').textContent;
toggleBookMark(screenName, true); // パネルを非表示に
});
}
// 全てのブックマークをOFFにする
function doBookMarkAllOff() {
const checkboxes = document.querySelectorAll('#selectionTableBody .bookMark-checkbox');
checkboxes.forEach(checkbox => {
checkbox.checked = false;
const screenName = checkbox.closest('tr').querySelector('td:nth-child(3)').textContent;
toggleBookMark(screenName, false); // ブックマークをOFFに
});
}
</script>
</body>
</html>