Mercurial
comparison design_system/src/components/primitives.js @ 254:2b6e732087ff
[ui] Add complete native component catalog
Co-authored-by: Copilot <[email protected]>
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Tue, 04 Aug 2026 15:12:09 -0700 |
| parents | |
| children | 60a876c4587a |
comparison
equal
deleted
inserted
replaced
| 253:fdf3816959cb | 254:2b6e732087ff |
|---|---|
| 1 /** @const {!ReadonlyArray<string>} Registered presentation-only elements. */ | |
| 2 export const ZEN_PRIMITIVE_ELEMENTS = [ | |
| 3 "zen-aspect-ratio", | |
| 4 "zen-attachment", | |
| 5 "zen-avatar", | |
| 6 "zen-badge", | |
| 7 "zen-breadcrumb", | |
| 8 "zen-bubble", | |
| 9 "zen-button-group", | |
| 10 "zen-chart", | |
| 11 "zen-direction", | |
| 12 "zen-empty", | |
| 13 "zen-input", | |
| 14 "zen-input-group", | |
| 15 "zen-item", | |
| 16 "zen-kbd", | |
| 17 "zen-label", | |
| 18 "zen-marker", | |
| 19 "zen-message", | |
| 20 "zen-native-select", | |
| 21 "zen-pagination", | |
| 22 "zen-progress", | |
| 23 "zen-scroll-area", | |
| 24 "zen-separator", | |
| 25 "zen-skeleton", | |
| 26 "zen-spinner", | |
| 27 "zen-table", | |
| 28 "zen-textarea", | |
| 29 "zen-typography", | |
| 30 ]; | |
| 31 | |
| 32 function parseRatio(value) { | |
| 33 const [width, height] = String(value || "16/9") | |
| 34 .split("/") | |
| 35 .map(Number); | |
| 36 if (!Number.isFinite(width) || !Number.isFinite(height) || | |
| 37 width <= 0 || height <= 0) return "16 / 9"; | |
| 38 return `${width} / ${height}`; | |
| 39 } | |
| 40 | |
| 41 /** | |
| 42 * Applies small semantic enhancements shared by presentation primitives. | |
| 43 * | |
| 44 * @extends {HTMLElement} | |
| 45 */ | |
| 46 export class ZenPrimitive extends HTMLElement { | |
| 47 connectedCallback() { | |
| 48 this.sync(); | |
| 49 } | |
| 50 | |
| 51 static get observedAttributes() { | |
| 52 return ["dir", "ratio", "value", "width"]; | |
| 53 } | |
| 54 | |
| 55 attributeChangedCallback() { | |
| 56 if (this.isConnected) this.sync(); | |
| 57 } | |
| 58 | |
| 59 sync() { | |
| 60 switch (this.localName) { | |
| 61 case "zen-aspect-ratio": | |
| 62 this.style.setProperty( | |
| 63 "--zen-aspect-ratio", | |
| 64 parseRatio(this.getAttribute("ratio")), | |
| 65 ); | |
| 66 break; | |
| 67 case "zen-breadcrumb": { | |
| 68 const navigation = this.querySelector(":scope > nav"); | |
| 69 if (navigation && !navigation.hasAttribute("aria-label")) { | |
| 70 navigation.setAttribute("aria-label", "Breadcrumb"); | |
| 71 } | |
| 72 break; | |
| 73 } | |
| 74 case "zen-chart": | |
| 75 if (!this.hasAttribute("role")) this.setAttribute("role", "img"); | |
| 76 break; | |
| 77 case "zen-direction": | |
| 78 if (!this.hasAttribute("dir") && this.hasAttribute("value")) { | |
| 79 this.dir = this.getAttribute("value"); | |
| 80 } | |
| 81 break; | |
| 82 case "zen-pagination": { | |
| 83 const navigation = this.querySelector(":scope > nav"); | |
| 84 if (navigation && !navigation.hasAttribute("aria-label")) { | |
| 85 navigation.setAttribute("aria-label", "Pagination"); | |
| 86 } | |
| 87 break; | |
| 88 } | |
| 89 case "zen-native-select": { | |
| 90 if (!this.querySelector("select")) break; | |
| 91 let icon = this.querySelector( | |
| 92 ":scope > zen-icon[data-zen-select-icon]", | |
| 93 ); | |
| 94 if (!icon) { | |
| 95 icon = document.createElement("zen-icon"); | |
| 96 icon.dataset.zenSelectIcon = ""; | |
| 97 icon.dataset.zenGenerated = "true"; | |
| 98 icon.setAttribute("name", "chevron-down"); | |
| 99 this.append(icon); | |
| 100 } | |
| 101 break; | |
| 102 } | |
| 103 case "zen-separator": | |
| 104 if (!this.querySelector(":scope > hr")) { | |
| 105 if (!this.hasAttribute("role")) { | |
| 106 this.setAttribute("role", "separator"); | |
| 107 } | |
| 108 if (!this.hasAttribute("aria-orientation")) { | |
| 109 this.setAttribute( | |
| 110 "aria-orientation", | |
| 111 this.getAttribute("orientation") === "vertical" | |
| 112 ? "vertical" | |
| 113 : "horizontal", | |
| 114 ); | |
| 115 } | |
| 116 } | |
| 117 break; | |
| 118 case "zen-skeleton": | |
| 119 if (this.hasAttribute("width")) { | |
| 120 this.style.setProperty( | |
| 121 "--zen-skeleton-width", | |
| 122 this.getAttribute("width"), | |
| 123 ); | |
| 124 } else { | |
| 125 this.style.removeProperty("--zen-skeleton-width"); | |
| 126 } | |
| 127 if (!this.hasAttribute("role")) this.setAttribute("aria-hidden", "true"); | |
| 128 break; | |
| 129 case "zen-spinner": | |
| 130 if (!this.hasAttribute("role")) this.setAttribute("role", "status"); | |
| 131 if (!this.hasAttribute("aria-label")) { | |
| 132 this.setAttribute("aria-label", "Loading"); | |
| 133 } | |
| 134 break; | |
| 135 } | |
| 136 } | |
| 137 } | |
| 138 | |
| 139 for (const name of ZEN_PRIMITIVE_ELEMENTS) { | |
| 140 if (!customElements.get(name)) { | |
| 141 customElements.define(name, class extends ZenPrimitive {}); | |
| 142 } | |
| 143 } |