@f-ewald/components 1.0.0 → 1.0.2
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 +6 -0
- package/custom-elements.json +891 -22
- package/dist/address-autocomplete.d.ts +8 -0
- package/dist/address-autocomplete.d.ts.map +1 -1
- package/dist/address-autocomplete.js +165 -15
- package/dist/address-autocomplete.js.map +1 -1
- package/dist/animate-confetti.d.ts +8 -5
- package/dist/animate-confetti.d.ts.map +1 -1
- package/dist/animate-confetti.js +46 -19
- package/dist/animate-confetti.js.map +1 -1
- package/dist/autocomplete-input.d.ts +8 -0
- package/dist/autocomplete-input.d.ts.map +1 -1
- package/dist/autocomplete-input.js +165 -15
- package/dist/autocomplete-input.js.map +1 -1
- package/dist/calendar-month.d.ts.map +1 -1
- package/dist/calendar-month.js +27 -13
- package/dist/calendar-month.js.map +1 -1
- package/dist/calendar-year.d.ts.map +1 -1
- package/dist/calendar-year.js +11 -2
- package/dist/calendar-year.js.map +1 -1
- package/dist/chat-message.d.ts.map +1 -1
- package/dist/chat-message.js +77 -24
- package/dist/chat-message.js.map +1 -1
- package/dist/confirm-dialog.d.ts +8 -1
- package/dist/confirm-dialog.d.ts.map +1 -1
- package/dist/confirm-dialog.js +130 -18
- package/dist/confirm-dialog.js.map +1 -1
- package/dist/copy-link-button.d.ts +2 -0
- package/dist/copy-link-button.d.ts.map +1 -1
- package/dist/copy-link-button.js +35 -3
- package/dist/copy-link-button.js.map +1 -1
- package/dist/data-table.d.ts +2 -0
- package/dist/data-table.d.ts.map +1 -1
- package/dist/data-table.js +83 -11
- package/dist/data-table.js.map +1 -1
- package/dist/distance-value.d.ts +0 -1
- package/dist/distance-value.d.ts.map +1 -1
- package/dist/distance-value.js +3 -8
- package/dist/distance-value.js.map +1 -1
- package/dist/distribution-chart.d.ts.map +1 -1
- package/dist/distribution-chart.js +54 -17
- package/dist/distribution-chart.js.map +1 -1
- package/dist/dropdown-button.d.ts.map +1 -1
- package/dist/dropdown-button.js +60 -11
- package/dist/dropdown-button.js.map +1 -1
- package/dist/editable-text.d.ts.map +1 -1
- package/dist/editable-text.js +46 -13
- package/dist/editable-text.js.map +1 -1
- package/dist/form-select.d.ts.map +1 -1
- package/dist/form-select.js +70 -22
- package/dist/form-select.js.map +1 -1
- package/dist/frame-box.d.ts.map +1 -1
- package/dist/frame-box.js +20 -12
- package/dist/frame-box.js.map +1 -1
- package/dist/icon-button.d.ts.map +1 -1
- package/dist/icon-button.js +11 -0
- package/dist/icon-button.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/kbd-hint.d.ts +30 -0
- package/dist/kbd-hint.d.ts.map +1 -0
- package/dist/kbd-hint.js +184 -0
- package/dist/kbd-hint.js.map +1 -0
- package/dist/live-timer.d.ts +0 -1
- package/dist/live-timer.d.ts.map +1 -1
- package/dist/live-timer.js +1 -6
- package/dist/live-timer.js.map +1 -1
- package/dist/map-circle.d.ts.map +1 -1
- package/dist/map-circle.js +16 -1
- package/dist/map-circle.js.map +1 -1
- package/dist/map-pin.d.ts.map +1 -1
- package/dist/map-pin.js +16 -1
- package/dist/map-pin.js.map +1 -1
- package/dist/percent-bar-chart.d.ts +3 -0
- package/dist/percent-bar-chart.d.ts.map +1 -1
- package/dist/percent-bar-chart.js +60 -4
- package/dist/percent-bar-chart.js.map +1 -1
- package/dist/photo-gallery.d.ts.map +1 -1
- package/dist/photo-gallery.js +12 -3
- package/dist/photo-gallery.js.map +1 -1
- package/dist/popover-panel.d.ts.map +1 -1
- package/dist/popover-panel.js +132 -23
- package/dist/popover-panel.js.map +1 -1
- package/dist/price-history-chart.d.ts.map +1 -1
- package/dist/price-history-chart.js +52 -18
- package/dist/price-history-chart.js.map +1 -1
- package/dist/radio-cards.d.ts +2 -0
- package/dist/radio-cards.d.ts.map +1 -1
- package/dist/radio-cards.js +51 -5
- package/dist/radio-cards.js.map +1 -1
- package/dist/radio-pills.d.ts +2 -0
- package/dist/radio-pills.d.ts.map +1 -1
- package/dist/radio-pills.js +46 -4
- package/dist/radio-pills.js.map +1 -1
- package/dist/relative-time.d.ts +0 -1
- package/dist/relative-time.d.ts.map +1 -1
- package/dist/relative-time.js +1 -6
- package/dist/relative-time.js.map +1 -1
- package/dist/reveal-button.d.ts +2 -0
- package/dist/reveal-button.d.ts.map +1 -1
- package/dist/reveal-button.js +42 -5
- package/dist/reveal-button.js.map +1 -1
- package/dist/roman-numeral.js +1 -1
- package/dist/roman-numeral.js.map +1 -1
- package/dist/slide-panel.d.ts +7 -3
- package/dist/slide-panel.d.ts.map +1 -1
- package/dist/slide-panel.js +95 -23
- package/dist/slide-panel.js.map +1 -1
- package/dist/stat-meter.d.ts.map +1 -1
- package/dist/stat-meter.js +17 -7
- package/dist/stat-meter.js.map +1 -1
- package/dist/status-pill.d.ts.map +1 -1
- package/dist/status-pill.js +26 -12
- package/dist/status-pill.js.map +1 -1
- package/dist/tile-grid.d.ts +8 -0
- package/dist/tile-grid.d.ts.map +1 -1
- package/dist/tile-grid.js +88 -12
- package/dist/tile-grid.js.map +1 -1
- package/dist/toast-notification.d.ts.map +1 -1
- package/dist/toast-notification.js +40 -11
- package/dist/toast-notification.js.map +1 -1
- package/dist/tokens.css +19 -0
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +13 -0
- package/dist/tokens.js.map +1 -1
- package/dist/ui-button.d.ts +2 -0
- package/dist/ui-button.d.ts.map +1 -1
- package/dist/ui-button.js +58 -9
- package/dist/ui-button.js.map +1 -1
- package/dist/user-avatar.d.ts.map +1 -1
- package/dist/user-avatar.js +18 -3
- package/dist/user-avatar.js.map +1 -1
- package/dist/utils/form.d.ts +3 -0
- package/dist/utils/form.d.ts.map +1 -0
- package/dist/utils/form.js +14 -0
- package/dist/utils/form.js.map +1 -0
- package/dist/utils/layer-stack.d.ts +9 -0
- package/dist/utils/layer-stack.d.ts.map +1 -0
- package/dist/utils/layer-stack.js +35 -0
- package/dist/utils/layer-stack.js.map +1 -0
- package/dist/weight-bar-chart.d.ts.map +1 -1
- package/dist/weight-bar-chart.js +27 -8
- package/dist/weight-bar-chart.js.map +1 -1
- package/docs/address-autocomplete.md +5 -0
- package/docs/autocomplete-input.md +5 -0
- package/docs/calendar-month.md +2 -0
- package/docs/chat-message.md +4 -0
- package/docs/confirm-dialog.md +2 -0
- package/docs/copy-link-button.md +2 -0
- package/docs/data-table.md +2 -0
- package/docs/design-language.md +149 -0
- package/docs/distribution-chart.md +4 -0
- package/docs/dropdown-button.md +1 -0
- package/docs/frame-box.md +2 -0
- package/docs/kbd-hint.md +42 -0
- package/docs/percent-bar-chart.md +4 -1
- package/docs/photo-gallery.md +1 -0
- package/docs/popover-panel.md +2 -0
- package/docs/price-history-chart.md +8 -1
- package/docs/radio-cards.md +2 -0
- package/docs/radio-pills.md +2 -0
- package/docs/reveal-button.md +3 -0
- package/docs/slide-panel.md +4 -2
- package/docs/status-pill.md +3 -0
- package/docs/tile-grid.md +10 -1
- package/docs/toast-notification.md +4 -1
- package/docs/ui-button.md +2 -0
- package/docs/user-avatar.md +1 -0
- package/llms.txt +59 -34
- package/package.json +5 -1
package/dist/ui-button.js
CHANGED
|
@@ -52,12 +52,21 @@ let UiButton = class UiButton extends LitElement {
|
|
|
52
52
|
.btn {
|
|
53
53
|
display: inline-flex;
|
|
54
54
|
align-items: center;
|
|
55
|
-
gap: 0.
|
|
55
|
+
gap: 0.25rem;
|
|
56
56
|
border-radius: var(--ui-radius-sm, 0.25rem);
|
|
57
|
-
padding: 0.5rem
|
|
57
|
+
padding: 0.5rem 1rem;
|
|
58
58
|
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
59
59
|
font-weight: 500;
|
|
60
|
-
font-family: var(
|
|
60
|
+
font-family: var(
|
|
61
|
+
--ui-font,
|
|
62
|
+
ui-sans-serif,
|
|
63
|
+
system-ui,
|
|
64
|
+
sans-serif,
|
|
65
|
+
"Apple Color Emoji",
|
|
66
|
+
"Segoe UI Emoji",
|
|
67
|
+
"Segoe UI Symbol",
|
|
68
|
+
"Noto Color Emoji"
|
|
69
|
+
);
|
|
61
70
|
cursor: pointer;
|
|
62
71
|
border: 1px solid transparent;
|
|
63
72
|
text-decoration: none;
|
|
@@ -65,7 +74,7 @@ let UiButton = class UiButton extends LitElement {
|
|
|
65
74
|
}
|
|
66
75
|
.btn.primary {
|
|
67
76
|
background: var(--ui-primary, #4f46e5);
|
|
68
|
-
color: #
|
|
77
|
+
color: var(--ui-on-accent, #ffffff);
|
|
69
78
|
}
|
|
70
79
|
.btn.primary:hover:not(:disabled) {
|
|
71
80
|
background: var(--ui-primary-hover, #4338ca);
|
|
@@ -80,7 +89,7 @@ let UiButton = class UiButton extends LitElement {
|
|
|
80
89
|
}
|
|
81
90
|
.btn.danger {
|
|
82
91
|
background: var(--ui-danger, #dc2626);
|
|
83
|
-
color: #
|
|
92
|
+
color: var(--ui-on-accent, #ffffff);
|
|
84
93
|
}
|
|
85
94
|
.btn.danger:hover:not(:disabled) {
|
|
86
95
|
background: var(--ui-danger-hover, #b91c1c);
|
|
@@ -91,6 +100,10 @@ let UiButton = class UiButton extends LitElement {
|
|
|
91
100
|
cursor: default;
|
|
92
101
|
pointer-events: none;
|
|
93
102
|
}
|
|
103
|
+
.btn:focus-visible {
|
|
104
|
+
outline: none;
|
|
105
|
+
box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));
|
|
106
|
+
}
|
|
94
107
|
.spin {
|
|
95
108
|
display: inline-flex;
|
|
96
109
|
animation: spin 0.8s linear infinite;
|
|
@@ -103,6 +116,24 @@ let UiButton = class UiButton extends LitElement {
|
|
|
103
116
|
transform: rotate(360deg);
|
|
104
117
|
}
|
|
105
118
|
}
|
|
119
|
+
@media (prefers-reduced-motion: reduce) {
|
|
120
|
+
.spin {
|
|
121
|
+
animation: none;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
@media (forced-colors: active) {
|
|
125
|
+
.btn:focus-visible {
|
|
126
|
+
outline: 2px solid CanvasText;
|
|
127
|
+
outline-offset: 2px;
|
|
128
|
+
box-shadow: none;
|
|
129
|
+
}
|
|
130
|
+
.btn:disabled,
|
|
131
|
+
.btn[aria-disabled="true"] {
|
|
132
|
+
color: GrayText;
|
|
133
|
+
border-color: GrayText;
|
|
134
|
+
opacity: 1;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
106
137
|
`,
|
|
107
138
|
]; }
|
|
108
139
|
/** Drives submit/reset on the ancestor form via ElementInternals, since a shadow-DOM button can't do it natively. */
|
|
@@ -112,21 +143,39 @@ let UiButton = class UiButton extends LitElement {
|
|
|
112
143
|
else if (this.type === "reset")
|
|
113
144
|
this.#internals.form?.reset();
|
|
114
145
|
}
|
|
146
|
+
/** Suppresses navigation while a link-styled button is disabled or busy. */
|
|
147
|
+
_onLinkClick(e) {
|
|
148
|
+
if (!this.disabled && !this.busy)
|
|
149
|
+
return;
|
|
150
|
+
e.preventDefault();
|
|
151
|
+
}
|
|
115
152
|
render() {
|
|
116
153
|
const classes = `btn ${this.variant}`;
|
|
117
154
|
const isDisabled = this.disabled || this.busy;
|
|
118
155
|
if (this.href) {
|
|
119
156
|
return html `
|
|
120
|
-
<a
|
|
121
|
-
|
|
157
|
+
<a
|
|
158
|
+
class=${classes}
|
|
159
|
+
href=${this.href}
|
|
160
|
+
aria-disabled=${isDisabled ? "true" : "false"}
|
|
161
|
+
aria-busy=${this.busy ? "true" : "false"}
|
|
162
|
+
@click=${this._onLinkClick}
|
|
163
|
+
>
|
|
164
|
+
<span class="spin" aria-hidden="true" ?hidden=${!this.busy}>${iconArrowPath(14)}</span>
|
|
122
165
|
<slot name="icon" ?hidden=${this.busy}></slot>
|
|
123
166
|
<slot></slot>
|
|
124
167
|
</a>
|
|
125
168
|
`;
|
|
126
169
|
}
|
|
127
170
|
return html `
|
|
128
|
-
<button
|
|
129
|
-
|
|
171
|
+
<button
|
|
172
|
+
class=${classes}
|
|
173
|
+
type="button"
|
|
174
|
+
?disabled=${isDisabled}
|
|
175
|
+
aria-busy=${this.busy ? "true" : "false"}
|
|
176
|
+
@click=${this._onClick}
|
|
177
|
+
>
|
|
178
|
+
<span class="spin" aria-hidden="true" ?hidden=${!this.busy}>${iconArrowPath(14)}</span>
|
|
130
179
|
<slot name="icon" ?hidden=${this.busy}></slot>
|
|
131
180
|
<slot></slot>
|
|
132
181
|
</button>
|
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":";;;;;;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;QA+FpC,qBAAqB;QACT,YAAO,GAAkB,SAAS,CAAC;QAC/C,oEAAoE;QACxD,SAAI,GAAkB,IAAI,CAAC;QACvC,wDAAwD;QAC5C,SAAI,GAAkC,QAAQ,CAAC;QAC3D,wCAAwC;QACX,aAAQ,GAAG,KAAK,CAAC;QAC9C,0EAA0E;QAC7C,SAAI,GAAG,KAAK,CAAC;IA8C5C,CAAC;aAxJQ,mBAAc,GAAG,IAAI,AAAP,CAAQ;IAE7B,UAAU,CAA0B;aACpB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAyFF;KACF,AA5FqB,CA4FpB;IAaF,qHAAqH;IAC7G,QAAQ;QACd,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;YAAE,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,aAAa,EAAE,CAAC;aAC7D,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO;YAAE,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;IAChE,CAAC;IAED,4EAA4E;IACpE,YAAY,CAAC,CAAa;QAChC,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,IAAI;YAAE,OAAO;QACzC,CAAC,CAAC,cAAc,EAAE,CAAC;IACrB,CAAC;IAEQ,MAAM;QACb,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;QACtC,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC;QAC9C,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,OAAO,IAAI,CAAA;;kBAEC,OAAO;iBACR,IAAI,CAAC,IAAI;0BACA,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;sBACjC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;mBAC/B,IAAI,CAAC,YAAY;;0DAEsB,CAAC,IAAI,CAAC,IAAI,IAAI,aAAa,CAAC,EAAE,CAAC;sCACnD,IAAI,CAAC,IAAI;;;OAGxC,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAA;;gBAEC,OAAO;;oBAEH,UAAU;oBACV,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;iBAC/B,IAAI,CAAC,QAAQ;;wDAE0B,CAAC,IAAI,CAAC,IAAI,IAAI,aAAa,CAAC,EAAE,CAAC;oCACnD,IAAI,CAAC,IAAI;;;KAGxC,CAAC;IACJ,CAAC;CACF,CAAA;AAtDa;IAAX,QAAQ,EAAE;yCAAoC;AAEnC;IAAX,QAAQ,EAAE;sCAA4B;AAE3B;IAAX,QAAQ,EAAE;sCAAgD;AAE9B;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;0CAAkB;AAEjB;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;sCAAc;AA3G/B,QAAQ;IADpB,aAAa,CAAC,WAAW,CAAC;GACd,QAAQ,CAyJpB","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 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: 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 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"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-avatar.d.ts","sourceRoot":"","sources":["../src/user-avatar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAK5C;;;;;;;;GAQG;AACH,qBACa,UAAW,SAAQ,UAAU;IACxC,OAAgB,MAAM,
|
|
1
|
+
{"version":3,"file":"user-avatar.d.ts","sourceRoot":"","sources":["../src/user-avatar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAK5C;;;;;;;;GAQG;AACH,qBACa,UAAW,SAAQ,UAAU;IACxC,OAAgB,MAAM,4BAwCpB;IAEF,mFAAmF;IACvE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAQ;IACtC,2GAA2G;IAC/F,IAAI,EAAE,MAAM,GAAG,IAAI,CAAQ;IACvC,qFAAqF;IACzE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAM;IAE9B,OAAO,CAAC,SAAS,CAAS;IAEnC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAKlC;IAEF,gFAAgF;IAChF,OAAO,CAAC,WAAW;IAMnB,UAAmB,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,QAE1D;IAEQ,MAAM,yCAiBd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,aAAa,EAAE,UAAU,CAAC;KAC3B;CACF"}
|
package/dist/user-avatar.js
CHANGED
|
@@ -38,12 +38,27 @@ let UserAvatar = class UserAvatar extends LitElement {
|
|
|
38
38
|
}
|
|
39
39
|
.avatar {
|
|
40
40
|
border-radius: 50%;
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
/* Match map-circle's 30% white/black vertical depth without tinting images. */
|
|
42
|
+
background: linear-gradient(
|
|
43
|
+
to bottom,
|
|
44
|
+
color-mix(in srgb, var(--ui-primary, #4f46e5) 70%, #ffffff) 0%,
|
|
45
|
+
color-mix(in srgb, var(--ui-primary, #4f46e5) 70%, #000000) 100%
|
|
46
|
+
);
|
|
47
|
+
color: var(--ui-on-accent, #ffffff);
|
|
48
|
+
text-shadow: 0 1px 2px rgb(0 0 0 / 0.35);
|
|
43
49
|
display: flex;
|
|
44
50
|
align-items: center;
|
|
45
51
|
justify-content: center;
|
|
46
|
-
font-family: var(
|
|
52
|
+
font-family: var(
|
|
53
|
+
--ui-font,
|
|
54
|
+
ui-sans-serif,
|
|
55
|
+
system-ui,
|
|
56
|
+
sans-serif,
|
|
57
|
+
"Apple Color Emoji",
|
|
58
|
+
"Segoe UI Emoji",
|
|
59
|
+
"Segoe UI Symbol",
|
|
60
|
+
"Noto Color Emoji"
|
|
61
|
+
);
|
|
47
62
|
font-weight: 600;
|
|
48
63
|
overflow: hidden;
|
|
49
64
|
flex: 0 0 auto;
|
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;;
|
|
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"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../../src/utils/form.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAWnE"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Submits a form through its default enabled native submit control when present. */
|
|
2
|
+
export function submitWithDefaultButton(form) {
|
|
3
|
+
const submitter = Array.from(form.elements).find((element) => (element instanceof HTMLButtonElement ||
|
|
4
|
+
element instanceof HTMLInputElement) &&
|
|
5
|
+
(element.type === "submit" ||
|
|
6
|
+
(element instanceof HTMLInputElement && element.type === "image")));
|
|
7
|
+
if (submitter?.matches(":disabled"))
|
|
8
|
+
return;
|
|
9
|
+
if (submitter)
|
|
10
|
+
submitter.click();
|
|
11
|
+
else
|
|
12
|
+
form.requestSubmit();
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=form.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form.js","sourceRoot":"","sources":["../../src/utils/form.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,MAAM,UAAU,uBAAuB,CAAC,IAAqB;IAC3D,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAC9C,CAAC,OAAO,EAAmD,EAAE,CAC3D,CAAC,OAAO,YAAY,iBAAiB;QACnC,OAAO,YAAY,gBAAgB,CAAC;QACtC,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ;YACxB,CAAC,OAAO,YAAY,gBAAgB,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CACvE,CAAC;IACF,IAAI,SAAS,EAAE,OAAO,CAAC,WAAW,CAAC;QAAE,OAAO;IAC5C,IAAI,SAAS;QAAE,SAAS,CAAC,KAAK,EAAE,CAAC;;QAC5B,IAAI,CAAC,aAAa,EAAE,CAAC;AAC5B,CAAC","sourcesContent":["/** Submits a form through its default enabled native submit control when present. */\nexport function submitWithDefaultButton(form: HTMLFormElement): void {\n const submitter = Array.from(form.elements).find(\n (element): element is HTMLButtonElement | HTMLInputElement =>\n (element instanceof HTMLButtonElement ||\n element instanceof HTMLInputElement) &&\n (element.type === \"submit\" ||\n (element instanceof HTMLInputElement && element.type === \"image\")),\n );\n if (submitter?.matches(\":disabled\")) return;\n if (submitter) submitter.click();\n else form.requestSubmit();\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Registers an open overlay as the current topmost interactive layer. */
|
|
2
|
+
export declare function activateLayer(layer: HTMLElement): void;
|
|
3
|
+
/** Removes a closed or disconnected overlay from the layer stack. */
|
|
4
|
+
export declare function deactivateLayer(layer: HTMLElement): void;
|
|
5
|
+
/** Whether this overlay currently owns global Escape/focus behavior. */
|
|
6
|
+
export declare function isTopLayer(layer: HTMLElement): boolean;
|
|
7
|
+
/** Claims Escape for the topmost layer and marks it handled for other listeners. */
|
|
8
|
+
export declare function claimEscape(layer: HTMLElement, event: KeyboardEvent): boolean;
|
|
9
|
+
//# sourceMappingURL=layer-stack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer-stack.d.ts","sourceRoot":"","sources":["../../src/utils/layer-stack.ts"],"names":[],"mappings":"AAUA,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAItD;AAED,qEAAqE;AACrE,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAKxD;AAED,wEAAwE;AACxE,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAEtD;AAED,oFAAoF;AACpF,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAM7E"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const layers = [];
|
|
2
|
+
const baseZIndex = 100;
|
|
3
|
+
/** Keeps visual z-order synchronized with interaction-stack order. */
|
|
4
|
+
function syncLayerOrder() {
|
|
5
|
+
layers.forEach((layer, index) => {
|
|
6
|
+
layer.style.setProperty("--component-layer-z", String(baseZIndex + index));
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
/** Registers an open overlay as the current topmost interactive layer. */
|
|
10
|
+
export function activateLayer(layer) {
|
|
11
|
+
deactivateLayer(layer);
|
|
12
|
+
layers.push(layer);
|
|
13
|
+
syncLayerOrder();
|
|
14
|
+
}
|
|
15
|
+
/** Removes a closed or disconnected overlay from the layer stack. */
|
|
16
|
+
export function deactivateLayer(layer) {
|
|
17
|
+
const index = layers.indexOf(layer);
|
|
18
|
+
if (index >= 0)
|
|
19
|
+
layers.splice(index, 1);
|
|
20
|
+
layer.style.removeProperty("--component-layer-z");
|
|
21
|
+
syncLayerOrder();
|
|
22
|
+
}
|
|
23
|
+
/** Whether this overlay currently owns global Escape/focus behavior. */
|
|
24
|
+
export function isTopLayer(layer) {
|
|
25
|
+
return layers.at(-1) === layer;
|
|
26
|
+
}
|
|
27
|
+
/** Claims Escape for the topmost layer and marks it handled for other listeners. */
|
|
28
|
+
export function claimEscape(layer, event) {
|
|
29
|
+
if (event.key !== "Escape" || event.defaultPrevented || !isTopLayer(layer)) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
event.preventDefault();
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=layer-stack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer-stack.js","sourceRoot":"","sources":["../../src/utils/layer-stack.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,GAAkB,EAAE,CAAC;AACjC,MAAM,UAAU,GAAG,GAAG,CAAC;AAEvB,sEAAsE;AACtE,SAAS,cAAc;IACrB,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QAC9B,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,qBAAqB,EAAE,MAAM,CAAC,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;AACL,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,aAAa,CAAC,KAAkB;IAC9C,eAAe,CAAC,KAAK,CAAC,CAAC;IACvB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnB,cAAc,EAAE,CAAC;AACnB,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,eAAe,CAAC,KAAkB;IAChD,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACpC,IAAI,KAAK,IAAI,CAAC;QAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACxC,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,qBAAqB,CAAC,CAAC;IAClD,cAAc,EAAE,CAAC;AACnB,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,UAAU,CAAC,KAAkB;IAC3C,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC;AACjC,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,WAAW,CAAC,KAAkB,EAAE,KAAoB;IAClE,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,gBAAgB,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3E,OAAO,KAAK,CAAC;IACf,CAAC;IACD,KAAK,CAAC,cAAc,EAAE,CAAC;IACvB,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["const layers: HTMLElement[] = [];\nconst baseZIndex = 100;\n\n/** Keeps visual z-order synchronized with interaction-stack order. */\nfunction syncLayerOrder(): void {\n layers.forEach((layer, index) => {\n layer.style.setProperty(\"--component-layer-z\", String(baseZIndex + index));\n });\n}\n\n/** Registers an open overlay as the current topmost interactive layer. */\nexport function activateLayer(layer: HTMLElement): void {\n deactivateLayer(layer);\n layers.push(layer);\n syncLayerOrder();\n}\n\n/** Removes a closed or disconnected overlay from the layer stack. */\nexport function deactivateLayer(layer: HTMLElement): void {\n const index = layers.indexOf(layer);\n if (index >= 0) layers.splice(index, 1);\n layer.style.removeProperty(\"--component-layer-z\");\n syncLayerOrder();\n}\n\n/** Whether this overlay currently owns global Escape/focus behavior. */\nexport function isTopLayer(layer: HTMLElement): boolean {\n return layers.at(-1) === layer;\n}\n\n/** Claims Escape for the topmost layer and marks it handled for other listeners. */\nexport function claimEscape(layer: HTMLElement, event: KeyboardEvent): boolean {\n if (event.key !== \"Escape\" || event.defaultPrevented || !isTopLayer(layer)) {\n return false;\n }\n event.preventDefault();\n return true;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"weight-bar-chart.d.ts","sourceRoot":"","sources":["../src/weight-bar-chart.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAK5C,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;GASG;AACH,qBACa,cAAe,SAAQ,UAAU;IAC5C,oEAAoE;IACpC,KAAK,EAAE,aAAa,EAAE,CAAM;IAE5D,OAAgB,MAAM,
|
|
1
|
+
{"version":3,"file":"weight-bar-chart.d.ts","sourceRoot":"","sources":["../src/weight-bar-chart.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAK5C,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;GASG;AACH,qBACa,cAAe,SAAQ,UAAU;IAC5C,oEAAoE;IACpC,KAAK,EAAE,aAAa,EAAE,CAAM;IAE5D,OAAgB,MAAM,4BA0DpB;IAEO,MAAM,gDAuBd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,kBAAkB,EAAE,cAAc,CAAC;KACpC;CACF"}
|
package/dist/weight-bar-chart.js
CHANGED
|
@@ -33,35 +33,54 @@ let WeightBarChart = class WeightBarChart extends LitElement {
|
|
|
33
33
|
.row {
|
|
34
34
|
display: flex;
|
|
35
35
|
align-items: center;
|
|
36
|
-
gap:
|
|
37
|
-
margin:
|
|
36
|
+
gap: 0.5rem;
|
|
37
|
+
margin: 0.5rem 0;
|
|
38
38
|
}
|
|
39
39
|
.label {
|
|
40
|
-
flex: 0 0
|
|
40
|
+
flex: 0 0 5.5rem;
|
|
41
41
|
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
42
|
-
font-family: var(
|
|
42
|
+
font-family: var(
|
|
43
|
+
--ui-font,
|
|
44
|
+
ui-sans-serif,
|
|
45
|
+
system-ui,
|
|
46
|
+
sans-serif,
|
|
47
|
+
"Apple Color Emoji",
|
|
48
|
+
"Segoe UI Emoji",
|
|
49
|
+
"Segoe UI Symbol",
|
|
50
|
+
"Noto Color Emoji"
|
|
51
|
+
);
|
|
43
52
|
color: var(--ui-text, #0f172a);
|
|
44
53
|
}
|
|
45
54
|
.track {
|
|
46
55
|
flex: 1 1 auto;
|
|
47
|
-
height:
|
|
56
|
+
height: 0.75rem;
|
|
48
57
|
background: var(--ui-surface-muted, #f8fafc);
|
|
49
58
|
border-radius: var(--ui-radius-sm, 0.25rem);
|
|
50
59
|
overflow: hidden;
|
|
51
60
|
}
|
|
52
61
|
.bar {
|
|
53
62
|
height: 100%;
|
|
54
|
-
|
|
63
|
+
/* Match map-circle's 30% white/black vertical depth while preserving token overrides. */
|
|
64
|
+
background: linear-gradient(
|
|
65
|
+
to bottom,
|
|
66
|
+
color-mix(in srgb, var(--ui-primary, #4f46e5) 70%, #ffffff) 0%,
|
|
67
|
+
color-mix(in srgb, var(--ui-primary, #4f46e5) 70%, #000000) 100%
|
|
68
|
+
);
|
|
55
69
|
border-radius: 0 var(--ui-radius-sm, 0.25rem) var(--ui-radius-sm, 0.25rem) 0;
|
|
56
|
-
transition: width
|
|
70
|
+
transition: width 150ms ease-out;
|
|
57
71
|
}
|
|
58
72
|
.pct {
|
|
59
|
-
flex: 0 0
|
|
73
|
+
flex: 0 0 2.5rem;
|
|
60
74
|
text-align: right;
|
|
61
75
|
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
62
76
|
font-variant-numeric: tabular-nums;
|
|
63
77
|
color: var(--ui-text-muted, #64748b);
|
|
64
78
|
}
|
|
79
|
+
@media (prefers-reduced-motion: reduce) {
|
|
80
|
+
.bar {
|
|
81
|
+
transition: none;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
65
84
|
`,
|
|
66
85
|
]; }
|
|
67
86
|
render() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"weight-bar-chart.js","sourceRoot":"","sources":["../src/weight-bar-chart.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,0BAA0B,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AASrC;;;;;;;;;GASG;AAEI,IAAM,cAAc,GAApB,MAAM,cAAe,SAAQ,UAAU;IAAvC;;QACL,oEAAoE;QACpC,UAAK,GAAoB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"weight-bar-chart.js","sourceRoot":"","sources":["../src/weight-bar-chart.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,0BAA0B,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AASrC;;;;;;;;;GASG;AAEI,IAAM,cAAc,GAApB,MAAM,cAAe,SAAQ,UAAU;IAAvC;;QACL,oEAAoE;QACpC,UAAK,GAAoB,EAAE,CAAC;IAsF9D,CAAC;aApFiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAuDF;KACF,AA1DqB,CA0DpB;IAEO,MAAM;QACb,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACzC,+EAA+E;QAC/E,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QACjE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAChE,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClG,OAAO,IAAI,CAAA;6CAC8B,OAAO;UAC1C,MAAM,CACN,MAAM,EACN,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,EACjB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAA;;oCAEY,IAAI,CAAC,KAAK;;iDAEG,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG;;kCAEvC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC;;WAEnD,CACF;;KAEJ,CAAC;IACJ,CAAC;CACF,CAAA;AAtFiC;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;6CAA6B;AAFjD,cAAc;IAD1B,aAAa,CAAC,kBAAkB,CAAC;GACrB,cAAc,CAwF1B","sourcesContent":["import { LitElement, css, html } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { repeat } from \"lit/directives/repeat.js\";\nimport { tokens } from \"./tokens.js\";\n\nexport interface WeightBarItem {\n id: string;\n label: string;\n /** Normalized fraction (fractions across all items sum to ~1). */\n value: number;\n}\n\n/**\n * Sorted horizontal bar chart of labeled weights (normalized fractions\n * summing to ~1). Bars sort descending — the order IS the priority ranking.\n * Widths scale relative to the largest weight (which fills its track); the\n * percent labels carry the absolute values. Rows are keyed by item id\n * (repeat directive) so a re-render with new weights moves the existing\n * rows; bar widths animate via CSS, re-sorting is instant.\n *\n * @element weight-bar-chart\n */\n@customElement(\"weight-bar-chart\")\nexport class WeightBarChart extends LitElement {\n /** Items to render as weighted rows, sorted descending by value. */\n @property({ attribute: false }) items: WeightBarItem[] = [];\n\n static override styles = [\n tokens,\n css`\n :host {\n display: block;\n }\n .row {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin: 0.5rem 0;\n }\n .label {\n flex: 0 0 5.5rem;\n font-size: var(--ui-font-size-sm, 0.75rem);\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 }\n .track {\n flex: 1 1 auto;\n height: 0.75rem;\n background: var(--ui-surface-muted, #f8fafc);\n border-radius: var(--ui-radius-sm, 0.25rem);\n overflow: hidden;\n }\n .bar {\n height: 100%;\n /* Match map-circle's 30% white/black vertical depth while preserving token overrides. */\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 border-radius: 0 var(--ui-radius-sm, 0.25rem) var(--ui-radius-sm, 0.25rem) 0;\n transition: width 150ms ease-out;\n }\n .pct {\n flex: 0 0 2.5rem;\n text-align: right;\n font-size: var(--ui-font-size-sm, 0.75rem);\n font-variant-numeric: tabular-nums;\n color: var(--ui-text-muted, #64748b);\n }\n @media (prefers-reduced-motion: reduce) {\n .bar {\n transition: none;\n }\n }\n `,\n ];\n\n override render() {\n if (this.items.length === 0) return null;\n // Stable sort: ties keep the incoming item order, so the chart never flickers.\n const sorted = [...this.items].sort((a, b) => b.value - a.value);\n const max = Math.max(0.01, ...sorted.map((item) => item.value));\n const summary = sorted.map((item) => `${item.label} ${Math.round(item.value * 100)}%`).join(\", \");\n return html`\n <div role=\"img\" aria-label=\"Weights: ${summary}\">\n ${repeat(\n sorted,\n (item) => item.id,\n (item) => html`\n <div class=\"row\">\n <span class=\"label\">${item.label}</span>\n <div class=\"track\">\n <div class=\"bar\" style=\"width: ${(item.value / max) * 100}%\"></div>\n </div>\n <span class=\"pct\">${Math.round(item.value * 100)}%</span>\n </div>\n `,\n )}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"weight-bar-chart\": WeightBarChart;\n }\n}\n"]}
|
|
@@ -35,6 +35,7 @@ import "@f-ewald/components/address-autocomplete.js";
|
|
|
35
35
|
| `value` | `value` | `string` | `""` | Current input value; also the form-associated value submitted with the form. |
|
|
36
36
|
| `placeholder` | `placeholder` | `string` | `""` | Placeholder text shown when the input is empty. |
|
|
37
37
|
| `required` | `required` | `boolean` | `false` | Marks the input as required for native form validation. |
|
|
38
|
+
| `disabled` | `disabled` | `boolean` | `false` | Disables the input and closes its suggestion popup. |
|
|
38
39
|
| `types` | `types` | `string` | `"address"` | Mapbox `types` param, e.g. "address" or "address,poi". |
|
|
39
40
|
| `endpoint` | `endpoint` | `string` | `"https://api.mapbox.com/search/geocode/v6/forward"` | Geocoding endpoint URL. Defaults to the Mapbox Geocoding v6 forward URL. |
|
|
40
41
|
| `accessToken` | `access-token` | `string` | `""` | Mapbox access token. Required for requests to succeed. |
|
|
@@ -60,9 +61,13 @@ _None._
|
|
|
60
61
|
| Custom property |
|
|
61
62
|
| --- |
|
|
62
63
|
| `--ui-border` |
|
|
64
|
+
| `--ui-focus-ring` |
|
|
63
65
|
| `--ui-font` |
|
|
64
66
|
| `--ui-font-size-sm` |
|
|
67
|
+
| `--ui-primary` |
|
|
65
68
|
| `--ui-radius-sm` |
|
|
66
69
|
| `--ui-shadow` |
|
|
67
70
|
| `--ui-surface` |
|
|
68
71
|
| `--ui-surface-muted` |
|
|
72
|
+
| `--ui-text` |
|
|
73
|
+
| `--ui-text-muted` |
|
|
@@ -49,6 +49,7 @@ import "@f-ewald/components/autocomplete-input.js";
|
|
|
49
49
|
| `value` | `value` | `string` | `""` | Current input value; also the form-associated value submitted with the form. |
|
|
50
50
|
| `placeholder` | `placeholder` | `string` | `""` | Placeholder text shown when the input is empty. |
|
|
51
51
|
| `required` | `required` | `boolean` | `false` | Marks the input as required for native form validation. |
|
|
52
|
+
| `disabled` | `disabled` | `boolean` | `false` | Disables the input and closes its suggestion popup. |
|
|
52
53
|
| `endpoint` | `endpoint` | `string` | `""` | API endpoint queried in API mode. Ignored when `options` is set. |
|
|
53
54
|
| `queryParam` | `query-param` | `string` | `"q"` | Query string parameter name the current input text is sent under. |
|
|
54
55
|
| `debounce` | `debounce` | `number` | `300` | Debounce delay (ms) before firing a request/filter after typing. |
|
|
@@ -71,9 +72,13 @@ _None._
|
|
|
71
72
|
| Custom property |
|
|
72
73
|
| --- |
|
|
73
74
|
| `--ui-border` |
|
|
75
|
+
| `--ui-focus-ring` |
|
|
74
76
|
| `--ui-font` |
|
|
75
77
|
| `--ui-font-size-sm` |
|
|
78
|
+
| `--ui-primary` |
|
|
76
79
|
| `--ui-radius-sm` |
|
|
77
80
|
| `--ui-shadow` |
|
|
78
81
|
| `--ui-surface` |
|
|
79
82
|
| `--ui-surface-muted` |
|
|
83
|
+
| `--ui-text` |
|
|
84
|
+
| `--ui-text-muted` |
|
package/docs/calendar-month.md
CHANGED
|
@@ -68,6 +68,7 @@ _None._
|
|
|
68
68
|
| `--ui-font-size-sm` |
|
|
69
69
|
| `--ui-font-size-xs` |
|
|
70
70
|
| `--ui-hover-overlay` |
|
|
71
|
+
| `--ui-info` |
|
|
71
72
|
| `--ui-primary` |
|
|
72
73
|
| `--ui-radius-sm` |
|
|
73
74
|
| `--ui-success` |
|
|
@@ -75,3 +76,4 @@ _None._
|
|
|
75
76
|
| `--ui-surface-muted` |
|
|
76
77
|
| `--ui-text` |
|
|
77
78
|
| `--ui-text-muted` |
|
|
79
|
+
| `--ui-warning` |
|
package/docs/chat-message.md
CHANGED
|
@@ -50,10 +50,14 @@ _None._
|
|
|
50
50
|
| Custom property |
|
|
51
51
|
| --- |
|
|
52
52
|
| `--ui-border` |
|
|
53
|
+
| `--ui-focus-ring` |
|
|
53
54
|
| `--ui-font` |
|
|
55
|
+
| `--ui-font-mono` |
|
|
54
56
|
| `--ui-font-size` |
|
|
57
|
+
| `--ui-font-size-sm` |
|
|
55
58
|
| `--ui-primary` |
|
|
56
59
|
| `--ui-radius` |
|
|
60
|
+
| `--ui-radius-sm` |
|
|
57
61
|
| `--ui-surface` |
|
|
58
62
|
| `--ui-text` |
|
|
59
63
|
| `--ui-text-muted` |
|
package/docs/confirm-dialog.md
CHANGED
|
@@ -51,9 +51,11 @@ import "@f-ewald/components/confirm-dialog.js";
|
|
|
51
51
|
| `--ui-border` |
|
|
52
52
|
| `--ui-danger` |
|
|
53
53
|
| `--ui-danger-hover` |
|
|
54
|
+
| `--ui-focus-ring` |
|
|
54
55
|
| `--ui-font` |
|
|
55
56
|
| `--ui-font-size` |
|
|
56
57
|
| `--ui-font-size-sm` |
|
|
58
|
+
| `--ui-on-accent` |
|
|
57
59
|
| `--ui-overlay` |
|
|
58
60
|
| `--ui-primary` |
|
|
59
61
|
| `--ui-primary-hover` |
|
package/docs/copy-link-button.md
CHANGED
|
@@ -24,6 +24,7 @@ import "@f-ewald/components/copy-link-button.js";
|
|
|
24
24
|
| --- | --- | --- | --- | --- |
|
|
25
25
|
| `value` | `value` | `string` | `""` | Text to copy. Defaults to `window.location.href` at click time. |
|
|
26
26
|
| `label` | `label` | `string` | `"Copy link"` | Accessible label / tooltip text. |
|
|
27
|
+
| `disabled` | `disabled` | `boolean` | `false` | Disables clipboard copying. |
|
|
27
28
|
|
|
28
29
|
## Events
|
|
29
30
|
|
|
@@ -40,6 +41,7 @@ _None._
|
|
|
40
41
|
|
|
41
42
|
| Custom property |
|
|
42
43
|
| --- |
|
|
44
|
+
| `--ui-focus-ring` |
|
|
43
45
|
| `--ui-radius-sm` |
|
|
44
46
|
| `--ui-surface-muted` |
|
|
45
47
|
| `--ui-text` |
|
package/docs/data-table.md
CHANGED
|
@@ -42,6 +42,7 @@ import "@f-ewald/components/data-table.js";
|
|
|
42
42
|
| `rowKey` | _(JS property only)_ | `(row: unknown, index: number) => string | number` | `—` | Stable identity for `rows[i]`, used as the repeat-directive key. Defaults to the row's index. |
|
|
43
43
|
| `renderCell` | _(JS property only)_ | `(row: unknown, key: string) => unknown` | `—` | Produces a cell's rendered content for `row`/`column.key`. Default: `row[key]`. |
|
|
44
44
|
| `rowHref` | _(JS property only)_ | `((row: unknown) => string | null) | null` | `null` | When set, clicking a row (outside any nested link/button) navigates to this hash. |
|
|
45
|
+
| `rowLabel` | _(JS property only)_ | `((row: unknown) => string) | null` | `null` | Accessible label for a row's keyboard link; defaults to primitive cell values. |
|
|
45
46
|
|
|
46
47
|
## Events
|
|
47
48
|
|
|
@@ -56,6 +57,7 @@ _None._
|
|
|
56
57
|
| Custom property |
|
|
57
58
|
| --- |
|
|
58
59
|
| `--ui-border` |
|
|
60
|
+
| `--ui-focus-ring` |
|
|
59
61
|
| `--ui-font` |
|
|
60
62
|
| `--ui-font-size-sm` |
|
|
61
63
|
| `--ui-surface-muted` |
|