@f-ewald/components 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +34 -0
  2. package/custom-elements.json +424 -1
  3. package/dist/dropdown-button.d.ts +37 -0
  4. package/dist/dropdown-button.d.ts.map +1 -0
  5. package/dist/dropdown-button.js +219 -0
  6. package/dist/dropdown-button.js.map +1 -0
  7. package/dist/form-select.d.ts +6 -0
  8. package/dist/form-select.d.ts.map +1 -1
  9. package/dist/form-select.js +9 -0
  10. package/dist/form-select.js.map +1 -1
  11. package/dist/frame-box.d.ts +22 -0
  12. package/dist/frame-box.d.ts.map +1 -0
  13. package/dist/frame-box.js +68 -0
  14. package/dist/frame-box.js.map +1 -0
  15. package/dist/icon-button.d.ts +27 -0
  16. package/dist/icon-button.d.ts.map +1 -0
  17. package/dist/icon-button.js +83 -0
  18. package/dist/icon-button.js.map +1 -0
  19. package/dist/icons.d.ts +1 -0
  20. package/dist/icons.d.ts.map +1 -1
  21. package/dist/icons.js +1 -0
  22. package/dist/icons.js.map +1 -1
  23. package/dist/index.d.ts +3 -0
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +3 -0
  26. package/dist/index.js.map +1 -1
  27. package/dist/mcp-server.d.ts +3 -0
  28. package/dist/mcp-server.d.ts.map +1 -0
  29. package/dist/mcp-server.js +82 -0
  30. package/dist/mcp-server.js.map +1 -0
  31. package/dist/popover-panel.d.ts +3 -0
  32. package/dist/popover-panel.d.ts.map +1 -1
  33. package/dist/popover-panel.js +11 -0
  34. package/dist/popover-panel.js.map +1 -1
  35. package/docs/dropdown-button.md +61 -0
  36. package/docs/form-select.md +6 -0
  37. package/docs/frame-box.md +43 -0
  38. package/docs/icon-button.md +52 -0
  39. package/docs/mcp-evaluation.md +18 -11
  40. package/docs/popover-panel.md +5 -1
  41. package/llms.txt +77 -0
  42. package/package.json +10 -3
