From 6cd84df952a229f488d5bb2f9c46c4d063dc9264 Mon Sep 17 00:00:00 2001 From: irof Date: Sun, 8 Feb 2026 02:35:17 +0900 Subject: [PATCH 1/2] Add glossary sort controls --- .../resources/templates/assets/glossary.js | 36 +++++++++++++++++-- .../main/resources/templates/glossary.html | 9 +++++ jig-core/src/test/js/glossary.test.js | 18 ++++++++++ 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/jig-core/src/main/resources/templates/assets/glossary.js b/jig-core/src/main/resources/templates/assets/glossary.js index d86800f61..13d4b8152 100644 --- a/jig-core/src/main/resources/templates/assets/glossary.js +++ b/jig-core/src/main/resources/templates/assets/glossary.js @@ -40,6 +40,25 @@ function updateArticleVisibility(controls) { } +// 文字列の比較は日本語を優先しつつ大小を無視する +const termCollator = new Intl.Collator("ja", {numeric: true, sensitivity: "base"}); + +function sortTerms(terms, sortKey) { + const keyMap = { + name: "title", + fqn: "fqn", + simple: "simpleText", + }; + const key = keyMap[sortKey] ?? "title"; + return [...terms].sort((left, right) => { + const leftValue = left?.[key] ?? ""; + const rightValue = right?.[key] ?? ""; + const primary = termCollator.compare(leftValue, rightValue); + if (primary !== 0) return primary; + return termCollator.compare(left?.fqn ?? "", right?.fqn ?? ""); + }); +} + function getFilteredTerms(terms, controls) { if (!controls) return terms; const showEmptyDescription = controls.showEmptyDescription.checked; @@ -167,13 +186,18 @@ function renderMarkdownDescriptions() { .forEach(node => node.innerHTML = marked.parse(node.innerHTML)); } +function renderFilteredTerms(terms, controls) { + const filteredTerms = getFilteredTerms(terms, controls); + const sortedTerms = sortTerms(filteredTerms, controls.sortOrder?.value); + renderGlossaryTerms(sortedTerms); + renderMarkdownDescriptions(); +} + if (typeof document !== "undefined") { document.addEventListener("DOMContentLoaded", function () { if (!document.body.classList.contains("glossary")) return; const terms = getGlossaryData(); - renderGlossaryTerms(terms); - renderMarkdownDescriptions(); const controls = { searchInput: document.getElementById("search-input"), @@ -182,9 +206,10 @@ if (typeof document !== "undefined") { showClass: document.getElementById("show-class"), showMethod: document.getElementById("show-method"), showField: document.getElementById("show-field"), + sortOrder: document.getElementById("sort-order"), }; - const updateArticles = () => updateArticleVisibility(controls); + const updateArticles = () => renderFilteredTerms(terms, controls); controls.searchInput.addEventListener("input", updateArticles); controls.showEmptyDescription.addEventListener("change", updateArticles); @@ -192,6 +217,9 @@ if (typeof document !== "undefined") { controls.showClass.addEventListener("change", updateArticles); controls.showMethod.addEventListener("change", updateArticles); controls.showField.addEventListener("change", updateArticles); + if (controls.sortOrder) { + controls.sortOrder.addEventListener("change", updateArticles); + } const exportButton = document.getElementById("export-csv"); if (exportButton) { exportButton.addEventListener("click", () => { @@ -209,11 +237,13 @@ if (typeof document !== "undefined") { if (typeof module !== "undefined" && module.exports) { module.exports = { updateArticleVisibility, + sortTerms, getFilteredTerms, getGlossaryData, escapeCsvValue, buildGlossaryCsv, renderGlossaryTerms, + renderFilteredTerms, renderMarkdownDescriptions, }; } diff --git a/jig-core/src/main/resources/templates/glossary.html b/jig-core/src/main/resources/templates/glossary.html index 9b306a30e..46926539f 100644 --- a/jig-core/src/main/resources/templates/glossary.html +++ b/jig-core/src/main/resources/templates/glossary.html @@ -28,6 +28,15 @@ +
+ 並び順: + + +
diff --git a/jig-core/src/test/js/glossary.test.js b/jig-core/src/test/js/glossary.test.js index 9893b6183..464f45906 100644 --- a/jig-core/src/test/js/glossary.test.js +++ b/jig-core/src/test/js/glossary.test.js @@ -92,6 +92,24 @@ test.describe('glossary.js', () => { }); }); + test.describe('ソート', () => { + test('名前・完全修飾名・単純名で並び替える', () => { + const terms = [ + {title: 'Order', simpleText: 'Order', fqn: 'app.Order'}, + {title: 'Account', simpleText: 'Account', fqn: 'app.Account'}, + {title: 'User', simpleText: 'User', fqn: 'app.domain.User'}, + ]; + + const byName = glossary.sortTerms(terms, 'name').map(term => term.title); + const byFqn = glossary.sortTerms(terms, 'fqn').map(term => term.fqn); + const bySimple = glossary.sortTerms(terms, 'simple').map(term => term.simpleText); + + assert.deepEqual(byName, ['Account', 'Order', 'User']); + assert.deepEqual(byFqn, ['app.Account', 'app.domain.User', 'app.Order']); + assert.deepEqual(bySimple, ['Account', 'Order', 'User']); + }); + }); + test.describe('データ読み込み', () => { test('glossary-dataから用語一覧を取得する', () => { const doc = setupDocument(); From c277f83347fa1f7311abbed0d1e7d5c79e04eef3 Mon Sep 17 00:00:00 2001 From: irof Date: Sun, 8 Feb 2026 02:41:19 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=E8=AA=BF=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jig-core/src/main/resources/templates/glossary.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/jig-core/src/main/resources/templates/glossary.html b/jig-core/src/main/resources/templates/glossary.html index 46926539f..a6f957f86 100644 --- a/jig-core/src/main/resources/templates/glossary.html +++ b/jig-core/src/main/resources/templates/glossary.html @@ -29,8 +29,7 @@
- 並び順: - +