diff --git a/tools/cldr-apps/js/package-lock.json b/tools/cldr-apps/js/package-lock.json index af8d0ec5c39..b556397fa93 100644 --- a/tools/cldr-apps/js/package-lock.json +++ b/tools/cldr-apps/js/package-lock.json @@ -16,6 +16,7 @@ "marked": "^4.3.0", "swagger-client": "^3.26.7", "vue": "^3.2.47", + "vue-virtual-scroller": "^2.0.0-beta.8", "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.0/xlsx-0.20.0.tgz" }, "devDependencies": { @@ -2767,6 +2768,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mitt": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-2.1.0.tgz", + "integrity": "sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg==" + }, "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", @@ -4269,6 +4275,22 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/vue-observe-visibility": { + "version": "2.0.0-alpha.1", + "resolved": "https://registry.npmjs.org/vue-observe-visibility/-/vue-observe-visibility-2.0.0-alpha.1.tgz", + "integrity": "sha512-flFbp/gs9pZniXR6fans8smv1kDScJ8RS7rEpMjhVabiKeq7Qz3D9+eGsypncjfIyyU84saU88XZ0zjbD6Gq/g==", + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/vue-resize": { + "version": "2.0.0-alpha.1", + "resolved": "https://registry.npmjs.org/vue-resize/-/vue-resize-2.0.0-alpha.1.tgz", + "integrity": "sha512-7+iqOueLU7uc9NrMfrzbG8hwMqchfVfSzpVlCMeJQe4pyibqyoifDNbKTZvwxZKDvGkB+PdFeKvnGZMoEb8esg==", + "peerDependencies": { + "vue": "^3.0.0" + } + }, "node_modules/vue-types": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/vue-types/-/vue-types-3.0.2.tgz", @@ -4291,6 +4313,19 @@ "node": ">=0.10.0" } }, + "node_modules/vue-virtual-scroller": { + "version": "2.0.0-beta.8", + "resolved": "https://registry.npmjs.org/vue-virtual-scroller/-/vue-virtual-scroller-2.0.0-beta.8.tgz", + "integrity": "sha512-b8/f5NQ5nIEBRTNi6GcPItE4s7kxNHw2AIHLtDp+2QvqdTjVN0FgONwX9cr53jWRgnu+HRLPaWDOR2JPI5MTfQ==", + "dependencies": { + "mitt": "^2.1.0", + "vue-observe-visibility": "^2.0.0-alpha.1", + "vue-resize": "^2.0.0-alpha.1" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, "node_modules/warning": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", diff --git a/tools/cldr-apps/js/package.json b/tools/cldr-apps/js/package.json index 132e4a03c05..bddc1fd55fe 100644 --- a/tools/cldr-apps/js/package.json +++ b/tools/cldr-apps/js/package.json @@ -48,6 +48,7 @@ "marked": "^4.3.0", "swagger-client": "^3.26.7", "vue": "^3.2.47", + "vue-virtual-scroller": "^2.0.0-beta.8", "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.0/xlsx-0.20.0.tgz" } } diff --git a/tools/cldr-apps/js/src/esm/cldrDash.mjs b/tools/cldr-apps/js/src/esm/cldrDash.mjs index e109db3c245..56c1927e986 100644 --- a/tools/cldr-apps/js/src/esm/cldrDash.mjs +++ b/tools/cldr-apps/js/src/esm/cldrDash.mjs @@ -10,13 +10,6 @@ import * as cldrStatus from "./cldrStatus.mjs"; import * as cldrSurvey from "./cldrSurvey.mjs"; import * as XLSX from "xlsx"; -/** - * Notifications in these categories are combined so that there is not more than one per page. - * These categories can have well over 10,000 notifications, causing performance problems on - * the front end. - */ -const CATS_ONE_PER_PAGE = ["Abstained" /* , "Missing" */]; - class DashData { /** * Construct a new DashData object @@ -31,8 +24,6 @@ class DashData { // An object whose keys are xpstrid (xpath hex IDs like "db7b4f2df0427e4"), and whose values are DashEntry objects this.pathIndex = {}; this.hiddenObject = null; - this.pageCombinedEntries = {}; // map page to array of notification xpstrid on that page - this.updatingPath = false; } addEntriesFromJson(notifications) { @@ -53,107 +44,32 @@ class DashData { * @param {Object} e (entry in old format, from json) */ addEntry(cat, group, e) { - try { - this.addCategory(cat); - this.catSize[cat]++; - if (!this.catFirst[cat]) { - this.catFirst[cat] = e.xpstrid; - } - if (CATS_ONE_PER_PAGE.includes(cat)) { - this.addCombinedEntry(cat, group, e); - } else if (this.pathIndex[e.xpstrid]) { - this.updateEntry(cat, group, e); - } else { - this.addNewEntry(cat, group, e); - } - } catch (err) { - console.error("Error in addEntry: " + err); + this.addCategory(cat); + this.catSize[cat]++; + if (!this.catFirst[cat]) { + this.catFirst[cat] = e.xpstrid; } - } - - addCombinedEntry(cat, group, e) { - try { - const page = group.page; - if (!this.pageCombinedEntries[cat]) { - this.pageCombinedEntries[cat] = {}; + if (this.pathIndex[e.xpstrid]) { + const dashEntry = this.pathIndex[e.xpstrid]; + dashEntry.addCategory(cat); + dashEntry.setWinning(e.winning); + if (e.comment) { + dashEntry.setComment(e.comment); } - if (!this.pageCombinedEntries[cat][page]?.length) { - this.pageCombinedEntries[cat][page] = new Array(e.xpstrid); - this.addNewEntry(cat, group, e); - } else { - // TODO: make this work. unshift instead of push may be appropriate - // during an update (following a vote), since the combined notification - // is temporarily removed then added back, and in this case it may belong - // at the start of the array, not the end. - // Reference: https://unicode-org.atlassian.net/browse/CLDR-17658 - if (this.updatingPath) { - this.pageCombinedEntries[cat][page].unshift(e.xpstrid); - } else { - this.pageCombinedEntries[cat][page].push(e.xpstrid); - } - // Use the FIRST item in the array as the representative, - // with its comment indicating the size of the array - const xpstrid = this.pageCombinedEntries[cat][page][0]; - if (!xpstrid) { - console.error( - "Existing xpstrid not found in addCombinedEntries for cat = " + - cat + - ", page = " + - page - ); - return; - } - const dashEntry = this.pathIndex[xpstrid]; - if (!dashEntry) { - console.error( - "Existing entry not found in addCombinedEntry for cat = " + - cat + - ", page = " + - page - ); - return; - } - this.setComment(dashEntry, cat, page, null); + if (e.subtype) { + dashEntry.setSubtype(e.subtype); } - } catch (err) { - console.error("Error in addCombinedEntry: " + err); - } - } - - updateEntry(cat, group, e) { - const dashEntry = this.pathIndex[e.xpstrid]; - dashEntry.addCategory(cat); - dashEntry.setWinning(e.winning); - this.setComment(dashEntry, cat, group.page, e.comment); - if (e.subtype) { - dashEntry.setSubtype(e.subtype); - } - } - - addNewEntry(cat, group, e) { - const dashEntry = new DashEntry(e.xpstrid, e.code, e.english); - dashEntry.setSectionPageHeader(group.section, group.page, group.header); - dashEntry.addCategory(cat); - dashEntry.setWinning(e.winning); - dashEntry.setPreviousEnglish(e.previousEnglish); - this.setComment(dashEntry, cat, group.page, e.comment); - dashEntry.setSubtype(e.subtype); - dashEntry.setChecked(this.itemIsChecked(e)); - this.entries.push(dashEntry); - this.pathIndex[e.xpstrid] = dashEntry; - return e.xpstrid; - } - - setComment(dashEntry, cat, page, comment) { - if (CATS_ONE_PER_PAGE.includes(cat)) { - dashEntry.setComment( - "Total " + - cat + - " entries on this page: " + - this.pageCombinedEntries[cat][page].length - ); } else { - dashEntry.setComment(comment); + const dashEntry = new DashEntry(e.xpstrid, e.code, e.english); + dashEntry.setSectionPageHeader(group.section, group.page, group.header); + dashEntry.addCategory(cat); + dashEntry.setWinning(e.winning); + dashEntry.setPreviousEnglish(e.previousEnglish); + dashEntry.setComment(e.comment); + dashEntry.setSubtype(e.subtype); + dashEntry.setChecked(this.itemIsChecked(e)); + this.entries.push(dashEntry); + this.pathIndex[e.xpstrid] = dashEntry; } } @@ -234,13 +150,9 @@ class DashData { } removeEntry(dashEntry) { - const xpstrid = dashEntry.xpstrid; this.removeEntryCats(dashEntry); - // Changed xpstrid means the entry wasn't really removed but was kept as representative of combined category - if (xpstrid === dashEntry.xpstrid) { - const index = this.entries.indexOf(dashEntry); - this.entries.splice(index, 1); - } + const index = this.entries.indexOf(dashEntry); + this.entries.splice(index, 1); } removeEntryCats(dashEntry) { @@ -250,42 +162,12 @@ class DashData { this.cats.delete(cat); delete this.catSize[cat]; } - if (CATS_ONE_PER_PAGE.includes(cat)) { - this.removeCombinedEntryCat(dashEntry, cat); - } else if (this.catFirst[cat] === dashEntry.xpstrid) { + if (this.catFirst[cat] === dashEntry.xpstrid) { this.findCatFirst(cat); } }); } - removeCombinedEntryCat(dashEntry, cat) { - const page = dashEntry.page; - this.pageCombinedEntries[cat][page].shift(); - if (this.pageCombinedEntries[cat][page].length > 0) { - if (this.catFirst[cat] === dashEntry.xpstrid) { - const nextXpstrid = this.pageCombinedEntries[cat][page][0]; - // If this is the only category for this entry, then we can revise the - // entry to use the next xpstrid for the page -- unless that next xpstrid - // is already present for a different category... - if (dashEntry.cats.size === 1 && !this.pathIndex[nextXpstrid]) { - dashEntry.xpstrid = nextXpstrid; - delete this.pathIndex.xpstrid; - this.pathIndex[nextXpstrid] = dashEntry; - this.catFirst[cat] = nextXpstrid; - this.setComment(dashEntry, cat, page, null); - } else { - // TODO: fix this; work in progress - // Reference: https://unicode-org.atlassian.net/browse/CLDR-17658 - // Remove this cat from the existing entry - dashEntry.cats.delete(cat); - // if (dashEntry.cats.size === 0) { - // delete this.pathIndex.xpstrid; - // } - } - } - } - } - findCatFirst(cat) { for (let dashEntry of this.entries) { if (dashEntry.cat === cat) { @@ -453,7 +335,6 @@ function convertData(json) { * containing notifications for a single path (old format) */ function updatePath(dashData, json) { - dashData.updatingPath = true; try { if (json.xpstrid in dashData.pathIndex) { // We already have an entry for this path @@ -471,7 +352,6 @@ function updatePath(dashData, json) { } catch (e) { cldrNotify.exception(e, "updating path for Dashboard"); } - dashData.updatingPath = false; return dashData; // for unit test } @@ -595,7 +475,7 @@ async function downloadXlsx(data, locale, cb) { * @returns {Array} */ async function getLocaleErrors(locale) { - const client = await cldrClient.getClient(); + const client = cldrClient.getClient(); return await client.apis.voting.getLocaleErrors({ locale }); } diff --git a/tools/cldr-apps/js/src/views/DashboardWidget.vue b/tools/cldr-apps/js/src/views/DashboardWidget.vue index a6819af49c7..0e0aa8282c1 100644 --- a/tools/cldr-apps/js/src/views/DashboardWidget.vue +++ b/tools/cldr-apps/js/src/views/DashboardWidget.vue @@ -79,6 +79,26 @@