@@ -0,0 +1,219 @@
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, state } from "lit/decorators.js";
9
+ import { iconChevronRight } from "./icons.js";
10
+ import { tokens } from "./tokens.js";
11
+ /**
12
+ * A primary-styled button with a label and chevron that opens an anchored
13
+ * menu of actions — essentially `form-select` minus "current value"
14
+ * semantics: a menu, not a select. Use for a set of mutually exclusive
15
+ * next-step actions (e.g. a failed task's Retry / Close / Backlog).
16
+ *
17
+ * @element dropdown-button
18
+ * @fires select - Fired with `{ value: string }` when a menu item is picked.
19
+ */
20
+ let DropdownButton = class DropdownButton extends LitElement {
21
+ constructor() {
22
+ super(...arguments);
23
+ /** The trigger button's label. */
24
+ this.label = "";
25
+ /** The menu's actions. */
26
+ this.options = [];
27
+ /** Disables the trigger, preventing the menu from opening. */
28
+ this.disabled = false;
29
+ this._open = false;
30
+ this._activeIndex = -1;
31
+ this.#onWindowMousedown = (e) => {
32
+ if (!e.composedPath().includes(this))
33
+ this._open = false;
34
+ };
35
+ }
36
+ static { this.styles = [
37
+ tokens,
38
+ css `
39
+ :host {
40
+ display: inline-block;
41
+ position: relative;
42
+ font-family: var(--ui-font, ui-sans-serif, system-ui, sans-serif);
43
+ }
44
+ button.trigger {
45
+ display: flex;
46
+ align-items: center;
47
+ gap: 0.35rem;
48
+ font: inherit;
49
+ font-size: var(--ui-font-size-sm, 0.75rem);
50
+ font-weight: 500;
51
+ color: #fff;
52
+ background: var(--ui-primary, #4f46e5);
53
+ border: 1px solid transparent;
54
+ border-radius: var(--ui-radius-sm, 0.25rem);
55
+ padding: 0.5rem 0.9rem;
56
+ cursor: pointer;
57
+ }
58
+ button.trigger:hover:not(:disabled) {
59
+ background: var(--ui-primary-hover, #4338ca);
60
+ }
61
+ button.trigger:disabled {
62
+ cursor: not-allowed;
63
+ opacity: 0.6;
64
+ }
65
+ button.trigger:focus-visible {
66
+ outline: none;
67
+ box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));
68
+ }
69
+ .chevron {
70
+ display: flex;
71
+ transform: rotate(90deg);
72
+ transition: transform 0.1s ease;
73
+ }
74
+ :host([open]) .chevron {
75
+ transform: rotate(-90deg);
76
+ }
77
+ ul.options {
78
+ position: absolute;
79
+ top: 100%;
80
+ right: 0;
81
+ z-index: 10;
82
+ min-width: 100%;
83
+ max-height: 40vh;
84
+ overflow-y: auto;
85
+ margin: 2px 0 0;
86
+ padding: 4px 0;
87
+ list-style: none;
88
+ white-space: nowrap;
89
+ background: var(--ui-surface, #fff);
90
+ border: 1px solid var(--ui-border, #e2e8f0);
91
+ border-radius: var(--ui-radius-sm, 0.25rem);
92
+ box-shadow: var(--ui-shadow, 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1));
93
+ }
94
+ li {
95
+ padding: 0.35rem 0.6rem;
96
+ cursor: pointer;
97
+ color: var(--ui-text, #0f172a);
98
+ }
99
+ li.active,
100
+ li:hover {
101
+ background: var(--ui-surface-muted, #f8fafc);
102
+ }
103
+ `,
104
+ ]; }
105
+ disconnectedCallback() {
106
+ super.disconnectedCallback();
107
+ window.removeEventListener("mousedown", this.#onWindowMousedown, true);
108
+ }
109
+ updated(changed) {
110
+ if (!changed.has("_open"))
111
+ return;
112
+ this.toggleAttribute("open", this._open);
113
+ if (this._open) {
114
+ window.addEventListener("mousedown", this.#onWindowMousedown, true);
115
+ }
116
+ else {
117
+ window.removeEventListener("mousedown", this.#onWindowMousedown, true);
118
+ }
119
+ }
120
+ #onWindowMousedown;
121
+ #toggle() {
122
+ if (this.disabled)
123
+ return;
124
+ this._open = !this._open;
125
+ if (this._open)
126
+ this._activeIndex = 0;
127
+ }
128
+ #select(option) {
129
+ this._open = false;
130
+ this.dispatchEvent(new CustomEvent("select", { detail: { value: option.value } }));
131
+ }
132
+ #onTriggerKeydown(e) {
133
+ if (e.key === "ArrowDown" || e.key === "ArrowUp" || e.key === "Enter" || e.key === " ") {
134
+ e.preventDefault();
135
+ if (!this._open) {
136
+ this.#toggle();
137
+ return;
138
+ }
139
+ if (e.key === "ArrowDown")
140
+ this.#moveActive(1);
141
+ else if (e.key === "ArrowUp")
142
+ this.#moveActive(-1);
143
+ else
144
+ this.#confirmActive();
145
+ }
146
+ else if (e.key === "Escape" && this._open) {
147
+ e.preventDefault();
148
+ this._open = false;
149
+ }
150
+ }
151
+ #moveActive(delta) {
152
+ if (this.options.length === 0)
153
+ return;
154
+ const n = this.options.length;
155
+ this._activeIndex = (this._activeIndex + delta + n) % n;
156
+ }
157
+ #confirmActive() {
158
+ const option = this.options[this._activeIndex];
159
+ if (option)
160
+ this.#select(option);
161
+ }
162
+ renderMenu() {
163
+ if (!this._open)
164
+ return nothing;
165
+ return html `
166
+ <ul class="options" role="menu">
167
+ ${this.options.map((o, i) => html `
168
+ <li
169
+ role="menuitem"
170
+ class=${i === this._activeIndex ? "active" : ""}
171
+ @mousedown=${(e) => {
172
+ e.preventDefault();
173
+ this.#select(o);
174
+ }}
175
+ >
176
+ ${o.label}
177
+ </li>
178
+ `)}
179
+ </ul>
180
+ `;
181
+ }
182
+ render() {
183
+ return html `
184
+ <button
185
+ type="button"
186
+ class="trigger"
187
+ aria-haspopup="menu"
188
+ aria-expanded=${this._open}
189
+ ?disabled=${this.disabled}
190
+ @click=${() => this.#toggle()}
191
+ @keydown=${(e) => this.#onTriggerKeydown(e)}
192
+ >
193
+ <span>${this.label}</span>
194
+ <span class="chevron">${iconChevronRight(14)}</span>
195
+ </button>
196
+ ${this.renderMenu()}
197
+ `;
198
+ }
199
+ };
200
+ __decorate([
201
+ property()
202
+ ], DropdownButton.prototype, "label", void 0);
203
+ __decorate([
204
+ property({ attribute: false })
205
+ ], DropdownButton.prototype, "options", void 0);
206
+ __decorate([
207
+ property({ type: Boolean })
208
+ ], DropdownButton.prototype, "disabled", void 0);
209
+ __decorate([
210
+ state()
211
+ ], DropdownButton.prototype, "_open", void 0);
212
+ __decorate([
213
+ state()
214
+ ], DropdownButton.prototype, "_activeIndex", void 0);
215
+ DropdownButton = __decorate([
216
+ customElement("dropdown-button")
217
+ ], DropdownButton);
218
+ export { DropdownButton };
219
+ //# sourceMappingURL=dropdown-button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dropdown-button.js","sourceRoot":"","sources":["../src/dropdown-button.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, css, html, nothing, type PropertyValues } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { iconChevronRight } from \"./icons.js\";\nimport { tokens } from \"./tokens.js\";\n\n/** A single menu action. */\nexport interface DropdownOption {\n value: string;\n label: string;\n}\n\n/**\n * A primary-styled button with a label and chevron that opens an anchored\n * menu of actions — essentially `form-select` minus \"current value\"\n * semantics: a menu, not a select. Use for a set of mutually exclusive\n * next-step actions (e.g. a failed task's Retry / Close / Backlog).\n *\n * @element dropdown-button\n * @fires select - Fired with `{ value: string }` when a menu item is picked.\n */\n@customElement(\"dropdown-button\")\nexport class DropdownButton extends LitElement {\n static override styles = [\n tokens,\n css`\n :host {\n display: inline-block;\n position: relative;\n font-family: var(--ui-font, ui-sans-serif, system-ui, sans-serif);\n }\n button.trigger {\n display: flex;\n align-items: center;\n gap: 0.35rem;\n font: inherit;\n font-size: var(--ui-font-size-sm, 0.75rem);\n font-weight: 500;\n color: #fff;\n background: var(--ui-primary, #4f46e5);\n border: 1px solid transparent;\n border-radius: var(--ui-radius-sm, 0.25rem);\n padding: 0.5rem 0.9rem;\n cursor: pointer;\n }\n button.trigger:hover:not(:disabled) {\n background: var(--ui-primary-hover, #4338ca);\n }\n button.trigger:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n }\n button.trigger:focus-visible {\n outline: none;\n box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));\n }\n .chevron {\n display: flex;\n transform: rotate(90deg);\n transition: transform 0.1s ease;\n }\n :host([open]) .chevron {\n transform: rotate(-90deg);\n }\n ul.options {\n position: absolute;\n top: 100%;\n right: 0;\n z-index: 10;\n min-width: 100%;\n max-height: 40vh;\n overflow-y: auto;\n margin: 2px 0 0;\n padding: 4px 0;\n list-style: none;\n white-space: nowrap;\n background: var(--ui-surface, #fff);\n border: 1px solid var(--ui-border, #e2e8f0);\n border-radius: var(--ui-radius-sm, 0.25rem);\n box-shadow: var(--ui-shadow, 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1));\n }\n li {\n padding: 0.35rem 0.6rem;\n cursor: pointer;\n color: var(--ui-text, #0f172a);\n }\n li.active,\n li:hover {\n background: var(--ui-surface-muted, #f8fafc);\n }\n `,\n ];\n\n /** The trigger button's label. */\n @property() label = \"\";\n /** The menu's actions. */\n @property({ attribute: false }) options: DropdownOption[] = [];\n /** Disables the trigger, preventing the menu from opening. */\n @property({ type: Boolean }) disabled = false;\n\n @state() private _open = false;\n @state() private _activeIndex = -1;\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n window.removeEventListener(\"mousedown\", this.#onWindowMousedown, true);\n }\n\n protected override updated(changed: PropertyValues): void {\n if (!changed.has(\"_open\")) return;\n this.toggleAttribute(\"open\", this._open);\n if (this._open) {\n window.addEventListener(\"mousedown\", this.#onWindowMousedown, true);\n } else {\n window.removeEventListener(\"mousedown\", this.#onWindowMousedown, true);\n }\n }\n\n #onWindowMousedown = (e: MouseEvent): void => {\n if (!e.composedPath().includes(this)) this._open = false;\n };\n\n #toggle(): void {\n if (this.disabled) return;\n this._open = !this._open;\n if (this._open) this._activeIndex = 0;\n }\n\n #select(option: DropdownOption): void {\n this._open = false;\n this.dispatchEvent(new CustomEvent(\"select\", { detail: { value: option.value } }));\n }\n\n #onTriggerKeydown(e: KeyboardEvent): void {\n if (e.key === \"ArrowDown\" || e.key === \"ArrowUp\" || e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n if (!this._open) {\n this.#toggle();\n return;\n }\n if (e.key === \"ArrowDown\") this.#moveActive(1);\n else if (e.key === \"ArrowUp\") this.#moveActive(-1);\n else this.#confirmActive();\n } else if (e.key === \"Escape\" && this._open) {\n e.preventDefault();\n this._open = false;\n }\n }\n\n #moveActive(delta: number): void {\n if (this.options.length === 0) return;\n const n = this.options.length;\n this._activeIndex = (this._activeIndex + delta + n) % n;\n }\n\n #confirmActive(): void {\n const option = this.options[this._activeIndex];\n if (option) this.#select(option);\n }\n\n private renderMenu() {\n if (!this._open) return nothing;\n return html`\n <ul class=\"options\" role=\"menu\">\n ${this.options.map(\n (o, i) => html`\n <li\n role=\"menuitem\"\n class=${i === this._activeIndex ? \"active\" : \"\"}\n @mousedown=${(e: MouseEvent) => {\n e.preventDefault();\n this.#select(o);\n }}\n >\n ${o.label}\n </li>\n `,\n )}\n </ul>\n `;\n }\n\n override render() {\n return html`\n <button\n type=\"button\"\n class=\"trigger\"\n aria-haspopup=\"menu\"\n aria-expanded=${this._open}\n ?disabled=${this.disabled}\n @click=${() => this.#toggle()}\n @keydown=${(e: KeyboardEvent) => this.#onTriggerKeydown(e)}\n >\n <span>${this.label}</span>\n <span class=\"chevron\">${iconChevronRight(14)}</span>\n </button>\n ${this.renderMenu()}\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"dropdown-button\": DropdownButton;\n }\n}\n"]}
@@ -11,6 +11,12 @@ export interface SelectOption {
11
11
  * `change` event carrying `{ value }` are wanted (e.g. a task's status
12
12
  * picker).
13
13
  *
14
+ * The trigger fills its host's width (`justify-content: space-between`
15
+ * pushes the chevron to the far edge), but the host itself stays
16
+ * `display: inline-block` — so usages that never size the host (a filter
17
+ * bar, a status picker) keep shrink-to-fit auto-width unchanged. To make an
18
+ * instance full-width, size the host itself: `form-select { width: 100%; }`.
19
+ *
14
20
  * @element form-select
