@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/confirm-dialog.js
CHANGED
|
@@ -8,6 +8,7 @@ import { LitElement, css, html, nothing } from "lit";
|
|
|
8
8
|
import { customElement, property } from "lit/decorators.js";
|
|
9
9
|
import { iconArrowPath } from "./icons.js";
|
|
10
10
|
import { tokens } from "./tokens.js";
|
|
11
|
+
import { activateLayer, claimEscape, deactivateLayer, isTopLayer, } from "./utils/layer-stack.js";
|
|
11
12
|
/**
|
|
12
13
|
* Reusable confirmation dialog: overlay + centered card with a slotted body,
|
|
13
14
|
* an optional error line, and Cancel/Confirm actions. Instant `display:none`
|
|
@@ -34,6 +35,38 @@ let ConfirmDialog = class ConfirmDialog extends LitElement {
|
|
|
34
35
|
this.busy = false;
|
|
35
36
|
/** Inline error line shown below the body, or null for none. */
|
|
36
37
|
this.error = null;
|
|
38
|
+
this._previousFocus = null;
|
|
39
|
+
this._onDocumentFocusIn = (event) => {
|
|
40
|
+
if (!this.open || !isTopLayer(this) || event.composedPath().includes(this))
|
|
41
|
+
return;
|
|
42
|
+
this._focusInitial();
|
|
43
|
+
};
|
|
44
|
+
this._onWindowKeydown = (event) => {
|
|
45
|
+
if (!this.open || !isTopLayer(this))
|
|
46
|
+
return;
|
|
47
|
+
if (claimEscape(this, event) && !this.busy) {
|
|
48
|
+
this._cancel();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (event.key !== "Tab")
|
|
52
|
+
return;
|
|
53
|
+
const focusable = this._getFocusableElements();
|
|
54
|
+
if (focusable.length === 0) {
|
|
55
|
+
event.preventDefault();
|
|
56
|
+
this._focusInitial();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const active = (this.shadowRoot?.activeElement ?? document.activeElement);
|
|
60
|
+
const index = focusable.indexOf(active);
|
|
61
|
+
if (event.shiftKey && index <= 0) {
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
focusable.at(-1)?.focus();
|
|
64
|
+
}
|
|
65
|
+
else if (!event.shiftKey && index === focusable.length - 1) {
|
|
66
|
+
event.preventDefault();
|
|
67
|
+
focusable[0]?.focus();
|
|
68
|
+
}
|
|
69
|
+
};
|
|
37
70
|
}
|
|
38
71
|
static { this.styles = [
|
|
39
72
|
tokens,
|
|
@@ -42,18 +75,18 @@ let ConfirmDialog = class ConfirmDialog extends LitElement {
|
|
|
42
75
|
position: fixed;
|
|
43
76
|
inset: 0;
|
|
44
77
|
background: var(--ui-overlay, rgb(15 23 42 / 0.45));
|
|
45
|
-
z-index: 50;
|
|
78
|
+
z-index: var(--component-layer-z, 50);
|
|
46
79
|
display: none;
|
|
47
80
|
align-items: flex-start;
|
|
48
81
|
justify-content: center;
|
|
49
|
-
padding: 10vh
|
|
82
|
+
padding: 10vh 1.25rem 1.25rem;
|
|
50
83
|
box-sizing: border-box;
|
|
51
84
|
}
|
|
52
85
|
.overlay.open {
|
|
53
86
|
display: flex;
|
|
54
87
|
}
|
|
55
88
|
.dialog {
|
|
56
|
-
background: var(--ui-surface, #
|
|
89
|
+
background: var(--ui-surface, #ffffff);
|
|
57
90
|
border-radius: var(--ui-radius, 0.5rem);
|
|
58
91
|
box-shadow: var(
|
|
59
92
|
--ui-shadow-lg,
|
|
@@ -61,28 +94,38 @@ let ConfirmDialog = class ConfirmDialog extends LitElement {
|
|
|
61
94
|
0 8px 10px -6px rgb(0 0 0 / 0.1)
|
|
62
95
|
);
|
|
63
96
|
width: 100%;
|
|
64
|
-
max-width:
|
|
97
|
+
max-width: 26.25rem;
|
|
65
98
|
padding: 1.25rem;
|
|
66
99
|
box-sizing: border-box;
|
|
100
|
+
outline: none;
|
|
67
101
|
}
|
|
68
102
|
.dialog-text {
|
|
69
103
|
margin: 0 0 1rem;
|
|
70
104
|
color: var(--ui-text, #0f172a);
|
|
71
|
-
font-family: var(
|
|
105
|
+
font-family: var(
|
|
106
|
+
--ui-font,
|
|
107
|
+
ui-sans-serif,
|
|
108
|
+
system-ui,
|
|
109
|
+
sans-serif,
|
|
110
|
+
"Apple Color Emoji",
|
|
111
|
+
"Segoe UI Emoji",
|
|
112
|
+
"Segoe UI Symbol",
|
|
113
|
+
"Noto Color Emoji"
|
|
114
|
+
);
|
|
72
115
|
font-size: var(--ui-font-size, 0.875rem);
|
|
73
116
|
line-height: 1.45;
|
|
74
117
|
}
|
|
75
118
|
.dialog-actions {
|
|
76
119
|
display: flex;
|
|
77
120
|
justify-content: flex-end;
|
|
78
|
-
gap: 0.
|
|
121
|
+
gap: 0.5rem;
|
|
79
122
|
}
|
|
80
123
|
.btn-cancel {
|
|
81
124
|
background: none;
|
|
82
125
|
border: 1px solid var(--ui-border, #e2e8f0);
|
|
83
126
|
color: var(--ui-text, #0f172a);
|
|
84
127
|
border-radius: var(--ui-radius-sm, 0.25rem);
|
|
85
|
-
padding: 0.5rem
|
|
128
|
+
padding: 0.5rem 1rem;
|
|
86
129
|
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
87
130
|
cursor: pointer;
|
|
88
131
|
font-family: inherit;
|
|
@@ -94,25 +137,25 @@ let ConfirmDialog = class ConfirmDialog extends LitElement {
|
|
|
94
137
|
.btn-primary {
|
|
95
138
|
border: none;
|
|
96
139
|
border-radius: var(--ui-radius-sm, 0.25rem);
|
|
97
|
-
padding: 0.5rem
|
|
140
|
+
padding: 0.5rem 1rem;
|
|
98
141
|
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
99
142
|
font-weight: 500;
|
|
100
143
|
cursor: pointer;
|
|
101
144
|
display: inline-flex;
|
|
102
145
|
align-items: center;
|
|
103
|
-
gap: 0.
|
|
146
|
+
gap: 0.25rem;
|
|
104
147
|
font-family: inherit;
|
|
105
148
|
}
|
|
106
149
|
.btn-danger {
|
|
107
150
|
background: var(--ui-danger, #dc2626);
|
|
108
|
-
color: #
|
|
151
|
+
color: var(--ui-on-accent, #ffffff);
|
|
109
152
|
}
|
|
110
153
|
.btn-danger:hover:not(:disabled) {
|
|
111
154
|
background: var(--ui-danger-hover, #b91c1c);
|
|
112
155
|
}
|
|
113
156
|
.btn-primary {
|
|
114
157
|
background: var(--ui-primary, #4f46e5);
|
|
115
|
-
color: #
|
|
158
|
+
color: var(--ui-on-accent, #ffffff);
|
|
116
159
|
}
|
|
117
160
|
.btn-primary:hover:not(:disabled) {
|
|
118
161
|
background: var(--ui-primary-hover, #4338ca);
|
|
@@ -121,10 +164,14 @@ let ConfirmDialog = class ConfirmDialog extends LitElement {
|
|
|
121
164
|
opacity: 0.6;
|
|
122
165
|
cursor: default;
|
|
123
166
|
}
|
|
167
|
+
button:focus-visible {
|
|
168
|
+
outline: none;
|
|
169
|
+
box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));
|
|
170
|
+
}
|
|
124
171
|
.error-text {
|
|
125
172
|
color: var(--ui-danger, #dc2626);
|
|
126
173
|
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
127
|
-
margin: 0.
|
|
174
|
+
margin: 0.5rem 0 0.75rem;
|
|
128
175
|
}
|
|
129
176
|
.spin {
|
|
130
177
|
display: inline-flex;
|
|
@@ -135,9 +182,21 @@ let ConfirmDialog = class ConfirmDialog extends LitElement {
|
|
|
135
182
|
transform: rotate(360deg);
|
|
136
183
|
}
|
|
137
184
|
}
|
|
138
|
-
@media (
|
|
185
|
+
@media (prefers-reduced-motion: reduce) {
|
|
186
|
+
.spin {
|
|
187
|
+
animation: none;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
@media (forced-colors: active) {
|
|
191
|
+
button:focus-visible {
|
|
192
|
+
outline: 2px solid CanvasText;
|
|
193
|
+
outline-offset: 2px;
|
|
194
|
+
box-shadow: none;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
@media (max-width: 48rem) {
|
|
139
198
|
.overlay {
|
|
140
|
-
padding:
|
|
199
|
+
padding: 0.75rem;
|
|
141
200
|
overflow-y: auto;
|
|
142
201
|
}
|
|
143
202
|
}
|
|
@@ -149,16 +208,69 @@ let ConfirmDialog = class ConfirmDialog extends LitElement {
|
|
|
149
208
|
_cancel() {
|
|
150
209
|
this.dispatchEvent(new CustomEvent("cancel", { bubbles: true, composed: true }));
|
|
151
210
|
}
|
|
211
|
+
_getFocusableElements() {
|
|
212
|
+
const selector = "a[href], button:not(:disabled), input:not(:disabled), select:not(:disabled), textarea:not(:disabled), [tabindex]:not([tabindex='-1'])";
|
|
213
|
+
const slotted = Array.from(this.querySelectorAll(selector));
|
|
214
|
+
const internal = Array.from(this.shadowRoot?.querySelectorAll(selector) ?? []).filter((element) => element.closest(".dialog") !== null);
|
|
215
|
+
return [...slotted, ...internal].filter((element) => !element.hasAttribute("hidden"));
|
|
216
|
+
}
|
|
217
|
+
_focusInitial() {
|
|
218
|
+
const autofocus = this.querySelector("[autofocus]");
|
|
219
|
+
const cancel = this.shadowRoot?.querySelector(".btn-cancel:not(:disabled)");
|
|
220
|
+
const dialog = this.shadowRoot?.querySelector(".dialog");
|
|
221
|
+
(autofocus ?? cancel ?? this._getFocusableElements()[0] ?? dialog)?.focus();
|
|
222
|
+
}
|
|
223
|
+
updated(changed) {
|
|
224
|
+
if (!changed.has("open"))
|
|
225
|
+
return;
|
|
226
|
+
if (this.open) {
|
|
227
|
+
activateLayer(this);
|
|
228
|
+
this._previousFocus = document.activeElement;
|
|
229
|
+
document.addEventListener("focusin", this._onDocumentFocusIn, true);
|
|
230
|
+
window.addEventListener("keydown", this._onWindowKeydown, true);
|
|
231
|
+
this._focusInitial();
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
deactivateLayer(this);
|
|
235
|
+
document.removeEventListener("focusin", this._onDocumentFocusIn, true);
|
|
236
|
+
window.removeEventListener("keydown", this._onWindowKeydown, true);
|
|
237
|
+
if (this._previousFocus?.isConnected)
|
|
238
|
+
this._previousFocus.focus();
|
|
239
|
+
this._previousFocus = null;
|
|
240
|
+
}
|
|
241
|
+
disconnectedCallback() {
|
|
242
|
+
super.disconnectedCallback();
|
|
243
|
+
document.removeEventListener("focusin", this._onDocumentFocusIn, true);
|
|
244
|
+
window.removeEventListener("keydown", this._onWindowKeydown, true);
|
|
245
|
+
deactivateLayer(this);
|
|
246
|
+
if (this._previousFocus?.isConnected)
|
|
247
|
+
this._previousFocus.focus();
|
|
248
|
+
this._previousFocus = null;
|
|
249
|
+
this.open = false;
|
|
250
|
+
}
|
|
152
251
|
render() {
|
|
252
|
+
const accessibleName = this.getAttribute("aria-label") ?? "Confirmation";
|
|
153
253
|
return html `
|
|
154
254
|
<div class="overlay ${this.open ? "open" : ""}">
|
|
155
|
-
<div
|
|
156
|
-
|
|
157
|
-
|
|
255
|
+
<div
|
|
256
|
+
class="dialog"
|
|
257
|
+
role="dialog"
|
|
258
|
+
aria-modal="true"
|
|
259
|
+
aria-label=${accessibleName}
|
|
260
|
+
aria-describedby=${this.error ? "dialog-text dialog-error" : "dialog-text"}
|
|
261
|
+
tabindex="-1"
|
|
262
|
+
>
|
|
263
|
+
<p id="dialog-text" class="dialog-text"><slot></slot></p>
|
|
264
|
+
${this.error
|
|
265
|
+
? html `<p id="dialog-error" class="error-text" role="alert">${this.error}</p>`
|
|
266
|
+
: nothing}
|
|
158
267
|
<div class="dialog-actions">
|
|
159
268
|
<button class="btn-cancel" ?disabled=${this.busy} @click=${this._cancel}>${this.cancelLabel}</button>
|
|
160
269
|
<button class=${this.danger ? "btn-danger" : "btn-primary"} ?disabled=${this.busy} @click=${this._confirm}>
|
|
161
|
-
${this.busy
|
|
270
|
+
${this.busy
|
|
271
|
+
? html `<span class="spin" aria-hidden="true">${iconArrowPath(14)}</span>`
|
|
272
|
+
: nothing}
|
|
273
|
+
${this.confirmLabel}
|
|
162
274
|
</button>
|
|
163
275
|
</div>
|
|
164
276
|
</div>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"confirm-dialog.js","sourceRoot":"","sources":["../src/confirm-dialog.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;AAErC;;;;;;;;;;GAUG;AAEI,IAAM,aAAa,GAAnB,MAAM,aAAc,SAAQ,UAAU;IAAtC;;QA8GL,qCAAqC;QACR,SAAI,GAAG,KAAK,CAAC;QAC1C,oCAAoC;QACM,iBAAY,GAAG,QAAQ,CAAC;QAClE,mCAAmC;QACM,gBAAW,GAAG,QAAQ,CAAC;QAChE,wEAAwE;QAC3C,WAAM,GAAG,IAAI,CAAC;QAC3C,8EAA8E;QACjD,SAAI,GAAG,KAAK,CAAC;QAC1C,gEAAgE;QACpD,UAAK,GAAkB,IAAI,CAAC;IA0B1C,CAAC;aAlJiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAwGF;KACF,AA3GqB,CA2GpB;IAeM,QAAQ;QACd,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACpF,CAAC;IAEO,OAAO;QACb,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACnF,CAAC;IAEQ,MAAM;QACb,OAAO,IAAI,CAAA;4BACa,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;;;YAGvC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA,yBAAyB,IAAI,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO;;mDAE7B,IAAI,CAAC,IAAI,WAAW,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,WAAW;4BAC3E,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,aAAa,cAAc,IAAI,CAAC,IAAI,WAAW,IAAI,CAAC,QAAQ;gBACrG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA,sBAAsB,aAAa,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,IAAI,IAAI,CAAC,YAAY;;;;;KAK1G,CAAC;IACJ,CAAC;CACF,CAAA;AApC8B;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;2CAAc;AAEA;IAAzC,QAAQ,CAAC,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC;mDAAyB;AAEzB;IAAxC,QAAQ,CAAC,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC;kDAAwB;AAEnC;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;6CAAe;AAEd;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;2CAAc;AAE9B;IAAX,QAAQ,EAAE;4CAA6B;AAzH7B,aAAa;IADzB,aAAa,CAAC,gBAAgB,CAAC;GACnB,aAAa,CAmJzB","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\n/**\n * Reusable confirmation dialog: overlay + centered card with a slotted body,\n * an optional error line, and Cancel/Confirm actions. Instant `display:none`\n * → `display:flex` toggle (no transitions). Fires `confirm`/`cancel`\n * (bubbling, composed) instead of owning any deletion logic itself —\n * callers stay in charge of the request.\n *\n * @element confirm-dialog\n * @fires confirm - User clicked the confirm button.\n * @fires cancel - User clicked the cancel button.\n */\n@customElement(\"confirm-dialog\")\nexport class ConfirmDialog extends LitElement {\n static override styles = [\n tokens,\n css`\n .overlay {\n position: fixed;\n inset: 0;\n background: var(--ui-overlay, rgb(15 23 42 / 0.45));\n z-index: 50;\n display: none;\n align-items: flex-start;\n justify-content: center;\n padding: 10vh 20px 20px;\n box-sizing: border-box;\n }\n .overlay.open {\n display: flex;\n }\n .dialog {\n background: var(--ui-surface, #fff);\n border-radius: var(--ui-radius, 0.5rem);\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 width: 100%;\n max-width: 420px;\n padding: 1.25rem;\n box-sizing: border-box;\n }\n .dialog-text {\n margin: 0 0 1rem;\n color: var(--ui-text, #0f172a);\n font-family: var(--ui-font, ui-sans-serif, system-ui, sans-serif);\n font-size: var(--ui-font-size, 0.875rem);\n line-height: 1.45;\n }\n .dialog-actions {\n display: flex;\n justify-content: flex-end;\n gap: 0.6rem;\n }\n .btn-cancel {\n background: none;\n border: 1px solid var(--ui-border, #e2e8f0);\n color: var(--ui-text, #0f172a);\n border-radius: var(--ui-radius-sm, 0.25rem);\n padding: 0.5rem 0.9rem;\n font-size: var(--ui-font-size-sm, 0.75rem);\n cursor: pointer;\n font-family: inherit;\n }\n .btn-cancel:hover:not(:disabled) {\n border-color: var(--ui-text-muted, #64748b);\n }\n .btn-danger,\n .btn-primary {\n border: none;\n border-radius: var(--ui-radius-sm, 0.25rem);\n padding: 0.5rem 0.9rem;\n font-size: var(--ui-font-size-sm, 0.75rem);\n font-weight: 500;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 0.35rem;\n font-family: inherit;\n }\n .btn-danger {\n background: var(--ui-danger, #dc2626);\n color: #fff;\n }\n .btn-danger:hover:not(:disabled) {\n background: var(--ui-danger-hover, #b91c1c);\n }\n .btn-primary {\n background: var(--ui-primary, #4f46e5);\n color: #fff;\n }\n .btn-primary:hover:not(:disabled) {\n background: var(--ui-primary-hover, #4338ca);\n }\n button:disabled {\n opacity: 0.6;\n cursor: default;\n }\n .error-text {\n color: var(--ui-danger, #dc2626);\n font-size: var(--ui-font-size-sm, 0.75rem);\n margin: 0.4rem 0 0.8rem;\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 (max-width: 768px) {\n .overlay {\n padding: 12px;\n overflow-y: auto;\n }\n }\n `,\n ];\n\n /** Whether the dialog is visible. */\n @property({ type: Boolean }) open = false;\n /** Label for the confirm button. */\n @property({ attribute: \"confirm-label\" }) confirmLabel = \"Delete\";\n /** Label for the cancel button. */\n @property({ attribute: \"cancel-label\" }) cancelLabel = \"Cancel\";\n /** Danger (red) vs. primary (indigo) styling for the confirm button. */\n @property({ type: Boolean }) danger = true;\n /** Shows a spinner and disables both buttons while a request is in flight. */\n @property({ type: Boolean }) busy = false;\n /** Inline error line shown below the body, or null for none. */\n @property() error: string | null = null;\n\n private _confirm() {\n this.dispatchEvent(new CustomEvent(\"confirm\", { bubbles: true, composed: true }));\n }\n\n private _cancel() {\n this.dispatchEvent(new CustomEvent(\"cancel\", { bubbles: true, composed: true }));\n }\n\n override render() {\n return html`\n <div class=\"overlay ${this.open ? \"open\" : \"\"}\">\n <div class=\"dialog\">\n <p class=\"dialog-text\"><slot></slot></p>\n ${this.error ? html`<p class=\"error-text\">${this.error}</p>` : nothing}\n <div class=\"dialog-actions\">\n <button class=\"btn-cancel\" ?disabled=${this.busy} @click=${this._cancel}>${this.cancelLabel}</button>\n <button class=${this.danger ? \"btn-danger\" : \"btn-primary\"} ?disabled=${this.busy} @click=${this._confirm}>\n ${this.busy ? html`<span class=\"spin\">${iconArrowPath(14)}</span>` : nothing} ${this.confirmLabel}\n </button>\n </div>\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"confirm-dialog\": ConfirmDialog;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"confirm-dialog.js","sourceRoot":"","sources":["../src/confirm-dialog.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 { iconArrowPath } from \"./icons.js\";\nimport { tokens } from \"./tokens.js\";\nimport {\n activateLayer,\n claimEscape,\n deactivateLayer,\n isTopLayer,\n} from \"./utils/layer-stack.js\";\n\n/**\n * Reusable confirmation dialog: overlay + centered card with a slotted body,\n * an optional error line, and Cancel/Confirm actions. Instant `display:none`\n * → `display:flex` toggle (no transitions). Fires `confirm`/`cancel`\n * (bubbling, composed) instead of owning any deletion logic itself —\n * callers stay in charge of the request.\n *\n * @element confirm-dialog\n * @fires confirm - User clicked the confirm button.\n * @fires cancel - User clicked the cancel button.\n */\n@customElement(\"confirm-dialog\")\nexport class ConfirmDialog extends LitElement {\n static override styles = [\n tokens,\n css`\n .overlay {\n position: fixed;\n inset: 0;\n background: var(--ui-overlay, rgb(15 23 42 / 0.45));\n z-index: var(--component-layer-z, 50);\n display: none;\n align-items: flex-start;\n justify-content: center;\n padding: 10vh 1.25rem 1.25rem;\n box-sizing: border-box;\n }\n .overlay.open {\n display: flex;\n }\n .dialog {\n background: var(--ui-surface, #ffffff);\n border-radius: var(--ui-radius, 0.5rem);\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 width: 100%;\n max-width: 26.25rem;\n padding: 1.25rem;\n box-sizing: border-box;\n outline: none;\n }\n .dialog-text {\n margin: 0 0 1rem;\n color: var(--ui-text, #0f172a);\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: 1.45;\n }\n .dialog-actions {\n display: flex;\n justify-content: flex-end;\n gap: 0.5rem;\n }\n .btn-cancel {\n background: none;\n border: 1px solid var(--ui-border, #e2e8f0);\n color: var(--ui-text, #0f172a);\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 cursor: pointer;\n font-family: inherit;\n }\n .btn-cancel:hover:not(:disabled) {\n border-color: var(--ui-text-muted, #64748b);\n }\n .btn-danger,\n .btn-primary {\n border: none;\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 cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n font-family: inherit;\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-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 button:disabled {\n opacity: 0.6;\n cursor: default;\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 .error-text {\n color: var(--ui-danger, #dc2626);\n font-size: var(--ui-font-size-sm, 0.75rem);\n margin: 0.5rem 0 0.75rem;\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 @media (forced-colors: active) {\n button:focus-visible {\n outline: 2px solid CanvasText;\n outline-offset: 2px;\n box-shadow: none;\n }\n }\n @media (max-width: 48rem) {\n .overlay {\n padding: 0.75rem;\n overflow-y: auto;\n }\n }\n `,\n ];\n\n /** Whether the dialog is visible. */\n @property({ type: Boolean }) open = false;\n /** Label for the confirm button. */\n @property({ attribute: \"confirm-label\" }) confirmLabel = \"Delete\";\n /** Label for the cancel button. */\n @property({ attribute: \"cancel-label\" }) cancelLabel = \"Cancel\";\n /** Danger (red) vs. primary (indigo) styling for the confirm button. */\n @property({ type: Boolean }) danger = true;\n /** Shows a spinner and disables both buttons while a request is in flight. */\n @property({ type: Boolean }) busy = false;\n /** Inline error line shown below the body, or null for none. */\n @property() error: string | null = null;\n\n private _previousFocus: HTMLElement | null = null;\n\n private _confirm() {\n this.dispatchEvent(new CustomEvent(\"confirm\", { bubbles: true, composed: true }));\n }\n\n private _cancel() {\n this.dispatchEvent(new CustomEvent(\"cancel\", { bubbles: true, composed: true }));\n }\n\n private _getFocusableElements(): HTMLElement[] {\n const selector =\n \"a[href], button:not(:disabled), input:not(:disabled), select:not(:disabled), textarea:not(:disabled), [tabindex]:not([tabindex='-1'])\";\n const slotted = Array.from(this.querySelectorAll<HTMLElement>(selector));\n const internal = Array.from(\n this.shadowRoot?.querySelectorAll<HTMLElement>(selector) ?? [],\n ).filter((element) => element.closest(\".dialog\") !== null);\n return [...slotted, ...internal].filter((element) => !element.hasAttribute(\"hidden\"));\n }\n\n private _focusInitial(): void {\n const autofocus = this.querySelector<HTMLElement>(\"[autofocus]\");\n const cancel = this.shadowRoot?.querySelector<HTMLElement>(\".btn-cancel:not(:disabled)\");\n const dialog = this.shadowRoot?.querySelector<HTMLElement>(\".dialog\");\n (autofocus ?? cancel ?? this._getFocusableElements()[0] ?? dialog)?.focus();\n }\n\n private _onDocumentFocusIn = (event: FocusEvent): void => {\n if (!this.open || !isTopLayer(this) || event.composedPath().includes(this)) return;\n this._focusInitial();\n };\n\n private _onWindowKeydown = (event: KeyboardEvent): void => {\n if (!this.open || !isTopLayer(this)) return;\n if (claimEscape(this, event) && !this.busy) {\n this._cancel();\n return;\n }\n if (event.key !== \"Tab\") return;\n\n const focusable = this._getFocusableElements();\n if (focusable.length === 0) {\n event.preventDefault();\n this._focusInitial();\n return;\n }\n const active = (this.shadowRoot?.activeElement ?? document.activeElement) as HTMLElement | null;\n const index = focusable.indexOf(active as HTMLElement);\n if (event.shiftKey && index <= 0) {\n event.preventDefault();\n focusable.at(-1)?.focus();\n } else if (!event.shiftKey && index === focusable.length - 1) {\n event.preventDefault();\n focusable[0]?.focus();\n }\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 document.addEventListener(\"focusin\", this._onDocumentFocusIn, true);\n window.addEventListener(\"keydown\", this._onWindowKeydown, true);\n this._focusInitial();\n return;\n }\n deactivateLayer(this);\n document.removeEventListener(\"focusin\", this._onDocumentFocusIn, true);\n window.removeEventListener(\"keydown\", this._onWindowKeydown, true);\n if (this._previousFocus?.isConnected) this._previousFocus.focus();\n this._previousFocus = null;\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n document.removeEventListener(\"focusin\", this._onDocumentFocusIn, true);\n window.removeEventListener(\"keydown\", this._onWindowKeydown, true);\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 accessibleName = this.getAttribute(\"aria-label\") ?? \"Confirmation\";\n return html`\n <div class=\"overlay ${this.open ? \"open\" : \"\"}\">\n <div\n class=\"dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=${accessibleName}\n aria-describedby=${this.error ? \"dialog-text dialog-error\" : \"dialog-text\"}\n tabindex=\"-1\"\n >\n <p id=\"dialog-text\" class=\"dialog-text\"><slot></slot></p>\n ${this.error\n ? html`<p id=\"dialog-error\" class=\"error-text\" role=\"alert\">${this.error}</p>`\n : nothing}\n <div class=\"dialog-actions\">\n <button class=\"btn-cancel\" ?disabled=${this.busy} @click=${this._cancel}>${this.cancelLabel}</button>\n <button class=${this.danger ? \"btn-danger\" : \"btn-primary\"} ?disabled=${this.busy} @click=${this._confirm}>\n ${this.busy\n ? html`<span class=\"spin\" aria-hidden=\"true\">${iconArrowPath(14)}</span>`\n : nothing}\n ${this.confirmLabel}\n </button>\n </div>\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"confirm-dialog\": ConfirmDialog;\n }\n}\n"]}
|
|
@@ -15,6 +15,8 @@ export declare class CopyLinkButton extends LitElement {
|
|
|
15
15
|
value: string;
|
|
16
16
|
/** Accessible label / tooltip text. */
|
|
17
17
|
label: string;
|
|
18
|
+
/** Disables clipboard copying. */
|
|
19
|
+
disabled: boolean;
|
|
18
20
|
static styles: import("lit").CSSResult[];
|
|
19
21
|
private _onClick;
|
|
20
22
|
render(): import("lit-html").TemplateResult<1>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copy-link-button.d.ts","sourceRoot":"","sources":["../src/copy-link-button.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAM5C;;;;;;;;;;GAUG;AACH,qBACa,cAAe,SAAQ,UAAU;IAC5C,sEAAsE;IAC1D,KAAK,SAAM;IACvB,uCAAuC;IAC3B,KAAK,SAAe;
|
|
1
|
+
{"version":3,"file":"copy-link-button.d.ts","sourceRoot":"","sources":["../src/copy-link-button.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAM5C;;;;;;;;;;GAUG;AACH,qBACa,cAAe,SAAQ,UAAU;IAC5C,sEAAsE;IAC1D,KAAK,SAAM;IACvB,uCAAuC;IAC3B,KAAK,SAAe;IAChC,kCAAkC;IACL,QAAQ,UAAS;IAE9C,OAAgB,MAAM,4BAyCpB;YAEY,QAAQ;IAab,MAAM,yCAYd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,kBAAkB,EAAE,cAAc,CAAC;KACpC;CACF"}
|
package/dist/copy-link-button.js
CHANGED
|
@@ -27,6 +27,8 @@ let CopyLinkButton = class CopyLinkButton extends LitElement {
|
|
|
27
27
|
this.value = "";
|
|
28
28
|
/** Accessible label / tooltip text. */
|
|
29
29
|
this.label = "Copy link";
|
|
30
|
+
/** Disables clipboard copying. */
|
|
31
|
+
this.disabled = false;
|
|
30
32
|
}
|
|
31
33
|
static { this.styles = [
|
|
32
34
|
tokens,
|
|
@@ -38,20 +40,41 @@ let CopyLinkButton = class CopyLinkButton extends LitElement {
|
|
|
38
40
|
background: none;
|
|
39
41
|
border: none;
|
|
40
42
|
cursor: pointer;
|
|
41
|
-
padding:
|
|
43
|
+
padding: 0.25rem;
|
|
42
44
|
color: var(--ui-text-muted, #64748b);
|
|
43
45
|
border-radius: var(--ui-radius-sm, 0.25rem);
|
|
44
46
|
display: inline-flex;
|
|
45
47
|
align-items: center;
|
|
46
48
|
justify-content: center;
|
|
47
49
|
}
|
|
48
|
-
button:hover {
|
|
50
|
+
button:hover:not(:disabled) {
|
|
49
51
|
background: var(--ui-surface-muted, #f8fafc);
|
|
50
52
|
color: var(--ui-text, #0f172a);
|
|
51
53
|
}
|
|
54
|
+
button:disabled {
|
|
55
|
+
cursor: not-allowed;
|
|
56
|
+
opacity: 0.6;
|
|
57
|
+
}
|
|
58
|
+
button:focus-visible {
|
|
59
|
+
outline: none;
|
|
60
|
+
box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));
|
|
61
|
+
}
|
|
62
|
+
@media (forced-colors: active) {
|
|
63
|
+
button:focus-visible {
|
|
64
|
+
outline: 2px solid CanvasText;
|
|
65
|
+
outline-offset: 2px;
|
|
66
|
+
box-shadow: none;
|
|
67
|
+
}
|
|
68
|
+
button:disabled {
|
|
69
|
+
color: GrayText;
|
|
70
|
+
opacity: 1;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
52
73
|
`,
|
|
53
74
|
]; }
|
|
54
75
|
async _onClick() {
|
|
76
|
+
if (this.disabled)
|
|
77
|
+
return;
|
|
55
78
|
const text = this.value || window.location.href;
|
|
56
79
|
try {
|
|
57
80
|
await navigator.clipboard.writeText(text);
|
|
@@ -65,7 +88,13 @@ let CopyLinkButton = class CopyLinkButton extends LitElement {
|
|
|
65
88
|
}
|
|
66
89
|
render() {
|
|
67
90
|
return html `
|
|
68
|
-
<button
|
|
91
|
+
<button
|
|
92
|
+
type="button"
|
|
93
|
+
aria-label=${this.label}
|
|
94
|
+
title=${this.label}
|
|
95
|
+
?disabled=${this.disabled}
|
|
96
|
+
@click=${this._onClick}
|
|
97
|
+
>
|
|
69
98
|
${iconLink(16)}
|
|
70
99
|
</button>
|
|
71
100
|
`;
|
|
@@ -77,6 +106,9 @@ __decorate([
|
|
|
77
106
|
__decorate([
|
|
78
107
|
property()
|
|
79
108
|
], CopyLinkButton.prototype, "label", void 0);
|
|
109
|
+
__decorate([
|
|
110
|
+
property({ type: Boolean })
|
|
111
|
+
], CopyLinkButton.prototype, "disabled", void 0);
|
|
80
112
|
CopyLinkButton = __decorate([
|
|
81
113
|
customElement("copy-link-button")
|
|
82
114
|
], CopyLinkButton);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copy-link-button.js","sourceRoot":"","sources":["../src/copy-link-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,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;;;;;;;GAUG;AAEI,IAAM,cAAc,GAApB,MAAM,cAAe,SAAQ,UAAU;IAAvC;;QACL,sEAAsE;QAC1D,UAAK,GAAG,EAAE,CAAC;QACvB,uCAAuC;QAC3B,UAAK,GAAG,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"copy-link-button.js","sourceRoot":"","sources":["../src/copy-link-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,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;;;;;;;GAUG;AAEI,IAAM,cAAc,GAApB,MAAM,cAAe,SAAQ,UAAU;IAAvC;;QACL,sEAAsE;QAC1D,UAAK,GAAG,EAAE,CAAC;QACvB,uCAAuC;QAC3B,UAAK,GAAG,WAAW,CAAC;QAChC,kCAAkC;QACL,aAAQ,GAAG,KAAK,CAAC;IAuEhD,CAAC;aArEiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAsCF;KACF,AAzCqB,CAyCpB;IAEM,KAAK,CAAC,QAAQ;QACpB,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;QAChD,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAC1C,aAAa,CAAC,0BAA0B,CAAC,CAAC;YAC1C,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,cAAc,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACzF,CAAC;QAAC,MAAM,CAAC;YACP,WAAW,CAAC,wBAAwB,CAAC,CAAC;YACtC,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,YAAY,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACvF,CAAC;IACH,CAAC;IAEQ,MAAM;QACb,OAAO,IAAI,CAAA;;;qBAGM,IAAI,CAAC,KAAK;gBACf,IAAI,CAAC,KAAK;oBACN,IAAI,CAAC,QAAQ;iBAChB,IAAI,CAAC,QAAQ;;UAEpB,QAAQ,CAAC,EAAE,CAAC;;KAEjB,CAAC;IACJ,CAAC;CACF,CAAA;AA3Ea;IAAX,QAAQ,EAAE;6CAAY;AAEX;IAAX,QAAQ,EAAE;6CAAqB;AAEH;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gDAAkB;AANnC,cAAc;IAD1B,aAAa,CAAC,kBAAkB,CAAC;GACrB,cAAc,CA6E1B","sourcesContent":["import { LitElement, css, html } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { iconLink } from \"./icons.js\";\nimport { notifyError, notifySuccess } from \"./toast-notification.js\";\nimport { tokens } from \"./tokens.js\";\n\n/**\n * Small icon button that copies `value` to the clipboard and shows a toast\n * on success/failure (if a `<toast-notification>` element is present), and\n * always dispatches a `copy-success`/`copy-error` CustomEvent so consumers\n * without a toast element can react. Defaults to the current page URL if\n * `value` is unset.\n *\n * @element copy-link-button\n * @fires copy-success - The value was copied to the clipboard.\n * @fires copy-error - Copying to the clipboard failed.\n */\n@customElement(\"copy-link-button\")\nexport class CopyLinkButton extends LitElement {\n /** Text to copy. Defaults to `window.location.href` at click time. */\n @property() value = \"\";\n /** Accessible label / tooltip text. */\n @property() label = \"Copy link\";\n /** Disables clipboard copying. */\n @property({ type: Boolean }) disabled = false;\n\n static override styles = [\n tokens,\n css`\n :host {\n display: inline-flex;\n }\n button {\n background: none;\n border: none;\n cursor: pointer;\n padding: 0.25rem;\n color: var(--ui-text-muted, #64748b);\n border-radius: var(--ui-radius-sm, 0.25rem);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n button:hover:not(:disabled) {\n background: var(--ui-surface-muted, #f8fafc);\n color: var(--ui-text, #0f172a);\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 private async _onClick() {\n if (this.disabled) return;\n const text = this.value || window.location.href;\n try {\n await navigator.clipboard.writeText(text);\n notifySuccess(\"Link copied to clipboard\");\n this.dispatchEvent(new CustomEvent(\"copy-success\", { bubbles: true, composed: true }));\n } catch {\n notifyError(\"Couldn't copy the link\");\n this.dispatchEvent(new CustomEvent(\"copy-error\", { bubbles: true, composed: true }));\n }\n }\n\n override render() {\n return html`\n <button\n type=\"button\"\n aria-label=${this.label}\n title=${this.label}\n ?disabled=${this.disabled}\n @click=${this._onClick}\n >\n ${iconLink(16)}\n </button>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"copy-link-button\": CopyLinkButton;\n }\n}\n"]}
|
package/dist/data-table.d.ts
CHANGED
|
@@ -29,6 +29,8 @@ export declare class DataTable extends LitElement {
|
|
|
29
29
|
renderCell: (row: unknown, key: string) => unknown;
|
|
30
30
|
/** When set, clicking a row (outside any nested link/button) navigates to this hash. */
|
|
31
31
|
rowHref: ((row: unknown) => string | null) | null;
|
|
32
|
+
/** Accessible label for a row's keyboard link; defaults to primitive cell values. */
|
|
33
|
+
rowLabel: ((row: unknown) => string) | null;
|
|
32
34
|
render(): import("lit-html").TemplateResult<1>;
|
|
33
35
|
}
|
|
34
36
|
declare global {
|
package/dist/data-table.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-table.d.ts","sourceRoot":"","sources":["../src/data-table.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAKrD,yGAAyG;AACzG,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;GAWG;AACH,qBACa,SAAU,SAAQ,UAAU;;IACvC,OAAgB,MAAM,4BA+
|
|
1
|
+
{"version":3,"file":"data-table.d.ts","sourceRoot":"","sources":["../src/data-table.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAKrD,yGAAyG;AACzG,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;GAWG;AACH,qBACa,SAAU,SAAQ,UAAU;;IACvC,OAAgB,MAAM,4BA+DpB;IAEF,wCAAwC;IACR,OAAO,EAAE,eAAe,EAAE,CAAM;IAChE,gFAAgF;IAChD,IAAI,EAAE,OAAO,EAAE,CAAM;IACrD,oGAAoG;IACpE,MAAM,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CAG7E;IACX,sFAAsF;IACtD,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAGvC;IAC3C,wFAAwF;IACxD,OAAO,EAAE,CAAC,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,IAAI,CAAQ;IACzF,qFAAqF;IACrD,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC,GAAG,IAAI,CAAQ;IA4B1E,MAAM,yCA0Cd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,YAAY,EAAE,SAAS,CAAC;KACzB;CACF"}
|
package/dist/data-table.js
CHANGED
|
@@ -33,13 +33,24 @@ let DataTable = class DataTable extends LitElement {
|
|
|
33
33
|
this.renderCell = (row, key) => row[key];
|
|
34
34
|
/** When set, clicking a row (outside any nested link/button) navigates to this hash. */
|
|
35
35
|
this.rowHref = null;
|
|
36
|
+
/** Accessible label for a row's keyboard link; defaults to primitive cell values. */
|
|
37
|
+
this.rowLabel = null;
|
|
36
38
|
}
|
|
37
39
|
static { this.styles = [
|
|
38
40
|
tokens,
|
|
39
41
|
css `
|
|
40
42
|
:host {
|
|
41
43
|
display: block;
|
|
42
|
-
font-family: var(
|
|
44
|
+
font-family: var(
|
|
45
|
+
--ui-font,
|
|
46
|
+
ui-sans-serif,
|
|
47
|
+
system-ui,
|
|
48
|
+
sans-serif,
|
|
49
|
+
"Apple Color Emoji",
|
|
50
|
+
"Segoe UI Emoji",
|
|
51
|
+
"Segoe UI Symbol",
|
|
52
|
+
"Noto Color Emoji"
|
|
53
|
+
);
|
|
43
54
|
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
44
55
|
}
|
|
45
56
|
table {
|
|
@@ -54,27 +65,69 @@ let DataTable = class DataTable extends LitElement {
|
|
|
54
65
|
border-bottom: 1px solid var(--ui-border, #e2e8f0);
|
|
55
66
|
}
|
|
56
67
|
td {
|
|
57
|
-
padding: 0.
|
|
68
|
+
padding: 0.5rem 0.75rem;
|
|
58
69
|
border-bottom: 1px solid var(--ui-border, #e2e8f0);
|
|
59
70
|
color: var(--ui-text, #0f172a);
|
|
60
71
|
}
|
|
72
|
+
td:first-child {
|
|
73
|
+
position: relative;
|
|
74
|
+
}
|
|
75
|
+
.row-link {
|
|
76
|
+
position: absolute;
|
|
77
|
+
width: 1px;
|
|
78
|
+
height: 1px;
|
|
79
|
+
overflow: hidden;
|
|
80
|
+
clip: rect(0 0 0 0);
|
|
81
|
+
clip-path: inset(50%);
|
|
82
|
+
white-space: nowrap;
|
|
83
|
+
}
|
|
61
84
|
tbody tr.clickable {
|
|
62
85
|
cursor: pointer;
|
|
63
86
|
}
|
|
64
87
|
tbody tr.clickable:hover {
|
|
65
88
|
background: var(--ui-surface-muted, #f8fafc);
|
|
66
89
|
}
|
|
90
|
+
tbody tr.clickable:has(.row-link:focus-visible) {
|
|
91
|
+
outline: none;
|
|
92
|
+
box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35)) inset;
|
|
93
|
+
}
|
|
94
|
+
@media (forced-colors: active) {
|
|
95
|
+
tbody tr.clickable:has(.row-link:focus-visible) {
|
|
96
|
+
outline: 2px solid CanvasText;
|
|
97
|
+
outline-offset: -2px;
|
|
98
|
+
box-shadow: none;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
67
101
|
`,
|
|
68
102
|
]; }
|
|
69
103
|
#onRowClick(row, e) {
|
|
70
104
|
if (!this.rowHref)
|
|
71
105
|
return;
|
|
72
|
-
if (e.
|
|
106
|
+
if (this.#isNestedInteractive(e.composedPath(), e.currentTarget))
|
|
73
107
|
return;
|
|
74
108
|
const href = this.rowHref(row);
|
|
75
109
|
if (href)
|
|
76
110
|
location.hash = href;
|
|
77
111
|
}
|
|
112
|
+
#isNestedInteractive(path, row) {
|
|
113
|
+
const selector = "a, button, input, select, textarea, summary, [contenteditable], [role='button'], [role='link'], [tabindex]";
|
|
114
|
+
for (const target of path) {
|
|
115
|
+
if (target === row)
|
|
116
|
+
return false;
|
|
117
|
+
if (target instanceof HTMLElement && target.matches(selector))
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
#rowLinkLabel(row) {
|
|
123
|
+
const explicitLabel = this.rowLabel?.(row).trim();
|
|
124
|
+
if (explicitLabel)
|
|
125
|
+
return explicitLabel;
|
|
126
|
+
const values = this.columns
|
|
127
|
+
.map((column) => row[column.key])
|
|
128
|
+
.filter((value) => typeof value === "string" || typeof value === "number");
|
|
129
|
+
return values.length > 0 ? `Open ${values.join(", ")}` : "Open row";
|
|
130
|
+
}
|
|
78
131
|
render() {
|
|
79
132
|
return html `
|
|
80
133
|
<table>
|
|
@@ -84,14 +137,30 @@ let DataTable = class DataTable extends LitElement {
|
|
|
84
137
|
</tr>
|
|
85
138
|
</thead>
|
|
86
139
|
<tbody>
|
|
87
|
-
${repeat(this.rows, (row, i) => this.rowKey(row, i), (row) =>
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
140
|
+
${repeat(this.rows, (row, i) => this.rowKey(row, i), (row) => {
|
|
141
|
+
const href = this.rowHref?.(row) ?? null;
|
|
142
|
+
return html `
|
|
143
|
+
<tr
|
|
144
|
+
class=${href ? "clickable" : ""}
|
|
145
|
+
@click=${(e) => this.#onRowClick(row, e)}
|
|
146
|
+
>
|
|
147
|
+
${this.columns.map((c, columnIndex) => html `
|
|
148
|
+
<td>
|
|
149
|
+
${href && columnIndex === 0
|
|
150
|
+
? html `
|
|
151
|
+
<a
|
|
152
|
+
class="row-link"
|
|
153
|
+
href=${href}
|
|
154
|
+
aria-label=${this.#rowLinkLabel(row)}
|
|
155
|
+
></a>
|
|
156
|
+
`
|
|
157
|
+
: nothing}
|
|
158
|
+
${this.renderCell(row, c.key) ?? nothing}
|
|
159
|
+
</td>
|
|
160
|
+
`)}
|
|
161
|
+
</tr>
|
|
162
|
+
`;
|
|
163
|
+
})}
|
|
95
164
|
</tbody>
|
|
96
165
|
</table>
|
|
97
166
|
`;
|
|
@@ -112,6 +181,9 @@ __decorate([
|
|
|
112
181
|
__decorate([
|
|
113
182
|
property({ attribute: false })
|
|
114
183
|
], DataTable.prototype, "rowHref", void 0);
|
|
184
|
+
__decorate([
|
|
185
|
+
property({ attribute: false })
|
|
186
|
+
], DataTable.prototype, "rowLabel", void 0);
|
|
115
187
|
DataTable = __decorate([
|
|
116
188
|
customElement("data-table")
|
|
117
189
|
], DataTable);
|
package/dist/data-table.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-table.js","sourceRoot":"","sources":["../src/data-table.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,MAAM,EAAE,MAAM,aAAa,CAAC;AAQrC;;;;;;;;;;;GAWG;AAEI,IAAM,SAAS,GAAf,MAAM,SAAU,SAAQ,UAAU;IAAlC;;
|
|
1
|
+
{"version":3,"file":"data-table.js","sourceRoot":"","sources":["../src/data-table.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 { tokens } from \"./tokens.js\";\n\n/** One column header: `key` is looked up via `renderCell`/`getCellValue`, `label` is the header text. */\nexport interface DataTableColumn {\n key: string;\n label: string;\n}\n\n/**\n * A generic, presentational table shell: renders a `<thead>` from `columns`\n * and one `<tr>` per entry in `rows`, with each cell's content produced by\n * `renderCell` (default: plain property lookup on the row object). Knows\n * nothing about what a \"row\" means — callers own the data shape entirely.\n *\n * Optional `rowHref` makes whole rows clickable (navigating via `location.hash`),\n * without hijacking clicks on nested interactive elements (links/buttons) a\n * cell's rendered content might contain.\n *\n * @element data-table\n */\n@customElement(\"data-table\")\nexport class DataTable 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 table {\n width: 100%;\n border-collapse: collapse;\n }\n th {\n text-align: left;\n font-weight: 600;\n color: var(--ui-text-muted, #64748b);\n padding: 0.5rem 0.75rem;\n border-bottom: 1px solid var(--ui-border, #e2e8f0);\n }\n td {\n padding: 0.5rem 0.75rem;\n border-bottom: 1px solid var(--ui-border, #e2e8f0);\n color: var(--ui-text, #0f172a);\n }\n td:first-child {\n position: relative;\n }\n .row-link {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n white-space: nowrap;\n }\n tbody tr.clickable {\n cursor: pointer;\n }\n tbody tr.clickable:hover {\n background: var(--ui-surface-muted, #f8fafc);\n }\n tbody tr.clickable:has(.row-link:focus-visible) {\n outline: none;\n box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35)) inset;\n }\n @media (forced-colors: active) {\n tbody tr.clickable:has(.row-link:focus-visible) {\n outline: 2px solid CanvasText;\n outline-offset: -2px;\n box-shadow: none;\n }\n }\n `,\n ];\n\n /** Column headers, in display order. */\n @property({ attribute: false }) columns: DataTableColumn[] = [];\n /** Row data; opaque to this component beyond what `renderCell` does with it. */\n @property({ attribute: false }) rows: unknown[] = [];\n /** Stable identity for `rows[i]`, used as the repeat-directive key. Defaults to the row's index. */\n @property({ attribute: false }) rowKey: (row: unknown, index: number) => string | number = (\n _row,\n index,\n ) => index;\n /** Produces a cell's rendered content for `row`/`column.key`. Default: `row[key]`. */\n @property({ attribute: false }) renderCell: (row: unknown, key: string) => unknown = (\n row,\n key,\n ) => (row as Record<string, unknown>)[key];\n /** When set, clicking a row (outside any nested link/button) navigates to this hash. */\n @property({ attribute: false }) rowHref: ((row: unknown) => string | null) | null = null;\n /** Accessible label for a row's keyboard link; defaults to primitive cell values. */\n @property({ attribute: false }) rowLabel: ((row: unknown) => string) | null = null;\n\n #onRowClick(row: unknown, e: MouseEvent): void {\n if (!this.rowHref) return;\n if (this.#isNestedInteractive(e.composedPath(), e.currentTarget)) return;\n const href = this.rowHref(row);\n if (href) location.hash = href;\n }\n\n #isNestedInteractive(path: EventTarget[], row: 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 === row) return false;\n if (target instanceof HTMLElement && target.matches(selector)) return true;\n }\n return false;\n }\n\n #rowLinkLabel(row: unknown): string {\n const explicitLabel = this.rowLabel?.(row).trim();\n if (explicitLabel) return explicitLabel;\n const values = this.columns\n .map((column) => (row as Record<string, unknown>)[column.key])\n .filter((value) => typeof value === \"string\" || typeof value === \"number\");\n return values.length > 0 ? `Open ${values.join(\", \")}` : \"Open row\";\n }\n\n override render() {\n return html`\n <table>\n <thead>\n <tr>\n ${this.columns.map((c) => html`<th>${c.label}</th>`)}\n </tr>\n </thead>\n <tbody>\n ${repeat(\n this.rows,\n (row, i) => this.rowKey(row, i),\n (row) => {\n const href = this.rowHref?.(row) ?? null;\n return html`\n <tr\n class=${href ? \"clickable\" : \"\"}\n @click=${(e: MouseEvent) => this.#onRowClick(row, e)}\n >\n ${this.columns.map(\n (c, columnIndex) => html`\n <td>\n ${href && columnIndex === 0\n ? html`\n <a\n class=\"row-link\"\n href=${href}\n aria-label=${this.#rowLinkLabel(row)}\n ></a>\n `\n : nothing}\n ${this.renderCell(row, c.key) ?? nothing}\n </td>\n `,\n )}\n </tr>\n `;\n },\n )}\n </tbody>\n </table>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"data-table\": DataTable;\n }\n}\n"]}
|
package/dist/distance-value.d.ts
CHANGED
|
@@ -9,7 +9,6 @@ import { LitElement, nothing } from "lit";
|
|
|
9
9
|
* @element distance-value
|
|
10
10
|
*/
|
|
11
11
|
export declare class DistanceValue extends LitElement {
|
|
12
|
-
static styles: import("lit").CSSResult;
|
|
13
12
|
/** Distance in miles (imperial). Switches to feet below 0.25 mi. */
|
|
14
13
|
miles: number | null;
|
|
15
14
|
/** Distance in kilometers (metric, future). Switches to meters below 0.5 km. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"distance-value.d.ts","sourceRoot":"","sources":["../src/distance-value.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"distance-value.d.ts","sourceRoot":"","sources":["../src/distance-value.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAQ,OAAO,EAAE,MAAM,KAAK,CAAC;AAIhD;;;;;;;;GAQG;AACH,qBACa,aAAc,SAAQ,UAAU;IAC3C,oEAAoE;IACxC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAQ;IAExD,gFAAgF;IACpD,EAAE,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE5C,MAAM,0DAId;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,gBAAgB,EAAE,aAAa,CAAC;KACjC;CACF"}
|