From e6fc46272be7a2c4940c11fb0d84e449f6b16f9d Mon Sep 17 00:00:00 2001 From: andylizi Date: Wed, 5 Oct 2022 17:24:11 +0800 Subject: [PATCH] Provide tooltips for features --- css/custom.css | 7 ++++- features.json | 32 ++++++++++++------- features.schema.json | 20 ++++++++++-- roadmap.js | 75 +++++++++++++++++++++++++++----------------- 4 files changed, 92 insertions(+), 42 deletions(-) diff --git a/css/custom.css b/css/custom.css index b9cdbb5..f5fd490 100644 --- a/css/custom.css +++ b/css/custom.css @@ -1974,7 +1974,7 @@ pre code::before, pre code::after { white-space: normal; } -#feature-support td { +#feature-support td, #feature-support th { position: relative; /* for tooltip */ } @@ -1987,6 +1987,10 @@ pre code::before, pre code::after { background: rgba(0, 0, 0, .04); } +#feature-support time { + white-space: nowrap; +} + .feature-cell { position: relative; height: 24px; /* height of the icon inside */ @@ -2058,6 +2062,7 @@ pre code::before, pre code::after { white-space: normal; background: #fefefe; font-size: 0.8em; + font-weight: normal; border-radius: 2px; outline: none; diff --git a/features.json b/features.json index 849bd04..30cce18 100644 --- a/features.json +++ b/features.json @@ -4,12 +4,14 @@ "bigInt": { "description": "JS BigInt to Wasm i64 integration", "url": "https://github.com/WebAssembly/JS-BigInt-integration", - "phase": 5 + "phase": 5, + "stdznDate": "2020-06-09" }, "bulkMemory": { "description": "Bulk memory operations", "url": "https://github.com/WebAssembly/bulk-memory-operations/blob/master/proposals/bulk-memory-operations/Overview.md", - "phase": 5 + "phase": 5, + "stdznDate": "2021-02-10" }, "exceptions": { "description": "Exception handling", @@ -19,7 +21,8 @@ "extendedConst": { "description": "Extended constant expressions", "url": "https://github.com/WebAssembly/extended-const/blob/master/proposals/extended-const/Overview.md", - "phase": 4 + "phase": 4, + "stdznDate": "2023-01-31" }, "gc": { "description": "Garbage collection", @@ -39,42 +42,49 @@ "multiValue": { "description": "Multi-value", "url": "https://github.com/WebAssembly/spec/blob/master/proposals/multi-value/Overview.md", - "phase": 4 + "phase": 4, + "stdznDate": "2020-03-11" }, "mutableGlobals": { "description": "Mutable globals", "url": "https://github.com/WebAssembly/mutable-global/blob/master/proposals/mutable-global/Overview.md", - "phase": 5 + "phase": 5, + "stdznDate": "2018-06-06" }, "referenceTypes": { "description": "Reference types", "url": "https://github.com/WebAssembly/reference-types/blob/master/proposals/reference-types/Overview.md", - "phase": 5 + "phase": 5, + "stdznDate": "2021-02-10" }, "relaxedSimd": { "description": "Relaxed SIMD", - "url": "https://github.com/WebAssembly/relaxed-simd/tree/main/proposals/relaxed-simd", + "url": "https://github.com/WebAssembly/relaxed-simd/blob/main/proposals/relaxed-simd/Overview.md", "phase": 3 }, "saturatedFloatToInt": { "description": "Non-trapping float-to-int conversions", "url": "https://github.com/WebAssembly/spec/blob/master/proposals/nontrapping-float-to-int-conversion/Overview.md", - "phase": 5 + "phase": 5, + "stdznDate": "2020-03-11" }, "signExtensions": { "description": "Sign-extension operations", "url": "https://github.com/WebAssembly/spec/blob/master/proposals/sign-extension-ops/Overview.md", - "phase": 5 + "phase": 5, + "stdznDate": "2020-03-11" }, "simd": { "description": "Fixed-width SIMD", "url": "https://github.com/WebAssembly/simd/blob/master/proposals/simd/SIMD.md", - "phase": 5 + "phase": 5, + "stdznDate": "2021-07-14" }, "tailCall": { "description": "Tail calls", "url": "https://github.com/WebAssembly/tail-call/blob/master/proposals/tail-call/Overview.md", - "phase": 4 + "phase": 4, + "stdznDate": "2023-01-17" }, "threads": { "description": "Threads and atomics", diff --git a/features.schema.json b/features.schema.json index c79b8ab..0f4a336 100644 --- a/features.schema.json +++ b/features.schema.json @@ -9,11 +9,22 @@ "properties": { "description": { "type": "string" }, "url": { "type": "string", "format": "uri-reference" }, - "phase": { "type": "integer", "minimum": 1 } + "phase": { "enum": [1, 2, 3] } }, "additionalProperties": false, "required": ["description", "url", "phase"] }, + "feature-info-standardized": { + "type": "object", + "properties": { + "description": { "type": "string" }, + "url": { "type": "string", "format": "uri-reference" }, + "phase": { "enum": [4, 5] }, + "stdznDate": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$" } + }, + "additionalProperties": false, + "required": ["description", "url", "phase", "stdznDate"] + }, "browser-features": { "type": "object", "properties": { @@ -66,7 +77,12 @@ "$schema": { "type": "string", "format": "uri-reference" }, "features": { "type": "object", - "additionalProperties": { "$ref": "#/definitions/feature-info" } + "additionalProperties": { + "oneOf": [ + { "$ref": "#/definitions/feature-info" }, + { "$ref": "#/definitions/feature-info-standardized" } + ] + } }, "browsers": { "type": "object", diff --git a/roadmap.js b/roadmap.js index 5e4a4c3..6729244 100644 --- a/roadmap.js +++ b/roadmap.js @@ -146,9 +146,10 @@ const __feature_table_error_handler = (e) => { }, [groupName]) ]) ); - for (const { name: featName, description, url } of features) { + for (const { name: featName, description, url, phase, stdznDate } of features) { + // Feature detection for "Your browser" const detectResult = h('td', { - headers: [idMap['table-col']('Your browser'), idMap['table-row'](featName)].join(' ') + headers: [idMap['table-col']('Your browser'), idMap['table-row'](featName)].join(' ') }, [buildCellInner('loading')]); detectWasmFeature(featName).then(supported => { @@ -161,13 +162,18 @@ const __feature_table_error_handler = (e) => { return addTooltip(detectResult, 'Detection unavailable for this feature', [tBody, scrollbox]); }); + // Feature name and it's tooltip + const featureLink = h('a', { href: url, target: '_blank' }, [description]); + const featureHeader = h('th', { + scope: 'row', + id: idMap['table-row'](featName), + headers: idMap['table-group'](groupName) + }, [featureLink]); + addTooltip(featureLink, buildFeatureTooltip(phase, stdznDate), [scrollbox], featureHeader); + tBody.append( h('tr', {}, [ - h('th', { - scope: 'row', - id: idMap['table-row'](featName), - headers: idMap['table-group'](groupName) - }, [h('a', { href: url, target: '_blank' }, [description])]), + featureHeader, detectResult, ...Object.entries(browsers).map(([browserName, { features }]) => { // Meaning of each entry: @@ -210,8 +216,13 @@ const __feature_table_error_handler = (e) => { } else { if (support !== true) throw new TypeError(); box = buildCellInner('yes'); - // Magic value, keep in sync with `renderNote` - note ||= '✓ Supported, introduced in unknown version'; + if (!note) { + note = document.createDocumentFragment(); + note.append('✓ Supported, introduced in unknown version ', h('a', { + href: 'https://github.com/WebAssembly/website/blob/main/features.json', + target: '_blank' + }, ['(contribute data)'])); + } } const cell = h('td', { @@ -252,19 +263,34 @@ const __feature_table_error_handler = (e) => { function buildCellInner(type, text) { const content = text || icon(type); - return h('div', { className: `feature-cell icon-${type}`}, [content]); + return h('div', { className: `feature-cell icon-${type}` }, [content]); + } + + function buildFeatureTooltip(phase, date) { + if (date) { + const fragment = document.createDocumentFragment(); + fragment.append(`Phase ${phase} proposal, standardized on `); + fragment.append(h('time', { dateTime: date }, [date])); + return fragment; + } else { + return `Phase ${phase} proposal` + } } function renderNote(note) { - const fragment = document.createDocumentFragment(); - const isMissingData = note.includes('introduced in unknown version'); - - // Transform markdown-like backticks into html - while (note) { - const [head, body, tail] = splitParts(note, '`'); - head && fragment.append(head); - body && fragment.appendChild(h('code', {}, [body])); - note = tail; + let fragment; + if (typeof note === 'string') { + fragment = document.createDocumentFragment(); + + // Transform markdown-like backticks into html + while (note) { + const [head, body, tail] = splitParts(note, '`'); + head && fragment.append(head); + body && fragment.appendChild(h('code', {}, [body])); + note = tail; + } + } else { + fragment = note; } const firstNode = fragment.firstChild; @@ -283,13 +309,6 @@ const __feature_table_error_handler = (e) => { } } - if (isMissingData) { - fragment.appendChild(h('a', { - href: 'https://github.com/WebAssembly/website/blob/master/features.json', - target: '_blank' - }, [' (contribute data)'])) - } - return fragment; } @@ -318,7 +337,7 @@ const __feature_table_error_handler = (e) => { window.addEventListener('resize', updateAll, { passive: true }); let counter = 0; - return (reference, note, boundary) => + return (reference, note, boundary, parent = reference) => module.then(({ computePosition, offset, flip, shift, arrow }) => { const tooltipId = `tooltip-${counter++}`; const tooltip = h('div', { id: tooltipId, className: 'feature-tooltip', role: 'tooltip' }); @@ -384,7 +403,7 @@ const __feature_table_error_handler = (e) => { timeout = setTimeout(() => setVisible(false), 80); }); - reference.appendChild(tooltip); + parent.appendChild(tooltip); reference.setAttribute('aria-describedby', tooltipId); return tooltip; }).catch(__feature_table_error_handler);