diff --git a/package-lock.json b/package-lock.json index 6419b97cf4a9efb64a39963bdb0b3b25a45da648..8ad1778e63a5747aae56e19ee2dc92eabc8a2a86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,19 @@ { "name": "dccviewer-js", - "version": "0.1.0", + "version": "0.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dccviewer-js", - "version": "0.1.0", + "version": "0.1.1", "dependencies": { "dsiunits-js": "^0.9.1", "events": "^3.3.0", "jsoneditor": "^9.5.6", - "mime": "^4.0.6", - "mime-types": "^2.1.35", "plotly.js-dist": "^2.18.2", + "prismjs": "^1.29.0", + "tippy.js": "^6.2.5", "xml2js": "^0.4.23" }, "devDependencies": { @@ -23,6 +23,8 @@ "identity-obj-proxy": "^3.0.0", "jest": "^29.0.0", "jest-environment-jsdom": "^29.7.0", + "mime": "^4.0.6", + "mime-types": "^2.1.35", "vite": "^4.5.9" } }, @@ -2543,6 +2545,16 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -5243,6 +5255,7 @@ "version": "4.0.6", "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.6.tgz", "integrity": "sha512-4rGt7rvQHBbaSOF9POGkk1ocRP16Md1x36Xma8sz8h8/vfCUI2OtEIeCqe4Ofes853x4xDoPiFLIT47J5fI/7A==", + "dev": true, "funding": [ "https://github.com/sponsors/broofa" ], @@ -5258,6 +5271,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -5267,6 +5281,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -5643,6 +5658,15 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -6143,6 +6167,15 @@ "node": ">=8" } }, + "node_modules/tippy.js": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.2.5.tgz", + "integrity": "sha512-UIf8G99PMXGmdWPrr36s/DjQBdfxMPwzvPUXsxs3tDFDTZ1SgvKG+Jvt6RJ+aBqYL0oe/STxh3MNkCV3IWAKmw==", + "license": "MIT", + "dependencies": { + "@popperjs/core": "^2.4.4" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", diff --git a/package.json b/package.json index 5f12e85c11112b65be200e107a3eca9b18675aa2..da02f4dc33bd6482569e2a5e34bf993e92207bee 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "events": "^3.3.0", "jsoneditor": "^9.5.6", "plotly.js-dist": "^2.18.2", + "tippy.js": "^6.2.5", "xml2js": "^0.4.23" }, "devDependencies": { @@ -34,9 +35,9 @@ "identity-obj-proxy": "^3.0.0", "jest": "^29.0.0", "jest-environment-jsdom": "^29.7.0", - "vite": "^4.5.9", "mime": "^4.0.6", - "mime-types": "^2.1.35" + "mime-types": "^2.1.35", + "vite": "^4.5.9" }, "babel": { "presets": [ diff --git a/src/renderers/InfluenceConditionsRenderer.js b/src/renderers/InfluenceConditionsRenderer.js index 3ed8474cd1274b9b310713121ab4e877932210bc..39ab26c660f90f31826e890823588d384532b465 100644 --- a/src/renderers/InfluenceConditionsRenderer.js +++ b/src/renderers/InfluenceConditionsRenderer.js @@ -1,5 +1,7 @@ // src/renderers/InfluenceConditionsRenderer.js import { DSIUnit } from "dsiunits-js"; +import tippy from 'tippy.js'; +import 'tippy.js/dist/tippy.css'; export class InfluenceConditionsRenderer { constructor(influenceConditionsData, language) { @@ -95,9 +97,12 @@ export class InfluenceConditionsRenderer { if (Object.keys(additional).length > 0) { const infoSpan = document.createElement('span'); infoSpan.textContent = ' ⓘ'; - infoSpan.style.cursor = 'pointer'; - infoSpan.style.color = '#888'; - infoSpan.title = JSON.stringify(additional, null, 2); + tippy(infoSpan, { + content: formatAdditionalInfoHTML(additional), + allowHTML: true, + theme: 'light' + }) + valueCell.appendChild(infoSpan); } row.appendChild(valueCell); @@ -230,7 +235,7 @@ export class InfluenceConditionsRenderer { // Helper: extract additional data (all keys except the common ones) for tooltip purposes _getAdditionalData(quantity) { - const exclude = ['dcc:name', 'dcc:description', 'si:real', 'si:realListXMLList', 'dcc:noQuantity']; + const exclude = ['dcc:name', 'dcc:description', 'si:real', 'si:realListXMLList']; const additional = {}; Object.keys(quantity).forEach(key => { if (!exclude.includes(key)) { @@ -240,3 +245,20 @@ export class InfluenceConditionsRenderer { return additional; } } + +function formatAdditionalInfoHTML(additional) { + if (typeof additional !== 'object' || additional === null) { + return String(additional); + } + let parts = []; + for (const [key, value] of Object.entries(additional)) { + const valStr = (typeof value === 'object' && value !== null) + ? JSON.stringify(value) + : String(value); + parts.push( + `<span class="tooltip-key" style="color:#007acc;">${key}</span>: ` + + `<span class="tooltip-value" style="color:#cc7000;">${valStr}</span>` + ); + } + return parts.join(', '); +} \ No newline at end of file diff --git a/src/renderers/MeasurementRenderer.js b/src/renderers/MeasurementRenderer.js index 52757c0f96b160e056aa92876ef35ce37b20ab0e..568e27a1402a4e6b577ce075dc00fde2e5e87f61 100644 --- a/src/renderers/MeasurementRenderer.js +++ b/src/renderers/MeasurementRenderer.js @@ -333,10 +333,44 @@ export function renderSingleMeasurementResult(resultObj, language, tabPanel) { uncertaintiesArray.push(info.uncertainty || []); conformityArray.push(info.conformity ? info.conformity.getConformityValues() : []); }); - // Remove the Comments column entirely - // headers.push('Comments'); + let commentsArray = []; + if (listData['dcc:measurementMetaData'] && listData['dcc:measurementMetaData']['dcc:metaData']) { + let metaData = listData['dcc:measurementMetaData']['dcc:metaData']; + if (!Array.isArray(metaData)) { metaData = [metaData]; } + metaData.forEach(md => { + if (md.$ && md.$.refType && md.$.refType.includes('basic_tableRowComment')) { + let commentName = ''; + if (md['dcc:name'] && md['dcc:name']['dcc:content']) { + let content = md['dcc:name']['dcc:content']; + commentName = Array.isArray(content) + ? (content.find(item => item.$ && item.$.lang === language) || content[0])._ || '' + : content._ || content; + } + let commentDescription = ''; + if (md['dcc:description'] && md['dcc:description']['dcc:content']) { + let desc = md['dcc:description']['dcc:content']; + commentDescription = Array.isArray(desc) + ? (desc.find(item => item.$ && item.$.lang === language) || desc[0])._ || '' + : desc._ || desc; + } + let validListStr = ''; + if (md['dcc:validXMLList']) { + validListStr = md['dcc:validXMLList'].trim(); + } + let validArray = validListStr.split(/\s+/); + commentsArray.push({ + name: commentName, + description: commentDescription, + valid: validArray + }); + } + }); + } + headers.push('Comments'); + const tableData = [headers]; +// Then, when building each data row: for (let i = 0; i < xValues.length; i++) { const row = []; row.push(xValues[i]); @@ -350,6 +384,22 @@ export function renderSingleMeasurementResult(resultObj, language, tabPanel) { row.push(conformityArray[idx][i] || ''); } }); + // NEW: Build comment cell by checking each comment's valid array for this row + let tooltipText = ''; + let headerText = ''; + commentsArray.forEach(comment => { + if (comment.valid[i] && comment.valid[i].toLowerCase() === 'true') { + tooltipText += `${comment.description}\n`; + if (!headerText) { + headerText = comment.name; + } + } + }); + if (tooltipText) { + row.push(`<strong>${headerText}</strong> <details style="display:inline;"><summary style="cursor:pointer; display:inline; list-style:none;">▼</summary><div>${tooltipText.trim()}</div></details>`); + } else { + row.push(''); + } tableData.push(row); } renderTable(tableData, computeConformityMapping()); @@ -528,7 +578,12 @@ export function renderSingleMeasurementResult(resultObj, language, tabPanel) { cell.style.backgroundColor = conformityMapping[cellIndex]; } } else { - cell.textContent = cellData; + // For non-header rows, if this is the last column (Comments), use innerHTML. + if (cellIndex === rowData.length - 1) { + cell.innerHTML = cellData; + } else { + cell.textContent = cellData; + } if ( tableData[0][cellIndex] && tableData[0][cellIndex].toLowerCase().includes('conformity') && @@ -544,12 +599,8 @@ export function renderSingleMeasurementResult(resultObj, language, tabPanel) { cell.style.border = '1px solid #ccc'; tr.appendChild(cell); }); - tr.addEventListener('mouseover', () => { - tr.style.backgroundColor = '#eef'; - }); - tr.addEventListener('mouseout', () => { - tr.style.backgroundColor = ''; - }); + tr.addEventListener('mouseover', () => { tr.style.backgroundColor = '#eef'; }); + tr.addEventListener('mouseout', () => { tr.style.backgroundColor = ''; }); table.appendChild(tr); }); tableContainer.appendChild(table); diff --git a/src/renderers/MeasuringEquipmentRenderer.js b/src/renderers/MeasuringEquipmentRenderer.js index defba945d7c660971b6bb5ce6f6e5d3fcb7506ae..e6125b6e3a2c4b7f4edf04579b4283c15617f4e1 100644 --- a/src/renderers/MeasuringEquipmentRenderer.js +++ b/src/renderers/MeasuringEquipmentRenderer.js @@ -1,5 +1,7 @@ // src/renderers/MeasuringEquipmentsRenderer.js -import { DCCRealListQuantity } from '../dccQuantity.js'; +import {DCCQuantity, DCCRealListQuantity, DCCRealQuantity} from '../dccQuantity.js'; +import tippy from 'tippy.js'; +import 'tippy.js/dist/tippy.css'; export class MeasuringEquipmentsRenderer { constructor(measuringEquipmentsData, language) { @@ -92,7 +94,7 @@ export class MeasuringEquipmentsRenderer { // Table header const headerRow = document.createElement('tr'); - ['Quantity', 'Value'].forEach(text => { + ['Quantity', 'Value','Description'].forEach(text => { const th = document.createElement('th'); th.textContent = text; th.style.border = '1px solid #ccc'; @@ -112,10 +114,30 @@ export class MeasuringEquipmentsRenderer { const valueCell = document.createElement('td'); valueCell.style.border = '1px solid #ccc'; valueCell.style.padding = '4px'; - const qtyObj = new DCCRealListQuantity(q); + const qtyObj = new DCCRealQuantity(q); // Use the DSIUnit library to convert the unit string into HTML valueCell.innerHTML = qtyObj.getValues().join(' ') + ' ' + qtyObj.getUnit({ oneLine: true }); + // Attach tooltip for additional data if present + const additional = this._getAdditionalData(q); + if (Object.keys(additional).length > 0) { + const infoSpan = document.createElement('span'); + infoSpan.textContent = ' ⓘ'; + infoSpan.style.cursor = 'pointer'; + infoSpan.style.color = '#888'; + valueCell.appendChild(infoSpan); + tippy(infoSpan, { + content: formatAdditionalInfoHTML(additional), + allowHTML: true, + theme: 'light' + }); + } row.appendChild(valueCell); + // Column 3: Description for the quantity + const descCell = document.createElement('td'); + descCell.style.border = '1px solid #ccc'; + descCell.style.padding = '4px'; + descCell.textContent = this._getText(q['dcc:description']); + row.appendChild(descCell); table.appendChild(row); }); contentDiv.appendChild(table); @@ -138,4 +160,33 @@ export class MeasuringEquipmentsRenderer { } return content._ || content; } + + // Helper: extract additional data (all keys except the common ones) for tooltip purposes + _getAdditionalData(quantity) { + const exclude = ['dcc:name', 'dcc:description', 'si:real', 'si:realListXMLList']; + const additional = {}; + Object.keys(quantity).forEach(key => { + if (!exclude.includes(key)) { + additional[key] = quantity[key]; + } + }); + return additional; + } } + +function formatAdditionalInfoHTML(additional) { + if (typeof additional !== 'object' || additional === null) { + return String(additional); + } + let parts = []; + for (const [key, value] of Object.entries(additional)) { + const valStr = (typeof value === 'object' && value !== null) + ? JSON.stringify(value) + : String(value); + parts.push( + `<span class="tooltip-key" style="color:#007acc;">${key}</span>: ` + + `<span class="tooltip-value" style="color:#cc7000;">${valStr}</span>` + ); + } + return parts.join(', '); +} \ No newline at end of file diff --git a/src/styles.css b/src/styles.css index a5dc6832ac437bf16e01e62135cc45d4a88f256e..2525970ee66189f57ce4acc2eee1bf993dd17b58 100644 --- a/src/styles.css +++ b/src/styles.css @@ -32,3 +32,9 @@ th, td { padding: 10px; border: 1px dashed #999; } + +/* Override Tippy's light theme padding */ +.tippy-box[data-theme~="light"] { + padding: 10px !important; /* Increase padding */ + font-size: 14px; /* Adjust font size if needed */ +}