15
21
  * @fires change - Fired with `{ value: string }` when a different option is picked.
16
22
  */
@@ -1 +1 @@
1
- {"version":3,"file":"form-select.d.ts","sourceRoot":"","sources":["../src/form-select.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAK1E,kCAAkC;AAClC,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;GASG;AACH,qBACa,UAAW,SAAQ,UAAU;;IACxC,OAAgB,MAAM,4BAwEpB;IAEF,2CAA2C;IACX,OAAO,EAAE,YAAY,EAAE,CAAM;IAC7D,qEAAqE;IACzD,KAAK,SAAM;IACvB,kDAAkD;IACtC,KAAK,SAAM;IACvB,iEAAiE;IACpC,QAAQ,UAAS;IAErC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,YAAY,CAAM;IAE1B,oBAAoB,IAAI,IAAI,CAGpC;IAED,UAAmB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAQxD;IAmDD,OAAO,CAAC,aAAa;IAuBZ,MAAM,yCAkBd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,aAAa,EAAE,UAAU,CAAC;KAC3B;CACF"}
1
+ {"version":3,"file":"form-select.d.ts","sourceRoot":"","sources":["../src/form-select.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAK1E,kCAAkC;AAClC,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBACa,UAAW,SAAQ,UAAU;;IACxC,OAAgB,MAAM,4BA2EpB;IAEF,2CAA2C;IACX,OAAO,EAAE,YAAY,EAAE,CAAM;IAC7D,qEAAqE;IACzD,KAAK,SAAM;IACvB,kDAAkD;IACtC,KAAK,SAAM;IACvB,iEAAiE;IACpC,QAAQ,UAAS;IAErC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,YAAY,CAAM;IAE1B,oBAAoB,IAAI,IAAI,CAGpC;IAED,UAAmB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAQxD;IAmDD,OAAO,CAAC,aAAa;IAuBZ,MAAM,yCAkBd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,aAAa,EAAE,UAAU,CAAC;KAC3B;CACF"}
@@ -15,6 +15,12 @@ import { tokens } from "./tokens.js";
15
15
  * `change` event carrying `{ value }` are wanted (e.g. a task's status
16
16
  * picker).
17
17
  *
18
+ * The trigger fills its host's width (`justify-content: space-between`
19
+ * pushes the chevron to the far edge), but the host itself stays
20
+ * `display: inline-block` — so usages that never size the host (a filter
21
+ * bar, a status picker) keep shrink-to-fit auto-width unchanged. To make an
22
+ * instance full-width, size the host itself: `form-select { width: 100%; }`.
23
+ *
18
24
  * @element form-select
19
25
  * @fires change - Fired with `{ value: string }` when a different option is picked.
20
26
  */
@@ -48,7 +54,10 @@ let FormSelect = class FormSelect extends LitElement {
48
54
  button.trigger {
49
55
  display: flex;
50
56
  align-items: center;
57
+ justify-content: space-between;
51
58
  gap: 0.35rem;
59
+ width: 100%;
60
+ box-sizing: border-box;
52
61
  font: inherit;
53
62
  color: var(--ui-text, #0f172a);
54
63
  background: var(--ui-surface, #fff);
@@ -1 +1 @@
1
- {"version":3,"file":"form-select.js","sourceRoot":"","sources":["../src/form-select.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, css, html, nothing, type PropertyValues } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { iconChevronRight } from \"./icons.js\";\nimport { tokens } from \"./tokens.js\";\n\n/** A single selectable option. */\nexport interface SelectOption {\n value: string;\n label: string;\n}\n\n/**\n * A styled dropdown select: a trigger button showing the current option's\n * label, opening a listbox popover on click. Drop-in generic replacement for\n * a native `<select>` wherever consistent cross-browser styling and a\n * `change` event carrying `{ value }` are wanted (e.g. a task's status\n * picker).\n *\n * @element form-select\n * @fires change - Fired with `{ value: string }` when a different option is picked.\n */\n@customElement(\"form-select\")\nexport class FormSelect extends LitElement {\n static override styles = [\n tokens,\n css`\n :host {\n display: inline-block;\n position: relative;\n font-family: var(--ui-font, ui-sans-serif, system-ui, sans-serif);\n font-size: var(--ui-font-size-sm, 0.75rem);\n }\n button.trigger {\n display: flex;\n align-items: center;\n gap: 0.35rem;\n font: inherit;\n color: var(--ui-text, #0f172a);\n background: var(--ui-surface, #fff);\n border: 1px solid var(--ui-border, #e2e8f0);\n border-radius: var(--ui-radius-sm, 0.25rem);\n padding: 0.3rem 0.5rem;\n cursor: pointer;\n }\n button.trigger:hover {\n background: var(--ui-surface-muted, #f8fafc);\n }\n button.trigger:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n }\n button.trigger:focus-visible {\n outline: none;\n border-color: var(--ui-primary, #4f46e5);\n box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));\n }\n .chevron {\n display: flex;\n color: var(--ui-text-muted, #64748b);\n transform: rotate(90deg);\n transition: transform 0.1s ease;\n }\n :host([open]) .chevron {\n transform: rotate(-90deg);\n }\n ul.options {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 10;\n min-width: 100%;\n max-height: 40vh;\n overflow-y: auto;\n margin: 2px 0 0;\n padding: 4px 0;\n list-style: none;\n white-space: nowrap;\n background: var(--ui-surface, #fff);\n border: 1px solid var(--ui-border, #e2e8f0);\n border-radius: var(--ui-radius-sm, 0.25rem);\n box-shadow: var(--ui-shadow, 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1));\n }\n li {\n padding: 0.35rem 0.6rem;\n cursor: pointer;\n }\n li.active,\n li:hover {\n background: var(--ui-surface-muted, #f8fafc);\n }\n li[aria-selected=\"true\"] {\n font-weight: 600;\n color: var(--ui-primary, #4f46e5);\n }\n `,\n ];\n\n /** The full list of selectable options. */\n @property({ attribute: false }) options: SelectOption[] = [];\n /** Currently selected value; must match one of `options[].value`. */\n @property() value = \"\";\n /** `aria-label` applied to the trigger button. */\n @property() label = \"\";\n /** Disables the trigger, preventing the popover from opening. */\n @property({ type: Boolean }) disabled = false;\n\n @state() private _open = false;\n @state() private _activeIndex = -1;\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n window.removeEventListener(\"mousedown\", this.#onWindowMousedown, true);\n }\n\n protected override updated(changed: PropertyValues): void {\n if (!changed.has(\"_open\")) return;\n this.toggleAttribute(\"open\", this._open);\n if (this._open) {\n window.addEventListener(\"mousedown\", this.#onWindowMousedown, true);\n } else {\n window.removeEventListener(\"mousedown\", this.#onWindowMousedown, true);\n }\n }\n\n #onWindowMousedown = (e: MouseEvent): void => {\n if (!e.composedPath().includes(this)) this._open = false;\n };\n\n #toggle(): void {\n if (this.disabled) return;\n this._open = !this._open;\n if (this._open) {\n this._activeIndex = Math.max(\n 0,\n this.options.findIndex((o) => o.value === this.value),\n );\n }\n }\n\n #select(option: SelectOption): void {\n this._open = false;\n if (option.value === this.value) return;\n this.value = option.value;\n this.dispatchEvent(new CustomEvent(\"change\", { detail: { value: option.value } }));\n }\n\n #onTriggerKeydown(e: KeyboardEvent): void {\n if (e.key === \"ArrowDown\" || e.key === \"ArrowUp\" || e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n if (!this._open) {\n this.#toggle();\n return;\n }\n if (e.key === \"ArrowDown\") this.#moveActive(1);\n else if (e.key === \"ArrowUp\") this.#moveActive(-1);\n else this.#confirmActive();\n } else if (e.key === \"Escape\" && this._open) {\n e.preventDefault();\n this._open = false;\n }\n }\n\n #moveActive(delta: number): void {\n if (this.options.length === 0) return;\n const n = this.options.length;\n this._activeIndex = (this._activeIndex + delta + n) % n;\n }\n\n #confirmActive(): void {\n const option = this.options[this._activeIndex];\n if (option) this.#select(option);\n }\n\n private renderListbox() {\n if (!this._open) return nothing;\n return html`\n <ul class=\"options\" role=\"listbox\">\n ${this.options.map(\n (o, i) => html`\n <li\n role=\"option\"\n aria-selected=${o.value === this.value}\n class=${i === this._activeIndex ? \"active\" : \"\"}\n @mousedown=${(e: MouseEvent) => {\n e.preventDefault();\n this.#select(o);\n }}\n >\n ${o.label}\n </li>\n `,\n )}\n </ul>\n `;\n }\n\n override render() {\n const current = this.options.find((o) => o.value === this.value);\n return html`\n <button\n type=\"button\"\n class=\"trigger\"\n aria-haspopup=\"listbox\"\n aria-expanded=${this._open}\n aria-label=${this.label || nothing}\n ?disabled=${this.disabled}\n @click=${() => this.#toggle()}\n @keydown=${(e: KeyboardEvent) => this.#onTriggerKeydown(e)}\n >\n <span>${current?.label ?? this.value}</span>\n <span class=\"chevron\">${iconChevronRight(14)}</span>\n </button>\n ${this.renderListbox()}\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"form-select\": FormSelect;\n }\n}\n"]}
1
+ {"version":3,"file":"form-select.js","sourceRoot":"","sources":["../src/form-select.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAuB,MAAM,KAAK,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAQrC;;;;;;;;;;;;;;;GAeG;AAEI,IAAM,UAAU,GAAhB,MAAM,UAAW,SAAQ,UAAU;IAAnC;;QA8EL,2CAA2C;QACX,YAAO,GAAmB,EAAE,CAAC;QAC7D,qEAAqE;QACzD,UAAK,GAAG,EAAE,CAAC;QACvB,kDAAkD;QACtC,UAAK,GAAG,EAAE,CAAC;QACvB,iEAAiE;QACpC,aAAQ,GAAG,KAAK,CAAC;QAE7B,UAAK,GAAG,KAAK,CAAC;QACd,iBAAY,GAAG,CAAC,CAAC,CAAC;QAiBnC,uBAAkB,GAAG,CAAC,CAAa,EAAQ,EAAE;YAC3C,IAAI,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QAC3D,CAAC,CAAC;IAyFJ,CAAC;aAnMiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAwEF;KACF,AA3EqB,CA2EpB;IAcO,oBAAoB;QAC3B,KAAK,CAAC,oBAAoB,EAAE,CAAC;QAC7B,MAAM,CAAC,mBAAmB,CAAC,WAAW,EAAE,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;IACzE,CAAC;IAEkB,OAAO,CAAC,OAAuB;QAChD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,OAAO;QAClC,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACzC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,gBAAgB,CAAC,WAAW,EAAE,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;QACtE,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,mBAAmB,CAAC,WAAW,EAAE,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;IAED,kBAAkB,CAEhB;IAEF,OAAO;QACL,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,IAAI,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC;QACzB,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,CAC1B,CAAC,EACD,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,CAAC,CACtD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,CAAC,MAAoB;QAC1B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK;YAAE,OAAO;QACxC,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QAC1B,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;IACrF,CAAC;IAED,iBAAiB,CAAC,CAAgB;QAChC,IAAI,CAAC,CAAC,GAAG,KAAK,WAAW,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;YACvF,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;gBAChB,IAAI,CAAC,OAAO,EAAE,CAAC;gBACf,OAAO;YACT,CAAC;YACD,IAAI,CAAC,CAAC,GAAG,KAAK,WAAW;gBAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;iBAC1C,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS;gBAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;;gBAC9C,IAAI,CAAC,cAAc,EAAE,CAAC;QAC7B,CAAC;aAAM,IAAI,CAAC,CAAC,GAAG,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC5C,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACrB,CAAC;IACH,CAAC;IAED,WAAW,CAAC,KAAa;QACvB,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACtC,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QAC9B,IAAI,CAAC,YAAY,GAAG,CAAC,IAAI,CAAC,YAAY,GAAG,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAC1D,CAAC;IAED,cAAc;QACZ,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC/C,IAAI,MAAM;YAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IAEO,aAAa;QACnB,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAO,OAAO,CAAC;QAChC,OAAO,IAAI,CAAA;;UAEL,IAAI,CAAC,OAAO,CAAC,GAAG,CAChB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAA;;;8BAGM,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK;sBAC9B,CAAC,KAAK,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;2BAClC,CAAC,CAAa,EAAE,EAAE;YAC7B,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;;gBAEC,CAAC,CAAC,KAAK;;WAEZ,CACF;;KAEJ,CAAC;IACJ,CAAC;IAEQ,MAAM;QACb,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC;QACjE,OAAO,IAAI,CAAA;;;;;wBAKS,IAAI,CAAC,KAAK;qBACb,IAAI,CAAC,KAAK,IAAI,OAAO;oBACtB,IAAI,CAAC,QAAQ;iBAChB,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE;mBAClB,CAAC,CAAgB,EAAE,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC;;gBAElD,OAAO,EAAE,KAAK,IAAI,IAAI,CAAC,KAAK;gCACZ,gBAAgB,CAAC,EAAE,CAAC;;QAE5C,IAAI,CAAC,aAAa,EAAE;KACvB,CAAC;IACJ,CAAC;CACF,CAAA;AArHiC;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;2CAA8B;AAEjD;IAAX,QAAQ,EAAE;yCAAY;AAEX;IAAX,QAAQ,EAAE;yCAAY;AAEM;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;4CAAkB;AAE7B;IAAhB,KAAK,EAAE;yCAAuB;AACd;IAAhB,KAAK,EAAE;gDAA2B;AAxFxB,UAAU;IADtB,aAAa,CAAC,aAAa,CAAC;GAChB,UAAU,CAoMtB","sourcesContent":["import { LitElement, css, html, nothing, type PropertyValues } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { iconChevronRight } from \"./icons.js\";\nimport { tokens } from \"./tokens.js\";\n\n/** A single selectable option. */\nexport interface SelectOption {\n value: string;\n label: string;\n}\n\n/**\n * A styled dropdown select: a trigger button showing the current option's\n * label, opening a listbox popover on click. Drop-in generic replacement for\n * a native `<select>` wherever consistent cross-browser styling and a\n * `change` event carrying `{ value }` are wanted (e.g. a task's status\n * picker).\n *\n * The trigger fills its host's width (`justify-content: space-between`\n * pushes the chevron to the far edge), but the host itself stays\n * `display: inline-block` — so usages that never size the host (a filter\n * bar, a status picker) keep shrink-to-fit auto-width unchanged. To make an\n * instance full-width, size the host itself: `form-select { width: 100%; }`.\n *\n * @element form-select\n * @fires change - Fired with `{ value: string }` when a different option is picked.\n */\n@customElement(\"form-select\")\nexport class FormSelect extends LitElement {\n static override styles = [\n tokens,\n css`\n :host {\n display: inline-block;\n position: relative;\n font-family: var(--ui-font, ui-sans-serif, system-ui, sans-serif);\n font-size: var(--ui-font-size-sm, 0.75rem);\n }\n button.trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.35rem;\n width: 100%;\n box-sizing: border-box;\n font: inherit;\n color: var(--ui-text, #0f172a);\n background: var(--ui-surface, #fff);\n border: 1px solid var(--ui-border, #e2e8f0);\n border-radius: var(--ui-radius-sm, 0.25rem);\n padding: 0.3rem 0.5rem;\n cursor: pointer;\n }\n button.trigger:hover {\n background: var(--ui-surface-muted, #f8fafc);\n }\n button.trigger:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n }\n button.trigger:focus-visible {\n outline: none;\n border-color: var(--ui-primary, #4f46e5);\n box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));\n }\n .chevron {\n display: flex;\n color: var(--ui-text-muted, #64748b);\n transform: rotate(90deg);\n transition: transform 0.1s ease;\n }\n :host([open]) .chevron {\n transform: rotate(-90deg);\n }\n ul.options {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 10;\n min-width: 100%;\n max-height: 40vh;\n overflow-y: auto;\n margin: 2px 0 0;\n padding: 4px 0;\n list-style: none;\n white-space: nowrap;\n background: var(--ui-surface, #fff);\n border: 1px solid var(--ui-border, #e2e8f0);\n border-radius: var(--ui-radius-sm, 0.25rem);\n box-shadow: var(--ui-shadow, 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1));\n }\n li {\n padding: 0.35rem 0.6rem;\n cursor: pointer;\n }\n li.active,\n li:hover {\n background: var(--ui-surface-muted, #f8fafc);\n }\n li[aria-selected=\"true\"] {\n font-weight: 600;\n color: var(--ui-primary, #4f46e5);\n }\n `,\n ];\n\n /** The full list of selectable options. */\n @property({ attribute: false }) options: SelectOption[] = [];\n /** Currently selected value; must match one of `options[].value`. */\n @property() value = \"\";\n /** `aria-label` applied to the trigger button. */\n @property() label = \"\";\n /** Disables the trigger, preventing the popover from opening. */\n @property({ type: Boolean }) disabled = false;\n\n @state() private _open = false;\n @state() private _activeIndex = -1;\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n window.removeEventListener(\"mousedown\", this.#onWindowMousedown, true);\n }\n\n protected override updated(changed: PropertyValues): void {\n if (!changed.has(\"_open\")) return;\n this.toggleAttribute(\"open\", this._open);\n if (this._open) {\n window.addEventListener(\"mousedown\", this.#onWindowMousedown, true);\n } else {\n window.removeEventListener(\"mousedown\", this.#onWindowMousedown, true);\n }\n }\n\n #onWindowMousedown = (e: MouseEvent): void => {\n if (!e.composedPath().includes(this)) this._open = false;\n };\n\n #toggle(): void {\n if (this.disabled) return;\n this._open = !this._open;\n if (this._open) {\n this._activeIndex = Math.max(\n 0,\n this.options.findIndex((o) => o.value === this.value),\n );\n }\n }\n\n #select(option: SelectOption): void {\n this._open = false;\n if (option.value === this.value) return;\n this.value = option.value;\n this.dispatchEvent(new CustomEvent(\"change\", { detail: { value: option.value } }));\n }\n\n #onTriggerKeydown(e: KeyboardEvent): void {\n if (e.key === \"ArrowDown\" || e.key === \"ArrowUp\" || e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n if (!this._open) {\n this.#toggle();\n return;\n }\n if (e.key === \"ArrowDown\") this.#moveActive(1);\n else if (e.key === \"ArrowUp\") this.#moveActive(-1);\n else this.#confirmActive();\n } else if (e.key === \"Escape\" && this._open) {\n e.preventDefault();\n this._open = false;\n }\n }\n\n #moveActive(delta: number): void {\n if (this.options.length === 0) return;\n const n = this.options.length;\n this._activeIndex = (this._activeIndex + delta + n) % n;\n }\n\n #confirmActive(): void {\n const option = this.options[this._activeIndex];\n if (option) this.#select(option);\n }\n\n private renderListbox() {\n if (!this._open) return nothing;\n return html`\n <ul class=\"options\" role=\"listbox\">\n ${this.options.map(\n (o, i) => html`\n <li\n role=\"option\"\n aria-selected=${o.value === this.value}\n class=${i === this._activeIndex ? \"active\" : \"\"}\n @mousedown=${(e: MouseEvent) => {\n e.preventDefault();\n this.#select(o);\n }}\n >\n ${o.label}\n </li>\n `,\n )}\n </ul>\n `;\n }\n\n override render() {\n const current = this.options.find((o) => o.value === this.value);\n return html`\n <button\n type=\"button\"\n class=\"trigger\"\n aria-haspopup=\"listbox\"\n aria-expanded=${this._open}\n aria-label=${this.label || nothing}\n ?disabled=${this.disabled}\n @click=${() => this.#toggle()}\n @keydown=${(e: KeyboardEvent) => this.#onTriggerKeydown(e)}\n >\n <span>${current?.label ?? this.value}</span>\n <span class=\"chevron\">${iconChevronRight(14)}</span>\n </button>\n ${this.renderListbox()}\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"form-select\": FormSelect;\n }\n}\n"]}
@@ -0,0 +1,22 @@
1
+ import { LitElement } from "lit";
2
+ /**
3
+ * A titled frame around a slot: a gray border with a small uppercase,
4
+ * muted label overlapping the top edge (fieldset/legend-style). Generic —
5
+ * the label text is entirely up to the consumer (e.g. "Debug" to visually
6
+ * fence off dev-only chrome from the product UI).
7
+ *
8
+ * @element frame-box
9
+ * @slot - Framed content.
10
+ */
11
+ export declare class FrameBox extends LitElement {
12
+ static styles: import("lit").CSSResult[];
13
+ /** The overlapping title label, e.g. "Debug". */
14
+ label: string;
15
+ render(): import("lit-html").TemplateResult<1>;
16
+ }
17
+ declare global {
18
+ interface HTMLElementTagNameMap {
19
+ "frame-box": FrameBox;
20
+ }
21
+ }
22
+ //# sourceMappingURL=frame-box.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame-box.d.ts","sourceRoot":"","sources":["../src/frame-box.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAI5C;;;;;;;;GAQG;AACH,qBACa,QAAS,SAAQ,UAAU;IACtC,OAAgB,MAAM,4BA0BpB;IAEF,iDAAiD;IACrC,KAAK,SAAM;IAEd,MAAM,yCAOd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,WAAW,EAAE,QAAQ,CAAC;KACvB;CACF"}
@@ -0,0 +1,68 @@
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 } from "lit";
8
+ import { customElement, property } from "lit/decorators.js";
9
+ import { tokens } from "./tokens.js";
10
+ /**
11
+ * A titled frame around a slot: a gray border with a small uppercase,
12
+ * muted label overlapping the top edge (fieldset/legend-style). Generic —
13
+ * the label text is entirely up to the consumer (e.g. "Debug" to visually
14
+ * fence off dev-only chrome from the product UI).
15
+ *
16
+ * @element frame-box
17
+ * @slot - Framed content.
18
+ */
19
+ let FrameBox = class FrameBox extends LitElement {
20
+ constructor() {
21
+ super(...arguments);
22
+ /** The overlapping title label, e.g. "Debug". */
23
+ this.label = "";
24
+ }
25
+ static { this.styles = [
26
+ tokens,
27
+ css `
28
+ :host {
29
+ display: block;
30
+ position: relative;
31
+ margin-top: 0.6rem;
32
+ }
33
+ .frame {
34
+ border: 1px solid var(--ui-border, #e2e8f0);
35
+ border-radius: var(--ui-radius-sm, 0.25rem);
36
+ padding: 1rem 0.75rem 0.75rem;
37
+ }
38
+ .label {
39
+ position: absolute;
40
+ top: -0.55rem;
41
+ left: 0.6rem;
42
+ padding: 0 0.4rem;
43
+ background: var(--ui-surface, #fff);
44
+ font-size: 0.65rem;
45
+ font-weight: 600;
46
+ text-transform: uppercase;
47
+ letter-spacing: 0.05em;
48
+ color: var(--ui-text-muted, #64748b);
49
+ }
50
+ `,
51
+ ]; }
52
+ render() {
53
+ return html `
54
+ <div class="frame">
55
+ ${this.label ? html `<span class="label">${this.label}</span>` : null}
56
+ <slot></slot>
57
+ </div>
58
+ `;
59
+ }
60
+ };
61
+ __decorate([
62
+ property()
63
+ ], FrameBox.prototype, "label", void 0);
64
+ FrameBox = __decorate([
65
+ customElement("frame-box")
66
+ ], FrameBox);
67
+ export { FrameBox };
68
+ //# sourceMappingURL=frame-box.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame-box.js","sourceRoot":"","sources":["../src/frame-box.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;;;;;GAQG;AAEI,IAAM,QAAQ,GAAd,MAAM,QAAS,SAAQ,UAAU;IAAjC;;QA6BL,iDAAiD;QACrC,UAAK,GAAG,EAAE,CAAC;IAUzB,CAAC;aAvCiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;KAuBF;KACF,AA1BqB,CA0BpB;IAKO,MAAM;QACb,OAAO,IAAI,CAAA;;UAEL,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA,uBAAuB,IAAI,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI;;;KAGvE,CAAC;IACJ,CAAC;CACF,CAAA;AAVa;IAAX,QAAQ,EAAE;uCAAY;AA9BZ,QAAQ;IADpB,aAAa,CAAC,WAAW,CAAC;GACd,QAAQ,CAwCpB","sourcesContent":["import { LitElement, css, html } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { tokens } from \"./tokens.js\";\n\n/**\n * A titled frame around a slot: a gray border with a small uppercase,\n * muted label overlapping the top edge (fieldset/legend-style). Generic —\n * the label text is entirely up to the consumer (e.g. \"Debug\" to visually\n * fence off dev-only chrome from the product UI).\n *\n * @element frame-box\n * @slot - Framed content.\n */\n@customElement(\"frame-box\")\nexport class FrameBox extends LitElement {\n static override styles = [\n tokens,\n css`\n :host {\n display: block;\n position: relative;\n margin-top: 0.6rem;\n }\n .frame {\n border: 1px solid var(--ui-border, #e2e8f0);\n border-radius: var(--ui-radius-sm, 0.25rem);\n padding: 1rem 0.75rem 0.75rem;\n }\n .label {\n position: absolute;\n top: -0.55rem;\n left: 0.6rem;\n padding: 0 0.4rem;\n background: var(--ui-surface, #fff);\n font-size: 0.65rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--ui-text-muted, #64748b);\n }\n `,\n ];\n\n /** The overlapping title label, e.g. \"Debug\". */\n @property() label = \"\";\n\n override render() {\n return html`\n <div class=\"frame\">\n ${this.label ? html`<span class=\"label\">${this.label}</span>` : null}\n <slot></slot>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"frame-box\": FrameBox;\n }\n}\n"]}
@@ -0,0 +1,27 @@
1
+ import { LitElement } from "lit";
2
+ import type { TemplateResult } from "lit";
3
+ /**
4
+ * A borderless button wrapping a passed-in icon, with a rounded
5
+ * hover-highlight background. Use for a low-emphasis affordance next to
6
+ * content it acts on (e.g. an "Edit" pencil at the end of a table row)
7
+ * where a bordered `ui-button` would be too heavy.
8
+ *
9
+ * @element icon-button
10
+ * @fires click - Native click, bubbling as usual — listen on the element itself.
11
+ */
12
+ export declare class IconButton extends LitElement {
13
+ static styles: import("lit").CSSResult[];
14
+ /** Pre-rendered icon template, e.g. `iconPencil(16)` from this package's icon set. */
15
+ icon: TemplateResult | null;
16
+ /** Required accessible label, applied as `aria-label`/`title`. */
17
+ label: string;
18
+ /** Disables the button and dims it. */
19
+ disabled: boolean;
20
+ render(): TemplateResult<1>;
21
+ }
22
+ declare global {
23
+ interface HTMLElementTagNameMap {
24
+ "icon-button": IconButton;
25
+ }
26
+ }
27
+ //# sourceMappingURL=icon-button.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icon-button.d.ts","sourceRoot":"","sources":["../src/icon-button.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAC;AAI1C;;;;;;;;GAQG;AACH,qBACa,UAAW,SAAQ,UAAU;IACxC,OAAgB,MAAM,4BAgCpB;IAEF,sFAAsF;IACtD,IAAI,EAAE,cAAc,GAAG,IAAI,CAAQ;IACnE,kEAAkE;IACtD,KAAK,SAAM;IACvB,uCAAuC;IACV,QAAQ,UAAS;IAErC,MAAM,sBAMd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,aAAa,EAAE,UAAU,CAAC;KAC3B;CACF"}
@@ -0,0 +1,83 @@
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 } from "lit/decorators.js";
9
+ import { tokens } from "./tokens.js";
10
+ /**
11
+ * A borderless button wrapping a passed-in icon, with a rounded
12
+ * hover-highlight background. Use for a low-emphasis affordance next to
13
+ * content it acts on (e.g. an "Edit" pencil at the end of a table row)
14
+ * where a bordered `ui-button` would be too heavy.
15
+ *
16
+ * @element icon-button
17
+ * @fires click - Native click, bubbling as usual — listen on the element itself.
18
+ */
19
+ let IconButton = class IconButton extends LitElement {
20
+ constructor() {
21
+ super(...arguments);
22
+ /** Pre-rendered icon template, e.g. `iconPencil(16)` from this package's icon set. */
23
+ this.icon = null;
24
+ /** Required accessible label, applied as `aria-label`/`title`. */
25
+ this.label = "";
26
+ /** Disables the button and dims it. */
27
+ this.disabled = false;
28
+ }
29
+ static { this.styles = [
30
+ tokens,
31
+ css `
32
+ :host {
33
+ display: inline-flex;
34
+ }
35
+ button {
36
+ display: inline-flex;
37
+ align-items: center;
38
+ justify-content: center;
39
+ width: 2rem;
40
+ height: 2rem;
41
+ padding: 0;
42
+ border: none;
43
+ border-radius: var(--ui-radius-sm, 0.25rem);
44
+ background: none;
45
+ color: var(--ui-text-muted, #64748b);
46
+ cursor: pointer;
47
+ }
48
+ button:hover:not(:disabled) {
49
+ background: var(--ui-surface-muted, #f8fafc);
50
+ color: var(--ui-text, #0f172a);
51
+ }
52
+ button:disabled {
53
+ cursor: not-allowed;
54
+ opacity: 0.5;
55
+ }
56
+ button:focus-visible {
57
+ outline: none;
58
+ box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));
59
+ }
60
+ `,
61
+ ]; }
62
+ render() {
63
+ return html `
64
+ <button type="button" aria-label=${this.label} title=${this.label} ?disabled=${this.disabled}>
65
+ ${this.icon ?? nothing}
66
+ </button>
67
+ `;
68
+ }
69
+ };
70
+ __decorate([
71
+ property({ attribute: false })
72
+ ], IconButton.prototype, "icon", void 0);
73
+ __decorate([
74
+ property()
75
+ ], IconButton.prototype, "label", void 0);
76
+ __decorate([
77
+ property({ type: Boolean })
78
+ ], IconButton.prototype, "disabled", void 0);
79
+ IconButton = __decorate([
80
+ customElement("icon-button")
81
+ ], IconButton);
82
+ export { IconButton };
83
+ //# sourceMappingURL=icon-button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icon-button.js","sourceRoot":"","sources":["../src/icon-button.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAErD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;;;;;GAQG;AAEI,IAAM,UAAU,GAAhB,MAAM,UAAW,SAAQ,UAAU;IAAnC;;QAmCL,sFAAsF;QACtD,SAAI,GAA0B,IAAI,CAAC;QACnE,kEAAkE;QACtD,UAAK,GAAG,EAAE,CAAC;QACvB,uCAAuC;QACV,aAAQ,GAAG,KAAK,CAAC;IAShD,CAAC;aAhDiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA6BF;KACF,AAhCqB,CAgCpB;IASO,MAAM;QACb,OAAO,IAAI,CAAA;yCAC0B,IAAI,CAAC,KAAK,UAAU,IAAI,CAAC,KAAK,cAAc,IAAI,CAAC,QAAQ;UACxF,IAAI,CAAC,IAAI,IAAI,OAAO;;KAEzB,CAAC;IACJ,CAAC;CACF,CAAA;AAbiC;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;wCAAoC;AAEvD;IAAX,QAAQ,EAAE;yCAAY;AAEM;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;4CAAkB;AAxCnC,UAAU;IADtB,aAAa,CAAC,aAAa,CAAC;GAChB,UAAU,CAiDtB","sourcesContent":["import { LitElement, css, html, nothing } from \"lit\";\nimport type { TemplateResult } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { tokens } from \"./tokens.js\";\n\n/**\n * A borderless button wrapping a passed-in icon, with a rounded\n * hover-highlight background. Use for a low-emphasis affordance next to\n * content it acts on (e.g. an \"Edit\" pencil at the end of a table row)\n * where a bordered `ui-button` would be too heavy.\n *\n * @element icon-button\n * @fires click - Native click, bubbling as usual — listen on the element itself.\n */\n@customElement(\"icon-button\")\nexport class IconButton extends LitElement {\n static override styles = [\n tokens,\n css`\n :host {\n display: inline-flex;\n }\n button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n padding: 0;\n border: none;\n border-radius: var(--ui-radius-sm, 0.25rem);\n background: none;\n color: var(--ui-text-muted, #64748b);\n cursor: pointer;\n }\n button:hover:not(:disabled) {\n background: var(--ui-surface-muted, #f8fafc);\n color: var(--ui-text, #0f172a);\n }\n button:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n }\n button:focus-visible {\n outline: none;\n box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));\n }\n `,\n ];\n\n /** Pre-rendered icon template, e.g. `iconPencil(16)` from this package's icon set. */\n @property({ attribute: false }) icon: TemplateResult | null = null;\n /** Required accessible label, applied as `aria-label`/`title`. */\n @property() label = \"\";\n /** Disables the button and dims it. */\n @property({ type: Boolean }) disabled = false;\n\n override render() {\n return html`\n <button type=\"button\" aria-label=${this.label} title=${this.label} ?disabled=${this.disabled}>\n ${this.icon ?? nothing}\n </button>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"icon-button\": IconButton;\n }\n}\n"]}
package/dist/icons.d.ts CHANGED
@@ -36,4 +36,5 @@ export declare const iconChevronDown: (size?: number) => import("lit-html").Temp
36
36
  export declare const iconDocument: (size?: number) => import("lit-html").TemplateResult<2>;
37
37
  export declare const iconSquares2x2: (size?: number) => import("lit-html").TemplateResult<2>;
38
38
  export declare const iconChatBubbleLeftRight: (size?: number) => import("lit-html").TemplateResult<2>;
39
+ export declare const iconArrowTopRightOnSquare: (size?: number) => import("lit-html").TemplateResult<2>;
39
40
  //# sourceMappingURL=icons.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../src/icons.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,KAAK,yDAC2O,CAAC;AAE9P,eAAO,MAAM,SAAS,yDAC+P,CAAC;AAEtR,eAAO,MAAM,eAAe,yDACwO,CAAC;AAErQ,eAAO,MAAM,gBAAgB,yDACqO,CAAC;AAEnQ,eAAO,MAAM,OAAO,yDAC2xC,CAAC;AAEhzC,eAAO,MAAM,QAAQ,yDAC4W,CAAC;AAElY,eAAO,MAAM,sBAAsB,yDAC6Y,CAAC;AAEjb,eAAO,MAAM,UAAU,yDACqX,CAAC;AAE7Y,eAAO,MAAM,SAAS,yDACgnB,CAAC;AAEvoB,eAAO,MAAM,QAAQ,yDAC0O,CAAC;AAEhQ,eAAO,MAAM,cAAc,yDACme,CAAC;AAE/f,eAAO,MAAM,kBAAkB,yDACob,CAAC;AAEpd,eAAO,MAAM,QAAQ,yDAC8b,CAAC;AAEpd,eAAO,MAAM,UAAU,yDACiY,CAAC;AAEzZ,eAAO,MAAM,SAAS,yDACmQ,CAAC;AAE1R,eAAO,MAAM,OAAO,yDACif,CAAC;AAEtgB,eAAO,MAAM,YAAY,yDACib,CAAC;AAE3c,eAAO,MAAM,qBAAqB,yDAC4V,CAAC;AAE/X,eAAO,MAAM,OAAO,yDACyhB,CAAC;AAE9iB,eAAO,MAAM,eAAe,yDAC2Q,CAAC;AAExS,eAAO,MAAM,OAAO,yDACsZ,CAAC;AAE3a,eAAO,MAAM,qBAAqB,yDACma,CAAC;AAEtc,eAAO,MAAM,aAAa,yDACsX,CAAC;AAEjZ,eAAO,MAAM,cAAc,yDACiP,CAAC;AAE7Q,eAAO,MAAM,QAAQ,yDAC6X,CAAC;AAEnZ,eAAO,MAAM,SAAS,yDACgX,CAAC;AAEvY,eAAO,MAAM,cAAc,yDAC8f,CAAC;AAE1hB,eAAO,MAAM,OAAO,yDAC4e,CAAC;AAEjgB,eAAO,MAAM,YAAY,yDAC4hB,CAAC;AAEtjB,eAAO,MAAM,eAAe,yDAC+Z,CAAC;AAE5b,eAAO,MAAM,cAAc,yDACqZ,CAAC;AAEjb,eAAO,MAAM,yBAAyB,yDACuxB,CAAC;AAE9zB,eAAO,MAAM,WAAW,yDACge,CAAC;AAEzf,eAAO,MAAM,UAAU,yDACqd,CAAC;AAE7e,eAAO,MAAM,eAAe,yDACuO,CAAC;AAEpQ,eAAO,MAAM,YAAY,yDACid,CAAC;AAE3e,eAAO,MAAM,cAAc,yDACosB,CAAC;AAEhuB,eAAO,MAAM,uBAAuB,yDAC2sB,CAAC"}
1
+ {"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../src/icons.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,KAAK,yDAC2O,CAAC;AAE9P,eAAO,MAAM,SAAS,yDAC+P,CAAC;AAEtR,eAAO,MAAM,eAAe,yDACwO,CAAC;AAErQ,eAAO,MAAM,gBAAgB,yDACqO,CAAC;AAEnQ,eAAO,MAAM,OAAO,yDAC2xC,CAAC;AAEhzC,eAAO,MAAM,QAAQ,yDAC4W,CAAC;AAElY,eAAO,MAAM,sBAAsB,yDAC6Y,CAAC;AAEjb,eAAO,MAAM,UAAU,yDACqX,CAAC;AAE7Y,eAAO,MAAM,SAAS,yDACgnB,CAAC;AAEvoB,eAAO,MAAM,QAAQ,yDAC0O,CAAC;AAEhQ,eAAO,MAAM,cAAc,yDACme,CAAC;AAE/f,eAAO,MAAM,kBAAkB,yDACob,CAAC;AAEpd,eAAO,MAAM,QAAQ,yDAC8b,CAAC;AAEpd,eAAO,MAAM,UAAU,yDACiY,CAAC;AAEzZ,eAAO,MAAM,SAAS,yDACmQ,CAAC;AAE1R,eAAO,MAAM,OAAO,yDACif,CAAC;AAEtgB,eAAO,MAAM,YAAY,yDACib,CAAC;AAE3c,eAAO,MAAM,qBAAqB,yDAC4V,CAAC;AAE/X,eAAO,MAAM,OAAO,yDACyhB,CAAC;AAE9iB,eAAO,MAAM,eAAe,yDAC2Q,CAAC;AAExS,eAAO,MAAM,OAAO,yDACsZ,CAAC;AAE3a,eAAO,MAAM,qBAAqB,yDACma,CAAC;AAEtc,eAAO,MAAM,aAAa,yDACsX,CAAC;AAEjZ,eAAO,MAAM,cAAc,yDACiP,CAAC;AAE7Q,eAAO,MAAM,QAAQ,yDAC6X,CAAC;AAEnZ,eAAO,MAAM,SAAS,yDACgX,CAAC;AAEvY,eAAO,MAAM,cAAc,yDAC8f,CAAC;AAE1hB,eAAO,MAAM,OAAO,yDAC4e,CAAC;AAEjgB,eAAO,MAAM,YAAY,yDAC4hB,CAAC;AAEtjB,eAAO,MAAM,eAAe,yDAC+Z,CAAC;AAE5b,eAAO,MAAM,cAAc,yDACqZ,CAAC;AAEjb,eAAO,MAAM,yBAAyB,yDACuxB,CAAC;AAE9zB,eAAO,MAAM,WAAW,yDACge,CAAC;AAEzf,eAAO,MAAM,UAAU,yDACqd,CAAC;AAE7e,eAAO,MAAM,eAAe,yDACuO,CAAC;AAEpQ,eAAO,MAAM,YAAY,yDACid,CAAC;AAE3e,eAAO,MAAM,cAAc,yDACosB,CAAC;AAEhuB,eAAO,MAAM,uBAAuB,yDAC2sB,CAAC;AAEhvB,eAAO,MAAM,yBAAyB,yDACuU,CAAC"}
package/dist/icons.js CHANGED
@@ -39,4 +39,5 @@ export const iconChevronDown = (size = 16) => svg `<svg xmlns="http://www.w3.org
39
39
  export const iconDocument = (size = 18) => svg `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke-width="1.5" stroke="currentColor" width=${size} height=${size} aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z"/></svg>`;
40
40
  export const iconSquares2x2 = (size = 18) => svg `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke-width="1.5" stroke="currentColor" width=${size} height=${size} aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6A2.25 2.25 0 0 1 6 3.75h2.25A2.25 2.25 0 0 1 10.5 6v2.25a2.25 2.25 0 0 1-2.25 2.25H6a2.25 2.25 0 0 1-2.25-2.25V6ZM3.75 15.75A2.25 2.25 0 0 1 6 13.5h2.25a2.25 2.25 0 0 1 2.25 2.25V18a2.25 2.25 0 0 1-2.25 2.25H6A2.25 2.25 0 0 1 3.75 18v-2.25ZM13.5 6a2.25 2.25 0 0 1 2.25-2.25H18A2.25 2.25 0 0 1 20.25 6v2.25A2.25 2.25 0 0 1 18 10.5h-2.25a2.25 2.25 0 0 1-2.25-2.25V6ZM13.5 15.75a2.25 2.25 0 0 1 2.25-2.25H18a2.25 2.25 0 0 1 2.25 2.25V18A2.25 2.25 0 0 1 18 20.25h-2.25A2.25 2.25 0 0 1 13.5 18v-2.25Z"/></svg>`;
41
41
  export const iconChatBubbleLeftRight = (size = 18) => svg `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke-width="1.5" stroke="currentColor" width=${size} height=${size} aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M20.25 8.511c.884.284 1.5 1.128 1.5 2.097v4.286c0 1.136-.847 2.1-1.98 2.193-.34.027-.68.052-1.02.072v3.091l-3-3c-1.354 0-2.694-.055-4.02-.163a2.115 2.115 0 0 1-.825-.242m9.345-8.334a2.126 2.126 0 0 0-.476-.095 48.64 48.64 0 0 0-8.048 0c-1.131.094-1.976 1.057-1.976 2.192v4.286c0 .837.46 1.58 1.155 1.951m9.345-8.334V6.637c0-1.621-1.152-3.026-2.76-3.235A48.455 48.455 0 0 0 11.25 3c-2.115 0-4.198.137-6.24.402-1.608.209-2.76 1.614-2.76 3.235v6.226c0 1.621 1.152 3.026 2.76 3.235.577.075 1.157.14 1.74.194V21l4.155-4.155"/></svg>`;
42
+ export const iconArrowTopRightOnSquare = (size = 16) => svg `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke-width="1.5" stroke="currentColor" width=${size} height=${size} aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25"/></svg>`;
42
43
  //# sourceMappingURL=icons.js.map