@f-ewald/components 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/custom-elements.json +7 -7
- package/dist/address-autocomplete.d.ts.map +1 -1
- package/dist/address-autocomplete.js +3 -1
- package/dist/address-autocomplete.js.map +1 -1
- package/dist/autocomplete-input.d.ts.map +1 -1
- package/dist/autocomplete-input.js +3 -1
- package/dist/autocomplete-input.js.map +1 -1
- package/dist/calendar-month.d.ts.map +1 -1
- package/dist/calendar-month.js +9 -8
- package/dist/calendar-month.js.map +1 -1
- package/dist/chat-message.d.ts.map +1 -1
- package/dist/chat-message.js +15 -5
- package/dist/chat-message.js.map +1 -1
- package/dist/confirm-dialog.d.ts.map +1 -1
- package/dist/confirm-dialog.js +24 -6
- package/dist/confirm-dialog.js.map +1 -1
- package/dist/copy-link-button.d.ts.map +1 -1
- package/dist/copy-link-button.js +4 -2
- package/dist/copy-link-button.js.map +1 -1
- package/dist/data-table.js +1 -1
- package/dist/data-table.js.map +1 -1
- package/dist/distribution-chart.d.ts +2 -2
- package/dist/distribution-chart.js +4 -4
- package/dist/distribution-chart.js.map +1 -1
- package/dist/dropdown-button.d.ts.map +1 -1
- package/dist/dropdown-button.js +13 -2
- package/dist/dropdown-button.js.map +1 -1
- package/dist/form-select.d.ts.map +1 -1
- package/dist/form-select.js +32 -8
- package/dist/form-select.js.map +1 -1
- package/dist/frame-box.js +3 -3
- package/dist/frame-box.js.map +1 -1
- package/dist/icon-button.d.ts +1 -1
- package/dist/icon-button.js +1 -1
- package/dist/icon-button.js.map +1 -1
- package/dist/kbd-hint.js +1 -1
- package/dist/kbd-hint.js.map +1 -1
- package/dist/map-circle.js +2 -2
- package/dist/map-circle.js.map +1 -1
- package/dist/map-pin.js +2 -2
- package/dist/map-pin.js.map +1 -1
- package/dist/percent-bar-chart.js +1 -1
- package/dist/percent-bar-chart.js.map +1 -1
- package/dist/photo-gallery.d.ts.map +1 -1
- package/dist/photo-gallery.js +33 -9
- package/dist/photo-gallery.js.map +1 -1
- package/dist/popover-panel.d.ts.map +1 -1
- package/dist/popover-panel.js +7 -4
- package/dist/popover-panel.js.map +1 -1
- package/dist/radio-cards.d.ts.map +1 -1
- package/dist/radio-cards.js +4 -3
- package/dist/radio-cards.js.map +1 -1
- package/dist/radio-pills.d.ts.map +1 -1
- package/dist/radio-pills.js +7 -4
- package/dist/radio-pills.js.map +1 -1
- package/dist/reveal-button.d.ts.map +1 -1
- package/dist/reveal-button.js +4 -1
- package/dist/reveal-button.js.map +1 -1
- package/dist/slide-panel.d.ts.map +1 -1
- package/dist/slide-panel.js +8 -5
- package/dist/slide-panel.js.map +1 -1
- package/dist/stat-meter.js +5 -5
- package/dist/stat-meter.js.map +1 -1
- package/dist/status-pill.js +2 -2
- package/dist/status-pill.js.map +1 -1
- package/dist/tile-grid.js +1 -1
- package/dist/tile-grid.js.map +1 -1
- package/dist/toast-notification.d.ts.map +1 -1
- package/dist/toast-notification.js +10 -5
- package/dist/toast-notification.js.map +1 -1
- package/dist/tokens.css +10 -0
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +15 -2
- package/dist/tokens.js.map +1 -1
- package/dist/ui-button.d.ts.map +1 -1
- package/dist/ui-button.js +3 -1
- package/dist/ui-button.js.map +1 -1
- package/dist/user-avatar.js +1 -1
- package/dist/user-avatar.js.map +1 -1
- package/dist/weight-bar-chart.js +2 -2
- package/dist/weight-bar-chart.js.map +1 -1
- package/docs/address-autocomplete.md +1 -0
- package/docs/autocomplete-input.md +1 -0
- package/docs/calendar-month.md +6 -1
- package/docs/chat-message.md +2 -0
- package/docs/confirm-dialog.md +3 -0
- package/docs/data-table.md +1 -0
- package/docs/design-language.md +68 -40
- package/docs/distribution-chart.md +3 -2
- package/docs/dropdown-button.md +2 -0
- package/docs/form-select.md +2 -0
- package/docs/frame-box.md +3 -1
- package/docs/icon-button.md +1 -1
- package/docs/kbd-hint.md +1 -0
- package/docs/map-circle.md +2 -0
- package/docs/map-pin.md +2 -0
- package/docs/photo-gallery.md +1 -0
- package/docs/popover-panel.md +3 -1
- package/docs/radio-cards.md +2 -0
- package/docs/radio-pills.md +1 -0
- package/docs/reveal-button.md +3 -1
- package/docs/slide-panel.md +3 -1
- package/docs/stat-meter.md +3 -0
- package/docs/status-pill.md +1 -0
- package/docs/toast-notification.md +2 -0
- package/docs/ui-button.md +2 -0
- package/docs/user-avatar.md +1 -0
- package/docs/weight-bar-chart.md +0 -1
- package/llms.txt +27 -27
- package/package.json +1 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reveal-button.js","sourceRoot":"","sources":["../src/reveal-button.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;GAIG;AAEI,IAAM,YAAY,GAAlB,MAAM,YAAa,SAAQ,UAAU;IAArC;;
|
|
1
|
+
{"version":3,"file":"reveal-button.js","sourceRoot":"","sources":["../src/reveal-button.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;GAIG;AAEI,IAAM,YAAY,GAAlB,MAAM,YAAa,SAAQ,UAAU;IAArC;;QAsDL,qDAAqD;QAErD,UAAK,GAAW,uBAAuB,CAAC;QACxC,8CAA8C;QACjB,aAAQ,GAAG,KAAK,CAAC;IAkBhD,CAAC;aA3EiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAgDF;KACF,AAnDqB,CAmDpB;IAQM,OAAO;QACb,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,UAAW,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,IAAI,CAAC,UAAW,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QACxD,GAAG,EAAE,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAChC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACrC,CAAC;IAEkB,MAAM;QACvB,OAAO,IAAI,CAAA;;wCAEyB,IAAI,CAAC,QAAQ,WAAW,IAAI,CAAC,OAAO;UAClE,IAAI,CAAC,KAAK;;KAEf,CAAC;IACJ,CAAC;CACF,CAAA;AApBC;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;2CACa;AAEX;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;8CAAkB;AA1DnC,YAAY;IADxB,aAAa,CAAC,eAAe,CAAC;GAClB,YAAY,CA4ExB","sourcesContent":["import { LitElement, css, html } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { tokens } from \"./tokens.js\";\n\n/**\n * Button that reveals hidden slotted content when clicked.\n *\n * @element reveal-button\n */\n@customElement(\"reveal-button\")\nexport class RevealButton extends LitElement {\n static override styles = [\n tokens,\n css`\n .hidden {\n display: none;\n }\n button {\n font-family: var(\n --ui-font,\n ui-sans-serif,\n system-ui,\n sans-serif,\n \"Apple Color Emoji\",\n \"Segoe UI Emoji\",\n \"Segoe UI Symbol\",\n \"Noto Color Emoji\"\n );\n font-size: var(--ui-font-size-sm, 0.75rem);\n font-weight: var(--ui-font-weight-medium, 500);\n line-height: var(--ui-line-height-tight, 1.25);\n height: 2rem;\n background: var(--ui-primary, #4f46e5);\n color: var(--ui-on-accent, #ffffff);\n border: none;\n border-radius: var(--ui-radius-sm, 0.25rem);\n padding: 0.5rem 1rem;\n cursor: pointer;\n }\n button:hover:not(:disabled) {\n background: var(--ui-primary-hover, #4338ca);\n }\n button:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n }\n button:focus-visible {\n outline: none;\n box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));\n }\n @media (forced-colors: active) {\n button:focus-visible {\n outline: 2px solid CanvasText;\n outline-offset: 2px;\n box-shadow: none;\n }\n button:disabled {\n color: GrayText;\n opacity: 1;\n }\n }\n `,\n ];\n\n /** Label shown on the button before it's clicked. */\n @property({ type: String })\n label: string = \"Reveal hidden content\";\n /** Disables revealing the slotted content. */\n @property({ type: Boolean }) disabled = false;\n\n private _reveal() {\n if (this.disabled) return;\n const div = this.shadowRoot!.querySelector(\"div\");\n const button = this.shadowRoot!.querySelector(\"button\");\n div?.classList.toggle(\"hidden\");\n button?.classList.toggle(\"hidden\");\n }\n\n protected override render() {\n return html`\n <div class=\"hidden\"><slot></slot></div>\n <button type=\"button\" ?disabled=${this.disabled} @click=${this._reveal}>\n ${this.label}\n </button>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"reveal-button\": RevealButton;\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slide-panel.d.ts","sourceRoot":"","sources":["../src/slide-panel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAM1E;;;;;;;;;;GAUG;AACH,qBACa,UAAW,SAAQ,UAAU;IACxC,8CAA8C;IACjB,IAAI,UAAS;IAC1C,2EAA2E;IAC/D,OAAO,SAAM;IAEzB,OAAgB,MAAM,
|
|
1
|
+
{"version":3,"file":"slide-panel.d.ts","sourceRoot":"","sources":["../src/slide-panel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAM1E;;;;;;;;;;GAUG;AACH,qBACa,UAAW,SAAQ,UAAU;IACxC,8CAA8C;IACjB,IAAI,UAAS;IAC1C,2EAA2E;IAC/D,OAAO,SAAM;IAEzB,OAAgB,MAAM,4BA0HpB;IAEF,OAAO,CAAC,cAAc,CAA4B;IAElD,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,gBAAgB,CAGtB;IAEF,UAAmB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAaxD;IAEQ,oBAAoB,IAAI,IAAI,CAOpC;IAEQ,MAAM,yCAwBd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,aAAa,EAAE,UAAU,CAAC;KAC3B;CACF"}
|
package/dist/slide-panel.js
CHANGED
|
@@ -45,7 +45,7 @@ let SlidePanel = class SlidePanel extends LitElement {
|
|
|
45
45
|
top: 0;
|
|
46
46
|
right: 0;
|
|
47
47
|
bottom: 0;
|
|
48
|
-
width:
|
|
48
|
+
width: 20rem;
|
|
49
49
|
z-index: var(--component-layer-z, 20);
|
|
50
50
|
background: var(--ui-surface, #ffffff);
|
|
51
51
|
box-shadow: var(--ui-shadow-lg, 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1));
|
|
@@ -62,13 +62,14 @@ let SlidePanel = class SlidePanel extends LitElement {
|
|
|
62
62
|
display: flex;
|
|
63
63
|
align-items: center;
|
|
64
64
|
justify-content: space-between;
|
|
65
|
-
padding: 0.
|
|
65
|
+
padding: 0.75rem;
|
|
66
66
|
border-bottom: 1px solid var(--ui-border, #e2e8f0);
|
|
67
67
|
flex: 0 0 auto;
|
|
68
68
|
}
|
|
69
69
|
.panel-title {
|
|
70
|
-
font-weight: 600;
|
|
71
|
-
font-size: var(--ui-font-size,
|
|
70
|
+
font-weight: var(--ui-font-weight-semibold, 600);
|
|
71
|
+
font-size: var(--ui-font-size-lg, 1rem);
|
|
72
|
+
line-height: var(--ui-line-height-tight, 1.25);
|
|
72
73
|
font-family: var(
|
|
73
74
|
--ui-font,
|
|
74
75
|
ui-sans-serif,
|
|
@@ -90,7 +91,9 @@ let SlidePanel = class SlidePanel extends LitElement {
|
|
|
90
91
|
background: none;
|
|
91
92
|
border: none;
|
|
92
93
|
cursor: pointer;
|
|
93
|
-
|
|
94
|
+
width: 2rem;
|
|
95
|
+
height: 2rem;
|
|
96
|
+
padding: 0;
|
|
94
97
|
color: var(--ui-text-muted, #64748b);
|
|
95
98
|
border-radius: var(--ui-radius-sm, 0.25rem);
|
|
96
99
|
flex: 0 0 auto;
|
package/dist/slide-panel.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slide-panel.js","sourceRoot":"","sources":["../src/slide-panel.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAuB,MAAM,KAAK,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAErF;;;;;;;;;;GAUG;AAEI,IAAM,UAAU,GAAhB,MAAM,UAAW,SAAQ,UAAU;IAAnC;;QACL,8CAA8C;QACjB,SAAI,GAAG,KAAK,CAAC;QAC1C,2EAA2E;QAC/D,YAAO,GAAG,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"slide-panel.js","sourceRoot":"","sources":["../src/slide-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, css, html, nothing, type PropertyValues } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { iconX } from \"./icons.js\";\nimport { tokens } from \"./tokens.js\";\nimport { activateLayer, claimEscape, deactivateLayer } from \"./utils/layer-stack.js\";\n\n/**\n * Generic sliding panel shell. Handles positioning, open/close animation,\n * header chrome, and a close button. Body content is provided via the\n * default slot; the consumer controls its own padding and overflow.\n *\n * Desktop: fixed right-side panel that slides from the right.\n * Mobile (≤48rem): bottom-sheet drawer (60vh) — reserved for future use.\n *\n * @element slide-panel\n * @fires panel-close - User clicked the close (✕) button.\n */\n@customElement(\"slide-panel\")\nexport class SlidePanel extends LitElement {\n /** Whether the panel is currently visible. */\n @property({ type: Boolean }) open = false;\n /** Title text shown in the panel header (overridable via slot=\"title\"). */\n @property() heading = \"\";\n\n static override styles = [\n tokens,\n css`\n :host {\n display: block;\n }\n .panel {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n width: 20rem;\n z-index: var(--component-layer-z, 20);\n background: var(--ui-surface, #ffffff);\n box-shadow: var(--ui-shadow-lg, 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1));\n transform: translateX(110%);\n transition: transform 250ms ease;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n .panel.open {\n transform: translateX(0);\n }\n .panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.75rem;\n border-bottom: 1px solid var(--ui-border, #e2e8f0);\n flex: 0 0 auto;\n }\n .panel-title {\n font-weight: var(--ui-font-weight-semibold, 600);\n font-size: var(--ui-font-size-lg, 1rem);\n line-height: var(--ui-line-height-tight, 1.25);\n font-family: var(\n --ui-font,\n ui-sans-serif,\n system-ui,\n sans-serif,\n \"Apple Color Emoji\",\n \"Segoe UI Emoji\",\n \"Segoe UI Symbol\",\n \"Noto Color Emoji\"\n );\n color: var(--ui-text, #0f172a);\n flex: 1 1 auto;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n padding-right: 0.5rem;\n }\n .close-btn {\n background: none;\n border: none;\n cursor: pointer;\n width: 2rem;\n height: 2rem;\n padding: 0;\n color: var(--ui-text-muted, #64748b);\n border-radius: var(--ui-radius-sm, 0.25rem);\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n .close-btn:hover {\n background: var(--ui-surface-muted, #f8fafc);\n }\n .close-btn:focus-visible {\n outline: none;\n box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));\n }\n .handle {\n display: none;\n }\n @media (prefers-reduced-motion: reduce) {\n .panel {\n transition: none;\n }\n }\n @media (forced-colors: active) {\n .close-btn:focus-visible {\n outline: 2px solid CanvasText;\n outline-offset: 2px;\n box-shadow: none;\n }\n }\n @media (max-width: 48rem) {\n .panel {\n top: auto;\n right: 0;\n bottom: 0;\n left: 0;\n width: auto;\n height: 60vh;\n border-radius: var(--ui-radius, 0.5rem) var(--ui-radius, 0.5rem) 0 0;\n box-shadow: var(\n --ui-shadow-lg,\n 0 20px 25px -5px rgb(0 0 0 / 0.1),\n 0 8px 10px -6px rgb(0 0 0 / 0.1)\n );\n transform: translateY(110%);\n }\n .panel.open {\n transform: translateY(0);\n }\n .handle {\n display: block;\n width: 2.25rem;\n height: 0.25rem;\n background: var(--ui-border, #e2e8f0);\n border-radius: 9999px;\n margin: 0.5rem auto 0;\n flex: 0 0 auto;\n }\n .panel-header {\n padding: 0.25rem 0.5rem 0.5rem;\n }\n }\n `,\n ];\n\n private _previousFocus: HTMLElement | null = null;\n\n private _close() {\n this.dispatchEvent(new CustomEvent(\"panel-close\", { bubbles: true, composed: true }));\n }\n\n private _onWindowKeydown = (event: KeyboardEvent): void => {\n if (!this.open || !claimEscape(this, event)) return;\n this._close();\n };\n\n protected override updated(changed: PropertyValues): void {\n if (!changed.has(\"open\")) return;\n if (this.open) {\n activateLayer(this);\n this._previousFocus = document.activeElement as HTMLElement | null;\n window.addEventListener(\"keydown\", this._onWindowKeydown);\n this.shadowRoot?.querySelector<HTMLElement>(\".close-btn\")?.focus();\n return;\n }\n deactivateLayer(this);\n window.removeEventListener(\"keydown\", this._onWindowKeydown);\n if (this._previousFocus?.isConnected) this._previousFocus.focus();\n this._previousFocus = null;\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n window.removeEventListener(\"keydown\", this._onWindowKeydown);\n deactivateLayer(this);\n if (this._previousFocus?.isConnected) this._previousFocus.focus();\n this._previousFocus = null;\n this.open = false;\n }\n\n override render() {\n const explicitLabel = this.getAttribute(\"aria-label\");\n return html`\n <div\n class=\"panel ${this.open ? \"open\" : \"\"}\"\n role=\"dialog\"\n aria-modal=\"false\"\n aria-label=${explicitLabel ?? nothing}\n aria-labelledby=${explicitLabel ? nothing : \"panel-title\"}\n aria-hidden=${String(!this.open)}\n ?inert=${!this.open}\n >\n <div class=\"handle\" aria-hidden=\"true\"></div>\n <div class=\"panel-header\">\n <span id=\"panel-title\" class=\"panel-title\">\n <slot name=\"title\">${this.heading || \"Panel\"}</slot>\n </span>\n <button class=\"close-btn\" aria-label=\"Close panel\" @click=${this._close}>\n <span aria-hidden=\"true\">${iconX(18)}</span>\n </button>\n </div>\n <slot></slot>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"slide-panel\": SlidePanel;\n }\n}\n"]}
|
package/dist/stat-meter.js
CHANGED
|
@@ -46,8 +46,8 @@ let StatMeter = class StatMeter extends LitElement {
|
|
|
46
46
|
|
|
47
47
|
.label {
|
|
48
48
|
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
49
|
-
font-weight: 600;
|
|
50
|
-
letter-spacing: 0.04em;
|
|
49
|
+
font-weight: var(--ui-font-weight-semibold, 600);
|
|
50
|
+
letter-spacing: var(--ui-tracking-wide, 0.04em);
|
|
51
51
|
color: var(--ui-text-muted, #64748b);
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -63,11 +63,11 @@ let StatMeter = class StatMeter extends LitElement {
|
|
|
63
63
|
.fill {
|
|
64
64
|
display: block;
|
|
65
65
|
height: 100%;
|
|
66
|
-
/*
|
|
66
|
+
/* Keep the top highlight while softening the dark edge for compact meters. */
|
|
67
67
|
background: linear-gradient(
|
|
68
68
|
to bottom,
|
|
69
69
|
color-mix(in srgb, var(--fill-color, var(--ui-success, #16a34a)) 70%, #ffffff) 0%,
|
|
70
|
-
color-mix(in srgb, var(--fill-color, var(--ui-success, #16a34a))
|
|
70
|
+
color-mix(in srgb, var(--fill-color, var(--ui-success, #16a34a)) 80%, #000000) 100%
|
|
71
71
|
);
|
|
72
72
|
transition: width 200ms ease;
|
|
73
73
|
}
|
|
@@ -75,7 +75,7 @@ let StatMeter = class StatMeter extends LitElement {
|
|
|
75
75
|
.value {
|
|
76
76
|
min-width: 2rem;
|
|
77
77
|
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
78
|
-
font-weight: 500;
|
|
78
|
+
font-weight: var(--ui-font-weight-medium, 500);
|
|
79
79
|
color: var(--ui-text, #0f172a);
|
|
80
80
|
font-variant-numeric: tabular-nums;
|
|
81
81
|
text-align: right;
|
package/dist/stat-meter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stat-meter.js","sourceRoot":"","sources":["../src/stat-meter.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;;;;GAOG;AAEI,IAAM,SAAS,GAAf,MAAM,SAAU,SAAQ,UAAU;IAAlC;;QAgEL,6DAA6D;QACjD,UAAK,GAAG,EAAE,CAAC;QAEvB,qFAAqF;QACzD,YAAO,GAAkB,IAAI,CAAC;QAE1D,mEAAmE;QACvD,UAAK,GAAG,EAAE,CAAC;IAczB,CAAC;aApFiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA0DF;KACF,AA7DqB,CA6DpB;IAWO,MAAM;QACb,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC;QACzB,MAAM,OAAO,GAAG,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QACnE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,OAAO,oBAAoB,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,UAAU,OAAO,GAAG,CAAC;QACxG,OAAO,IAAI,CAAA;4BACa,IAAI,CAAC,KAAK;mDACa,IAAI,CAAC,KAAK,IAAI,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG;mCACrE,SAAS;;4BAEhB,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG;KACrD,CAAC;IACJ,CAAC;CACF,CAAA;AApBa;IAAX,QAAQ,EAAE;wCAAY;AAGK;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;0CAA+B;AAG9C;IAAX,QAAQ,EAAE;wCAAY;AAvEZ,SAAS;IADrB,aAAa,CAAC,YAAY,CAAC;GACf,SAAS,CAqFrB","sourcesContent":["import { LitElement, css, html } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { tokens } from \"./tokens.js\";\n\n/**\n * A compact labeled meter for a single percentage reading — e.g. CPU or\n * memory usage in a dashboard header. `percent` may be `null` when no\n * reading is available yet (e.g. the first tick of a polling metric); the\n * bar then renders empty and the value shows an em dash instead of \"0%\".\n *\n * @element stat-meter\n */\n@customElement(\"stat-meter\")\nexport class StatMeter extends LitElement {\n static override styles = [\n tokens,\n css`\n :host {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n font-family: var(\n --ui-font,\n ui-sans-serif,\n system-ui,\n sans-serif,\n \"Apple Color Emoji\",\n \"Segoe UI Emoji\",\n \"Segoe UI Symbol\",\n \"Noto Color Emoji\"\n );\n }\n\n .label {\n font-size: var(--ui-font-size-sm, 0.75rem);\n font-weight: 600;\n letter-spacing: 0.04em;\n color: var(--ui-text-muted, #64748b);\n }\n\n .track {\n display: block;\n width: 4rem;\n height: 0.375rem;\n border-radius: 9999px;\n background: var(--ui-surface-muted, #f8fafc);\n overflow: hidden;\n }\n\n .fill {\n display: block;\n height: 100%;\n /*
|
|
1
|
+
{"version":3,"file":"stat-meter.js","sourceRoot":"","sources":["../src/stat-meter.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;;;;GAOG;AAEI,IAAM,SAAS,GAAf,MAAM,SAAU,SAAQ,UAAU;IAAlC;;QAgEL,6DAA6D;QACjD,UAAK,GAAG,EAAE,CAAC;QAEvB,qFAAqF;QACzD,YAAO,GAAkB,IAAI,CAAC;QAE1D,mEAAmE;QACvD,UAAK,GAAG,EAAE,CAAC;IAczB,CAAC;aApFiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA0DF;KACF,AA7DqB,CA6DpB;IAWO,MAAM;QACb,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC;QACzB,MAAM,OAAO,GAAG,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QACnE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,OAAO,oBAAoB,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,UAAU,OAAO,GAAG,CAAC;QACxG,OAAO,IAAI,CAAA;4BACa,IAAI,CAAC,KAAK;mDACa,IAAI,CAAC,KAAK,IAAI,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG;mCACrE,SAAS;;4BAEhB,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG;KACrD,CAAC;IACJ,CAAC;CACF,CAAA;AApBa;IAAX,QAAQ,EAAE;wCAAY;AAGK;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;0CAA+B;AAG9C;IAAX,QAAQ,EAAE;wCAAY;AAvEZ,SAAS;IADrB,aAAa,CAAC,YAAY,CAAC;GACf,SAAS,CAqFrB","sourcesContent":["import { LitElement, css, html } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { tokens } from \"./tokens.js\";\n\n/**\n * A compact labeled meter for a single percentage reading — e.g. CPU or\n * memory usage in a dashboard header. `percent` may be `null` when no\n * reading is available yet (e.g. the first tick of a polling metric); the\n * bar then renders empty and the value shows an em dash instead of \"0%\".\n *\n * @element stat-meter\n */\n@customElement(\"stat-meter\")\nexport class StatMeter extends LitElement {\n static override styles = [\n tokens,\n css`\n :host {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n font-family: var(\n --ui-font,\n ui-sans-serif,\n system-ui,\n sans-serif,\n \"Apple Color Emoji\",\n \"Segoe UI Emoji\",\n \"Segoe UI Symbol\",\n \"Noto Color Emoji\"\n );\n }\n\n .label {\n font-size: var(--ui-font-size-sm, 0.75rem);\n font-weight: var(--ui-font-weight-semibold, 600);\n letter-spacing: var(--ui-tracking-wide, 0.04em);\n color: var(--ui-text-muted, #64748b);\n }\n\n .track {\n display: block;\n width: 4rem;\n height: 0.375rem;\n border-radius: 9999px;\n background: var(--ui-surface-muted, #f8fafc);\n overflow: hidden;\n }\n\n .fill {\n display: block;\n height: 100%;\n /* Keep the top highlight while softening the dark edge for compact meters. */\n background: linear-gradient(\n to bottom,\n color-mix(in srgb, var(--fill-color, var(--ui-success, #16a34a)) 70%, #ffffff) 0%,\n color-mix(in srgb, var(--fill-color, var(--ui-success, #16a34a)) 80%, #000000) 100%\n );\n transition: width 200ms ease;\n }\n\n .value {\n min-width: 2rem;\n font-size: var(--ui-font-size-sm, 0.75rem);\n font-weight: var(--ui-font-weight-medium, 500);\n color: var(--ui-text, #0f172a);\n font-variant-numeric: tabular-nums;\n text-align: right;\n }\n @media (prefers-reduced-motion: reduce) {\n .fill {\n transition: none;\n }\n }\n `,\n ];\n\n /** Short label shown before the bar, e.g. \"CPU\" or \"MEM\". */\n @property() label = \"\";\n\n /** Percentage 0-100. `null` renders an empty bar and a \"—\" value instead of \"0%\". */\n @property({ type: Number }) percent: number | null = null;\n\n /** Fill color override; falls back to the `--ui-success` token. */\n @property() color = \"\";\n\n override render() {\n const pct = this.percent;\n const clamped = pct === null ? 0 : Math.min(100, Math.max(0, pct));\n const fillStyle = this.color ? `width: ${clamped}%; --fill-color: ${this.color}` : `width: ${clamped}%`;\n return html`\n <span class=\"label\">${this.label}</span>\n <span class=\"track\" role=\"img\" aria-label=\"${this.label} ${pct === null ? \"no reading\" : `${pct}%`}\">\n <span class=\"fill\" style=${fillStyle}></span>\n </span>\n <span class=\"value\">${pct === null ? \"—\" : `${pct}%`}</span>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"stat-meter\": StatMeter;\n }\n}\n"]}
|
package/dist/status-pill.js
CHANGED
|
@@ -37,7 +37,7 @@ let StatusPill = class StatusPill extends LitElement {
|
|
|
37
37
|
border-radius: 9999px;
|
|
38
38
|
padding: 0.25rem 0.5rem;
|
|
39
39
|
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
40
|
-
font-weight: 500;
|
|
40
|
+
font-weight: var(--ui-font-weight-medium, 500);
|
|
41
41
|
font-family: var(
|
|
42
42
|
--ui-font,
|
|
43
43
|
ui-sans-serif,
|
|
@@ -93,7 +93,7 @@ let StatusPill = class StatusPill extends LitElement {
|
|
|
93
93
|
render() {
|
|
94
94
|
return html `
|
|
95
95
|
<span class="pill ${this.color}">
|
|
96
|
-
${this.spinner ? html `<span class="spin" aria-hidden="true">${iconArrowPath(
|
|
96
|
+
${this.spinner ? html `<span class="spin" aria-hidden="true">${iconArrowPath(14)}</span>` : nothing}
|
|
97
97
|
${this.label}
|
|
98
98
|
</span>
|
|
99
99
|
`;
|
package/dist/status-pill.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status-pill.js","sourceRoot":"","sources":["../src/status-pill.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrC;;;;;GAKG;AAEI,IAAM,UAAU,GAAhB,MAAM,UAAW,SAAQ,UAAU;IAAnC;;QAoEL,kCAAkC;QACtB,UAAK,GAAG,EAAE,CAAC;QACvB,qBAAqB;QACT,UAAK,GAAoB,SAAS,CAAC;QAC/C,sDAAsD;QACzB,YAAO,GAAG,KAAK,CAAC;IAU/C,CAAC;aAlFiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA8DF;KACF,AAjEqB,CAiEpB;IASO,MAAM;QACb,OAAO,IAAI,CAAA;0BACW,IAAI,CAAC,KAAK;UAC1B,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAA,yCAAyC,aAAa,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;UAChG,IAAI,CAAC,KAAK;;KAEf,CAAC;IACJ,CAAC;CACF,CAAA;AAda;IAAX,QAAQ,EAAE;yCAAY;AAEX;IAAX,QAAQ,EAAE;yCAAoC;AAElB;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;2CAAiB;AAzElC,UAAU;IADtB,aAAa,CAAC,aAAa,CAAC;GAChB,UAAU,CAmFtB","sourcesContent":["import { LitElement, css, html, nothing } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { iconArrowPath } from \"./icons.js\";\nimport { tokens } from \"./tokens.js\";\n\nexport type StatusPillColor = \"neutral\" | \"info\" | \"primary\" | \"success\" | \"warning\" | \"danger\";\n\n/**\n * Small colored status pill, optionally with a spinning icon — for task/run\n * states (\"Open\", \"Blocked\", \"Done\") and live-activity indicators (\"Running\").\n *\n * @element status-pill\n */\n@customElement(\"status-pill\")\nexport class StatusPill extends LitElement {\n static override styles = [\n tokens,\n css`\n :host {\n display: inline-flex;\n }\n .pill {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n border-radius: 9999px;\n padding: 0.25rem 0.5rem;\n font-size: var(--ui-font-size-sm, 0.75rem);\n font-weight: 500;\n font-family: var(\n --ui-font,\n ui-sans-serif,\n system-ui,\n sans-serif,\n \"Apple Color Emoji\",\n \"Segoe UI Emoji\",\n \"Segoe UI Symbol\",\n \"Noto Color Emoji\"\n );\n white-space: nowrap;\n }\n .pill.neutral {\n background: color-mix(in srgb, var(--ui-text-muted, #64748b) 15%, transparent);\n color: var(--ui-text-muted, #64748b);\n }\n .pill.info {\n background: color-mix(in srgb, var(--ui-info, #0ea5e9) 15%, transparent);\n color: var(--ui-info, #0ea5e9);\n }\n .pill.primary {\n background: color-mix(in srgb, var(--ui-primary, #4f46e5) 15%, transparent);\n color: var(--ui-primary, #4f46e5);\n }\n .pill.success {\n background: color-mix(in srgb, var(--ui-success, #16a34a) 15%, transparent);\n color: var(--ui-success, #16a34a);\n }\n .pill.warning {\n background: color-mix(in srgb, var(--ui-warning, #d97706) 15%, transparent);\n color: var(--ui-warning, #d97706);\n }\n .pill.danger {\n background: color-mix(in srgb, var(--ui-danger, #dc2626) 15%, transparent);\n color: var(--ui-danger, #dc2626);\n }\n .spin {\n display: inline-flex;\n animation: spin 0.8s linear infinite;\n }\n @keyframes spin {\n to {\n transform: rotate(360deg);\n }\n }\n @media (prefers-reduced-motion: reduce) {\n .spin {\n animation: none;\n }\n }\n `,\n ];\n\n /** Text shown inside the pill. */\n @property() label = \"\";\n /** Color variant. */\n @property() color: StatusPillColor = \"neutral\";\n /** Renders a small spinning icon before the label. */\n @property({ type: Boolean }) spinner = false;\n\n override render() {\n return html`\n <span class=\"pill ${this.color}\">\n ${this.spinner ? html`<span class=\"spin\" aria-hidden=\"true\">${iconArrowPath(
|
|
1
|
+
{"version":3,"file":"status-pill.js","sourceRoot":"","sources":["../src/status-pill.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrC;;;;;GAKG;AAEI,IAAM,UAAU,GAAhB,MAAM,UAAW,SAAQ,UAAU;IAAnC;;QAoEL,kCAAkC;QACtB,UAAK,GAAG,EAAE,CAAC;QACvB,qBAAqB;QACT,UAAK,GAAoB,SAAS,CAAC;QAC/C,sDAAsD;QACzB,YAAO,GAAG,KAAK,CAAC;IAU/C,CAAC;aAlFiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA8DF;KACF,AAjEqB,CAiEpB;IASO,MAAM;QACb,OAAO,IAAI,CAAA;0BACW,IAAI,CAAC,KAAK;UAC1B,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAA,yCAAyC,aAAa,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;UAChG,IAAI,CAAC,KAAK;;KAEf,CAAC;IACJ,CAAC;CACF,CAAA;AAda;IAAX,QAAQ,EAAE;yCAAY;AAEX;IAAX,QAAQ,EAAE;yCAAoC;AAElB;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;2CAAiB;AAzElC,UAAU;IADtB,aAAa,CAAC,aAAa,CAAC;GAChB,UAAU,CAmFtB","sourcesContent":["import { LitElement, css, html, nothing } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { iconArrowPath } from \"./icons.js\";\nimport { tokens } from \"./tokens.js\";\n\nexport type StatusPillColor = \"neutral\" | \"info\" | \"primary\" | \"success\" | \"warning\" | \"danger\";\n\n/**\n * Small colored status pill, optionally with a spinning icon — for task/run\n * states (\"Open\", \"Blocked\", \"Done\") and live-activity indicators (\"Running\").\n *\n * @element status-pill\n */\n@customElement(\"status-pill\")\nexport class StatusPill extends LitElement {\n static override styles = [\n tokens,\n css`\n :host {\n display: inline-flex;\n }\n .pill {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n border-radius: 9999px;\n padding: 0.25rem 0.5rem;\n font-size: var(--ui-font-size-sm, 0.75rem);\n font-weight: var(--ui-font-weight-medium, 500);\n font-family: var(\n --ui-font,\n ui-sans-serif,\n system-ui,\n sans-serif,\n \"Apple Color Emoji\",\n \"Segoe UI Emoji\",\n \"Segoe UI Symbol\",\n \"Noto Color Emoji\"\n );\n white-space: nowrap;\n }\n .pill.neutral {\n background: color-mix(in srgb, var(--ui-text-muted, #64748b) 15%, transparent);\n color: var(--ui-text-muted, #64748b);\n }\n .pill.info {\n background: color-mix(in srgb, var(--ui-info, #0ea5e9) 15%, transparent);\n color: var(--ui-info, #0ea5e9);\n }\n .pill.primary {\n background: color-mix(in srgb, var(--ui-primary, #4f46e5) 15%, transparent);\n color: var(--ui-primary, #4f46e5);\n }\n .pill.success {\n background: color-mix(in srgb, var(--ui-success, #16a34a) 15%, transparent);\n color: var(--ui-success, #16a34a);\n }\n .pill.warning {\n background: color-mix(in srgb, var(--ui-warning, #d97706) 15%, transparent);\n color: var(--ui-warning, #d97706);\n }\n .pill.danger {\n background: color-mix(in srgb, var(--ui-danger, #dc2626) 15%, transparent);\n color: var(--ui-danger, #dc2626);\n }\n .spin {\n display: inline-flex;\n animation: spin 0.8s linear infinite;\n }\n @keyframes spin {\n to {\n transform: rotate(360deg);\n }\n }\n @media (prefers-reduced-motion: reduce) {\n .spin {\n animation: none;\n }\n }\n `,\n ];\n\n /** Text shown inside the pill. */\n @property() label = \"\";\n /** Color variant. */\n @property() color: StatusPillColor = \"neutral\";\n /** Renders a small spinning icon before the label. */\n @property({ type: Boolean }) spinner = false;\n\n override render() {\n return html`\n <span class=\"pill ${this.color}\">\n ${this.spinner ? html`<span class=\"spin\" aria-hidden=\"true\">${iconArrowPath(14)}</span>` : nothing}\n ${this.label}\n </span>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"status-pill\": StatusPill;\n }\n}\n"]}
|
package/dist/tile-grid.js
CHANGED
|
@@ -144,7 +144,7 @@ let TileGrid = class TileGrid extends LitElement {
|
|
|
144
144
|
${this.fileIcon
|
|
145
145
|
? html `
|
|
146
146
|
<div class="tile-body">
|
|
147
|
-
<span class="tile-icon" aria-hidden="true">${iconDocument(
|
|
147
|
+
<span class="tile-icon" aria-hidden="true">${iconDocument(14)}</span>
|
|
148
148
|
<div class="tile-content">${this.renderTile(item)}</div>
|
|
149
149
|
</div>
|
|
150
150
|
`
|
package/dist/tile-grid.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tile-grid.js","sourceRoot":"","sources":["../src/tile-grid.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;;;;;;;;;;;;;;GAiBG;AAEI,IAAM,QAAQ,GAAd,MAAM,QAAS,SAAQ,UAAU;IAAjC;;QAgEL,iFAAiF;QACjD,UAAK,GAAc,EAAE,CAAC;QACtD,sGAAsG;QACtE,YAAO,GAAsD,CAC3F,KAAK,EACL,KAAK,EACL,EAAE,CAAC,KAAK,CAAC;QACX,yEAAyE;QACzC,eAAU,GAA+B,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAChG,yFAAyF;QACzD,aAAQ,GAA8C,IAAI,CAAC;QAC3F,0GAA0G;QACrD,aAAQ,GAAG,KAAK,CAAC;IAwDxE,CAAC;aAnIiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA0DF;KACF,AA7DqB,CA6DpB;IAgBF,YAAY,CAAC,IAAa,EAAE,CAAa;QACvC,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC3B,IAAI,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC;YAAE,OAAO;QACzE,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,IAAI;YAAE,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC;IACjC,CAAC;IAED,cAAc,CAAC,IAAY,EAAE,CAAgB;QAC3C,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,aAAa;YAAE,OAAO;QAC9D,CAAC,CAAC,cAAc,EAAE,CAAC;QACnB,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC;IACvB,CAAC;IAED,oBAAoB,CAAC,IAAmB,EAAE,IAAwB;QAChE,MAAM,QAAQ,GACZ,4GAA4G,CAAC;QAC/G,KAAK,MAAM,MAAM,IAAI,IAAI,EAAE,CAAC;YAC1B,IAAI,MAAM,KAAK,IAAI;gBAAE,OAAO,KAAK,CAAC;YAClC,IAAI,MAAM,YAAY,WAAW,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;gBAAE,OAAO,IAAI,CAAC;QAC7E,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAEQ,MAAM;QACb,OAAO,IAAI,CAAA;;UAEL,MAAM,CACN,IAAI,CAAC,KAAK,EACV,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,EAClC,CAAC,IAAI,EAAE,EAAE;YACP,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;YAC3C,OAAO,IAAI,CAAA;;wBAEC,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,MAAM;uBACjC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;2BACnB,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO;yBACtB,CAAC,CAAa,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC;2BAC3C,IAAI,CAAC,CAAC,CAAC,CAAC,CAAgB,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO;;kBAE5E,IAAI,CAAC,QAAQ;gBACb,CAAC,CAAC,IAAI,CAAA;;qEAE6C,YAAY,CAAC,EAAE,CAAC;oDACjC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;;qBAEpD;gBACH,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;;aAE5B,CAAC;QACJ,CAAC,CACF;;KAEJ,CAAC;IACJ,CAAC;CACF,CAAA;AAnEiC;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;uCAAuB;AAEtB;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;yCAGpB;AAEqB;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;4CAAiE;AAEhE;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;0CAA4D;AAEtC;IAApD,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC;0CAAkB;AA5E3D,QAAQ;IADpB,aAAa,CAAC,WAAW,CAAC;GACd,QAAQ,CAoIpB","sourcesContent":["import { LitElement, css, html, nothing } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { repeat } from \"lit/directives/repeat.js\";\nimport { iconDocument } from \"./icons.js\";\nimport { tokens } from \"./tokens.js\";\n\n/**\n * A generic, presentational grid shell: renders one bordered card per entry\n * in `items`, with each tile's content produced by `renderTile` (default:\n * stringify). Knows nothing about what an \"item\" means — callers own the\n * data shape entirely. Modeled directly on `data-table`'s headless pattern.\n *\n * Optional `itemHref` makes whole tiles clickable (navigating via\n * `location.hash`), without hijacking clicks on nested interactive elements\n * (links/buttons) a tile's rendered content might contain.\n *\n * Optional `fileIcon` prefixes each tile with a decorative Heroicons\n * \"document\" glyph, for grids whose items represent files — the icon never\n * carries its own accessible name since the rendered tile content (e.g. a\n * filename) already identifies the item. Off by default, so it consumes no\n * layout space or markup for grids of non-file items.\n *\n * @element tile-grid\n */\n@customElement(\"tile-grid\")\nexport class TileGrid extends LitElement {\n static override styles = [\n tokens,\n css`\n :host {\n display: block;\n font-family: var(\n --ui-font,\n ui-sans-serif,\n system-ui,\n sans-serif,\n \"Apple Color Emoji\",\n \"Segoe UI Emoji\",\n \"Segoe UI Symbol\",\n \"Noto Color Emoji\"\n );\n font-size: var(--ui-font-size-sm, 0.75rem);\n }\n .grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(8.75rem, 1fr));\n gap: 0.75rem;\n }\n .tile {\n border: 1px solid var(--ui-border, #e2e8f0);\n border-radius: var(--ui-radius, 0.5rem);\n padding: 0.75rem;\n color: var(--ui-text, #0f172a);\n }\n .tile.clickable {\n cursor: pointer;\n }\n .tile.clickable:hover {\n background: var(--ui-surface-muted, #f8fafc);\n }\n .tile.clickable:focus-visible {\n outline: none;\n box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));\n }\n .tile-body {\n display: flex;\n align-items: flex-start;\n gap: 0.5rem;\n }\n .tile-icon {\n display: inline-flex;\n flex-shrink: 0;\n margin-top: 0.125rem;\n color: var(--ui-text-muted, #64748b);\n }\n .tile-content {\n min-width: 0;\n flex: 1;\n }\n @media (forced-colors: active) {\n .tile.clickable:focus-visible {\n outline: 2px solid CanvasText;\n outline-offset: 2px;\n box-shadow: none;\n }\n }\n `,\n ];\n\n /** Tile data; opaque to this component beyond what `renderTile` does with it. */\n @property({ attribute: false }) items: unknown[] = [];\n /** Stable identity for `items[i]`, used as the repeat-directive key. Defaults to the item's index. */\n @property({ attribute: false }) itemKey: (item: unknown, index: number) => string | number = (\n _item,\n index,\n ) => index;\n /** Produces a tile's rendered content for `item`. Default: stringify. */\n @property({ attribute: false }) renderTile: (item: unknown) => unknown = (item) => String(item);\n /** When set, clicking a tile (outside any nested link/button) navigates to this hash. */\n @property({ attribute: false }) itemHref: ((item: unknown) => string | null) | null = null;\n /** Prefixes each tile with a decorative \"document\" icon, for grids of file-like items. Off by default. */\n @property({ type: Boolean, attribute: \"file-icon\" }) fileIcon = false;\n\n #onTileClick(item: unknown, e: MouseEvent): void {\n if (!this.itemHref) return;\n if (this.#isNestedInteractive(e.composedPath(), e.currentTarget)) return;\n const href = this.itemHref(item);\n if (href) location.hash = href;\n }\n\n #onTileKeydown(href: string, e: KeyboardEvent): void {\n if (e.key !== \"Enter\" || e.target !== e.currentTarget) return;\n e.preventDefault();\n location.hash = href;\n }\n\n #isNestedInteractive(path: EventTarget[], tile: EventTarget | null): boolean {\n const selector =\n \"a, button, input, select, textarea, summary, [contenteditable], [role='button'], [role='link'], [tabindex]\";\n for (const target of path) {\n if (target === tile) return false;\n if (target instanceof HTMLElement && target.matches(selector)) return true;\n }\n return false;\n }\n\n override render() {\n return html`\n <div class=\"grid\">\n ${repeat(\n this.items,\n (item, i) => this.itemKey(item, i),\n (item) => {\n const href = this.itemHref?.(item) ?? null;\n return html`\n <div\n class=${href ? \"tile clickable\" : \"tile\"}\n role=${href ? \"link\" : nothing}\n tabindex=${href ? \"0\" : nothing}\n @click=${(e: MouseEvent) => this.#onTileClick(item, e)}\n @keydown=${href ? (e: KeyboardEvent) => this.#onTileKeydown(href, e) : nothing}\n >\n ${this.fileIcon\n ? html`\n <div class=\"tile-body\">\n <span class=\"tile-icon\" aria-hidden=\"true\">${iconDocument(
|
|
1
|
+
{"version":3,"file":"tile-grid.js","sourceRoot":"","sources":["../src/tile-grid.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, css, html, nothing } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { repeat } from \"lit/directives/repeat.js\";\nimport { iconDocument } from \"./icons.js\";\nimport { tokens } from \"./tokens.js\";\n\n/**\n * A generic, presentational grid shell: renders one bordered card per entry\n * in `items`, with each tile's content produced by `renderTile` (default:\n * stringify). Knows nothing about what an \"item\" means — callers own the\n * data shape entirely. Modeled directly on `data-table`'s headless pattern.\n *\n * Optional `itemHref` makes whole tiles clickable (navigating via\n * `location.hash`), without hijacking clicks on nested interactive elements\n * (links/buttons) a tile's rendered content might contain.\n *\n * Optional `fileIcon` prefixes each tile with a decorative Heroicons\n * \"document\" glyph, for grids whose items represent files — the icon never\n * carries its own accessible name since the rendered tile content (e.g. a\n * filename) already identifies the item. Off by default, so it consumes no\n * layout space or markup for grids of non-file items.\n *\n * @element tile-grid\n */\n@customElement(\"tile-grid\")\nexport class TileGrid extends LitElement {\n static override styles = [\n tokens,\n css`\n :host {\n display: block;\n font-family: var(\n --ui-font,\n ui-sans-serif,\n system-ui,\n sans-serif,\n \"Apple Color Emoji\",\n \"Segoe UI Emoji\",\n \"Segoe UI Symbol\",\n \"Noto Color Emoji\"\n );\n font-size: var(--ui-font-size-sm, 0.75rem);\n }\n .grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(8.75rem, 1fr));\n gap: 0.75rem;\n }\n .tile {\n border: 1px solid var(--ui-border, #e2e8f0);\n border-radius: var(--ui-radius, 0.5rem);\n padding: 0.75rem;\n color: var(--ui-text, #0f172a);\n }\n .tile.clickable {\n cursor: pointer;\n }\n .tile.clickable:hover {\n background: var(--ui-surface-muted, #f8fafc);\n }\n .tile.clickable:focus-visible {\n outline: none;\n box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));\n }\n .tile-body {\n display: flex;\n align-items: flex-start;\n gap: 0.5rem;\n }\n .tile-icon {\n display: inline-flex;\n flex-shrink: 0;\n margin-top: 0.125rem;\n color: var(--ui-text-muted, #64748b);\n }\n .tile-content {\n min-width: 0;\n flex: 1;\n }\n @media (forced-colors: active) {\n .tile.clickable:focus-visible {\n outline: 2px solid CanvasText;\n outline-offset: 2px;\n box-shadow: none;\n }\n }\n `,\n ];\n\n /** Tile data; opaque to this component beyond what `renderTile` does with it. */\n @property({ attribute: false }) items: unknown[] = [];\n /** Stable identity for `items[i]`, used as the repeat-directive key. Defaults to the item's index. */\n @property({ attribute: false }) itemKey: (item: unknown, index: number) => string | number = (\n _item,\n index,\n ) => index;\n /** Produces a tile's rendered content for `item`. Default: stringify. */\n @property({ attribute: false }) renderTile: (item: unknown) => unknown = (item) => String(item);\n /** When set, clicking a tile (outside any nested link/button) navigates to this hash. */\n @property({ attribute: false }) itemHref: ((item: unknown) => string | null) | null = null;\n /** Prefixes each tile with a decorative \"document\" icon, for grids of file-like items. Off by default. */\n @property({ type: Boolean, attribute: \"file-icon\" }) fileIcon = false;\n\n #onTileClick(item: unknown, e: MouseEvent): void {\n if (!this.itemHref) return;\n if (this.#isNestedInteractive(e.composedPath(), e.currentTarget)) return;\n const href = this.itemHref(item);\n if (href) location.hash = href;\n }\n\n #onTileKeydown(href: string, e: KeyboardEvent): void {\n if (e.key !== \"Enter\" || e.target !== e.currentTarget) return;\n e.preventDefault();\n location.hash = href;\n }\n\n #isNestedInteractive(path: EventTarget[], tile: EventTarget | null): boolean {\n const selector =\n \"a, button, input, select, textarea, summary, [contenteditable], [role='button'], [role='link'], [tabindex]\";\n for (const target of path) {\n if (target === tile) return false;\n if (target instanceof HTMLElement && target.matches(selector)) return true;\n }\n return false;\n }\n\n override render() {\n return html`\n <div class=\"grid\">\n ${repeat(\n this.items,\n (item, i) => this.itemKey(item, i),\n (item) => {\n const href = this.itemHref?.(item) ?? null;\n return html`\n <div\n class=${href ? \"tile clickable\" : \"tile\"}\n role=${href ? \"link\" : nothing}\n tabindex=${href ? \"0\" : nothing}\n @click=${(e: MouseEvent) => this.#onTileClick(item, e)}\n @keydown=${href ? (e: KeyboardEvent) => this.#onTileKeydown(href, e) : nothing}\n >\n ${this.fileIcon\n ? html`\n <div class=\"tile-body\">\n <span class=\"tile-icon\" aria-hidden=\"true\">${iconDocument(14)}</span>\n <div class=\"tile-content\">${this.renderTile(item)}</div>\n </div>\n `\n : this.renderTile(item)}\n </div>\n `;\n },\n )}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"tile-grid\": TileGrid;\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast-notification.d.ts","sourceRoot":"","sources":["../src/toast-notification.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,OAAO,EAAE,MAAM,KAAK,CAAC;AAKrD,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAExD,UAAU,YAAY;IACpB,uEAAuE;IACvE,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAUD;;;;;;;;;;;GAWG;AACH,qBACa,iBAAkB,SAAQ,UAAU;IACtC,OAAO,CAAC,OAAO,CAAe;IAEvC,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,OAAO,CAAoD;IAEnE,OAAgB,MAAM,
|
|
1
|
+
{"version":3,"file":"toast-notification.d.ts","sourceRoot":"","sources":["../src/toast-notification.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,OAAO,EAAE,MAAM,KAAK,CAAC;AAKrD,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAExD,UAAU,YAAY;IACpB,uEAAuE;IACvE,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAUD;;;;;;;;;;;GAWG;AACH,qBACa,iBAAkB,SAAQ,UAAU;IACtC,OAAO,CAAC,OAAO,CAAe;IAEvC,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,OAAO,CAAoD;IAEnE,OAAgB,MAAM,4BAoGpB;IAEF;;;;;OAKG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,MAAM,CAYxD;IAED;;;OAGG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,QAOjB;IAEQ,oBAAoB,SAI5B;IAEQ,MAAM,0DAkBd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,oBAAoB,EAAE,iBAAiB,CAAC;KACzC;CACF;AAMD,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,QAE1C;AAED,6BAA6B;AAC7B,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,QAE5C;AAED,2BAA2B;AAC3B,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,QAEzC"}
|
|
@@ -48,7 +48,7 @@ let ToastNotification = class ToastNotification extends LitElement {
|
|
|
48
48
|
align-items: flex-start;
|
|
49
49
|
gap: 0.5rem;
|
|
50
50
|
border-radius: var(--ui-radius, 0.5rem);
|
|
51
|
-
padding: 0.
|
|
51
|
+
padding: 0.75rem;
|
|
52
52
|
box-shadow: var(
|
|
53
53
|
--ui-shadow-lg,
|
|
54
54
|
0 20px 25px -5px rgb(0 0 0 / 0.1),
|
|
@@ -65,7 +65,7 @@ let ToastNotification = class ToastNotification extends LitElement {
|
|
|
65
65
|
"Noto Color Emoji"
|
|
66
66
|
);
|
|
67
67
|
font-size: var(--ui-font-size, 0.875rem);
|
|
68
|
-
line-height: 1.
|
|
68
|
+
line-height: var(--ui-line-height-normal, 1.5);
|
|
69
69
|
color: var(--ui-on-accent, #ffffff);
|
|
70
70
|
background: var(--ui-text, #0f172a);
|
|
71
71
|
}
|
|
@@ -84,14 +84,19 @@ let ToastNotification = class ToastNotification extends LitElement {
|
|
|
84
84
|
}
|
|
85
85
|
.close {
|
|
86
86
|
flex: 0 0 auto;
|
|
87
|
+
display: inline-flex;
|
|
88
|
+
align-items: center;
|
|
89
|
+
justify-content: center;
|
|
90
|
+
width: 2rem;
|
|
91
|
+
height: 2rem;
|
|
87
92
|
background: none;
|
|
88
93
|
border: none;
|
|
89
|
-
padding: 0
|
|
94
|
+
padding: 0;
|
|
90
95
|
margin: -0.25rem -0.25rem -0.25rem 0;
|
|
91
96
|
color: inherit;
|
|
92
97
|
opacity: 0.8;
|
|
93
98
|
cursor: pointer;
|
|
94
|
-
line-height:
|
|
99
|
+
line-height: var(--ui-line-height-glyph, 1);
|
|
95
100
|
border-radius: var(--ui-radius-sm, 0.25rem);
|
|
96
101
|
}
|
|
97
102
|
.close:hover {
|
|
@@ -170,7 +175,7 @@ let ToastNotification = class ToastNotification extends LitElement {
|
|
|
170
175
|
>
|
|
171
176
|
<span class="message">${t.message}</span>
|
|
172
177
|
<button class="close" aria-label="Dismiss notification" @click=${() => this.dismiss(t.id)}>
|
|
173
|
-
<span aria-hidden="true">${iconX(
|
|
178
|
+
<span aria-hidden="true">${iconX(18)}</span>
|
|
174
179
|
</button>
|
|
175
180
|
</div>
|
|
176
181
|
`)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast-notification.js","sourceRoot":"","sources":["../src/toast-notification.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAiBrC,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAEjC;;;;;;;;;;;GAWG;AAEI,IAAM,iBAAiB,GAAvB,MAAM,iBAAkB,SAAQ,UAAU;IAA1C;;QACY,YAAO,GAAY,EAAE,CAAC;QAE/B,YAAO,GAAG,CAAC,CAAC;QACZ,YAAO,GAAG,IAAI,GAAG,EAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"toast-notification.js","sourceRoot":"","sources":["../src/toast-notification.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, css, html, nothing } from \"lit\";\nimport { customElement, state } from \"lit/decorators.js\";\nimport { iconX } from \"./icons.js\";\nimport { tokens } from \"./tokens.js\";\n\nexport type ToastVariant = \"error\" | \"info\" | \"success\";\n\ninterface ToastOptions {\n /** Visual style; also selects the accent color. Defaults to \"info\". */\n variant?: ToastVariant;\n /** Auto-dismiss delay in ms. Pass 0 to require a manual close. Defaults to 5000. */\n duration?: number;\n}\n\ninterface Toast {\n id: number;\n message: string;\n variant: ToastVariant;\n}\n\nconst DEFAULT_DURATION_MS = 5000;\n\n/**\n * Fixed-position stack of dismissible notifications, anchored top-right\n * (top-full-width on mobile). Not wired to any app state yet — callers add\n * toasts imperatively via the `show()` method on a live element reference,\n * e.g. `document.querySelector('toast-notification')?.show('Offline', { variant: 'error' })`,\n * or via the `notifySuccess`/`notifyError`/`notifyInfo` module-level helpers\n * exported from this file. Each toast auto-dismisses after `duration` ms and\n * can also be dismissed via its ✕ button. Appears/disappears instantly — no\n * slide/fade transitions.\n *\n * @element toast-notification\n */\n@customElement(\"toast-notification\")\nexport class ToastNotification extends LitElement {\n @state() private _toasts: Toast[] = [];\n\n private _nextId = 0;\n private _timers = new Map<number, ReturnType<typeof setTimeout>>();\n\n static override styles = [\n tokens,\n css`\n :host {\n position: fixed;\n top: 1rem;\n right: 1rem;\n z-index: 200;\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n max-width: 22.5rem;\n pointer-events: none;\n }\n .toast {\n pointer-events: auto;\n display: flex;\n align-items: flex-start;\n gap: 0.5rem;\n border-radius: var(--ui-radius, 0.5rem);\n padding: 0.75rem;\n box-shadow: var(\n --ui-shadow-lg,\n 0 20px 25px -5px rgb(0 0 0 / 0.1),\n 0 8px 10px -6px rgb(0 0 0 / 0.1)\n );\n font-family: var(\n --ui-font,\n ui-sans-serif,\n system-ui,\n sans-serif,\n \"Apple Color Emoji\",\n \"Segoe UI Emoji\",\n \"Segoe UI Symbol\",\n \"Noto Color Emoji\"\n );\n font-size: var(--ui-font-size, 0.875rem);\n line-height: var(--ui-line-height-normal, 1.5);\n color: var(--ui-on-accent, #ffffff);\n background: var(--ui-text, #0f172a);\n }\n .toast.error {\n background: var(--ui-danger, #dc2626);\n }\n .toast.success {\n background: var(--ui-success, #16a34a);\n }\n .toast.info {\n background: var(--ui-info, #0ea5e9);\n }\n .message {\n flex: 1 1 auto;\n word-break: break-word;\n }\n .close {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n background: none;\n border: none;\n padding: 0;\n margin: -0.25rem -0.25rem -0.25rem 0;\n color: inherit;\n opacity: 0.8;\n cursor: pointer;\n line-height: var(--ui-line-height-glyph, 1);\n border-radius: var(--ui-radius-sm, 0.25rem);\n }\n .close:hover {\n opacity: 1;\n background: var(--ui-hover-overlay, rgb(255 255 255 / 0.32));\n }\n .close:focus-visible {\n outline: none;\n opacity: 1;\n box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));\n }\n @media (forced-colors: active) {\n .toast {\n border: 1px solid CanvasText;\n forced-color-adjust: auto;\n }\n .close:focus-visible {\n outline: 2px solid CanvasText;\n outline-offset: 2px;\n box-shadow: none;\n }\n }\n @media (max-width: 48rem) {\n :host {\n top: 0.75rem;\n right: 0.75rem;\n left: 0.75rem;\n max-width: none;\n }\n }\n `,\n ];\n\n /**\n * Queues a toast for display.\n * @param message Text to show.\n * @param options Variant + auto-dismiss duration (see {@link ToastOptions}).\n * @returns The toast's id — pass to `dismiss()` to remove it early.\n */\n show(message: string, options: ToastOptions = {}): number {\n const id = this._nextId++;\n const variant = options.variant ?? \"info\";\n const duration = options.duration ?? DEFAULT_DURATION_MS;\n this._toasts = [...this._toasts, { id, message, variant }];\n if (duration > 0) {\n this._timers.set(\n id,\n setTimeout(() => this.dismiss(id), duration),\n );\n }\n return id;\n }\n\n /**\n * Removes a toast immediately, cancelling its pending auto-dismiss timer if any.\n * @param id The id returned by `show()`.\n */\n dismiss(id: number) {\n const timer = this._timers.get(id);\n if (timer) {\n clearTimeout(timer);\n this._timers.delete(id);\n }\n this._toasts = this._toasts.filter((t) => t.id !== id);\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n for (const timer of this._timers.values()) clearTimeout(timer);\n this._timers.clear();\n }\n\n override render() {\n if (this._toasts.length === 0) return nothing;\n return html`\n ${this._toasts.map(\n (t) => html`\n <div\n class=\"toast ${t.variant}\"\n role=${t.variant === \"error\" ? \"alert\" : \"status\"}\n aria-atomic=\"true\"\n >\n <span class=\"message\">${t.message}</span>\n <button class=\"close\" aria-label=\"Dismiss notification\" @click=${() => this.dismiss(t.id)}>\n <span aria-hidden=\"true\">${iconX(18)}</span>\n </button>\n </div>\n `,\n )}\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"toast-notification\": ToastNotification;\n }\n}\n\nfunction getToast(): ToastNotification | null {\n return document.querySelector(\"toast-notification\");\n}\n\n/** Shows an error toast. Use for connection/network failures, not validation errors. */\nexport function notifyError(message: string) {\n getToast()?.show(message, { variant: \"error\" });\n}\n\n/** Shows a success toast. */\nexport function notifySuccess(message: string) {\n getToast()?.show(message, { variant: \"success\" });\n}\n\n/** Shows an info toast. */\nexport function notifyInfo(message: string) {\n getToast()?.show(message, { variant: \"info\" });\n}\n"]}
|
package/dist/tokens.css
CHANGED
|
@@ -25,9 +25,19 @@
|
|
|
25
25
|
--ui-focus-ring: 0 0 0 3px rgb(79 70 229 / 0.35);
|
|
26
26
|
--ui-font: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
|
27
27
|
--ui-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
28
|
+
--ui-font-size-lg: 1rem;
|
|
28
29
|
--ui-font-size: 0.875rem;
|
|
29
30
|
--ui-font-size-sm: 0.75rem;
|
|
30
31
|
--ui-font-size-xs: 0.6875rem;
|
|
32
|
+
--ui-font-weight-regular: 400;
|
|
33
|
+
--ui-font-weight-medium: 500;
|
|
34
|
+
--ui-font-weight-semibold: 600;
|
|
35
|
+
--ui-font-weight-bold: 700;
|
|
36
|
+
--ui-line-height-glyph: 1;
|
|
37
|
+
--ui-line-height-tight: 1.25;
|
|
38
|
+
--ui-line-height-normal: 1.5;
|
|
39
|
+
--ui-tracking-normal: 0;
|
|
40
|
+
--ui-tracking-wide: 0.04em;
|
|
31
41
|
}
|
|
32
42
|
|
|
33
43
|
@media (prefers-color-scheme: dark) {
|
package/dist/tokens.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAsD9C,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAoBlD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,MAAM,yBAAQ,CAAC"}
|
package/dist/tokens.js
CHANGED
|
@@ -36,9 +36,22 @@ export const tokenValues = {
|
|
|
36
36
|
// Type
|
|
37
37
|
"--ui-font": 'ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',
|
|
38
38
|
"--ui-font-mono": "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
|
|
39
|
-
"--ui-font-size": "
|
|
40
|
-
"--ui-font-size
|
|
39
|
+
"--ui-font-size-lg": "1rem", // text-base
|
|
40
|
+
"--ui-font-size": "0.875rem", // text-sm
|
|
41
|
+
"--ui-font-size-sm": "0.75rem", // text-xs
|
|
41
42
|
"--ui-font-size-xs": "0.6875rem",
|
|
43
|
+
// Font weights — the only four weights the system uses.
|
|
44
|
+
"--ui-font-weight-regular": "400",
|
|
45
|
+
"--ui-font-weight-medium": "500",
|
|
46
|
+
"--ui-font-weight-semibold": "600",
|
|
47
|
+
"--ui-font-weight-bold": "700",
|
|
48
|
+
// Line heights — glyph (icon/marker box), tight (headings), normal (body).
|
|
49
|
+
"--ui-line-height-glyph": "1",
|
|
50
|
+
"--ui-line-height-tight": "1.25",
|
|
51
|
+
"--ui-line-height-normal": "1.5",
|
|
52
|
+
// Letter spacing / tracking — default and the single widened step.
|
|
53
|
+
"--ui-tracking-normal": "0",
|
|
54
|
+
"--ui-tracking-wide": "0.04em",
|
|
42
55
|
};
|
|
43
56
|
/**
|
|
44
57
|
* Dark-mode overrides for the same `--ui-*` custom properties, applied by
|
package/dist/tokens.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,WAAW,GAA2B;IACjD,SAAS;IACT,cAAc,EAAE,SAAS,EAAE,aAAa;IACxC,oBAAoB,EAAE,SAAS,EAAE,aAAa;IAC9C,WAAW,EAAE,SAAS,EAAE,UAAU;IAClC,iBAAiB,EAAE,SAAS,EAAE,UAAU;IACxC,aAAa,EAAE,SAAS,EAAE,UAAU;IACpC,mBAAmB,EAAE,SAAS,EAAE,UAAU;IAC1C,cAAc,EAAE,SAAS,EAAE,YAAY;IACvC,cAAc,EAAE,SAAS,EAAE,YAAY;IACvC,oBAAoB,EAAE,SAAS,EAAE,YAAY;IAC7C,gBAAgB,EAAE,SAAS;IAC3B,WAAW,EAAE,SAAS,EAAE,YAAY;IACpC,iBAAiB,EAAE,SAAS,EAAE,YAAY;IAC1C,aAAa,EAAE,SAAS,EAAE,YAAY;IACtC,cAAc,EAAE,SAAS,EAAE,QAAQ;IACnC,oBAAoB,EAAE,SAAS,EAAE,WAAW;IAC5C,cAAc,EAAE,SAAS,EAAE,YAAY;IACvC,oBAAoB,EAAE,yBAAyB;IAC/C,cAAc,EAAE,sBAAsB,EAAE,kBAAkB;IAE1D,gBAAgB;IAChB,aAAa,EAAE,QAAQ,EAAE,aAAa;IACtC,gBAAgB,EAAE,SAAS,EAAE,UAAU;IACvC,aAAa,EACX,kEAAkE,EAAE,YAAY;IAClF,gBAAgB,EACd,qEAAqE,EAAE,YAAY;IACrF,iBAAiB,EAAE,iCAAiC;IAEpD,OAAO;IACP,WAAW,EACT,oHAAoH;IACtH,gBAAgB,EACd,kEAAkE;IACpE,gBAAgB,EAAE,UAAU;
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,WAAW,GAA2B;IACjD,SAAS;IACT,cAAc,EAAE,SAAS,EAAE,aAAa;IACxC,oBAAoB,EAAE,SAAS,EAAE,aAAa;IAC9C,WAAW,EAAE,SAAS,EAAE,UAAU;IAClC,iBAAiB,EAAE,SAAS,EAAE,UAAU;IACxC,aAAa,EAAE,SAAS,EAAE,UAAU;IACpC,mBAAmB,EAAE,SAAS,EAAE,UAAU;IAC1C,cAAc,EAAE,SAAS,EAAE,YAAY;IACvC,cAAc,EAAE,SAAS,EAAE,YAAY;IACvC,oBAAoB,EAAE,SAAS,EAAE,YAAY;IAC7C,gBAAgB,EAAE,SAAS;IAC3B,WAAW,EAAE,SAAS,EAAE,YAAY;IACpC,iBAAiB,EAAE,SAAS,EAAE,YAAY;IAC1C,aAAa,EAAE,SAAS,EAAE,YAAY;IACtC,cAAc,EAAE,SAAS,EAAE,QAAQ;IACnC,oBAAoB,EAAE,SAAS,EAAE,WAAW;IAC5C,cAAc,EAAE,SAAS,EAAE,YAAY;IACvC,oBAAoB,EAAE,yBAAyB;IAC/C,cAAc,EAAE,sBAAsB,EAAE,kBAAkB;IAE1D,gBAAgB;IAChB,aAAa,EAAE,QAAQ,EAAE,aAAa;IACtC,gBAAgB,EAAE,SAAS,EAAE,UAAU;IACvC,aAAa,EACX,kEAAkE,EAAE,YAAY;IAClF,gBAAgB,EACd,qEAAqE,EAAE,YAAY;IACrF,iBAAiB,EAAE,iCAAiC;IAEpD,OAAO;IACP,WAAW,EACT,oHAAoH;IACtH,gBAAgB,EACd,kEAAkE;IACpE,mBAAmB,EAAE,MAAM,EAAE,YAAY;IACzC,gBAAgB,EAAE,UAAU,EAAE,UAAU;IACxC,mBAAmB,EAAE,SAAS,EAAE,UAAU;IAC1C,mBAAmB,EAAE,WAAW;IAEhC,wDAAwD;IACxD,0BAA0B,EAAE,KAAK;IACjC,yBAAyB,EAAE,KAAK;IAChC,2BAA2B,EAAE,KAAK;IAClC,uBAAuB,EAAE,KAAK;IAE9B,2EAA2E;IAC3E,wBAAwB,EAAE,GAAG;IAC7B,wBAAwB,EAAE,MAAM;IAChC,yBAAyB,EAAE,KAAK;IAEhC,mEAAmE;IACnE,sBAAsB,EAAE,GAAG;IAC3B,oBAAoB,EAAE,QAAQ;CAC/B,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,eAAe,GAA2B;IACrD,cAAc,EAAE,SAAS,EAAE,aAAa;IACxC,oBAAoB,EAAE,SAAS,EAAE,aAAa;IAC9C,WAAW,EAAE,SAAS,EAAE,UAAU;IAClC,iBAAiB,EAAE,SAAS,EAAE,UAAU;IACxC,aAAa,EAAE,SAAS,EAAE,UAAU;IACpC,mBAAmB,EAAE,SAAS,EAAE,UAAU;IAC1C,cAAc,EAAE,SAAS,EAAE,YAAY;IACvC,cAAc,EAAE,SAAS,EAAE,YAAY;IACvC,oBAAoB,EAAE,SAAS,EAAE,YAAY;IAC7C,gBAAgB,EAAE,SAAS;IAC3B,WAAW,EAAE,SAAS,EAAE,YAAY;IACpC,iBAAiB,EAAE,SAAS,EAAE,YAAY;IAC1C,aAAa,EAAE,SAAS,EAAE,YAAY;IACtC,cAAc,EAAE,SAAS,EAAE,YAAY;IACvC,oBAAoB,EAAE,SAAS,EAAE,YAAY;IAC7C,cAAc,EAAE,SAAS,EAAE,YAAY;IACvC,oBAAoB,EAAE,yBAAyB;IAC/C,cAAc,EAAE,mBAAmB,EAAE,kBAAkB;IACvD,iBAAiB,EAAE,kCAAkC;CACtD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,GAAG,CAAA,EAAE,CAAC","sourcesContent":["import { css } from \"lit\";\n\n/**\n * Design token values, sourced from Tailwind v4's default theme palette.\n * Each entry maps a `--ui-*` custom property name to its default value.\n * These are the same values baked in as `var(--ui-*, <fallback>)` fallbacks\n * throughout component styles, so components render correctly with zero\n * external CSS. Consumers may override any of these by setting the custom\n * property on `:root` or an ancestor element (see `tokens.css`).\n */\nexport const tokenValues: Record<string, string> = {\n // Colors\n \"--ui-primary\": \"#4f46e5\", // indigo-600\n \"--ui-primary-hover\": \"#4338ca\", // indigo-700\n \"--ui-info\": \"#0ea5e9\", // sky-500\n \"--ui-info-hover\": \"#0284c7\", // sky-600\n \"--ui-danger\": \"#dc2626\", // red-600\n \"--ui-danger-hover\": \"#b91c1c\", // red-700\n \"--ui-success\": \"#16a34a\", // green-600\n \"--ui-warning\": \"#d97706\", // amber-600\n \"--ui-warning-hover\": \"#b45309\", // amber-700\n \"--ui-on-accent\": \"#ffffff\",\n \"--ui-text\": \"#0f172a\", // slate-900\n \"--ui-text-muted\": \"#64748b\", // slate-500\n \"--ui-border\": \"#e2e8f0\", // slate-200\n \"--ui-surface\": \"#ffffff\", // white\n \"--ui-surface-muted\": \"#f8fafc\", // slate-50\n \"--ui-tooltip\": \"#0f172a\", // slate-900\n \"--ui-hover-overlay\": \"rgb(255 255 255 / 0.32)\",\n \"--ui-overlay\": \"rgb(15 23 42 / 0.45)\", // slate-900 / 45%\n\n // Shape / depth\n \"--ui-radius\": \"0.5rem\", // rounded-lg\n \"--ui-radius-sm\": \"0.25rem\", // rounded\n \"--ui-shadow\":\n \"0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)\", // shadow-md\n \"--ui-shadow-lg\":\n \"0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)\", // shadow-xl\n \"--ui-focus-ring\": \"0 0 0 3px rgb(79 70 229 / 0.35)\",\n\n // Type\n \"--ui-font\":\n 'ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"',\n \"--ui-font-mono\":\n \"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace\",\n \"--ui-font-size-lg\": \"1rem\", // text-base\n \"--ui-font-size\": \"0.875rem\", // text-sm\n \"--ui-font-size-sm\": \"0.75rem\", // text-xs\n \"--ui-font-size-xs\": \"0.6875rem\",\n\n // Font weights — the only four weights the system uses.\n \"--ui-font-weight-regular\": \"400\",\n \"--ui-font-weight-medium\": \"500\",\n \"--ui-font-weight-semibold\": \"600\",\n \"--ui-font-weight-bold\": \"700\",\n\n // Line heights — glyph (icon/marker box), tight (headings), normal (body).\n \"--ui-line-height-glyph\": \"1\",\n \"--ui-line-height-tight\": \"1.25\",\n \"--ui-line-height-normal\": \"1.5\",\n\n // Letter spacing / tracking — default and the single widened step.\n \"--ui-tracking-normal\": \"0\",\n \"--ui-tracking-wide\": \"0.04em\",\n};\n\n/**\n * Dark-mode overrides for the same `--ui-*` custom properties, applied by\n * `tokens.css` via `@media (prefers-color-scheme: dark)` and `[data-theme]`\n * (see generate-tokens-css.mjs). Same Tailwind slate scale as the light\n * palette, inverted, with primary/danger/success lightened one step (500\n * instead of 600) for sufficient contrast against dark surfaces. Shape/type\n * tokens are theme-independent and not repeated here.\n */\nexport const darkTokenValues: Record<string, string> = {\n \"--ui-primary\": \"#6366f1\", // indigo-500\n \"--ui-primary-hover\": \"#818cf8\", // indigo-400\n \"--ui-info\": \"#38bdf8\", // sky-400\n \"--ui-info-hover\": \"#7dd3fc\", // sky-300\n \"--ui-danger\": \"#ef4444\", // red-500\n \"--ui-danger-hover\": \"#f87171\", // red-400\n \"--ui-success\": \"#22c55e\", // green-500\n \"--ui-warning\": \"#f59e0b\", // amber-500\n \"--ui-warning-hover\": \"#fbbf24\", // amber-400\n \"--ui-on-accent\": \"#ffffff\",\n \"--ui-text\": \"#f1f5f9\", // slate-100\n \"--ui-text-muted\": \"#94a3b8\", // slate-400\n \"--ui-border\": \"#334155\", // slate-700\n \"--ui-surface\": \"#0f172a\", // slate-900\n \"--ui-surface-muted\": \"#1e293b\", // slate-800\n \"--ui-tooltip\": \"#020617\", // slate-950\n \"--ui-hover-overlay\": \"rgb(255 255 255 / 0.12)\",\n \"--ui-overlay\": \"rgb(2 6 23 / 0.6)\", // slate-950 / 60%\n \"--ui-focus-ring\": \"0 0 0 3px rgb(99 102 241 / 0.45)\",\n};\n\n/**\n * Historically a shared `:host { --ui-x: var(--ui-x, fallback); } ` block,\n * re-declaring every token on each component's own host element as a\n * \"materialize the inherited value, or this fallback\" trick. That pattern\n * turned out to be a genuine bug, not a working default: browsers treat\n * `--ui-x: var(--ui-x, ...)` on `:host` as a self-referencing declaration\n * that computes to the guaranteed-invalid value REGARDLESS of what an\n * ancestor (e.g. `:root`) set — the host's own `:host` rule wins the cascade\n * over the ancestor's, so this dropped whatever the app actually set and\n * silently substituted the component's own fallback everywhere. It went\n * unnoticed because every component's fallback happened to equal the\n * intended light-mode value — dark mode (a real, different value at\n * `:root`) exposed it immediately (colors rendering as if no theme were\n * applied at all). Fix: don't redeclare tokens on `:host` — plain\n * inheritance already carries `:root`'s values into every shadow root\n * correctly, and every component already has its own `var(--ui-x, fallback)`\n * at each point of use, so nothing here is needed for the \"zero external\n * CSS\" default to keep working. Kept as an empty export so the existing\n * `static override styles = [tokens, css\\`...\\`]` import in every component\n * keeps compiling.\n */\nexport const tokens = css``;\n"]}
|
package/dist/ui-button.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui-button.d.ts","sourceRoot":"","sources":["../src/ui-button.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAK5C,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE/D;;;;;;;;;;;;;;;;;GAiBG;AACH,qBACa,QAAS,SAAQ,UAAU;;IACtC,MAAM,CAAC,cAAc,UAAQ;IAG7B,OAAgB,MAAM,
|
|
1
|
+
{"version":3,"file":"ui-button.d.ts","sourceRoot":"","sources":["../src/ui-button.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAK5C,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE/D;;;;;;;;;;;;;;;;;GAiBG;AACH,qBACa,QAAS,SAAQ,UAAU;;IACtC,MAAM,CAAC,cAAc,UAAQ;IAG7B,OAAgB,MAAM,4BA8FpB;IAEF,qBAAqB;IACT,OAAO,EAAE,aAAa,CAAa;IAC/C,oEAAoE;IACxD,IAAI,EAAE,MAAM,GAAG,IAAI,CAAQ;IACvC,wDAAwD;IAC5C,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAY;IAC3D,wCAAwC;IACX,QAAQ,UAAS;IAC9C,0EAA0E;IAC7C,IAAI,UAAS;IAE1C,qHAAqH;IACrH,OAAO,CAAC,QAAQ;IAKhB,4EAA4E;IAC5E,OAAO,CAAC,YAAY;IAKX,MAAM,yCA+Bd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,WAAW,EAAE,QAAQ,CAAC;KACvB;CACF"}
|
package/dist/ui-button.js
CHANGED
|
@@ -53,10 +53,12 @@ let UiButton = class UiButton extends LitElement {
|
|
|
53
53
|
display: inline-flex;
|
|
54
54
|
align-items: center;
|
|
55
55
|
gap: 0.25rem;
|
|
56
|
+
height: 2rem;
|
|
56
57
|
border-radius: var(--ui-radius-sm, 0.25rem);
|
|
57
58
|
padding: 0.5rem 1rem;
|
|
58
59
|
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
59
|
-
font-weight: 500;
|
|
60
|
+
font-weight: var(--ui-font-weight-medium, 500);
|
|
61
|
+
line-height: var(--ui-line-height-tight, 1.25);
|
|
60
62
|
font-family: var(
|
|
61
63
|
--ui-font,
|
|
62
64
|
ui-sans-serif,
|
package/dist/ui-button.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui-button.js","sourceRoot":"","sources":["../src/ui-button.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrC;;;;;;;;;;;;;;;;;GAiBG;AAEI,IAAM,QAAQ,GAAd,MAAM,QAAS,SAAQ,UAAU;IAAjC;;QAGL,eAAU,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"ui-button.js","sourceRoot":"","sources":["../src/ui-button.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, css, html } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { iconArrowPath } from \"./icons.js\";\nimport { tokens } from \"./tokens.js\";\n\nexport type ButtonVariant = \"primary\" | \"secondary\" | \"danger\";\n\n/**\n * Button (or link styled as one) with an optional leading icon, in three\n * visual weights. Set `href` to render an `<a>` instead of a `<button>` —\n * same styling either way — for cross-page navigation that should look like\n * an action button; a disabled/busy link stays a real `<a>` with\n * `aria-disabled` + `pointer-events: none` rather than losing its href.\n * Put the icon in the `icon` slot and the label in the default slot.\n *\n * Form-associated (`type=\"submit\"`/`\"reset\"`): the actual `<button>` lives in\n * this element's shadow root, which native HTML form association does not\n * cross into from an ancestor light-DOM `<form>`. `type=\"submit\"`/`\"reset\"`\n * is instead wired through `ElementInternals.form` — the same mechanism\n * `address-autocomplete` uses to associate with an ancestor form.\n *\n * @element ui-button\n * @slot icon - Optional leading icon (e.g. an inline SVG).\n * @slot - Button label.\n */\n@customElement(\"ui-button\")\nexport class UiButton extends LitElement {\n static formAssociated = true;\n\n #internals = this.attachInternals();\n static override styles = [\n tokens,\n css`\n :host {\n display: inline-flex;\n }\n .btn {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n height: 2rem;\n border-radius: var(--ui-radius-sm, 0.25rem);\n padding: 0.5rem 1rem;\n font-size: var(--ui-font-size-sm, 0.75rem);\n font-weight: var(--ui-font-weight-medium, 500);\n line-height: var(--ui-line-height-tight, 1.25);\n font-family: var(\n --ui-font,\n ui-sans-serif,\n system-ui,\n sans-serif,\n \"Apple Color Emoji\",\n \"Segoe UI Emoji\",\n \"Segoe UI Symbol\",\n \"Noto Color Emoji\"\n );\n cursor: pointer;\n border: 1px solid transparent;\n text-decoration: none;\n box-sizing: border-box;\n }\n .btn.primary {\n background: var(--ui-primary, #4f46e5);\n color: var(--ui-on-accent, #ffffff);\n }\n .btn.primary:hover:not(:disabled) {\n background: var(--ui-primary-hover, #4338ca);\n }\n .btn.secondary {\n background: none;\n border-color: var(--ui-border, #e2e8f0);\n color: var(--ui-text, #0f172a);\n }\n .btn.secondary:hover:not(:disabled) {\n border-color: var(--ui-text-muted, #64748b);\n }\n .btn.danger {\n background: var(--ui-danger, #dc2626);\n color: var(--ui-on-accent, #ffffff);\n }\n .btn.danger:hover:not(:disabled) {\n background: var(--ui-danger-hover, #b91c1c);\n }\n .btn:disabled,\n .btn[aria-disabled=\"true\"] {\n opacity: 0.6;\n cursor: default;\n pointer-events: none;\n }\n .btn:focus-visible {\n outline: none;\n box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));\n }\n .spin {\n display: inline-flex;\n animation: spin 0.8s linear infinite;\n }\n .spin[hidden] {\n display: none;\n }\n @keyframes spin {\n to {\n transform: rotate(360deg);\n }\n }\n @media (prefers-reduced-motion: reduce) {\n .spin {\n animation: none;\n }\n }\n @media (forced-colors: active) {\n .btn:focus-visible {\n outline: 2px solid CanvasText;\n outline-offset: 2px;\n box-shadow: none;\n }\n .btn:disabled,\n .btn[aria-disabled=\"true\"] {\n color: GrayText;\n border-color: GrayText;\n opacity: 1;\n }\n }\n `,\n ];\n\n /** Visual weight. */\n @property() variant: ButtonVariant = \"primary\";\n /** Renders an `<a href=\"...\">` instead of a `<button>` when set. */\n @property() href: string | null = null;\n /** Native button `type`. Ignored when `href` is set. */\n @property() type: \"button\" | \"submit\" | \"reset\" = \"button\";\n /** Disables the control and dims it. */\n @property({ type: Boolean }) disabled = false;\n /** Shows a spinner in place of the icon slot and disables the control. */\n @property({ type: Boolean }) busy = false;\n\n /** Drives submit/reset on the ancestor form via ElementInternals, since a shadow-DOM button can't do it natively. */\n private _onClick() {\n if (this.type === \"submit\") this.#internals.form?.requestSubmit();\n else if (this.type === \"reset\") this.#internals.form?.reset();\n }\n\n /** Suppresses navigation while a link-styled button is disabled or busy. */\n private _onLinkClick(e: MouseEvent) {\n if (!this.disabled && !this.busy) return;\n e.preventDefault();\n }\n\n override render() {\n const classes = `btn ${this.variant}`;\n const isDisabled = this.disabled || this.busy;\n if (this.href) {\n return html`\n <a\n class=${classes}\n href=${this.href}\n aria-disabled=${isDisabled ? \"true\" : \"false\"}\n aria-busy=${this.busy ? \"true\" : \"false\"}\n @click=${this._onLinkClick}\n >\n <span class=\"spin\" aria-hidden=\"true\" ?hidden=${!this.busy}>${iconArrowPath(14)}</span>\n <slot name=\"icon\" ?hidden=${this.busy}></slot>\n <slot></slot>\n </a>\n `;\n }\n return html`\n <button\n class=${classes}\n type=\"button\"\n ?disabled=${isDisabled}\n aria-busy=${this.busy ? \"true\" : \"false\"}\n @click=${this._onClick}\n >\n <span class=\"spin\" aria-hidden=\"true\" ?hidden=${!this.busy}>${iconArrowPath(14)}</span>\n <slot name=\"icon\" ?hidden=${this.busy}></slot>\n <slot></slot>\n </button>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ui-button\": UiButton;\n }\n}\n"]}
|
package/dist/user-avatar.js
CHANGED
package/dist/user-avatar.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-avatar.js","sourceRoot":"","sources":["../src/user-avatar.ts"],"names":[],"mappings":";;;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;;;;;GAQG;AAEI,IAAM,UAAU,GAAhB,MAAM,UAAW,SAAQ,UAAU;IAAnC;;QA2CL,mFAAmF;QACvE,QAAG,GAAkB,IAAI,CAAC;QACtC,2GAA2G;QAC/F,SAAI,GAAkB,IAAI,CAAC;QACvC,qFAAqF;QACzE,SAAI,GAAoB,EAAE,CAAC;QAEtB,cAAS,GAAG,KAAK,CAAC;IAsCrC,CAAC;;aAvFiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAqCF;KACF,AAxCqB,CAwCpB;aAWsB,iBAAY,GAA2B;QAC7D,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;KACP,AALmC,CAKlC;IAEF,gFAAgF;IACxE,WAAW;QACjB,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;YAAE,OAAO,OAAO,CAAC;QAC3C,OAAO,aAAW,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1D,CAAC;IAEkB,UAAU,CAAC,OAA6B;QACzD,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;IACjD,CAAC;IAEQ,MAAM;QACb,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,CAAC;QACxB,MAAM,SAAS,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC;QAChD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;QACpD,OAAO,IAAI,CAAA;0CAC2B,GAAG,WAAW,GAAG,cAAc,IAAI,GAAG,IAAI;UAC1E,SAAS;YACT,CAAC,CAAC,IAAI,CAAA;oBACI,IAAI,CAAC,GAAI;;;uBAGN,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;eACrC;YACL,CAAC,CAAC,OAAO,IAAI,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;;KAEzD,CAAC;IACJ,CAAC;CACF,CAAA;AA5Ca;IAAX,QAAQ,EAAE;uCAA2B;AAE1B;IAAX,QAAQ,EAAE;wCAA4B;AAE3B;IAAX,QAAQ,EAAE;wCAA4B;AAEtB;IAAhB,KAAK,EAAE;6CAA2B;AAlDxB,UAAU;IADtB,aAAa,CAAC,aAAa,CAAC;GAChB,UAAU,CAwFtB","sourcesContent":["import { LitElement, css, html } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { iconUserCircle } from \"./icons.js\";\nimport { tokens } from \"./tokens.js\";\n\n/**\n * Circular avatar. Shows `src` when it loads successfully; falls back to the\n * first letter of `name` (uppercased) if `src` is unset or fails to load\n * (e.g. an expired OAuth profile-photo URL); falls back further to a generic\n * person icon if `name` is also unset. A broken image never leaves a blank\n * circle.\n *\n * @element user-avatar\n */\n@customElement(\"user-avatar\")\nexport class UserAvatar extends LitElement {\n static override styles = [\n tokens,\n css`\n :host {\n display: inline-flex;\n }\n .avatar {\n border-radius: 50%;\n /* Match map-circle's 30% white/black vertical depth without tinting images. */\n background: linear-gradient(\n to bottom,\n color-mix(in srgb, var(--ui-primary, #4f46e5) 70%, #ffffff) 0%,\n color-mix(in srgb, var(--ui-primary, #4f46e5) 70%, #000000) 100%\n );\n color: var(--ui-on-accent, #ffffff);\n text-shadow: 0 1px 2px rgb(0 0 0 / 0.35);\n display: flex;\n align-items: center;\n justify-content: center;\n font-family: var(\n --ui-font,\n ui-sans-serif,\n system-ui,\n sans-serif,\n \"Apple Color Emoji\",\n \"Segoe UI Emoji\",\n \"Segoe UI Symbol\",\n \"Noto Color Emoji\"\n );\n font-weight: 600;\n overflow: hidden;\n flex: 0 0 auto;\n }\n img {\n width: 100%;\n height: 100%;\n border-radius: 50%;\n object-fit: cover;\n }\n `,\n ];\n\n /** Image URL to show. Falls back to initials/icon if unset or it fails to load. */\n @property() src: string | null = null;\n /** Source string for the fallback initial (e.g. a display name or email) — first character, uppercased. */\n @property() name: string | null = null;\n /** Diameter in CSS pixels, or a preset name (`xs`=18, `sm`=24, `md`=32, `lg`=48). */\n @property() size: string | number = 32;\n\n @state() private _imgError = false;\n\n private static readonly SIZE_PRESETS: Record<string, number> = {\n xs: 18,\n sm: 24,\n md: 32,\n lg: 48,\n };\n\n /** Resolves the `size` property (number or preset name) to a pixel diameter. */\n private resolveSize(): number {\n const numeric = Number(this.size);\n if (!Number.isNaN(numeric)) return numeric;\n return UserAvatar.SIZE_PRESETS[String(this.size)] ?? 32;\n }\n\n protected override willUpdate(changed: Map<string, unknown>) {\n if (changed.has(\"src\")) this._imgError = false;\n }\n\n override render() {\n const size = this.resolveSize();\n const dim = `${size}px`;\n const showImage = !!this.src && !this._imgError;\n const initial = this.name?.trim()[0]?.toUpperCase();\n return html`\n <span class=\"avatar\" style=\"width:${dim};height:${dim};font-size:${size * 0.42}px\">\n ${showImage\n ? html`<img\n src=${this.src!}\n alt=\"\"\n referrerpolicy=\"no-referrer\"\n @error=${() => (this._imgError = true)}\n />`\n : initial || iconUserCircle(Math.round(size * 0.65))}\n </span>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"user-avatar\": UserAvatar;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"user-avatar.js","sourceRoot":"","sources":["../src/user-avatar.ts"],"names":[],"mappings":";;;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;;;;;GAQG;AAEI,IAAM,UAAU,GAAhB,MAAM,UAAW,SAAQ,UAAU;IAAnC;;QA2CL,mFAAmF;QACvE,QAAG,GAAkB,IAAI,CAAC;QACtC,2GAA2G;QAC/F,SAAI,GAAkB,IAAI,CAAC;QACvC,qFAAqF;QACzE,SAAI,GAAoB,EAAE,CAAC;QAEtB,cAAS,GAAG,KAAK,CAAC;IAsCrC,CAAC;;aAvFiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAqCF;KACF,AAxCqB,CAwCpB;aAWsB,iBAAY,GAA2B;QAC7D,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;KACP,AALmC,CAKlC;IAEF,gFAAgF;IACxE,WAAW;QACjB,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;YAAE,OAAO,OAAO,CAAC;QAC3C,OAAO,aAAW,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1D,CAAC;IAEkB,UAAU,CAAC,OAA6B;QACzD,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;IACjD,CAAC;IAEQ,MAAM;QACb,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,CAAC;QACxB,MAAM,SAAS,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC;QAChD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;QACpD,OAAO,IAAI,CAAA;0CAC2B,GAAG,WAAW,GAAG,cAAc,IAAI,GAAG,IAAI;UAC1E,SAAS;YACT,CAAC,CAAC,IAAI,CAAA;oBACI,IAAI,CAAC,GAAI;;;uBAGN,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;eACrC;YACL,CAAC,CAAC,OAAO,IAAI,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;;KAEzD,CAAC;IACJ,CAAC;CACF,CAAA;AA5Ca;IAAX,QAAQ,EAAE;uCAA2B;AAE1B;IAAX,QAAQ,EAAE;wCAA4B;AAE3B;IAAX,QAAQ,EAAE;wCAA4B;AAEtB;IAAhB,KAAK,EAAE;6CAA2B;AAlDxB,UAAU;IADtB,aAAa,CAAC,aAAa,CAAC;GAChB,UAAU,CAwFtB","sourcesContent":["import { LitElement, css, html } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { iconUserCircle } from \"./icons.js\";\nimport { tokens } from \"./tokens.js\";\n\n/**\n * Circular avatar. Shows `src` when it loads successfully; falls back to the\n * first letter of `name` (uppercased) if `src` is unset or fails to load\n * (e.g. an expired OAuth profile-photo URL); falls back further to a generic\n * person icon if `name` is also unset. A broken image never leaves a blank\n * circle.\n *\n * @element user-avatar\n */\n@customElement(\"user-avatar\")\nexport class UserAvatar extends LitElement {\n static override styles = [\n tokens,\n css`\n :host {\n display: inline-flex;\n }\n .avatar {\n border-radius: 50%;\n /* Match map-circle's 30% white/black vertical depth without tinting images. */\n background: linear-gradient(\n to bottom,\n color-mix(in srgb, var(--ui-primary, #4f46e5) 70%, #ffffff) 0%,\n color-mix(in srgb, var(--ui-primary, #4f46e5) 70%, #000000) 100%\n );\n color: var(--ui-on-accent, #ffffff);\n text-shadow: 0 1px 2px rgb(0 0 0 / 0.35);\n display: flex;\n align-items: center;\n justify-content: center;\n font-family: var(\n --ui-font,\n ui-sans-serif,\n system-ui,\n sans-serif,\n \"Apple Color Emoji\",\n \"Segoe UI Emoji\",\n \"Segoe UI Symbol\",\n \"Noto Color Emoji\"\n );\n font-weight: var(--ui-font-weight-semibold, 600);\n overflow: hidden;\n flex: 0 0 auto;\n }\n img {\n width: 100%;\n height: 100%;\n border-radius: 50%;\n object-fit: cover;\n }\n `,\n ];\n\n /** Image URL to show. Falls back to initials/icon if unset or it fails to load. */\n @property() src: string | null = null;\n /** Source string for the fallback initial (e.g. a display name or email) — first character, uppercased. */\n @property() name: string | null = null;\n /** Diameter in CSS pixels, or a preset name (`xs`=18, `sm`=24, `md`=32, `lg`=48). */\n @property() size: string | number = 32;\n\n @state() private _imgError = false;\n\n private static readonly SIZE_PRESETS: Record<string, number> = {\n xs: 18,\n sm: 24,\n md: 32,\n lg: 48,\n };\n\n /** Resolves the `size` property (number or preset name) to a pixel diameter. */\n private resolveSize(): number {\n const numeric = Number(this.size);\n if (!Number.isNaN(numeric)) return numeric;\n return UserAvatar.SIZE_PRESETS[String(this.size)] ?? 32;\n }\n\n protected override willUpdate(changed: Map<string, unknown>) {\n if (changed.has(\"src\")) this._imgError = false;\n }\n\n override render() {\n const size = this.resolveSize();\n const dim = `${size}px`;\n const showImage = !!this.src && !this._imgError;\n const initial = this.name?.trim()[0]?.toUpperCase();\n return html`\n <span class=\"avatar\" style=\"width:${dim};height:${dim};font-size:${size * 0.42}px\">\n ${showImage\n ? html`<img\n src=${this.src!}\n alt=\"\"\n referrerpolicy=\"no-referrer\"\n @error=${() => (this._imgError = true)}\n />`\n : initial || iconUserCircle(Math.round(size * 0.65))}\n </span>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"user-avatar\": UserAvatar;\n }\n}\n"]}
|