diff --git a/package.json b/package.json index 6ca12795..a7ebe1ae 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "@nodesecure/documentation-ui": "^1.3.0", "@nodesecure/flags": "^2.4.0", "@nodesecure/i18n": "^3.4.0", + "@nodesecure/licenses-conformance": "^2.1.0", "@nodesecure/npm-registry-sdk": "^1.6.1", "@nodesecure/ossf-scorecard-sdk": "^2.0.0", "@nodesecure/rc": "^1.5.0", diff --git a/public/css/components/gauge.css b/public/css/components/gauge.css index 982f73e2..281e51b3 100644 --- a/public/css/components/gauge.css +++ b/public/css/components/gauge.css @@ -6,8 +6,7 @@ .gauge>.line { display: flex; - height: 24px; - align-items: center; + flex-direction: column; color: #546884; } @@ -19,7 +18,25 @@ margin-top: 5px; } -.gauge>.line .item-name { +.gauge>.line>.line--column { + display: flex; + height: 24px; + align-items: center; + justify-content: flex-end; +} + +.gauge>.line>.line--column span { + width: 20px; + text-align: right; + font-family: "mononoki"; + color: var(--secondary-darker); +} +.gauge>.line>.line--column.border-bottom { + border-bottom: 1px solid #8080803d; + padding-bottom: 5px; +} + +.gauge .item-name { width: 130px; flex-shrink: 0; white-space: nowrap; @@ -30,7 +47,7 @@ letter-spacing: 1px; } -.gauge>.line .gauge--bar { +.gauge .gauge--bar { flex-grow: 1; margin: 0 10px; background: #e1e4e6; @@ -38,14 +55,21 @@ border-radius: 4px; overflow: hidden; } -.gauge>.line .gauge--bar >.usage { +.gauge .gauge--bar >.usage { height: inherit; background-color: var(--secondary-darker); } -.gauge>.line span { - width: 20px; - text-align: right; +.gauge .chip { font-family: "mononoki"; - color: var(--secondary-darker); + background: #8dabe536; + border-radius: 8px; + font-size: 14px; + padding: 3px 5px; +} +.gauge .chip:last-child { + margin-right: 30px; +} +.gauge .chip + .chip { + margin-left: 10px; } diff --git a/public/js/components/gauge.js b/public/js/components/gauge.js index c3557170..3fc3d3b2 100644 --- a/public/js/components/gauge.js +++ b/public/js/components/gauge.js @@ -3,13 +3,16 @@ import * as utils from "../utils.js"; export class Gauge { /** - * @param {[string, number][]} data + * @param {{ name: string, value: number, chips?: string[] }[]} data */ - constructor(data, options = {}) { - this.searchName = options.searchName ?? null; + constructor( + data, + options = {} + ) { + this.maxLength = options.maxLength ?? 8; this.data = data; - this.length = data.reduce((prev, curr) => prev + curr[1], 0); + this.length = data.reduce((prev, curr) => prev + curr.value, 0); } pourcentFromValue(value) { @@ -28,44 +31,70 @@ export class Gauge { }); } - createLine(text, value) { + *createChips(chips) { + for (const text of chips) { + yield utils.createDOMElement("div", { + className: "chip", + text + }); + } + } + + /** + * @param {!string} text + * @param {!number} value + * @param {string[]} chips + * @returns {HTMLDivElement} + */ + createLine( + text, + value, + chips + ) { + const columnsLines = [ + utils.createDOMElement("div", { + className: "line--column", + childs: [ + utils.createDOMElement("p", { className: "item-name", text }), + this.createGaugeBar(this.pourcentFromValue(value)), + utils.createDOMElement("span", { text: value }) + ] + }) + ]; + if (chips !== null) { + columnsLines.push( + utils.createDOMElement("div", { + classList: ["line--column", "border-bottom"], + childs: [...this.createChips(chips)] + }) + ); + } + return utils.createDOMElement("div", { className: "line", - childs: [ - utils.createDOMElement("p", { className: "item-name", text }), - this.createGaugeBar(this.pourcentFromValue(value)), - utils.createDOMElement("span", { text: value }) - ] + childs: columnsLines }); } render() { const childs = []; - const hideItemsLength = 8; - const hideItems = this.data.length > hideItemsLength; + const hideItems = this.data.length > this.maxLength; for (let id = 0; id < this.data.length; id++) { - const [name, value] = this.data[id]; + const { name, value, chips = null } = this.data[id]; if (value === 0) { continue; } - const line = this.createLine(name, value); - // if (this.searchName !== null) { - // line.addEventListener("click", () => { - // console.log(name, value); - // window.searchbar.addNewSearchText(this.searchName, name); - // }); - // } - - if (hideItems && id >= hideItemsLength) { + const line = this.createLine(name, value, chips); + if (hideItems && id >= this.maxLength) { line.classList.add("hidden"); } childs.push(line); } if (hideItems) { - childs.push(utils.createExpandableSpan(hideItemsLength)); + childs.push(utils.createExpandableSpan(this.maxLength)); } return utils.createDOMElement("div", { diff --git a/public/js/components/home.js b/public/js/components/home.js index a00f7e84..20b1d282 100644 --- a/public/js/components/home.js +++ b/public/js/components/home.js @@ -1,5 +1,6 @@ // Import Third-party Dependencies import { NodeSecureDataSet, getJSON } from "@nodesecure/vis-network"; +import { licenseIdConformance } from "@nodesecure/licenses-conformance"; // Import Internal Dependencies import * as utils from "../utils.js"; @@ -151,16 +152,33 @@ export class HomeView { generateExtensions() { const extensions = [...Object.entries(this.secureDataSet.extensions)] - .sort(([, left], [, right]) => right - left); + .sort(([, left], [, right]) => right - left) + .map(([name, value]) => ({ name, value })); document.getElementById("home-extensions").appendChild( - new Gauge(extensions, { searchName: "ext" }).render() + new Gauge(extensions).render() ); } generateLicenses() { const licenses = [...Object.entries(this.secureDataSet.licenses)] - .sort(([, left], [, right]) => right - left); + .sort(([, left], [, right]) => right - left) + .flatMap(([name, value]) => { + const result = licenseIdConformance(name); + if (!result.ok) { + return []; + } + + return [ + { + name, + value, + chips: Object.entries(result.value.spdx) + .filter(([key]) => key !== "includesDeprecated") + .map(([key, value]) => `${value ? "✔️" : "❌"} ${key}`) + } + ]; + }); document.getElementById("home-licenses").appendChild( new Gauge(licenses).render()