/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { html, LitElement, classMap, } from "chrome://global/content/vendor/lit.all.mjs"; import { storybookTables, variableLookupTable } from "./tokens-storybook.mjs"; import styles from "./tokens-table.css"; export default { title: "Docs/Tokens Table", parameters: { options: { showPanel: false, }, }, }; const HCM_MAP = { ActiveText: "#8080FF", ButtonBorder: "#000000", ButtonFace: "#000000", ButtonText: "#FFEE32", Canvas: "#000000", CanvasText: "#ffffff", Field: "#000000", FieldText: "#ffffff", GrayText: "#A6A6A6", Highlight: "#D6B4FD", HighlightText: "#2B2B2B", LinkText: "#8080FF", Mark: "#000000", MarkText: "#000000", SelectedItem: "#D6B4FD", SelectedItemText: "#2B2B2B", AccentColor: "8080FF", AccentColorText: "#2B2B2B", VisitedText: "#8080FF", }; const THEMED_TABLES = [ "attention-dot", "background-color", "border", "border-color", "opacity", "text-color", "color", "outline", "icon-color", "link", ]; /** * */ class TablesPage extends LitElement { #query = ""; static properties = { surface: { type: String, state: true }, tokensData: { type: Object, state: true }, filteredTokens: { type: Object, state: true }, }; constructor() { super(); this.surface = "brand"; this.tokensData = storybookTables; } handleSurfaceChange(e) { this.surface = e.target.value; } handleInput(e) { this.#query = e.originalTarget.value.trim().toLowerCase(); e.preventDefault(); this.handleSearch(); } debounce(fn, delayMs = 300) { let timeout; return function () { clearTimeout(timeout); timeout = setTimeout(() => { fn.apply(this, arguments); }, delayMs); }; } handleSearch() { // Clear filteredTokens to show all data. if (!this.#query) { this.filteredTokens = null; } let filteredTokens = Object.entries(this.tokensData).reduce( (acc, [key, tokens]) => { if (key.includes(this.#query)) { return { ...acc, [key]: tokens }; } let filteredItems = tokens.filter(({ name: tokenName, value }) => this.filterTokens(this.#query, tokenName, value) ); if (filteredItems.length) { return { ...acc, [key]: filteredItems }; } return acc; }, {} ); this.filteredTokens = filteredTokens; } filterTokens(searchTerm, tokenName, tokenVal) { if ( tokenName.includes(searchTerm) || (typeof tokenVal == "string" && tokenVal.includes(searchTerm)) ) { return true; } if (typeof tokenVal == "object") { return Object.entries(tokenVal).some(([key, val]) => this.filterTokens(searchTerm, key, val) ); } return false; } handleClearSearch(e) { this.#query = ""; e.preventDefault(); this.handleSearch(); } render() { if (!this.tokensData) { return ""; } return html`
${Object.entries(this.filteredTokens ?? this.tokensData).map( ([tableName, tableEntries]) => { return html` `; } )}
`; } } customElements.define("tables-page", TablesPage); /** * */ class TokensTable extends LitElement { TEMPLATES = { "font-size": this.fontTemplate, "font-weight": this.fontTemplate, "icon-color": this.iconTemplate, "icon-size": this.iconTemplate, link: this.linkTemplate, margin: this.spaceAndSizeTemplate, "min-height": this.spaceAndSizeTemplate, outline: this.outlineTemplate, padding: this.paddingTemplate, size: this.spaceAndSizeTemplate, space: this.spaceAndSizeTemplate, "text-color": this.fontTemplate, }; static properties = { name: { type: String }, tokens: { type: Array }, surface: { type: String }, }; createRenderRoot() { return this; } getDisplayValues(theme, token) { let value = this.getResolvedValue(theme, token); let raw = this.getRawValue(theme, value); return { value, ...(raw !== value && { raw }) }; } // Return the value with variable references. // e.g. var(--color-white) getResolvedValue(theme, token) { if (typeof token == "string" || typeof token == "number") { return token; } if (theme == "hcm") { return ( token.forcedColors || token.prefersContrast || token[this.surface]?.default || token.default ); } if (token[this.surface]) { return this.getResolvedValue(theme, token[this.surface]); } if (token[theme]) { return token[theme]; } return token.default; } // Return the value with any variables replaced by what they represent. // e.g. #ffffff getRawValue(theme, token) { let cssRegex = /(?var\(--(?[a-z-0-9,\s]+)\))/; let matches = cssRegex.exec(token); if (matches) { let variableValue = variableLookupTable[matches.groups?.lookupKey]; if (typeof variableValue == "undefined") { return token; } if (typeof variableValue == "object") { variableValue = this.getResolvedValue(theme, variableValue); } let rawValue = token.replace(matches.groups?.var, variableValue); if (rawValue.match(cssRegex)) { return this.getRawValue(theme, rawValue); } return rawValue; } return token; } getTemplate(value, tokenName, category = this.name) { // 0 is a valid value if (value == undefined) { return "N/A"; } let templateFn = this.TEMPLATES[category]?.bind(this); if (templateFn) { return templateFn(category, value, tokenName); } return html`
`; } outlineTemplate(_, value, tokenName) { let property = tokenName.replaceAll(/--|focus-|-error/g, ""); if (property == "outline-inset") { property = "outline-offset"; } return html`
`; } fontTemplate(category, value) { return html`

The quick brown fox jumps over the lazy dog

`; } iconTemplate(_, value, tokenName) { let property = tokenName.includes("color") ? "background-color" : "height"; return html`
`; } linkTemplate(_, value, tokenName) { let hasOutline = tokenName.includes("outline"); return html` Click me! `; } spaceAndSizeTemplate(_, value, tokenName) { let isSize = tokenName.includes("size") || tokenName.includes("height"); let items = isSize ? html`
` : html`
`; return html`
${items}
`; } paddingTemplate(_, value) { return html`
`; } getDisplayProperty(category) { switch (category) { case "attention-dot": case "color": return "background-color"; case "text-color": return "color"; default: return category.replace("button", ""); } } cellTemplate(type, tokenValue, tokenName) { let { value, raw } = this.getDisplayValues("default", tokenValue); return html`
${type == "preview" ? html`${this.getTemplate(raw ?? value, tokenName)}` : html`

${value}

${raw ? html`

${raw}

` : ""}`}
`; } themeCellTemplate(theme, tokenValue, tokenName) { let { value, raw } = this.getDisplayValues(theme, tokenValue); return html`
${this.getTemplate(raw ?? value, tokenName)}

${value}

${raw ? html`

${raw}

` : ""}
`; } render() { let themedTable = THEMED_TABLES.includes(this.name); return html`

${this.name}

${themedTable ? html` ` : html` `} ${this.tokens.map(({ name: tokenName, value }) => { return html` ${themedTable ? html` ${this.themeCellTemplate("light", value, tokenName)} ${this.themeCellTemplate("dark", value, tokenName)} ${this.themeCellTemplate("hcm", value, tokenName)} ` : html` ${this.cellTemplate("value", value, tokenName)} ${this.cellTemplate("preview", value, tokenName)} `} `; })}
NameLight Dark High contrastValue Preview
${tokenName}
`; } } customElements.define("tokens-table", TokensTable); export const Default = () => { return html``; };