@f-ewald/components 0.5.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.
- package/README.md +61 -0
- package/custom-elements.json +3808 -930
- package/dist/chat-message.d.ts +39 -0
- package/dist/chat-message.d.ts.map +1 -0
- package/dist/chat-message.js +155 -0
- package/dist/chat-message.js.map +1 -0
- package/dist/data-table.d.ts +39 -0
- package/dist/data-table.d.ts.map +1 -0
- package/dist/data-table.js +119 -0
- package/dist/data-table.js.map +1 -0
- package/dist/dropdown-button.d.ts +37 -0
- package/dist/dropdown-button.d.ts.map +1 -0
- package/dist/dropdown-button.js +219 -0
- package/dist/dropdown-button.js.map +1 -0
- package/dist/editable-text.d.ts +52 -0
- package/dist/editable-text.d.ts.map +1 -0
- package/dist/editable-text.js +224 -0
- package/dist/editable-text.js.map +1 -0
- package/dist/form-select.d.ts +46 -0
- package/dist/form-select.d.ts.map +1 -0
- package/dist/form-select.js +245 -0
- package/dist/form-select.js.map +1 -0
- package/dist/frame-box.d.ts +22 -0
- package/dist/frame-box.d.ts.map +1 -0
- package/dist/frame-box.js +68 -0
- package/dist/frame-box.js.map +1 -0
- package/dist/gallery-item-variant.d.ts +21 -0
- package/dist/gallery-item-variant.d.ts.map +1 -0
- package/dist/gallery-item-variant.js +44 -0
- package/dist/gallery-item-variant.js.map +1 -0
- package/dist/gallery-item.d.ts +28 -0
- package/dist/gallery-item.d.ts.map +1 -0
- package/dist/gallery-item.js +54 -0
- package/dist/gallery-item.js.map +1 -0
- package/dist/icon-button.d.ts +27 -0
- package/dist/icon-button.d.ts.map +1 -0
- package/dist/icon-button.js +83 -0
- package/dist/icon-button.js.map +1 -0
- package/dist/icons.d.ts +8 -0
- package/dist/icons.d.ts.map +1 -1
- package/dist/icons.js +8 -0
- package/dist/icons.js.map +1 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -1
- package/dist/index.js.map +1 -1
- package/dist/live-timer.d.ts +31 -0
- package/dist/live-timer.d.ts.map +1 -0
- package/dist/live-timer.js +78 -0
- package/dist/live-timer.js.map +1 -0
- package/dist/mcp-server.d.ts +3 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +82 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/photo-gallery.d.ts +117 -0
- package/dist/photo-gallery.d.ts.map +1 -0
- package/dist/photo-gallery.js +620 -0
- package/dist/photo-gallery.js.map +1 -0
- package/dist/popover-panel.d.ts +37 -0
- package/dist/popover-panel.d.ts.map +1 -0
- package/dist/popover-panel.js +184 -0
- package/dist/popover-panel.js.map +1 -0
- package/dist/status-pill.d.ts +24 -0
- package/dist/status-pill.d.ts.map +1 -0
- package/dist/status-pill.js +101 -0
- package/dist/status-pill.js.map +1 -0
- package/dist/tile-grid.d.ts +32 -0
- package/dist/tile-grid.d.ts.map +1 -0
- package/dist/tile-grid.js +102 -0
- package/dist/tile-grid.js.map +1 -0
- package/dist/tokens.css +39 -0
- package/dist/tokens.d.ts +28 -3
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +42 -43
- package/dist/tokens.js.map +1 -1
- package/dist/user-avatar.d.ts +5 -2
- package/dist/user-avatar.d.ts.map +1 -1
- package/dist/user-avatar.js +22 -6
- package/dist/user-avatar.js.map +1 -1
- package/dist/utils/duration.d.ts +12 -0
- package/dist/utils/duration.d.ts.map +1 -0
- package/dist/utils/duration.js +27 -0
- package/dist/utils/duration.js.map +1 -0
- package/docs/chat-message.md +59 -0
- package/docs/data-table.md +63 -0
- package/docs/dropdown-button.md +61 -0
- package/docs/editable-text.md +51 -0
- package/docs/form-select.md +70 -0
- package/docs/frame-box.md +43 -0
- package/docs/gallery-item-variant.md +37 -0
- package/docs/gallery-item.md +47 -0
- package/docs/icon-button.md +52 -0
- package/docs/live-timer.md +39 -0
- package/docs/mcp-evaluation.md +18 -11
- package/docs/photo-gallery.md +77 -0
- package/docs/popover-panel.md +70 -0
- package/docs/status-pill.md +43 -0
- package/docs/tile-grid.md +58 -0
- package/docs/user-avatar.md +1 -1
- package/llms.txt +357 -1
- package/package.json +10 -3
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
import "./relative-time.js";
|
|
3
|
+
export type ChatMessageRole = "user" | "agent" | "system";
|
|
4
|
+
export type ChatMessageVariant = "normal" | "tool" | "thinking";
|
|
5
|
+
/**
|
|
6
|
+
* One conversation entry in a chat-style activity feed. Tool calls and
|
|
7
|
+
* "thinking" traces are variants of this component rather than separate
|
|
8
|
+
* ones — they share the same header, collapse behavior, and body card as a
|
|
9
|
+
* normal message, just dimmed and collapsible with an always-visible summary.
|
|
10
|
+
*
|
|
11
|
+
* @element chat-message
|
|
12
|
+
* @slot - Message body (consumers slot in already-rendered content, e.g. sanitized markdown HTML).
|
|
13
|
+
* @fires toggle - Fired with `{ collapsed: boolean }` when the header/summary is clicked in collapsible mode.
|
|
14
|
+
*/
|
|
15
|
+
export declare class ChatMessage extends LitElement {
|
|
16
|
+
static styles: import("lit").CSSResult[];
|
|
17
|
+
/** Whose message this is; drives the card background/border. */
|
|
18
|
+
role: ChatMessageRole;
|
|
19
|
+
/** `tool`/`thinking` render dimmed and smaller, with `tool` using monospace for its body. */
|
|
20
|
+
variant: ChatMessageVariant;
|
|
21
|
+
/** Header label, e.g. "Freddy" or "Architect". */
|
|
22
|
+
author: string;
|
|
23
|
+
/** ISO-8601 timestamp, rendered via `relative-time` in the header. */
|
|
24
|
+
timestamp: string | null;
|
|
25
|
+
/** Always-visible one-liner for `tool`/`thinking` variants (e.g. a truncated args preview). */
|
|
26
|
+
summary: string;
|
|
27
|
+
/** Whether clicking the header/summary toggles the body slot. */
|
|
28
|
+
collapsible: boolean;
|
|
29
|
+
/** Current collapse state (reflected as an attribute). */
|
|
30
|
+
collapsed: boolean;
|
|
31
|
+
private _toggle;
|
|
32
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
33
|
+
}
|
|
34
|
+
declare global {
|
|
35
|
+
interface HTMLElementTagNameMap {
|
|
36
|
+
"chat-message": ChatMessage;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=chat-message.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,155 @@
|
|
|
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 { iconChevronRight } from "./icons.js";
|
|
10
|
+
import "./relative-time.js";
|
|
11
|
+
import { tokens } from "./tokens.js";
|
|
12
|
+
/**
|
|
13
|
+
* One conversation entry in a chat-style activity feed. Tool calls and
|
|
14
|
+
* "thinking" traces are variants of this component rather than separate
|
|
15
|
+
* ones — they share the same header, collapse behavior, and body card as a
|
|
16
|
+
* normal message, just dimmed and collapsible with an always-visible summary.
|
|
17
|
+
*
|
|
18
|
+
* @element chat-message
|
|
19
|
+
* @slot - Message body (consumers slot in already-rendered content, e.g. sanitized markdown HTML).
|
|
20
|
+
* @fires toggle - Fired with `{ collapsed: boolean }` when the header/summary is clicked in collapsible mode.
|
|
21
|
+
*/
|
|
22
|
+
let ChatMessage = class ChatMessage extends LitElement {
|
|
23
|
+
constructor() {
|
|
24
|
+
super(...arguments);
|
|
25
|
+
/** Whose message this is; drives the card background/border. */
|
|
26
|
+
this.role = "agent";
|
|
27
|
+
/** `tool`/`thinking` render dimmed and smaller, with `tool` using monospace for its body. */
|
|
28
|
+
this.variant = "normal";
|
|
29
|
+
/** Header label, e.g. "Freddy" or "Architect". */
|
|
30
|
+
this.author = "";
|
|
31
|
+
/** ISO-8601 timestamp, rendered via `relative-time` in the header. */
|
|
32
|
+
this.timestamp = null;
|
|
33
|
+
/** Always-visible one-liner for `tool`/`thinking` variants (e.g. a truncated args preview). */
|
|
34
|
+
this.summary = "";
|
|
35
|
+
/** Whether clicking the header/summary toggles the body slot. */
|
|
36
|
+
this.collapsible = false;
|
|
37
|
+
/** Current collapse state (reflected as an attribute). */
|
|
38
|
+
this.collapsed = false;
|
|
39
|
+
}
|
|
40
|
+
static { this.styles = [
|
|
41
|
+
tokens,
|
|
42
|
+
css `
|
|
43
|
+
:host {
|
|
44
|
+
display: block;
|
|
45
|
+
font-family: var(--ui-font, ui-sans-serif, system-ui, sans-serif);
|
|
46
|
+
font-size: var(--ui-font-size, 0.875rem);
|
|
47
|
+
}
|
|
48
|
+
.header {
|
|
49
|
+
display: flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
gap: 0.4rem;
|
|
52
|
+
font-size: 0.75rem;
|
|
53
|
+
color: var(--ui-text-muted, #64748b);
|
|
54
|
+
margin-bottom: 0.3rem;
|
|
55
|
+
}
|
|
56
|
+
.header.clickable {
|
|
57
|
+
cursor: pointer;
|
|
58
|
+
}
|
|
59
|
+
.chevron {
|
|
60
|
+
display: inline-flex;
|
|
61
|
+
transition: transform 0.15s ease;
|
|
62
|
+
}
|
|
63
|
+
.chevron.expanded {
|
|
64
|
+
transform: rotate(90deg);
|
|
65
|
+
}
|
|
66
|
+
.author {
|
|
67
|
+
font-weight: 600;
|
|
68
|
+
color: var(--ui-text, #0f172a);
|
|
69
|
+
}
|
|
70
|
+
.summary {
|
|
71
|
+
overflow: hidden;
|
|
72
|
+
text-overflow: ellipsis;
|
|
73
|
+
white-space: nowrap;
|
|
74
|
+
}
|
|
75
|
+
.body-card {
|
|
76
|
+
border-radius: var(--ui-radius, 0.5rem);
|
|
77
|
+
padding: 0.6rem 0.8rem;
|
|
78
|
+
color: var(--ui-text, #0f172a);
|
|
79
|
+
line-height: 1.5;
|
|
80
|
+
}
|
|
81
|
+
.body-card[hidden] {
|
|
82
|
+
display: none;
|
|
83
|
+
}
|
|
84
|
+
:host([role="user"]) .body-card {
|
|
85
|
+
background: color-mix(in srgb, var(--ui-primary, #4f46e5) 8%, var(--ui-surface, #fff));
|
|
86
|
+
}
|
|
87
|
+
:host([role="agent"]) .body-card {
|
|
88
|
+
background: var(--ui-surface, #fff);
|
|
89
|
+
border: 1px solid var(--ui-border, #e2e8f0);
|
|
90
|
+
}
|
|
91
|
+
:host([role="system"]) .body-card {
|
|
92
|
+
padding: 0 0 0 1.1rem;
|
|
93
|
+
font-size: 0.8rem;
|
|
94
|
+
color: var(--ui-text-muted, #64748b);
|
|
95
|
+
}
|
|
96
|
+
:host([variant="tool"]) .body-card,
|
|
97
|
+
:host([variant="thinking"]) .body-card {
|
|
98
|
+
opacity: 0.7;
|
|
99
|
+
font-size: 0.8rem;
|
|
100
|
+
}
|
|
101
|
+
:host([variant="tool"]) .body-card {
|
|
102
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
103
|
+
}
|
|
104
|
+
`,
|
|
105
|
+
]; }
|
|
106
|
+
_toggle() {
|
|
107
|
+
if (!this.collapsible)
|
|
108
|
+
return;
|
|
109
|
+
this.collapsed = !this.collapsed;
|
|
110
|
+
this.dispatchEvent(new CustomEvent("toggle", { detail: { collapsed: this.collapsed } }));
|
|
111
|
+
}
|
|
112
|
+
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
|
+
>`
|
|
119
|
+
: 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}>
|
|
125
|
+
<slot></slot>
|
|
126
|
+
</div>
|
|
127
|
+
`;
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
__decorate([
|
|
131
|
+
property({ reflect: true })
|
|
132
|
+
], ChatMessage.prototype, "role", void 0);
|
|
133
|
+
__decorate([
|
|
134
|
+
property({ reflect: true })
|
|
135
|
+
], ChatMessage.prototype, "variant", void 0);
|
|
136
|
+
__decorate([
|
|
137
|
+
property()
|
|
138
|
+
], ChatMessage.prototype, "author", void 0);
|
|
139
|
+
__decorate([
|
|
140
|
+
property()
|
|
141
|
+
], ChatMessage.prototype, "timestamp", void 0);
|
|
142
|
+
__decorate([
|
|
143
|
+
property()
|
|
144
|
+
], ChatMessage.prototype, "summary", void 0);
|
|
145
|
+
__decorate([
|
|
146
|
+
property({ type: Boolean })
|
|
147
|
+
], ChatMessage.prototype, "collapsible", void 0);
|
|
148
|
+
__decorate([
|
|
149
|
+
property({ type: Boolean, reflect: true })
|
|
150
|
+
], ChatMessage.prototype, "collapsed", void 0);
|
|
151
|
+
ChatMessage = __decorate([
|
|
152
|
+
customElement("chat-message")
|
|
153
|
+
], ChatMessage);
|
|
154
|
+
export { ChatMessage };
|
|
155
|
+
//# sourceMappingURL=chat-message.js.map
|
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
/** One column header: `key` is looked up via `renderCell`/`getCellValue`, `label` is the header text. */
|
|
3
|
+
export interface DataTableColumn {
|
|
4
|
+
key: string;
|
|
5
|
+
label: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* A generic, presentational table shell: renders a `<thead>` from `columns`
|
|
9
|
+
* and one `<tr>` per entry in `rows`, with each cell's content produced by
|
|
10
|
+
* `renderCell` (default: plain property lookup on the row object). Knows
|
|
11
|
+
* nothing about what a "row" means — callers own the data shape entirely.
|
|
12
|
+
*
|
|
13
|
+
* Optional `rowHref` makes whole rows clickable (navigating via `location.hash`),
|
|
14
|
+
* without hijacking clicks on nested interactive elements (links/buttons) a
|
|
15
|
+
* cell's rendered content might contain.
|
|
16
|
+
*
|
|
17
|
+
* @element data-table
|
|
18
|
+
*/
|
|
19
|
+
export declare class DataTable extends LitElement {
|
|
20
|
+
#private;
|
|
21
|
+
static styles: import("lit").CSSResult[];
|
|
22
|
+
/** Column headers, in display order. */
|
|
23
|
+
columns: DataTableColumn[];
|
|
24
|
+
/** Row data; opaque to this component beyond what `renderCell` does with it. */
|
|
25
|
+
rows: unknown[];
|
|
26
|
+
/** Stable identity for `rows[i]`, used as the repeat-directive key. Defaults to the row's index. */
|
|
27
|
+
rowKey: (row: unknown, index: number) => string | number;
|
|
28
|
+
/** Produces a cell's rendered content for `row`/`column.key`. Default: `row[key]`. */
|
|
29
|
+
renderCell: (row: unknown, key: string) => unknown;
|
|
30
|
+
/** When set, clicking a row (outside any nested link/button) navigates to this hash. */
|
|
31
|
+
rowHref: ((row: unknown) => string | null) | null;
|
|
32
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
33
|
+
}
|
|
34
|
+
declare global {
|
|
35
|
+
interface HTMLElementTagNameMap {
|
|
36
|
+
"data-table": DataTable;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=data-table.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-table.d.ts","sourceRoot":"","sources":["../src/data-table.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAKrD,yGAAyG;AACzG,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;GAWG;AACH,qBACa,SAAU,SAAQ,UAAU;;IACvC,OAAgB,MAAM,4BA+BpB;IAEF,wCAAwC;IACR,OAAO,EAAE,eAAe,EAAE,CAAM;IAChE,gFAAgF;IAChD,IAAI,EAAE,OAAO,EAAE,CAAM;IACrD,oGAAoG;IACpE,MAAM,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CAG7E;IACX,sFAAsF;IACtD,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAGvC;IAC3C,wFAAwF;IACxD,OAAO,EAAE,CAAC,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,IAAI,CAAQ;IAShF,MAAM,yCAwBd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,YAAY,EAAE,SAAS,CAAC;KACzB;CACF"}
|
|
@@ -0,0 +1,119 @@
|
|
|
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 { repeat } from "lit/directives/repeat.js";
|
|
10
|
+
import { tokens } from "./tokens.js";
|
|
11
|
+
/**
|
|
12
|
+
* A generic, presentational table shell: renders a `<thead>` from `columns`
|
|
13
|
+
* and one `<tr>` per entry in `rows`, with each cell's content produced by
|
|
14
|
+
* `renderCell` (default: plain property lookup on the row object). Knows
|
|
15
|
+
* nothing about what a "row" means — callers own the data shape entirely.
|
|
16
|
+
*
|
|
17
|
+
* Optional `rowHref` makes whole rows clickable (navigating via `location.hash`),
|
|
18
|
+
* without hijacking clicks on nested interactive elements (links/buttons) a
|
|
19
|
+
* cell's rendered content might contain.
|
|
20
|
+
*
|
|
21
|
+
* @element data-table
|
|
22
|
+
*/
|
|
23
|
+
let DataTable = class DataTable extends LitElement {
|
|
24
|
+
constructor() {
|
|
25
|
+
super(...arguments);
|
|
26
|
+
/** Column headers, in display order. */
|
|
27
|
+
this.columns = [];
|
|
28
|
+
/** Row data; opaque to this component beyond what `renderCell` does with it. */
|
|
29
|
+
this.rows = [];
|
|
30
|
+
/** Stable identity for `rows[i]`, used as the repeat-directive key. Defaults to the row's index. */
|
|
31
|
+
this.rowKey = (_row, index) => index;
|
|
32
|
+
/** Produces a cell's rendered content for `row`/`column.key`. Default: `row[key]`. */
|
|
33
|
+
this.renderCell = (row, key) => row[key];
|
|
34
|
+
/** When set, clicking a row (outside any nested link/button) navigates to this hash. */
|
|
35
|
+
this.rowHref = null;
|
|
36
|
+
}
|
|
37
|
+
static { this.styles = [
|
|
38
|
+
tokens,
|
|
39
|
+
css `
|
|
40
|
+
:host {
|
|
41
|
+
display: block;
|
|
42
|
+
font-family: var(--ui-font, ui-sans-serif, system-ui, sans-serif);
|
|
43
|
+
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
44
|
+
}
|
|
45
|
+
table {
|
|
46
|
+
width: 100%;
|
|
47
|
+
border-collapse: collapse;
|
|
48
|
+
}
|
|
49
|
+
th {
|
|
50
|
+
text-align: left;
|
|
51
|
+
font-weight: 600;
|
|
52
|
+
color: var(--ui-text-muted, #64748b);
|
|
53
|
+
padding: 0.5rem 0.75rem;
|
|
54
|
+
border-bottom: 1px solid var(--ui-border, #e2e8f0);
|
|
55
|
+
}
|
|
56
|
+
td {
|
|
57
|
+
padding: 0.6rem 0.75rem;
|
|
58
|
+
border-bottom: 1px solid var(--ui-border, #e2e8f0);
|
|
59
|
+
color: var(--ui-text, #0f172a);
|
|
60
|
+
}
|
|
61
|
+
tbody tr.clickable {
|
|
62
|
+
cursor: pointer;
|
|
63
|
+
}
|
|
64
|
+
tbody tr.clickable:hover {
|
|
65
|
+
background: var(--ui-surface-muted, #f8fafc);
|
|
66
|
+
}
|
|
67
|
+
`,
|
|
68
|
+
]; }
|
|
69
|
+
#onRowClick(row, e) {
|
|
70
|
+
if (!this.rowHref)
|
|
71
|
+
return;
|
|
72
|
+
if (e.target.closest("a, button"))
|
|
73
|
+
return;
|
|
74
|
+
const href = this.rowHref(row);
|
|
75
|
+
if (href)
|
|
76
|
+
location.hash = href;
|
|
77
|
+
}
|
|
78
|
+
render() {
|
|
79
|
+
return html `
|
|
80
|
+
<table>
|
|
81
|
+
<thead>
|
|
82
|
+
<tr>
|
|
83
|
+
${this.columns.map((c) => html `<th>${c.label}</th>`)}
|
|
84
|
+
</tr>
|
|
85
|
+
</thead>
|
|
86
|
+
<tbody>
|
|
87
|
+
${repeat(this.rows, (row, i) => this.rowKey(row, i), (row) => html `
|
|
88
|
+
<tr
|
|
89
|
+
class=${this.rowHref ? "clickable" : ""}
|
|
90
|
+
@click=${(e) => this.#onRowClick(row, e)}
|
|
91
|
+
>
|
|
92
|
+
${this.columns.map((c) => html `<td>${this.renderCell(row, c.key) ?? nothing}</td>`)}
|
|
93
|
+
</tr>
|
|
94
|
+
`)}
|
|
95
|
+
</tbody>
|
|
96
|
+
</table>
|
|
97
|
+
`;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
__decorate([
|
|
101
|
+
property({ attribute: false })
|
|
102
|
+
], DataTable.prototype, "columns", void 0);
|
|
103
|
+
__decorate([
|
|
104
|
+
property({ attribute: false })
|
|
105
|
+
], DataTable.prototype, "rows", void 0);
|
|
106
|
+
__decorate([
|
|
107
|
+
property({ attribute: false })
|
|
108
|
+
], DataTable.prototype, "rowKey", void 0);
|
|
109
|
+
__decorate([
|
|
110
|
+
property({ attribute: false })
|
|
111
|
+
], DataTable.prototype, "renderCell", void 0);
|
|
112
|
+
__decorate([
|
|
113
|
+
property({ attribute: false })
|
|
114
|
+
], DataTable.prototype, "rowHref", void 0);
|
|
115
|
+
DataTable = __decorate([
|
|
116
|
+
customElement("data-table")
|
|
117
|
+
], DataTable);
|
|
118
|
+
export { DataTable };
|
|
119
|
+
//# sourceMappingURL=data-table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-table.js","sourceRoot":"","sources":["../src/data-table.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAQrC;;;;;;;;;;;GAWG;AAEI,IAAM,SAAS,GAAf,MAAM,SAAU,SAAQ,UAAU;IAAlC;;QAkCL,wCAAwC;QACR,YAAO,GAAsB,EAAE,CAAC;QAChE,gFAAgF;QAChD,SAAI,GAAc,EAAE,CAAC;QACrD,oGAAoG;QACpE,WAAM,GAAqD,CACzF,IAAI,EACJ,KAAK,EACL,EAAE,CAAC,KAAK,CAAC;QACX,sFAAsF;QACtD,eAAU,GAA2C,CACnF,GAAG,EACH,GAAG,EACH,EAAE,CAAE,GAA+B,CAAC,GAAG,CAAC,CAAC;QAC3C,wFAAwF;QACxD,YAAO,GAA6C,IAAI,CAAC;IAkC3F,CAAC;aAlFiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA4BF;KACF,AA/BqB,CA+BpB;IAmBF,WAAW,CAAC,GAAY,EAAE,CAAa;QACrC,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,IAAK,CAAC,CAAC,MAAsB,CAAC,OAAO,CAAC,WAAW,CAAC;YAAE,OAAO;QAC3D,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,IAAI;YAAE,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC;IACjC,CAAC;IAEQ,MAAM;QACb,OAAO,IAAI,CAAA;;;;cAID,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAA,OAAO,CAAC,CAAC,KAAK,OAAO,CAAC;;;;YAIpD,MAAM,CACN,IAAI,CAAC,IAAI,EACT,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,EAC/B,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAA;;wBAED,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE;yBAC9B,CAAC,CAAa,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC;;kBAElD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAA,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,OAAO,OAAO,CAAC;;aAEtF,CACF;;;KAGN,CAAC;IACJ,CAAC;CACF,CAAA;AAhDiC;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;0CAAiC;AAEhC;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;uCAAsB;AAErB;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;yCAGpB;AAEqB;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;6CAGY;AAEX;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;0CAA0D;AAjD9E,SAAS;IADrB,aAAa,CAAC,YAAY,CAAC;GACf,SAAS,CAmFrB","sourcesContent":["import { LitElement, css, html, nothing } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { repeat } from \"lit/directives/repeat.js\";\nimport { tokens } from \"./tokens.js\";\n\n/** One column header: `key` is looked up via `renderCell`/`getCellValue`, `label` is the header text. */\nexport interface DataTableColumn {\n key: string;\n label: string;\n}\n\n/**\n * A generic, presentational table shell: renders a `<thead>` from `columns`\n * and one `<tr>` per entry in `rows`, with each cell's content produced by\n * `renderCell` (default: plain property lookup on the row object). Knows\n * nothing about what a \"row\" means — callers own the data shape entirely.\n *\n * Optional `rowHref` makes whole rows clickable (navigating via `location.hash`),\n * without hijacking clicks on nested interactive elements (links/buttons) a\n * cell's rendered content might contain.\n *\n * @element data-table\n */\n@customElement(\"data-table\")\nexport class DataTable extends LitElement {\n static override styles = [\n tokens,\n css`\n :host {\n display: block;\n font-family: var(--ui-font, ui-sans-serif, system-ui, sans-serif);\n font-size: var(--ui-font-size-sm, 0.75rem);\n }\n table {\n width: 100%;\n border-collapse: collapse;\n }\n th {\n text-align: left;\n font-weight: 600;\n color: var(--ui-text-muted, #64748b);\n padding: 0.5rem 0.75rem;\n border-bottom: 1px solid var(--ui-border, #e2e8f0);\n }\n td {\n padding: 0.6rem 0.75rem;\n border-bottom: 1px solid var(--ui-border, #e2e8f0);\n color: var(--ui-text, #0f172a);\n }\n tbody tr.clickable {\n cursor: pointer;\n }\n tbody tr.clickable:hover {\n background: var(--ui-surface-muted, #f8fafc);\n }\n `,\n ];\n\n /** Column headers, in display order. */\n @property({ attribute: false }) columns: DataTableColumn[] = [];\n /** Row data; opaque to this component beyond what `renderCell` does with it. */\n @property({ attribute: false }) rows: unknown[] = [];\n /** Stable identity for `rows[i]`, used as the repeat-directive key. Defaults to the row's index. */\n @property({ attribute: false }) rowKey: (row: unknown, index: number) => string | number = (\n _row,\n index,\n ) => index;\n /** Produces a cell's rendered content for `row`/`column.key`. Default: `row[key]`. */\n @property({ attribute: false }) renderCell: (row: unknown, key: string) => unknown = (\n row,\n key,\n ) => (row as Record<string, unknown>)[key];\n /** When set, clicking a row (outside any nested link/button) navigates to this hash. */\n @property({ attribute: false }) rowHref: ((row: unknown) => string | null) | null = null;\n\n #onRowClick(row: unknown, e: MouseEvent): void {\n if (!this.rowHref) return;\n if ((e.target as HTMLElement).closest(\"a, button\")) return;\n const href = this.rowHref(row);\n if (href) location.hash = href;\n }\n\n override render() {\n return html`\n <table>\n <thead>\n <tr>\n ${this.columns.map((c) => html`<th>${c.label}</th>`)}\n </tr>\n </thead>\n <tbody>\n ${repeat(\n this.rows,\n (row, i) => this.rowKey(row, i),\n (row) => html`\n <tr\n class=${this.rowHref ? \"clickable\" : \"\"}\n @click=${(e: MouseEvent) => this.#onRowClick(row, e)}\n >\n ${this.columns.map((c) => html`<td>${this.renderCell(row, c.key) ?? nothing}</td>`)}\n </tr>\n `,\n )}\n </tbody>\n </table>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"data-table\": DataTable;\n }\n}\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { LitElement, type PropertyValues } from "lit";
|
|
2
|
+
/** A single menu action. */
|
|
3
|
+
export interface DropdownOption {
|
|
4
|
+
value: string;
|
|
5
|
+
label: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* A primary-styled button with a label and chevron that opens an anchored
|
|
9
|
+
* menu of actions — essentially `form-select` minus "current value"
|
|
10
|
+
* semantics: a menu, not a select. Use for a set of mutually exclusive
|
|
11
|
+
* next-step actions (e.g. a failed task's Retry / Close / Backlog).
|
|
12
|
+
*
|
|
13
|
+
* @element dropdown-button
|
|
14
|
+
* @fires select - Fired with `{ value: string }` when a menu item is picked.
|
|
15
|
+
*/
|
|
16
|
+
export declare class DropdownButton extends LitElement {
|
|
17
|
+
#private;
|
|
18
|
+
static styles: import("lit").CSSResult[];
|
|
19
|
+
/** The trigger button's label. */
|
|
20
|
+
label: string;
|
|
21
|
+
/** The menu's actions. */
|
|
22
|
+
options: DropdownOption[];
|
|
23
|
+
/** Disables the trigger, preventing the menu from opening. */
|
|
24
|
+
disabled: boolean;
|
|
25
|
+
private _open;
|
|
26
|
+
private _activeIndex;
|
|
27
|
+
disconnectedCallback(): void;
|
|
28
|
+
protected updated(changed: PropertyValues): void;
|
|
29
|
+
private renderMenu;
|
|
30
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
31
|
+
}
|
|
32
|
+
declare global {
|
|
33
|
+
interface HTMLElementTagNameMap {
|
|
34
|
+
"dropdown-button": DropdownButton;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=dropdown-button.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dropdown-button.d.ts","sourceRoot":"","sources":["../src/dropdown-button.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAK1E,4BAA4B;AAC5B,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;GAQG;AACH,qBACa,cAAe,SAAQ,UAAU;;IAC5C,OAAgB,MAAM,4BAoEpB;IAEF,kCAAkC;IACtB,KAAK,SAAM;IACvB,0BAA0B;IACM,OAAO,EAAE,cAAc,EAAE,CAAM;IAC/D,8DAA8D;IACjC,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;IA4CD,OAAO,CAAC,UAAU;IAsBT,MAAM,yCAgBd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,iBAAiB,EAAE,cAAc,CAAC;KACnC;CACF"}
|
|
@@ -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
|