@f-ewald/components 0.7.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 +76 -47
- package/custom-elements.json +3049 -910
- 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-entry.d.ts +33 -0
- package/dist/calendar-entry.d.ts.map +1 -0
- package/dist/calendar-entry.js +64 -0
- package/dist/calendar-entry.js.map +1 -0
- package/dist/calendar-month.d.ts +67 -0
- package/dist/calendar-month.d.ts.map +1 -0
- package/dist/calendar-month.js +469 -0
- package/dist/calendar-month.js.map +1 -0
- package/dist/calendar-year.d.ts +43 -0
- package/dist/calendar-year.d.ts.map +1 -0
- package/dist/calendar-year.js +145 -0
- package/dist/calendar-year.js.map +1 -0
- 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 +21 -2
- package/dist/form-select.d.ts.map +1 -1
- package/dist/form-select.js +489 -37
- 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 +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- 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 +3 -0
- package/dist/map-circle.d.ts.map +1 -1
- package/dist/map-circle.js +19 -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 +23 -0
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +16 -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/calendar.d.ts +77 -0
- package/dist/utils/calendar.d.ts.map +1 -0
- package/dist/utils/calendar.js +135 -0
- package/dist/utils/calendar.js.map +1 -0
- 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-entry.md +53 -0
- package/docs/calendar-month.md +79 -0
- package/docs/calendar-year.md +53 -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/form-select.md +15 -3
- package/docs/frame-box.md +2 -0
- package/docs/kbd-hint.md +42 -0
- package/docs/map-circle.md +4 -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 +175 -61
- package/package.json +14 -2
- package/dist/map-point.d.ts +0 -31
- package/dist/map-point.d.ts.map +0 -1
- package/dist/map-point.js +0 -83
- package/dist/map-point.js.map +0 -1
- package/docs/map-point.md +0 -44
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { LitElement, css, html, nothing } from "lit";
|
|
8
|
+
import { customElement, property, queryAssignedElements, state } from "lit/decorators.js";
|
|
9
|
+
import { ifDefined } from "lit/directives/if-defined.js";
|
|
10
|
+
import { repeat } from "lit/directives/repeat.js";
|
|
11
|
+
import "./calendar-month.js";
|
|
12
|
+
import { tokens } from "./tokens.js";
|
|
13
|
+
import { CALENDAR_ENTRY_ATTRIBUTES, daysInMonth, overlapsRange, readCalendarEntryElement, resolveEntry, } from "./utils/calendar.js";
|
|
14
|
+
const MONTHS = Array.from({ length: 12 }, (_, i) => i + 1);
|
|
15
|
+
/**
|
|
16
|
+
* A full year of `calendar-month` blocks, generated from declarative
|
|
17
|
+
* `calendar-entry` children. Each entry is re-projected into the
|
|
18
|
+
* `calendar-month` blocks it overlaps as a freshly-created `calendar-entry`
|
|
19
|
+
* element — the original elements stay slotted here and are never moved,
|
|
20
|
+
* since a DOM node can only have one parent. Read-only.
|
|
21
|
+
*
|
|
22
|
+
* @element calendar-year
|
|
23
|
+
* @slot - Declarative `calendar-entry` elements spanning the displayed year.
|
|
24
|
+
*/
|
|
25
|
+
let CalendarYear = class CalendarYear extends LitElement {
|
|
26
|
+
constructor() {
|
|
27
|
+
super(...arguments);
|
|
28
|
+
/** Calendar year to render all 12 months for, e.g. `2026`. */
|
|
29
|
+
this.year = new Date().getFullYear();
|
|
30
|
+
/** Bumped whenever entry elements, attributes, or slotted text change to force a re-render. */
|
|
31
|
+
this._entriesVersion = 0;
|
|
32
|
+
this._entryKeys = new WeakMap();
|
|
33
|
+
this._nextEntryKey = 0;
|
|
34
|
+
}
|
|
35
|
+
static { this.styles = [
|
|
36
|
+
tokens,
|
|
37
|
+
css `
|
|
38
|
+
:host {
|
|
39
|
+
display: block;
|
|
40
|
+
font-family: var(
|
|
41
|
+
--ui-font,
|
|
42
|
+
ui-sans-serif,
|
|
43
|
+
system-ui,
|
|
44
|
+
sans-serif,
|
|
45
|
+
"Apple Color Emoji",
|
|
46
|
+
"Segoe UI Emoji",
|
|
47
|
+
"Segoe UI Symbol",
|
|
48
|
+
"Noto Color Emoji"
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
.year {
|
|
52
|
+
display: grid;
|
|
53
|
+
grid-template-columns: repeat(auto-fill, minmax(13.75rem, 1fr));
|
|
54
|
+
gap: 1.5rem;
|
|
55
|
+
}
|
|
56
|
+
slot {
|
|
57
|
+
display: none;
|
|
58
|
+
}
|
|
59
|
+
`,
|
|
60
|
+
]; }
|
|
61
|
+
/**
|
|
62
|
+
* Watches slotted `calendar-entry` attributes and title/detail text.
|
|
63
|
+
* Neither reflected-property changes nor edits inside assigned elements
|
|
64
|
+
* fire this component's `slotchange`, so a subtree observer keeps the
|
|
65
|
+
* projected month entries synchronized.
|
|
66
|
+
*/
|
|
67
|
+
connectedCallback() {
|
|
68
|
+
super.connectedCallback();
|
|
69
|
+
this._entriesVersion++;
|
|
70
|
+
this._entriesObserver ??= new MutationObserver(() => this._entriesVersion++);
|
|
71
|
+
this._entriesObserver.observe(this, {
|
|
72
|
+
attributes: true,
|
|
73
|
+
attributeFilter: CALENDAR_ENTRY_ATTRIBUTES,
|
|
74
|
+
characterData: true,
|
|
75
|
+
childList: true,
|
|
76
|
+
subtree: true,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/** Releases the mutation observer when the element disconnects. */
|
|
80
|
+
disconnectedCallback() {
|
|
81
|
+
this._entriesObserver?.disconnect();
|
|
82
|
+
super.disconnectedCallback();
|
|
83
|
+
}
|
|
84
|
+
/** Re-renders when declarative `calendar-entry` children are added or removed. */
|
|
85
|
+
_handleSlotChange() {
|
|
86
|
+
this._entriesVersion++;
|
|
87
|
+
}
|
|
88
|
+
/** Returns an immutable identity for each original slotted entry. */
|
|
89
|
+
_entryKey(element) {
|
|
90
|
+
let key = this._entryKeys.get(element);
|
|
91
|
+
if (!key) {
|
|
92
|
+
key = `entry-${this._nextEntryKey++}`;
|
|
93
|
+
this._entryKeys.set(element, key);
|
|
94
|
+
}
|
|
95
|
+
return key;
|
|
96
|
+
}
|
|
97
|
+
render() {
|
|
98
|
+
const entries = this._entryElements
|
|
99
|
+
.map((element) => {
|
|
100
|
+
const entry = resolveEntry(readCalendarEntryElement(element));
|
|
101
|
+
return entry ? { ...entry, projectionKey: this._entryKey(element) } : null;
|
|
102
|
+
})
|
|
103
|
+
.filter((entry) => entry !== null);
|
|
104
|
+
return html `
|
|
105
|
+
<div class="year">
|
|
106
|
+
${repeat(MONTHS, (m) => m, (m) => {
|
|
107
|
+
const monthStart = new Date(this.year, m - 1, 1);
|
|
108
|
+
const monthEnd = new Date(this.year, m - 1, daysInMonth(this.year, m));
|
|
109
|
+
const monthEntries = entries.filter((entry) => overlapsRange(entry, monthStart, monthEnd));
|
|
110
|
+
return html `
|
|
111
|
+
<calendar-month .year=${this.year} .month=${m}>
|
|
112
|
+
${repeat(monthEntries, (entry) => entry.projectionKey, (entry) => html `
|
|
113
|
+
<calendar-entry
|
|
114
|
+
start=${entry.start}
|
|
115
|
+
end=${entry.end}
|
|
116
|
+
label=${entry.label}
|
|
117
|
+
color=${entry.color}
|
|
118
|
+
href=${ifDefined(entry.href)}
|
|
119
|
+
>
|
|
120
|
+
${(entry.details ?? []).map((detail) => html `<span slot="detail">${detail}</span>`)}
|
|
121
|
+
${entry.footer ? html `<span slot="footer">${entry.footer}</span>` : nothing}
|
|
122
|
+
</calendar-entry>
|
|
123
|
+
`)}
|
|
124
|
+
</calendar-month>
|
|
125
|
+
`;
|
|
126
|
+
})}
|
|
127
|
+
</div>
|
|
128
|
+
<slot @slotchange=${this._handleSlotChange}></slot>
|
|
129
|
+
`;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
__decorate([
|
|
133
|
+
property({ type: Number, reflect: true })
|
|
134
|
+
], CalendarYear.prototype, "year", void 0);
|
|
135
|
+
__decorate([
|
|
136
|
+
queryAssignedElements({ selector: "calendar-entry" })
|
|
137
|
+
], CalendarYear.prototype, "_entryElements", void 0);
|
|
138
|
+
__decorate([
|
|
139
|
+
state()
|
|
140
|
+
], CalendarYear.prototype, "_entriesVersion", void 0);
|
|
141
|
+
CalendarYear = __decorate([
|
|
142
|
+
customElement("calendar-year")
|
|
143
|
+
], CalendarYear);
|
|
144
|
+
export { CalendarYear };
|
|
145
|
+
//# sourceMappingURL=calendar-year.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calendar-year.js","sourceRoot":"","sources":["../src/calendar-year.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, css, html, nothing } from \"lit\";\nimport { customElement, property, queryAssignedElements, state } from \"lit/decorators.js\";\nimport { ifDefined } from \"lit/directives/if-defined.js\";\nimport { repeat } from \"lit/directives/repeat.js\";\nimport { CalendarEntry } from \"./calendar-entry.js\";\nimport \"./calendar-month.js\";\nimport { tokens } from \"./tokens.js\";\nimport {\n CALENDAR_ENTRY_ATTRIBUTES,\n daysInMonth,\n overlapsRange,\n readCalendarEntryElement,\n resolveEntry,\n type ResolvedCalendarEntry,\n} from \"./utils/calendar.js\";\n\nconst MONTHS = Array.from({ length: 12 }, (_, i) => i + 1);\ntype ProjectedCalendarEntry = ResolvedCalendarEntry & { projectionKey: string };\n\n/**\n * A full year of `calendar-month` blocks, generated from declarative\n * `calendar-entry` children. Each entry is re-projected into the\n * `calendar-month` blocks it overlaps as a freshly-created `calendar-entry`\n * element — the original elements stay slotted here and are never moved,\n * since a DOM node can only have one parent. Read-only.\n *\n * @element calendar-year\n * @slot - Declarative `calendar-entry` elements spanning the displayed year.\n */\n@customElement(\"calendar-year\")\nexport class CalendarYear extends LitElement {\n /** Calendar year to render all 12 months for, e.g. `2026`. */\n @property({ type: Number, reflect: true }) year: number = new Date().getFullYear();\n\n @queryAssignedElements({ selector: \"calendar-entry\" })\n private readonly _entryElements!: CalendarEntry[];\n\n /** Bumped whenever entry elements, attributes, or slotted text change to force a re-render. */\n @state() private _entriesVersion = 0;\n\n private _entriesObserver?: MutationObserver;\n private readonly _entryKeys = new WeakMap<CalendarEntry, string>();\n private _nextEntryKey = 0;\n\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 }\n .year {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(13.75rem, 1fr));\n gap: 1.5rem;\n }\n slot {\n display: none;\n }\n `,\n ];\n\n /**\n * Watches slotted `calendar-entry` attributes and title/detail text.\n * Neither reflected-property changes nor edits inside assigned elements\n * fire this component's `slotchange`, so a subtree observer keeps the\n * projected month entries synchronized.\n */\n override connectedCallback(): void {\n super.connectedCallback();\n this._entriesVersion++;\n this._entriesObserver ??= new MutationObserver(() => this._entriesVersion++);\n this._entriesObserver.observe(this, {\n attributes: true,\n attributeFilter: CALENDAR_ENTRY_ATTRIBUTES,\n characterData: true,\n childList: true,\n subtree: true,\n });\n }\n\n /** Releases the mutation observer when the element disconnects. */\n override disconnectedCallback(): void {\n this._entriesObserver?.disconnect();\n super.disconnectedCallback();\n }\n\n /** Re-renders when declarative `calendar-entry` children are added or removed. */\n private _handleSlotChange(): void {\n this._entriesVersion++;\n }\n\n /** Returns an immutable identity for each original slotted entry. */\n private _entryKey(element: CalendarEntry): string {\n let key = this._entryKeys.get(element);\n if (!key) {\n key = `entry-${this._nextEntryKey++}`;\n this._entryKeys.set(element, key);\n }\n return key;\n }\n\n protected override render() {\n const entries = this._entryElements\n .map((element) => {\n const entry = resolveEntry(readCalendarEntryElement(element));\n return entry ? { ...entry, projectionKey: this._entryKey(element) } : null;\n })\n .filter((entry): entry is ProjectedCalendarEntry => entry !== null);\n\n return html`\n <div class=\"year\">\n ${repeat(\n MONTHS,\n (m) => m,\n (m) => {\n const monthStart = new Date(this.year, m - 1, 1);\n const monthEnd = new Date(this.year, m - 1, daysInMonth(this.year, m));\n const monthEntries = entries.filter((entry) => overlapsRange(entry, monthStart, monthEnd));\n return html`\n <calendar-month .year=${this.year} .month=${m}>\n ${repeat(\n monthEntries,\n (entry) => entry.projectionKey,\n (entry) => html`\n <calendar-entry\n start=${entry.start}\n end=${entry.end}\n label=${entry.label}\n color=${entry.color}\n href=${ifDefined(entry.href)}\n >\n ${(entry.details ?? []).map((detail) => html`<span slot=\"detail\">${detail}</span>`)}\n ${entry.footer ? html`<span slot=\"footer\">${entry.footer}</span>` : nothing}\n </calendar-entry>\n `,\n )}\n </calendar-month>\n `;\n },\n )}\n </div>\n <slot @slotchange=${this._handleSlotChange}></slot>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"calendar-year\": CalendarYear;\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-message.d.ts","sourceRoot":"","sources":["../src/chat-message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAGrD,OAAO,oBAAoB,CAAC;AAG5B,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAC1D,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAEhE;;;;;;;;;GASG;AACH,qBACa,WAAY,SAAQ,UAAU;IACzC,OAAgB,MAAM,
|
|
1
|
+
{"version":3,"file":"chat-message.d.ts","sourceRoot":"","sources":["../src/chat-message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAGrD,OAAO,oBAAoB,CAAC;AAG5B,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAC1D,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAEhE;;;;;;;;;GASG;AACH,qBACa,WAAY,SAAQ,UAAU;IACzC,OAAgB,MAAM,4BAyGpB;IAEF,gEAAgE;IAC1B,IAAI,EAAE,eAAe,CAAW;IACtE,6FAA6F;IAChE,OAAO,EAAE,kBAAkB,CAAY;IACpE,kDAAkD;IACtC,MAAM,SAAM;IACxB,sEAAsE;IAC1D,SAAS,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC5C,+FAA+F;IACnF,OAAO,SAAM;IACzB,iEAAiE;IACpC,WAAW,UAAS;IACjD,0DAA0D;IACd,SAAS,UAAS;IAE9D,OAAO,CAAC,OAAO;IAMN,MAAM,yCA8Bd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,cAAc,EAAE,WAAW,CAAC;KAC7B;CACF"}
|
package/dist/chat-message.js
CHANGED
|
@@ -42,23 +42,43 @@ let ChatMessage = class ChatMessage extends LitElement {
|
|
|
42
42
|
css `
|
|
43
43
|
:host {
|
|
44
44
|
display: block;
|
|
45
|
-
font-family: var(
|
|
45
|
+
font-family: var(
|
|
46
|
+
--ui-font,
|
|
47
|
+
ui-sans-serif,
|
|
48
|
+
system-ui,
|
|
49
|
+
sans-serif,
|
|
50
|
+
"Apple Color Emoji",
|
|
51
|
+
"Segoe UI Emoji",
|
|
52
|
+
"Segoe UI Symbol",
|
|
53
|
+
"Noto Color Emoji"
|
|
54
|
+
);
|
|
46
55
|
font-size: var(--ui-font-size, 0.875rem);
|
|
47
56
|
}
|
|
48
57
|
.header {
|
|
49
58
|
display: flex;
|
|
50
59
|
align-items: center;
|
|
51
|
-
gap: 0.
|
|
52
|
-
font-size: 0.75rem;
|
|
60
|
+
gap: 0.5rem;
|
|
61
|
+
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
53
62
|
color: var(--ui-text-muted, #64748b);
|
|
54
|
-
margin-bottom: 0.
|
|
63
|
+
margin-bottom: 0.25rem;
|
|
55
64
|
}
|
|
56
|
-
.header
|
|
65
|
+
button.header {
|
|
66
|
+
width: 100%;
|
|
67
|
+
padding: 0;
|
|
68
|
+
border: 0;
|
|
69
|
+
background: none;
|
|
70
|
+
font-family: inherit;
|
|
71
|
+
text-align: left;
|
|
57
72
|
cursor: pointer;
|
|
58
73
|
}
|
|
74
|
+
button.header:focus-visible {
|
|
75
|
+
outline: none;
|
|
76
|
+
border-radius: var(--ui-radius-sm, 0.25rem);
|
|
77
|
+
box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));
|
|
78
|
+
}
|
|
59
79
|
.chevron {
|
|
60
80
|
display: inline-flex;
|
|
61
|
-
transition: transform
|
|
81
|
+
transition: transform 150ms ease;
|
|
62
82
|
}
|
|
63
83
|
.chevron.expanded {
|
|
64
84
|
transform: rotate(90deg);
|
|
@@ -74,7 +94,7 @@ let ChatMessage = class ChatMessage extends LitElement {
|
|
|
74
94
|
}
|
|
75
95
|
.body-card {
|
|
76
96
|
border-radius: var(--ui-radius, 0.5rem);
|
|
77
|
-
padding: 0.
|
|
97
|
+
padding: 0.5rem 0.75rem;
|
|
78
98
|
color: var(--ui-text, #0f172a);
|
|
79
99
|
line-height: 1.5;
|
|
80
100
|
}
|
|
@@ -82,24 +102,44 @@ let ChatMessage = class ChatMessage extends LitElement {
|
|
|
82
102
|
display: none;
|
|
83
103
|
}
|
|
84
104
|
:host([role="user"]) .body-card {
|
|
85
|
-
background: color-mix(in srgb, var(--ui-primary, #4f46e5) 8%, var(--ui-surface, #
|
|
105
|
+
background: color-mix(in srgb, var(--ui-primary, #4f46e5) 8%, var(--ui-surface, #ffffff));
|
|
86
106
|
}
|
|
87
107
|
:host([role="agent"]) .body-card {
|
|
88
|
-
background: var(--ui-surface, #
|
|
108
|
+
background: var(--ui-surface, #ffffff);
|
|
89
109
|
border: 1px solid var(--ui-border, #e2e8f0);
|
|
90
110
|
}
|
|
91
111
|
:host([role="system"]) .body-card {
|
|
92
|
-
padding: 0 0 0
|
|
93
|
-
font-size: 0.
|
|
112
|
+
padding: 0 0 0 1rem;
|
|
113
|
+
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
94
114
|
color: var(--ui-text-muted, #64748b);
|
|
95
115
|
}
|
|
96
116
|
:host([variant="tool"]) .body-card,
|
|
97
117
|
:host([variant="thinking"]) .body-card {
|
|
98
118
|
opacity: 0.7;
|
|
99
|
-
font-size: 0.
|
|
119
|
+
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
100
120
|
}
|
|
101
121
|
:host([variant="tool"]) .body-card {
|
|
102
|
-
font-family:
|
|
122
|
+
font-family: var(
|
|
123
|
+
--ui-font-mono,
|
|
124
|
+
ui-monospace,
|
|
125
|
+
SFMono-Regular,
|
|
126
|
+
Menlo,
|
|
127
|
+
Monaco,
|
|
128
|
+
Consolas,
|
|
129
|
+
monospace
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
@media (prefers-reduced-motion: reduce) {
|
|
133
|
+
.chevron {
|
|
134
|
+
transition: none;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
@media (forced-colors: active) {
|
|
138
|
+
button.header:focus-visible {
|
|
139
|
+
outline: 2px solid CanvasText;
|
|
140
|
+
outline-offset: 2px;
|
|
141
|
+
box-shadow: none;
|
|
142
|
+
}
|
|
103
143
|
}
|
|
104
144
|
`,
|
|
105
145
|
]; }
|
|
@@ -110,18 +150,31 @@ let ChatMessage = class ChatMessage extends LitElement {
|
|
|
110
150
|
this.dispatchEvent(new CustomEvent("toggle", { detail: { collapsed: this.collapsed } }));
|
|
111
151
|
}
|
|
112
152
|
render() {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
>`
|
|
153
|
+
const headerContent = html `
|
|
154
|
+
${this.collapsible
|
|
155
|
+
? html `<span class="chevron ${this.collapsed ? "" : "expanded"}" aria-hidden="true"
|
|
156
|
+
>${iconChevronRight(12)}</span
|
|
157
|
+
>`
|
|
119
158
|
: nothing}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
159
|
+
${this.author ? html `<span class="author">${this.author}</span>` : nothing}
|
|
160
|
+
${this.timestamp ? html `<relative-time datetime=${this.timestamp}></relative-time>` : nothing}
|
|
161
|
+
${this.summary ? html `<span class="summary">${this.summary}</span>` : nothing}
|
|
162
|
+
`;
|
|
163
|
+
return html `
|
|
164
|
+
${this.collapsible
|
|
165
|
+
? html `
|
|
166
|
+
<button
|
|
167
|
+
class="header"
|
|
168
|
+
type="button"
|
|
169
|
+
aria-expanded=${String(!this.collapsed)}
|
|
170
|
+
aria-controls="message-body"
|
|
171
|
+
@click=${this._toggle}
|
|
172
|
+
>
|
|
173
|
+
${headerContent}
|
|
174
|
+
</button>
|
|
175
|
+
`
|
|
176
|
+
: html `<div class="header">${headerContent}</div>`}
|
|
177
|
+
<div id="message-body" class="body-card" ?hidden=${this.collapsible && this.collapsed}>
|
|
125
178
|
<slot></slot>
|
|
126
179
|
</div>
|
|
127
180
|
`;
|
package/dist/chat-message.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-message.js","sourceRoot":"","sources":["../src/chat-message.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,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,oBAAoB,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAKrC;;;;;;;;;GASG;AAEI,IAAM,WAAW,GAAjB,MAAM,WAAY,SAAQ,UAAU;IAApC;;
|
|
1
|
+
{"version":3,"file":"chat-message.js","sourceRoot":"","sources":["../src/chat-message.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, css, html, nothing } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { iconChevronRight } from \"./icons.js\";\nimport \"./relative-time.js\";\nimport { tokens } from \"./tokens.js\";\n\nexport type ChatMessageRole = \"user\" | \"agent\" | \"system\";\nexport type ChatMessageVariant = \"normal\" | \"tool\" | \"thinking\";\n\n/**\n * One conversation entry in a chat-style activity feed. Tool calls and\n * \"thinking\" traces are variants of this component rather than separate\n * ones — they share the same header, collapse behavior, and body card as a\n * normal message, just dimmed and collapsible with an always-visible summary.\n *\n * @element chat-message\n * @slot - Message body (consumers slot in already-rendered content, e.g. sanitized markdown HTML).\n * @fires toggle - Fired with `{ collapsed: boolean }` when the header/summary is clicked in collapsible mode.\n */\n@customElement(\"chat-message\")\nexport class ChatMessage 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, 0.875rem);\n }\n .header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-size: var(--ui-font-size-sm, 0.75rem);\n color: var(--ui-text-muted, #64748b);\n margin-bottom: 0.25rem;\n }\n button.header {\n width: 100%;\n padding: 0;\n border: 0;\n background: none;\n font-family: inherit;\n text-align: left;\n cursor: pointer;\n }\n button.header:focus-visible {\n outline: none;\n border-radius: var(--ui-radius-sm, 0.25rem);\n box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));\n }\n .chevron {\n display: inline-flex;\n transition: transform 150ms ease;\n }\n .chevron.expanded {\n transform: rotate(90deg);\n }\n .author {\n font-weight: 600;\n color: var(--ui-text, #0f172a);\n }\n .summary {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .body-card {\n border-radius: var(--ui-radius, 0.5rem);\n padding: 0.5rem 0.75rem;\n color: var(--ui-text, #0f172a);\n line-height: 1.5;\n }\n .body-card[hidden] {\n display: none;\n }\n :host([role=\"user\"]) .body-card {\n background: color-mix(in srgb, var(--ui-primary, #4f46e5) 8%, var(--ui-surface, #ffffff));\n }\n :host([role=\"agent\"]) .body-card {\n background: var(--ui-surface, #ffffff);\n border: 1px solid var(--ui-border, #e2e8f0);\n }\n :host([role=\"system\"]) .body-card {\n padding: 0 0 0 1rem;\n font-size: var(--ui-font-size-sm, 0.75rem);\n color: var(--ui-text-muted, #64748b);\n }\n :host([variant=\"tool\"]) .body-card,\n :host([variant=\"thinking\"]) .body-card {\n opacity: 0.7;\n font-size: var(--ui-font-size-sm, 0.75rem);\n }\n :host([variant=\"tool\"]) .body-card {\n font-family: var(\n --ui-font-mono,\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Monaco,\n Consolas,\n monospace\n );\n }\n @media (prefers-reduced-motion: reduce) {\n .chevron {\n transition: none;\n }\n }\n @media (forced-colors: active) {\n button.header:focus-visible {\n outline: 2px solid CanvasText;\n outline-offset: 2px;\n box-shadow: none;\n }\n }\n `,\n ];\n\n /** Whose message this is; drives the card background/border. */\n @property({ reflect: true }) override role: ChatMessageRole = \"agent\";\n /** `tool`/`thinking` render dimmed and smaller, with `tool` using monospace for its body. */\n @property({ reflect: true }) variant: ChatMessageVariant = \"normal\";\n /** Header label, e.g. \"Freddy\" or \"Architect\". */\n @property() author = \"\";\n /** ISO-8601 timestamp, rendered via `relative-time` in the header. */\n @property() timestamp: string | null = null;\n /** Always-visible one-liner for `tool`/`thinking` variants (e.g. a truncated args preview). */\n @property() summary = \"\";\n /** Whether clicking the header/summary toggles the body slot. */\n @property({ type: Boolean }) collapsible = false;\n /** Current collapse state (reflected as an attribute). */\n @property({ type: Boolean, reflect: true }) collapsed = false;\n\n private _toggle() {\n if (!this.collapsible) return;\n this.collapsed = !this.collapsed;\n this.dispatchEvent(new CustomEvent(\"toggle\", { detail: { collapsed: this.collapsed } }));\n }\n\n override render() {\n const headerContent = html`\n ${this.collapsible\n ? html`<span class=\"chevron ${this.collapsed ? \"\" : \"expanded\"}\" aria-hidden=\"true\"\n >${iconChevronRight(12)}</span\n >`\n : nothing}\n ${this.author ? html`<span class=\"author\">${this.author}</span>` : nothing}\n ${this.timestamp ? html`<relative-time datetime=${this.timestamp}></relative-time>` : nothing}\n ${this.summary ? html`<span class=\"summary\">${this.summary}</span>` : nothing}\n `;\n\n return html`\n ${this.collapsible\n ? html`\n <button\n class=\"header\"\n type=\"button\"\n aria-expanded=${String(!this.collapsed)}\n aria-controls=\"message-body\"\n @click=${this._toggle}\n >\n ${headerContent}\n </button>\n `\n : html`<div class=\"header\">${headerContent}</div>`}\n <div id=\"message-body\" class=\"body-card\" ?hidden=${this.collapsible && this.collapsed}>\n <slot></slot>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"chat-message\": ChatMessage;\n }\n}\n"]}
|
package/dist/confirm-dialog.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LitElement } from "lit";
|
|
1
|
+
import { LitElement, type PropertyValues } from "lit";
|
|
2
2
|
/**
|
|
3
3
|
* Reusable confirmation dialog: overlay + centered card with a slotted body,
|
|
4
4
|
* an optional error line, and Cancel/Confirm actions. Instant `display:none`
|
|
@@ -24,8 +24,15 @@ export declare class ConfirmDialog extends LitElement {
|
|
|
24
24
|
busy: boolean;
|
|
25
25
|
/** Inline error line shown below the body, or null for none. */
|
|
26
26
|
error: string | null;
|
|
27
|
+
private _previousFocus;
|
|
27
28
|
private _confirm;
|
|
28
29
|
private _cancel;
|
|
30
|
+
private _getFocusableElements;
|
|
31
|
+
private _focusInitial;
|
|
32
|
+
private _onDocumentFocusIn;
|
|
33
|
+
private _onWindowKeydown;
|
|
34
|
+
protected updated(changed: PropertyValues): void;
|
|
35
|
+
disconnectedCallback(): void;
|
|
29
36
|
render(): import("lit-html").TemplateResult<1>;
|
|
30
37
|
}
|
|
31
38
|
declare global {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"confirm-dialog.d.ts","sourceRoot":"","sources":["../src/confirm-dialog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"confirm-dialog.d.ts","sourceRoot":"","sources":["../src/confirm-dialog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAW1E;;;;;;;;;;GAUG;AACH,qBACa,aAAc,SAAQ,UAAU;IAC3C,OAAgB,MAAM,4BAqIpB;IAEF,qCAAqC;IACR,IAAI,UAAS;IAC1C,oCAAoC;IACM,YAAY,SAAY;IAClE,mCAAmC;IACM,WAAW,SAAY;IAChE,wEAAwE;IAC3C,MAAM,UAAQ;IAC3C,8EAA8E;IACjD,IAAI,UAAS;IAC1C,gEAAgE;IACpD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAQ;IAExC,OAAO,CAAC,cAAc,CAA4B;IAElD,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,qBAAqB;IAU7B,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,kBAAkB,CAGxB;IAEF,OAAO,CAAC,gBAAgB,CAuBtB;IAEF,UAAmB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAexD;IAEQ,oBAAoB,IAAI,IAAI,CAQpC;IAEQ,MAAM,yCA4Bd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,gBAAgB,EAAE,aAAa,CAAC;KACjC;CACF"}
|
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>
|