@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.
Files changed (193) hide show
  1. package/README.md +76 -47
  2. package/custom-elements.json +3049 -910
  3. package/dist/address-autocomplete.d.ts +8 -0
  4. package/dist/address-autocomplete.d.ts.map +1 -1
  5. package/dist/address-autocomplete.js +165 -15
  6. package/dist/address-autocomplete.js.map +1 -1
  7. package/dist/animate-confetti.d.ts +8 -5
  8. package/dist/animate-confetti.d.ts.map +1 -1
  9. package/dist/animate-confetti.js +46 -19
  10. package/dist/animate-confetti.js.map +1 -1
  11. package/dist/autocomplete-input.d.ts +8 -0
  12. package/dist/autocomplete-input.d.ts.map +1 -1
  13. package/dist/autocomplete-input.js +165 -15
  14. package/dist/autocomplete-input.js.map +1 -1
  15. package/dist/calendar-entry.d.ts +33 -0
  16. package/dist/calendar-entry.d.ts.map +1 -0
  17. package/dist/calendar-entry.js +64 -0
  18. package/dist/calendar-entry.js.map +1 -0
  19. package/dist/calendar-month.d.ts +67 -0
  20. package/dist/calendar-month.d.ts.map +1 -0
  21. package/dist/calendar-month.js +469 -0
  22. package/dist/calendar-month.js.map +1 -0
  23. package/dist/calendar-year.d.ts +43 -0
  24. package/dist/calendar-year.d.ts.map +1 -0
  25. package/dist/calendar-year.js +145 -0
  26. package/dist/calendar-year.js.map +1 -0
  27. package/dist/chat-message.d.ts.map +1 -1
  28. package/dist/chat-message.js +77 -24
  29. package/dist/chat-message.js.map +1 -1
  30. package/dist/confirm-dialog.d.ts +8 -1
  31. package/dist/confirm-dialog.d.ts.map +1 -1
  32. package/dist/confirm-dialog.js +130 -18
  33. package/dist/confirm-dialog.js.map +1 -1
  34. package/dist/copy-link-button.d.ts +2 -0
  35. package/dist/copy-link-button.d.ts.map +1 -1
  36. package/dist/copy-link-button.js +35 -3
  37. package/dist/copy-link-button.js.map +1 -1
  38. package/dist/data-table.d.ts +2 -0
  39. package/dist/data-table.d.ts.map +1 -1
  40. package/dist/data-table.js +83 -11
  41. package/dist/data-table.js.map +1 -1
  42. package/dist/distance-value.d.ts +0 -1
  43. package/dist/distance-value.d.ts.map +1 -1
  44. package/dist/distance-value.js +3 -8
  45. package/dist/distance-value.js.map +1 -1
  46. package/dist/distribution-chart.d.ts.map +1 -1
  47. package/dist/distribution-chart.js +54 -17
  48. package/dist/distribution-chart.js.map +1 -1
  49. package/dist/dropdown-button.d.ts.map +1 -1
  50. package/dist/dropdown-button.js +60 -11
  51. package/dist/dropdown-button.js.map +1 -1
  52. package/dist/editable-text.d.ts.map +1 -1
  53. package/dist/editable-text.js +46 -13
  54. package/dist/editable-text.js.map +1 -1
  55. package/dist/form-select.d.ts +21 -2
  56. package/dist/form-select.d.ts.map +1 -1
  57. package/dist/form-select.js +489 -37
  58. package/dist/form-select.js.map +1 -1
  59. package/dist/frame-box.d.ts.map +1 -1
  60. package/dist/frame-box.js +20 -12
  61. package/dist/frame-box.js.map +1 -1
  62. package/dist/icon-button.d.ts.map +1 -1
  63. package/dist/icon-button.js +11 -0
  64. package/dist/icon-button.js.map +1 -1
  65. package/dist/index.d.ts +4 -1
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +4 -1
  68. package/dist/index.js.map +1 -1
  69. package/dist/kbd-hint.d.ts +30 -0
  70. package/dist/kbd-hint.d.ts.map +1 -0
  71. package/dist/kbd-hint.js +184 -0
  72. package/dist/kbd-hint.js.map +1 -0
  73. package/dist/live-timer.d.ts +0 -1
  74. package/dist/live-timer.d.ts.map +1 -1
  75. package/dist/live-timer.js +1 -6
  76. package/dist/live-timer.js.map +1 -1
  77. package/dist/map-circle.d.ts +3 -0
  78. package/dist/map-circle.d.ts.map +1 -1
  79. package/dist/map-circle.js +19 -1
  80. package/dist/map-circle.js.map +1 -1
  81. package/dist/map-pin.d.ts.map +1 -1
  82. package/dist/map-pin.js +16 -1
  83. package/dist/map-pin.js.map +1 -1
  84. package/dist/percent-bar-chart.d.ts +3 -0
  85. package/dist/percent-bar-chart.d.ts.map +1 -1
  86. package/dist/percent-bar-chart.js +60 -4
  87. package/dist/percent-bar-chart.js.map +1 -1
  88. package/dist/photo-gallery.d.ts.map +1 -1
  89. package/dist/photo-gallery.js +12 -3
  90. package/dist/photo-gallery.js.map +1 -1
  91. package/dist/popover-panel.d.ts.map +1 -1
  92. package/dist/popover-panel.js +132 -23
  93. package/dist/popover-panel.js.map +1 -1
  94. package/dist/price-history-chart.d.ts.map +1 -1
  95. package/dist/price-history-chart.js +52 -18
  96. package/dist/price-history-chart.js.map +1 -1
  97. package/dist/radio-cards.d.ts +2 -0
  98. package/dist/radio-cards.d.ts.map +1 -1
  99. package/dist/radio-cards.js +51 -5
  100. package/dist/radio-cards.js.map +1 -1
  101. package/dist/radio-pills.d.ts +2 -0
  102. package/dist/radio-pills.d.ts.map +1 -1
  103. package/dist/radio-pills.js +46 -4
  104. package/dist/radio-pills.js.map +1 -1
  105. package/dist/relative-time.d.ts +0 -1
  106. package/dist/relative-time.d.ts.map +1 -1
  107. package/dist/relative-time.js +1 -6
  108. package/dist/relative-time.js.map +1 -1
  109. package/dist/reveal-button.d.ts +2 -0
  110. package/dist/reveal-button.d.ts.map +1 -1
  111. package/dist/reveal-button.js +42 -5
  112. package/dist/reveal-button.js.map +1 -1
  113. package/dist/roman-numeral.js +1 -1
  114. package/dist/roman-numeral.js.map +1 -1
  115. package/dist/slide-panel.d.ts +7 -3
  116. package/dist/slide-panel.d.ts.map +1 -1
  117. package/dist/slide-panel.js +95 -23
  118. package/dist/slide-panel.js.map +1 -1
  119. package/dist/stat-meter.d.ts.map +1 -1
  120. package/dist/stat-meter.js +17 -7
  121. package/dist/stat-meter.js.map +1 -1
  122. package/dist/status-pill.d.ts.map +1 -1
  123. package/dist/status-pill.js +26 -12
  124. package/dist/status-pill.js.map +1 -1
  125. package/dist/tile-grid.d.ts +8 -0
  126. package/dist/tile-grid.d.ts.map +1 -1
  127. package/dist/tile-grid.js +88 -12
  128. package/dist/tile-grid.js.map +1 -1
  129. package/dist/toast-notification.d.ts.map +1 -1
  130. package/dist/toast-notification.js +40 -11
  131. package/dist/toast-notification.js.map +1 -1
  132. package/dist/tokens.css +23 -0
  133. package/dist/tokens.d.ts.map +1 -1
  134. package/dist/tokens.js +16 -0
  135. package/dist/tokens.js.map +1 -1
  136. package/dist/ui-button.d.ts +2 -0
  137. package/dist/ui-button.d.ts.map +1 -1
  138. package/dist/ui-button.js +58 -9
  139. package/dist/ui-button.js.map +1 -1
  140. package/dist/user-avatar.d.ts.map +1 -1
  141. package/dist/user-avatar.js +18 -3
  142. package/dist/user-avatar.js.map +1 -1
  143. package/dist/utils/calendar.d.ts +77 -0
  144. package/dist/utils/calendar.d.ts.map +1 -0
  145. package/dist/utils/calendar.js +135 -0
  146. package/dist/utils/calendar.js.map +1 -0
  147. package/dist/utils/form.d.ts +3 -0
  148. package/dist/utils/form.d.ts.map +1 -0
  149. package/dist/utils/form.js +14 -0
  150. package/dist/utils/form.js.map +1 -0
  151. package/dist/utils/layer-stack.d.ts +9 -0
  152. package/dist/utils/layer-stack.d.ts.map +1 -0
  153. package/dist/utils/layer-stack.js +35 -0
  154. package/dist/utils/layer-stack.js.map +1 -0
  155. package/dist/weight-bar-chart.d.ts.map +1 -1
  156. package/dist/weight-bar-chart.js +27 -8
  157. package/dist/weight-bar-chart.js.map +1 -1
  158. package/docs/address-autocomplete.md +5 -0
  159. package/docs/autocomplete-input.md +5 -0
  160. package/docs/calendar-entry.md +53 -0
  161. package/docs/calendar-month.md +79 -0
  162. package/docs/calendar-year.md +53 -0
  163. package/docs/chat-message.md +4 -0
  164. package/docs/confirm-dialog.md +2 -0
  165. package/docs/copy-link-button.md +2 -0
  166. package/docs/data-table.md +2 -0
  167. package/docs/design-language.md +149 -0
  168. package/docs/distribution-chart.md +4 -0
  169. package/docs/dropdown-button.md +1 -0
  170. package/docs/form-select.md +15 -3
  171. package/docs/frame-box.md +2 -0
  172. package/docs/kbd-hint.md +42 -0
  173. package/docs/map-circle.md +4 -0
  174. package/docs/percent-bar-chart.md +4 -1
  175. package/docs/photo-gallery.md +1 -0
  176. package/docs/popover-panel.md +2 -0
  177. package/docs/price-history-chart.md +8 -1
  178. package/docs/radio-cards.md +2 -0
  179. package/docs/radio-pills.md +2 -0
  180. package/docs/reveal-button.md +3 -0
  181. package/docs/slide-panel.md +4 -2
  182. package/docs/status-pill.md +3 -0
  183. package/docs/tile-grid.md +10 -1
  184. package/docs/toast-notification.md +4 -1
  185. package/docs/ui-button.md +2 -0
  186. package/docs/user-avatar.md +1 -0
  187. package/llms.txt +175 -61
  188. package/package.json +14 -2
  189. package/dist/map-point.d.ts +0 -31
  190. package/dist/map-point.d.ts.map +0 -1
  191. package/dist/map-point.js +0 -83
  192. package/dist/map-point.js.map +0 -1
  193. 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,4BAiEpB;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,yCAgBd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,cAAc,EAAE,WAAW,CAAC;KAC7B;CACF"}
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"}
@@ -42,23 +42,43 @@ let ChatMessage = class ChatMessage extends LitElement {
42
42
  css `
43
43
  :host {
44
44
  display: block;
45
- font-family: var(--ui-font, ui-sans-serif, system-ui, sans-serif);
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.4rem;
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.3rem;
63
+ margin-bottom: 0.25rem;
55
64
  }
56
- .header.clickable {
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 0.15s ease;
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.6rem 0.8rem;
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, #fff));
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, #fff);
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 1.1rem;
93
- font-size: 0.8rem;
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.8rem;
119
+ font-size: var(--ui-font-size-sm, 0.75rem);
100
120
  }
101
121
  :host([variant="tool"]) .body-card {
102
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
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
- return html `
114
- <div class="header ${this.collapsible ? "clickable" : ""}" @click=${this._toggle}>
115
- ${this.collapsible
116
- ? html `<span class="chevron ${this.collapsed ? "" : "expanded"}"
117
- >${iconChevronRight(12)}</span
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
- ${this.author ? html `<span class="author">${this.author}</span>` : nothing}
121
- ${this.timestamp ? html `<relative-time datetime=${this.timestamp}></relative-time>` : nothing}
122
- ${this.summary ? html `<span class="summary">${this.summary}</span>` : nothing}
123
- </div>
124
- <div class="body-card" ?hidden=${this.collapsible && this.collapsed}>
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
  `;
@@ -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;;QAoEL,gEAAgE;QAC1B,SAAI,GAAoB,OAAO,CAAC;QACtE,6FAA6F;QAChE,YAAO,GAAuB,QAAQ,CAAC;QACpE,kDAAkD;QACtC,WAAM,GAAG,EAAE,CAAC;QACxB,sEAAsE;QAC1D,cAAS,GAAkB,IAAI,CAAC;QAC5C,+FAA+F;QACnF,YAAO,GAAG,EAAE,CAAC;QACzB,iEAAiE;QACpC,gBAAW,GAAG,KAAK,CAAC;QACjD,0DAA0D;QACd,cAAS,GAAG,KAAK,CAAC;IAyBhE,CAAC;aAzGiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA8DF;KACF,AAjEqB,CAiEpB;IAiBM,OAAO;QACb,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO;QAC9B,IAAI,CAAC,SAAS,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;QACjC,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC;IAC3F,CAAC;IAEQ,MAAM;QACb,OAAO,IAAI,CAAA;2BACY,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,YAAY,IAAI,CAAC,OAAO;UAC5E,IAAI,CAAC,WAAW;YAChB,CAAC,CAAC,IAAI,CAAA,wBAAwB,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU;iBACvD,gBAAgB,CAAC,EAAE,CAAC;cACvB;YACJ,CAAC,CAAC,OAAO;UACT,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA,wBAAwB,IAAI,CAAC,MAAM,SAAS,CAAC,CAAC,CAAC,OAAO;UACxE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAA,2BAA2B,IAAI,CAAC,SAAS,mBAAmB,CAAC,CAAC,CAAC,OAAO;UAC3F,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAA,yBAAyB,IAAI,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,OAAO;;uCAE9C,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,SAAS;;;KAGpE,CAAC;IACJ,CAAC;CACF,CAAA;AArCuC;IAArC,QAAQ,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;yCAA0C;AAEzC;IAA5B,QAAQ,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;4CAAwC;AAExD;IAAX,QAAQ,EAAE;2CAAa;AAEZ;IAAX,QAAQ,EAAE;8CAAiC;AAEhC;IAAX,QAAQ,EAAE;4CAAc;AAEI;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gDAAqB;AAEL;IAA3C,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;8CAAmB;AAjFnD,WAAW;IADvB,aAAa,CAAC,cAAc,CAAC;GACjB,WAAW,CA0GvB","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(--ui-font, ui-sans-serif, system-ui, sans-serif);\n font-size: var(--ui-font-size, 0.875rem);\n }\n .header {\n display: flex;\n align-items: center;\n gap: 0.4rem;\n font-size: 0.75rem;\n color: var(--ui-text-muted, #64748b);\n margin-bottom: 0.3rem;\n }\n .header.clickable {\n cursor: pointer;\n }\n .chevron {\n display: inline-flex;\n transition: transform 0.15s 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.6rem 0.8rem;\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, #fff));\n }\n :host([role=\"agent\"]) .body-card {\n background: var(--ui-surface, #fff);\n border: 1px solid var(--ui-border, #e2e8f0);\n }\n :host([role=\"system\"]) .body-card {\n padding: 0 0 0 1.1rem;\n font-size: 0.8rem;\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: 0.8rem;\n }\n :host([variant=\"tool\"]) .body-card {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\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 return html`\n <div class=\"header ${this.collapsible ? \"clickable\" : \"\"}\" @click=${this._toggle}>\n ${this.collapsible\n ? html`<span class=\"chevron ${this.collapsed ? \"\" : \"expanded\"}\"\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 </div>\n <div 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"]}
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"]}
@@ -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;AAKrD;;;;;;;;;;GAUG;AACH,qBACa,aAAc,SAAQ,UAAU;IAC3C,OAAgB,MAAM,4BA2GpB;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,QAAQ;IAIhB,OAAO,CAAC,OAAO;IAIN,MAAM,yCAed;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,gBAAgB,EAAE,aAAa,CAAC;KACjC;CACF"}
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"}
@@ -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 20px 20px;
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, #fff);
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: 420px;
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(--ui-font, ui-sans-serif, system-ui, sans-serif);
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.6rem;
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 0.9rem;
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 0.9rem;
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.35rem;
146
+ gap: 0.25rem;
104
147
  font-family: inherit;
105
148
  }
106
149
  .btn-danger {
107
150
  background: var(--ui-danger, #dc2626);
108
- color: #fff;
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: #fff;
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.4rem 0 0.8rem;
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 (max-width: 768px) {
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: 12px;
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 class="dialog">
156
- <p class="dialog-text"><slot></slot></p>
157
- ${this.error ? html `<p class="error-text">${this.error}</p>` : nothing}
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 ? html `<span class="spin">${iconArrowPath(14)}</span>` : nothing} ${this.confirmLabel}
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>