UNPKG

@devoinc/genesys-brand-devo

Version:
1,133 lines (1,033 loc) 39 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>Design Tokens</title> <link href='https://fonts.googleapis.com/css?family=Poppins:400,500,600' rel='stylesheet'> <style> :root { --box-shadow-size: 6px; --color-heading: #1f282e; --color-body: #5b6870; --color-strong: #3c4952; --color-weak: #81919c; --fs-base: 1.4rem; --fs-sm: 1.3rem; --lh-base: 2.4rem; --lh-sm: 2rem; --preview-width: 6rem; --preview-height: 2rem; } /* GLOBAL ----------------------------------------------------------------*/ html { font-size: 0.625rem; } h1 { color: var(--color-heading); margin: 3.6rem 0 1.2rem 0; } body { margin: 0; padding: 0 4.8rem; font-family: 'Poppins', sans-serif; font-size: var(--fs-base); color: var(--color-body); } p { padding: 0; border: 0; margin: 0; max-width: 99.2rem; -webkit-font-smoothing: antialiased; } label { color: var(--color-strong); } mark { background-color: #c6dbf5; } .hidden { display: none !important; } .column { display: flex; flex-direction: column; gap: 2.4rem; } .count { text-align: right; margin: 2.4rem 0; font-size: var(--fs-sm); } .wrapper { display: flex; gap: 4.8rem; margin-top: 3.6rem; font-size: 1.4rem; line-height: var(--lh-base); } .wrapper__aside { flex: 0 0 30rem; position: sticky; top: 0; } .wrapper__main { flex: 1 1 auto; } .no-margin-top { margin-top: 0; } /* FILTER ----------------------------------------------------------------*/ .filter { padding-top: 1rem; position: sticky; top: 0; display: flex; flex-direction: column; } .filter__heading { margin: 2.4rem 0 0.8rem 0; color: var(--color-strong); } /* TABLE -----------------------------------------------------------------*/ table { box-sizing: inherit; border: 0; font: inherit; vertical-align: baseline; border-spacing: 0; table-layout: fixed; width: 100%; padding: 0; border-collapse: collapse; } table tbody { font-size: var(--fs-sm); line-height: 1.5; } table tr th { padding: 1rem 1.2rem; white-space: nowrap; text-align: left; color: var(--color-heading); font-weight: bold; } table tr td { padding: 0.8rem 1.2rem; border-bottom: 0.1rem solid rgba(0, 0, 0, 0.08); overflow-wrap: break-word; } table tr th, table tr td { width: 25%; } table tr th:first-child, table tr td:first-child { width: 50%; } table tr td:first-child { color: var(--color-heading); } table tr th:last-child, table tr td:last-child { width: 20%; } .table__sticky { position: sticky; top: 0; box-shadow: 0 4px var(--box-shadow-size) 0 rgba(12, 41, 56, 0.08), 0 2px 2px 1px rgba(12, 41, 56, 0.04); background-color: #fff; } .table__sticky::after, .table__sticky::before, .table__header::after { content: ""; position: absolute; z-index: 1; background-color: #fff; } .table__header::after { top: calc(var(--box-shadow-size) * -1); z-index: 1; height: var(--box-shadow-size); width: 100%; background-color: #fff; } .table__sticky::before, .table__sticky::after { content: ""; position: absolute; top: 0; z-index: 1; width: var(--box-shadow-size); height: calc(100% + (var(--box-shadow-size) * 2)); background-color: #fff; } .table__sticky::after { right: calc(var(--box-shadow-size) * -1); } .table__sticky::before { left: calc(var(--box-shadow-size) * -1); } /* FIELDS ----------------------------------------------------------------*/ .field { display: flex; flex-direction: column; flex: 1 1 auto; gap: 0.4rem; } .field+.field { margin-top: 1.2rem } input, select { width: 100%; height: 3.2rem; padding: 0 1.2rem; border-width: 0.1rem; border-style: solid; border-radius: 0.4rem; box-sizing: border-box; border-color: #c3d3de; } .button__container { display: flex; gap: 0.5rem; cursor: pointer; } .button__container button { width: 4rem; border-width: 0rem; border-style: solid; border-radius: 4px; box-sizing: border-box; background-color: rgba(0, 0, 0, 0.08); color: rgba(0, 0, 0, 0.64); cursor: pointer; } .button__container button:hover { background-color: #cfdce4; } .button__container button.active { background-color: #C6DBF5; text-shadow: 0 0 0.5px; } #regexErrorMsg { color: #d62433; font-size: var(--fs-sm); } /* PREVIEW ---------------------------------------------------------------*/ .preview--border, .preview--border-size, .preview--border-radius, .preview--border-color, .preview--box-shadow, .preview--color { width: var(--preview-width); height: var(--preview-height); } .preview--color, .preview--border, .preview--border-size, .preview--box-shadow, .preview--border-color { border-radius: 0.4rem; } .preview--color { border: 0.2rem solid #fff; outline: 0.1rem solid rgba(0, 0, 0, 0.1); } .preview--border-radius { background-color: rgb(249, 234, 252); } .preview--border-color { border: solid 0.2rem; } .preview--border-size { border-style: solid; border-color: rgba(0, 0, 0, 0.12); } .preview--text-color { font-weight: 600; } .preview--size { max-height: 6.4rem; max-width: 6.4rem; background-color: rgb(225, 250, 242); display: flex; justify-content: center; align-items: center; font-weight: 700; } .preview--line-height { background-color: rgb(225, 250, 242); } </style> </head> <body> <div id="heading"> <h1> <!-- To be filled dinamically --> </h1> <p>The different token formats available in this preview are generated on the fly from the internal JSON output. This preview should not be used to visually validate anything other than tokens in JSON format. </p> </div> <div class="wrapper"> <div id="aside" class="column wrapper__aside"> <div class="filter"> <div class="field"> <label for="searchSelector">Free search</label> <div class="button__container"> <input type="text" id="searchSelector" name="search query" val="" placeholder="Search by token name" /> <button id="regexSelector" title="Use Regular Expression">(.*)</button> </div> <label for="regexSelector" id="regexErrorMsg" class="error__message hidden"><!-- To be filled dinamically --></label> </div> <div class="field"> <label for="formatSelector">Format</label> <select name="format" id="formatSelector"> <option value="js">JavaScript</option> <option value="scss">SCSS</option> <option value="css">CSS</option> <option value="figma">FIGMA</option> <option value="json">JSON (internal)</option> </select> </div> <div class="field"> <label for="schemaSelector">Schema</label> <select name="schema" id="schemaSelector"> <!-- To be filled dinamically --> </select> </div> <div class="filter__heading">Advanced filters </div> <div class="field"> <label for="tierSelector">Tier</label> <select name="tier" id="tierSelector"> <!-- To be filled dinamically --> </select> </div> <div class="field hidden" id="componentSelectorContainer"> <label for="componentSelector">Component</label> <select name="component" id="componentSelector" disabled> <!-- To be filled dinamically --> </select> </div> <div class="field"> <label for="categorySelector">Category</label> <select name="category" id="categorySelector" disabled> <!-- To be filled dinamically --> </select> </div> <div class="field"> <label for="propertySelector">Property</label> <select name="property" id="propertySelector" disabled> <!-- To be filled dinamically --> </select> </div> <p class="count"> <!-- To be filled dinamically --> </p> </div> </div> <div class="wrapper__main"> <div class="table"> <table> <thead class="table__sticky"> <tr> <th>Token</th> <th>Raw Value</th> <th>Preview</th> </tr> </thead> <tbody> <!-- To be filled dinamically --> </tbody> </table> </div> </div> </div> </body> <script> // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -- Variables ------------------------------------------------------------- window.defaultAllOption = "all"; window.advancedAttrs = ["tier", "component", "category", "property"]; window.datasets = []; window.filteredTokens = []; // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -- Fill categories in selects -------------------------------------------- const setTitle = () => { document.getElementsByTagName("h1")[0].innerText = window.config.title || "Design Tokens"; }; // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -- Fill categories in selects -------------------------------------------- const buildFilterOps = (datasets) => { const p = datasets[window.config.scheme.options[0]].reduce( (acc, { attributes, path }) => { const buildObj = (attrPath, obj) => { const defaultUnknown = `__${attrPath[0]}__`; const attrVal = attributes[attrPath[0]] || defaultUnknown; if ( attrPath.length === 1 && Array.isArray(obj) && !obj.includes(attrVal) ) { obj.push(attrVal); } else { const options = window.config?.advancedAttrs?.[attrPath[0]]?.options; if ( obj && !obj[attrVal] && (!options || options?.includes(attrVal)) ) { obj[attrVal] = attrPath.length > 2 ? {} : []; } } if (obj && attrPath.length >= 2) { buildObj(attrPath.slice(1), obj[attributes[attrPath[0]] || defaultUnknown]); } }; buildObj(window.advancedAttrs, acc); return acc; }, {} ); return p; }; const appendOptions = (elId, value, attrs) => { // Do not append if internal. if (value.startsWith('__')) return; const el = document.querySelector(elId); const optEl = document.createElement("option"); optEl.value = value; optEl.innerHTML = value; Object.entries(attrs || []).forEach(([attr, value]) => optEl.setAttribute(attr, value) ); el.appendChild(optEl); }; const fillAdvancedSelectsOptions = () => { const filterOptions = buildFilterOps(window.datasets); // Append defaultAllOption option window.advancedAttrs.forEach((key) => appendOptions(`#${key}Selector`, window.defaultAllOption) ); Object.keys(filterOptions).forEach((tier) => { Object.keys(filterOptions[tier]).forEach((component) => { Object.keys(filterOptions[tier][component]).forEach((category) => { filterOptions[tier][component][category].forEach((property) => { appendOptions("#propertySelector", property, { "data-tier": tier, "data-component": component, "data-category": category, }); }); appendOptions("#categorySelector", category, { "data-tier": tier, "data-component": component }); }); appendOptions("#componentSelector", component, { "data-tier": tier }); }); appendOptions("#tierSelector", tier); }); }; fillSchemaSelector = () => { window.config.scheme.options.forEach((schema) => appendOptions("#schemaSelector", schema) ); }; // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -- Fetch config ---------------------------------------------------------- const fetchConfig = () => { return new Promise((resolve, reject) => { const xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = () => { if (xmlhttp.readyState == XMLHttpRequest.DONE) { if (xmlhttp.status == 200) { const config = JSON.parse(xmlhttp.responseText); resolve(config); } else if (xmlhttp.status == 404) { reject("Invalid path to config file"); } else { reject("Ups, something unexpected happened"); } } }; const path = `./preview-config.json`; xmlhttp.open("GET", path, true); xmlhttp.send(); }); }; // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -- Fetch datasets -------------------------------------------------------- const fetchDatasets = () => { const datasetsPromises = window.config.scheme.options.map( (schema) => new Promise((resolve, reject) => { const xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = () => { if (xmlhttp.readyState == XMLHttpRequest.DONE) { if (xmlhttp.status == 200) { const ds = JSON.parse(xmlhttp.responseText); resolve({ schema, tokens: ds.sort((a, b) => a.name.localeCompare(b.name)), }); } else if (xmlhttp.status == 404) { reject("Invalid path to tokens"); } else { reject("Ups, something unexpected happened"); } } }; const path = `${schema}/json/tokens.json.all.json`; xmlhttp.open("GET", path, true); xmlhttp.send(); }) ); return Promise.all(datasetsPromises); }; // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -- Render content -------------------------------------------------------- const camelToKebabCase = (str) => str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`); const name = (el) => "<td>" + el + "</td>"; const rawValue = (el) => "<td>" + el + "</td>"; const colorPreview = (color) => '<div class="preview preview--color" style="background-color:' + color + ';"></div>'; const borderColorPreview = (color) => '<div class="preview preview--border-color" style="border-color:' + color + ';"></div>'; const boxShadowPreview = (shadow) => '<div class="preview preview--box-shadow" style="box-shadow:' + shadow + ';"></div>'; const borderRadiusPreview = (radius) => '<div class="preview preview--border-radius" style="border-radius:' + radius + ';"></div>'; const borderPreview = (border) => '<div class="preview preview--border" style="border:' + border + ';"></div>'; const borderSizePreview = (size) => '<div class="preview preview--border-size" style="border-width:' + size + ';"></div>'; const textColorPreview = (color) => '<div class="preview preview--text-color" style="color:' + color + ';">TOKEN</div>'; const fontSizePreview = (size) => '<div style="font-size:' + size + ';">TOKEN</div>'; const fontWeightPreview = (weight) => '<div style="font-weight:' + weight + ';">TOKEN</div>'; const lineSizePreview = (size) => '<div class="preview preview--line-height" style="height:' + size + ';"></div>'; const sizePreview = (size) => { const sizeNumber = parseFloat(size?.substring(0, size.length - 3)); const result = sizeNumber < 6 ? size : "6rem"; return ('<div class="preview preview--size" style="height:' + result + "; width:" + result + ';">' + (sizeNumber < 6 ? "" : "bigger") + "</div>"); }; const preview = (token) => { const { path, name, value } = token; const container = (content = "") => "<td>" + content + "</td>"; if (path.includes("color")) { if (path.includes("text")) { return container(textColorPreview(value)); } else if (path.includes("border")) { return container(borderColorPreview(value)) } else { return container(colorPreview(value)); } } if (path.includes("boxShadow") || path.includes("textShadow")) { return container(boxShadowPreview(value)); } if (path.includes("size")) { return container(sizePreview(value)); } if (path.includes("fontSize")) { return container(fontSizePreview(value)); } if (path.includes("fontWeight")) { return container(fontWeightPreview(value)); } if (path.includes("lineHeight")) { return container(lineSizePreview(value)); } if (path.includes("shape")) { if (path.includes("border")) { return container(borderPreview(value)); } if (path.includes("borderSize")) { return container(borderSizePreview(value)); } if (path.includes("borderRadius")) { return container(borderRadiusPreview(value)); } return container("n/a"); } return container("n/a"); }; const createRow = (el) => { return `<tr> ${name(el._formatted)} ${rawValue(el.highlighted || el.value)} ${preview( el )} </tr>`; }; const renderContent = () => { const list = document.querySelector("table tbody"); const count = document.querySelector(".count"); const query = document.querySelector("#searchSelector").value; const rows = window.filteredTokens.map((el) => createRow(el)); list.innerHTML = rows.join(""); count.innerHTML = "Showing " + rows.length + "/" + window.datasets[window.config.scheme.options[0]].length; }; // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -- Form elements handlers ------------------------------------------------ const handleTierSelectorChange = (value) => { // Enable/disabled next select if (value !== window.defaultAllOption) { if (value === 'cmp') { document.getElementById("componentSelector").disabled = false; } else { document.getElementById("categorySelector").disabled = false; } } else { document.getElementById("componentSelector").disabled = true; document.getElementById("categorySelector").disabled = true; document.getElementById("propertySelector").disabled = true; } // Show or hide Component select if (value === 'cmp') { document.getElementById("componentSelectorContainer").classList.remove('hidden'); } else { document.getElementById("componentSelectorContainer").classList.add('hidden'); } // Update self updateUrl("tier", value); // Update component selector document.getElementById("componentSelector").value = window.defaultAllOption; updateAdvancedSearchOptions("tier", "component", value, `option${dataTag('tier', value)}`); updateUrl("component", window.defaultAllOption); // Update category selector document.getElementById("categorySelector").value = window.defaultAllOption; updateAdvancedSearchOptions("tier", "category", value, `option${dataTag('tier', value)}`); updateUrl("category", window.defaultAllOption); // Update property selector document.getElementById("propertySelector").value = window.defaultAllOption; updateAdvancedSearchOptions("tier", "property", value, `option${dataTag('tier', value)}`); updateUrl("property", window.defaultAllOption); // Update preview applyFilters(); renderContent(); } const handleComponentSelectorChange = (value) => { // Enable/disabled next select if (value !== window.defaultAllOption) { document.getElementById("categorySelector").disabled = false; } else { document.getElementById("categorySelector").disabled = true; document.getElementById("propertySelector").disabled = true; } // Update self updateUrl("component", value); const tierVal = document.querySelector("#tierSelector").value; const query = `option${dataTag('tier', tierVal)}${dataTag('component', value)}`; // Update category selector document.getElementById("categorySelector").value = window.defaultAllOption; updateAdvancedSearchOptions("component", "category", value, query); updateUrl("category", window.defaultAllOption); // Update property selector document.getElementById("propertySelector").value = window.defaultAllOption; updateAdvancedSearchOptions("component", "property", value, query); updateUrl("property", window.defaultAllOption); // Update preview applyFilters(); renderContent(); } const handleCategorySelectorChange = (value) => { // Enable/disabled next select if (value !== window.defaultAllOption) { document.getElementById("propertySelector").disabled = false; } else { document.getElementById("propertySelector").disabled = true; } // Update self updateUrl("category", value); // Update property selector const tierVal = document.querySelector("#tierSelector").value; const componentVal = document.querySelector("#componentSelector").value; document.getElementById("propertySelector").value = window.defaultAllOption; if (tierVal === 'cmp') { updateAdvancedSearchOptions("category", "property", value, `option${dataTag('tier', tierVal)}${dataTag('component', componentVal)}${dataTag('category', value)}`); } else { updateAdvancedSearchOptions("category", "property", value, `option${dataTag('tier', tierVal)}${dataTag('category', value)}`); } updateUrl("property", window.defaultAllOption); // Update preview applyFilters(); renderContent(); } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -- Reactive Form elements ------------------------------------------------ const debounce = (cb, ms) => { let timerId; return (...args) => { clearTimeout(timerId); timerId = setTimeout(() => { cb(...args); }, ms); }; } const updateUrl = (param, value) => { const url = new URL(window.location.href); const searchParams = url.searchParams; if (!value || value === "" || value === window.defaultAllOption) { searchParams.delete(param); } else { searchParams.set(param, value); } window.history.pushState({}, null, url.toString()); }; const handleBaseParamChange = (param, value) => { updateUrl(param, value); applyFilters(); renderContent(); }; const updateAdvancedSearchOptions = (parent, targetId, value, selector) => { const parentEl = document.getElementById(`${targetId}Selector`); parentEl .querySelectorAll(`option`) .forEach((el) => el.classList.add("hidden")); if (value !== window.defaultAllOption) { parentEl.querySelector(`option[value=${window.defaultAllOption}]`).classList.remove("hidden"); parentEl .querySelectorAll(selector) .forEach((el) => { el.classList.remove("hidden"); }); } else { parentEl .querySelectorAll(`option`) .forEach((el) => el.classList.remove("hidden")); } }; const toggleRegexMode = (element) => { element.classList.toggle('active'); updateUrl('regex', element.classList.contains("active")); applyFilters(); renderContent(); } // Utils const dataTag = (key, val) => val !== window.defaultAllOption ? `[data-${key}=${val}]` : ''; // Search const searchSelector = document.querySelector("#searchSelector"); searchSelector.addEventListener("input", debounce((ev) => handleBaseParamChange("query", ev.target.value), 200) ); // Regex const regexSelector = document.querySelector("#regexSelector"); regexSelector.addEventListener("click", () => toggleRegexMode(regexSelector) ); // Format const formatSelector = document.querySelector("#formatSelector"); formatSelector.addEventListener("change", (ev) => handleBaseParamChange("format", ev.target.value) ); // Schema const schemaSelector = document.querySelector("#schemaSelector"); schemaSelector.addEventListener("change", (ev) => handleBaseParamChange("schema", ev.target.value) ); // Tier const tierSelector = document.querySelector("#tierSelector"); tierSelector.addEventListener("change", (ev) => handleTierSelectorChange(ev.target.value)); // Component const componentSelector = document.querySelector("#componentSelector"); componentSelector.addEventListener("change", (ev) => handleComponentSelectorChange(ev.target.value)); // Category const categorySelector = document.querySelector("#categorySelector"); categorySelector.addEventListener("change", (ev) => handleCategorySelectorChange(ev.target.value)); // Property const propertySelector = document.querySelector("#propertySelector"); propertySelector.addEventListener("change", (ev) => handleBaseParamChange("property", ev.target.value) ); // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -- Initialize form elements ---------------------------------------------- const initializeFormElements = () => { // Fill form elements with custom options fillAdvancedSelectsOptions(); const search = window.location.search; const urlParams = new URLSearchParams(search); // Selectors const searchSelector = document.querySelector("#searchSelector"); const regexSelector = document.querySelector("#regexSelector"); const formatSelector = document.querySelector("#formatSelector"); const schemaSelector = document.querySelector("#schemaSelector"); const tierSelector = document.querySelector("#tierSelector"); const componentSelector = document.querySelector("#componentSelector"); const categorySelector = document.querySelector("#categorySelector"); const propertySelector = document.querySelector("#propertySelector"); const fullscreenSelector = urlParams.get("fullscreen") || 'false'; // Set value of all form elements based on url params. searchSelector.value = urlParams.get("query") || ""; regexSelector.classList.toggle('active', !!urlParams.get("regex")); formatSelector.value = urlParams.get("format") || "js"; schemaSelector.value = urlParams.get("schema") || window.config.scheme.options[0]; tierSelector.value = urlParams.get("tier") || window.defaultAllOption; componentSelector.value = urlParams.get("component") || window.defaultAllOption; categorySelector.value = urlParams.get("category") || window.defaultAllOption; propertySelector.value = urlParams.get("property") || window.defaultAllOption; // Enable disable dependant selects if (tierSelector.value !== window.defaultAllOption) { if (tierSelector.value === 'cmp') { componentSelector.disabled = false; } else { categorySelector.disabled = false; } } if (componentSelector.value !== window.defaultAllOption) { categorySelector.disabled = false; } if (categorySelector.value !== window.defaultAllOption) { propertySelector.disabled = false; } // Show conditional selects if (tierSelector.value === 'cmp') { document.getElementById("componentSelectorContainer").classList.remove('hidden'); } // Fullscreen - show/hide aside and heading if (fullscreenSelector === 'true') { document.getElementById("aside").classList.add('hidden'); document.getElementById("heading").classList.add('hidden'); // remove margin top from wrapper document.querySelector(".wrapper").classList.add('no-margin-top'); } // Update options in selects // component updateAdvancedSearchOptions("component", "category", componentSelector.value, `option${dataTag('tier', tierSelector.value)}${dataTag('component', componentSelector.value)}`); updateAdvancedSearchOptions("component", "property", componentSelector.value, `option${dataTag('tier', tierSelector.value)}${dataTag('component', componentSelector.value)}`); // category if (tierSelector.value === 'cmp') { updateAdvancedSearchOptions("category", "property", categorySelector.value, `option${dataTag('tier', tierSelector.value)}${dataTag('component', componentSelector.value)}${dataTag('category', categorySelector.value)}`); } else { updateAdvancedSearchOptions("category", "property", categorySelector.value, `option${dataTag('tier', tierSelector.value)}${dataTag('category', categorySelector.value)}`); } //property }; // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -- Filtering tasks ------------------------------------------------------- const filtersFns = new Map( // Order matters; Object.entries({ schema: (schema) => (datasets) => { // If no schema, return default value. if (!schema) return datasets[window.config.scheme.options[0]]; // else return selection. return datasets[schema]; }, tier: (tier) => (tokens) => { // Options to be visible (if any). Else all are shown. const tierOpts = window.config?.advancedAttrs?.tier?.options; // If there is no filter selected or 'all' is selected and there // is no internal filtering from config. if ((!tier || tier === window.defaultAllOption) && !tierOpts) { return tokens; } // If there is no filter selected or 'all' is selected and there // is an internal filtering from config required. if ((!tier || tier == window.defaultAllOption) && tierOpts) { const filteredTokens = tokens.filter((token) => tierOpts.includes(token.attributes.tier) ); return filteredTokens; } // If filter is active. return tokens.filter((token) => token.attributes.tier === tier); }, component: (component) => (tokens) => { // Options to be visible (if any). Else all are shown. const componentOpts = window.config?.advancedAttrs?.component?.options; // If there is no filter selected or 'all' is selected and there // is no internal filtering from config. if ((!component || component === window.defaultAllOption) && !componentOpts) { return tokens; } // If there is no filter selected or 'all' is selected and there // is an internal filtering from config required. if ((!component || component == window.defaultAllOption) && componentOpts) { const filteredTokens = tokens.filter((token) => componentOpts.includes(token.attributes.component) ); return filteredTokens; } // If filter is active. return tokens.filter((token) => token.attributes.component === component); }, category: (category) => (tokens) => { // Options to be visible (if any). Else all are shown. const categoryOpts = window.config?.advancedAttrs?.category?.options; // If there is no filter selected or 'all' is selected and there // is no internal filtering from config. if ((!category || category === window.defaultAllOption) && !categoryOpts) { return tokens; } // If there is no filter selected or 'all' is selected and there // is an internal filtering from config required. if (category == window.defaultAllOption && categoryOpts) { return tokens.filter((token) => categoryOpts.includes(token.attributes.category) ); } // If filter is active. return tokens.filter((token) => token.attributes.category === category); }, property: (property) => (tokens) => { // Options to be visible (if any). Else all are shown. const propertyOpts = window.config?.advancedAttrs?.property?.options; // If there is no filter selected or 'all' is selected and there // is no internal filtering from config. if ((!property || property === window.defaultAllOption) && !propertyOpts) { return tokens; } // If there is no filter selected or 'all' is selected and there // is an internal filtering from config required. if (property == window.defaultAllOption && propertyOpts) { return tokens.filter((token) => propertyOpts.includes(token.attributes.property) ); } // If filter is active. return tokens.filter((token) => token.attributes.property === property); }, format: (format) => (tokens) => { return tokens.map((token) => { if (!format || format === "js") return { ...token, _formatted: token.name.replaceAll("-", ".") }; if (format === "scss") return { ...token, _formatted: `$${camelToKebabCase(token.name)}` }; if (format === "css") return { ...token, _formatted: `--${camelToKebabCase(token.name)}` }; if (format === "figma") return { ...token, _formatted: token.name.replaceAll("-", "/") }; if (format === "json") return { ...token, _formatted: token.name }; }); }, query: (query, { regexMode }) => (tokens) => { const errMsgElement = document.querySelector('#regexErrorMsg'); errMsgElement.classList.toggle('hidden', true); if (!query) return { query, tokens }; if (regexMode) { try { return { query: '', tokens: tokens.filter((token) => token._formatted.match(query) || token.value.toString().match(query) ) }; } catch (err) { errMsgElement.classList.toggle('hidden'); errMsgElement.innerHTML = err.message; return { query: '', tokens: [] }; } } else { const cleanQuery = query.trim().toLowerCase(); return { query: cleanQuery, tokens: tokens.filter((token) => token._formatted.toLowerCase().includes(cleanQuery) || token.value.toString().toLowerCase().includes(cleanQuery) ) }; } }, highlight: (_query, { regexMode, fullScreen }) => ({ query, tokens }) => { if (!query) return tokens; if (regexMode || fullScreen) return tokens; return tokens.map(token => { let name = token._formatted; let value = token.value; const nameMatchIdx = token._formatted.toLowerCase().indexOf(query); const valueMatchIdx = token.value.toString().toLowerCase().indexOf(query); // Display match in token name if (nameMatchIdx !== -1) { const pre = token._formatted.substring(0, nameMatchIdx); const match = token._formatted.substring(nameMatchIdx, nameMatchIdx + query.length); const pos = token._formatted.substring(nameMatchIdx + query.length); name = `${pre}<mark>${match}</mark>${pos}`; } // Display match in raw value name if (valueMatchIdx !== -1) { const pre = token.value.toString().substring(0, valueMatchIdx); const match = token.value.toString().substring(valueMatchIdx, valueMatchIdx + query.length); const pos = token.value.toString().substring(valueMatchIdx + query.length); value = `${pre}<mark>${match}</mark>${pos}`; } return { ...token, highlighted: value, _formatted: name, } }); }, }) ); const applyFilters = () => { const search = window.location.search; const urlParams = new URLSearchParams(search); const regexMode = urlParams.get('regex') === 'true'; const fullScreen = urlParams.get('fullscreen') === 'true'; // Apply all filters from url window.filteredTokens = [...filtersFns.entries()].reduce( (acc, [filterId, filterFn]) => filterFn(urlParams.get(filterId), { regexMode, fullScreen })(acc), window.datasets ); }; // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -- entrypoint ------------------------------------------------------------ // HIDDEN FEATURES // - Fullscreen mode: add fullscreen=true to the url // - Preview global tokens: add tier=global to the url (async () => { window.config = await fetchConfig(); const rawDatasets = await fetchDatasets(); window.datasets = rawDatasets.reduce( (acc, { schema, tokens }) => ({ ...acc, [schema]: tokens }), {} ); // Set H1 title setTitle(); // Set initial values to form elements initializeFormElements(); // Fill schema selector options fillSchemaSelector(); // Apply data filters applyFilters(); // Render tokens list renderContent(); })(); </script> </html>