@f-ewald/components 1.21.1 → 1.22.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.
@@ -14343,7 +14343,7 @@
14343
14343
  "declarations": [
14344
14344
  {
14345
14345
  "kind": "class",
14346
- "description": "One event on a `timeline-container`: a dot on the vertical line, an optional\nheadline, a relative timestamp (\"3 hours ago\"), and freely nested content.\nThe connecting line is drawn here — its segment above the dot is hidden on\nthe first entry and the segment below is hidden on the last, so the line caps\nexactly at the first and last dots. Only meaningful inside a\n`timeline-container`; demonstrated through it.\n\n`datetime` is the entry's one timestamp. A nested element that has its own\ntimestamp prop (e.g. `chat-message`'s `timestamp`) should leave it unset —\nsetting both renders the same time twice.\n\nThe dot's `color` types the entry using the shared status-pill palette —\n`primary` by default, plus `neutral`, `info`, `success`, `warning`, and\n`danger`.\n\nSet `compact` for dense, one-line system-status entries (running spinners,\nstate changes): it tightens the vertical spacing and renders the content\nsmaller and muted.",
14346
+ "description": "One event on a `timeline-container`: a dot on the vertical line, an optional\nheadline, a relative timestamp (\"3 hours ago\"), and freely nested content.\nThe connecting line is drawn here — its segment above the dot is hidden on\nthe first entry and the segment below is hidden on the last, so the line caps\nexactly at the first and last dots. Only meaningful inside a\n`timeline-container`; demonstrated through it.\n\n`datetime` is the entry's one timestamp. A nested element that has its own\ntimestamp prop (e.g. `chat-message`'s `timestamp`) should leave it unset —\nsetting both renders the same time twice.\n\nThe dot's `color` types the entry using the shared status-pill palette —\n`primary` by default, plus `neutral`, `info`, `success`, `warning`, and\n`danger`.\n\nSet `compact` for dense, one-line system-status entries: it tightens the\nvertical spacing and renders the content smaller and muted.\n\nSet `running` to replace the dot with an animated gray ring spinner,\nindicating the entry represents in-progress work; `color` has no visible\neffect while `running` is set.",
14347
14347
  "name": "TimelineEntry",
14348
14348
  "slots": [
14349
14349
  {
@@ -14387,6 +14387,17 @@
14387
14387
  "attribute": "compact",
14388
14388
  "reflects": true
14389
14389
  },
14390
+ {
14391
+ "kind": "field",
14392
+ "name": "running",
14393
+ "type": {
14394
+ "text": "boolean"
14395
+ },
14396
+ "default": "false",
14397
+ "description": "Shows an animated ring spinner in place of the dot, indicating the entry\nrepresents in-progress work. Overrides `color` while set — the spinner\nalways uses the muted/gray palette.",
14398
+ "attribute": "running",
14399
+ "reflects": true
14400
+ },
14390
14401
  {
14391
14402
  "kind": "field",
14392
14403
  "name": "_hasHeadline",
@@ -14444,6 +14455,15 @@
14444
14455
  "default": "false",
14445
14456
  "description": "Dense, one-line presentation for system-status entries: tighter vertical\nspacing and smaller, muted content.",
14446
14457
  "fieldName": "compact"
14458
+ },
14459
+ {
14460
+ "name": "running",
14461
+ "type": {
14462
+ "text": "boolean"
14463
+ },
14464
+ "default": "false",
14465
+ "description": "Shows an animated ring spinner in place of the dot, indicating the entry\nrepresents in-progress work. Overrides `color` while set — the spinner\nalways uses the muted/gray palette.",
14466
+ "fieldName": "running"
14447
14467
  }
14448
14468
  ],
14449
14469
  "superclass": {
@@ -17,9 +17,12 @@ import type { StatusPillColor } from "./status-pill.js";
17
17
  * `primary` by default, plus `neutral`, `info`, `success`, `warning`, and
18
18
  * `danger`.
19
19
  *
20
- * Set `compact` for dense, one-line system-status entries (running spinners,
21
- * state changes): it tightens the vertical spacing and renders the content
22
- * smaller and muted.
20
+ * Set `compact` for dense, one-line system-status entries: it tightens the
21
+ * vertical spacing and renders the content smaller and muted.
22
+ *
23
+ * Set `running` to replace the dot with an animated gray ring spinner,
24
+ * indicating the entry represents in-progress work; `color` has no visible
25
+ * effect while `running` is set.
23
26
  *
24
27
  * @element timeline-entry
25
28
  * @slot headline - Optional headline/title for the event.
@@ -38,6 +41,12 @@ export declare class TimelineEntry extends LitElement {
38
41
  * spacing and smaller, muted content.
39
42
  */
40
43
  compact: boolean;
44
+ /**
45
+ * Shows an animated ring spinner in place of the dot, indicating the entry
46
+ * represents in-progress work. Overrides `color` while set — the spinner
47
+ * always uses the muted/gray palette.
48
+ */
49
+ running: boolean;
41
50
  /** Whether the headline slot currently has assigned content. */
42
51
  private _hasHeadline;
43
52
  static styles: import("lit").CSSResult[];
@@ -1 +1 @@
1
- {"version":3,"file":"timeline-entry.d.ts","sourceRoot":"","sources":["../src/timeline-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAErD,OAAO,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBACa,aAAc,SAAQ,UAAU;IAC3C,uEAAuE;IAC3D,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE3C;;;OAGG;IACS,KAAK,EAAE,eAAe,CAAa;IAE/C;;;OAGG;IACyC,OAAO,UAAS;IAE5D,gEAAgE;IACvD,OAAO,CAAC,YAAY,CAAS;IAEtC,OAAgB,MAAM,4BA+HpB;IAEO,iBAAiB,IAAI,IAAI,CAGjC;IAED,6EAA6E;IAC7E,OAAO,CAAC,qBAAqB;IAIpB,MAAM,yCAqBd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,gBAAgB,EAAE,aAAa,CAAC;KACjC;CACF"}
1
+ {"version":3,"file":"timeline-entry.d.ts","sourceRoot":"","sources":["../src/timeline-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAErD,OAAO,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBACa,aAAc,SAAQ,UAAU;IAC3C,uEAAuE;IAC3D,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE3C;;;OAGG;IACS,KAAK,EAAE,eAAe,CAAa;IAE/C;;;OAGG;IACyC,OAAO,UAAS;IAE5D;;;;OAIG;IACyC,OAAO,UAAS;IAE5D,gEAAgE;IACvD,OAAO,CAAC,YAAY,CAAS;IAEtC,OAAgB,MAAM,4BAsKpB;IAEO,iBAAiB,IAAI,IAAI,CAGjC;IAED,6EAA6E;IAC7E,OAAO,CAAC,qBAAqB;IAIpB,MAAM,yCA4Bd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,gBAAgB,EAAE,aAAa,CAAC;KACjC;CACF"}
@@ -24,9 +24,12 @@ import { tokens } from "./tokens.js";
24
24
  * `primary` by default, plus `neutral`, `info`, `success`, `warning`, and
25
25
  * `danger`.
26
26
  *
27
- * Set `compact` for dense, one-line system-status entries (running spinners,
28
- * state changes): it tightens the vertical spacing and renders the content
29
- * smaller and muted.
27
+ * Set `compact` for dense, one-line system-status entries: it tightens the
28
+ * vertical spacing and renders the content smaller and muted.
29
+ *
30
+ * Set `running` to replace the dot with an animated gray ring spinner,
31
+ * indicating the entry represents in-progress work; `color` has no visible
32
+ * effect while `running` is set.
30
33
  *
31
34
  * @element timeline-entry
32
35
  * @slot headline - Optional headline/title for the event.
@@ -47,6 +50,12 @@ let TimelineEntry = class TimelineEntry extends LitElement {
47
50
  * spacing and smaller, muted content.
48
51
  */
49
52
  this.compact = false;
53
+ /**
54
+ * Shows an animated ring spinner in place of the dot, indicating the entry
55
+ * represents in-progress work. Overrides `color` while set — the spinner
56
+ * always uses the muted/gray palette.
57
+ */
58
+ this.running = false;
50
59
  /** Whether the headline slot currently has assigned content. */
51
60
  this._hasHeadline = false;
52
61
  }
@@ -125,6 +134,45 @@ let TimelineEntry = class TimelineEntry extends LitElement {
125
134
  .dot.danger {
126
135
  --_dot: var(--ui-danger, #dc2626);
127
136
  }
137
+ .spinner {
138
+ position: absolute;
139
+ top: 0.125rem;
140
+ left: 50%;
141
+ width: 0.75rem;
142
+ height: 0.75rem;
143
+ transform: translateX(-50%);
144
+ animation: spin 0.8s linear infinite;
145
+ }
146
+ .spinner-track {
147
+ fill: none;
148
+ stroke: color-mix(in srgb, var(--ui-text-muted, #64748b) 25%, transparent);
149
+ stroke-width: 3;
150
+ }
151
+ .spinner-arc {
152
+ fill: none;
153
+ stroke: var(--ui-text-muted, #64748b);
154
+ stroke-width: 3;
155
+ stroke-linecap: round;
156
+ stroke-dasharray: 42 100;
157
+ }
158
+ @keyframes spin {
159
+ to {
160
+ transform: rotate(360deg);
161
+ }
162
+ }
163
+ @media (prefers-reduced-motion: reduce) {
164
+ .spinner {
165
+ animation: none;
166
+ }
167
+ }
168
+ @media (forced-colors: active) {
169
+ .spinner-track {
170
+ stroke: GrayText;
171
+ }
172
+ .spinner-arc {
173
+ stroke: CanvasText;
174
+ }
175
+ }
128
176
  :host(:first-child) .line-top {
129
177
  display: none;
130
178
  }
@@ -193,7 +241,14 @@ let TimelineEntry = class TimelineEntry extends LitElement {
193
241
  <div class="rail" aria-hidden="true">
194
242
  <span class="line line-top"></span>
195
243
  <span class="line line-bottom"></span>
196
- <span class="dot ${this.color}"></span>
244
+ ${this.running
245
+ ? html `
246
+ <svg class="spinner" viewBox="0 0 24 24">
247
+ <circle class="spinner-track" cx="12" cy="12" r="9"></circle>
248
+ <circle class="spinner-arc" cx="12" cy="12" r="9"></circle>
249
+ </svg>
250
+ `
251
+ : html `<span class="dot ${this.color}"></span>`}
197
252
  </div>
198
253
  <div class="body">
199
254
  <div class="head">
@@ -219,6 +274,9 @@ __decorate([
219
274
  __decorate([
220
275
  property({ type: Boolean, reflect: true })
221
276
  ], TimelineEntry.prototype, "compact", void 0);
277
+ __decorate([
278
+ property({ type: Boolean, reflect: true })
279
+ ], TimelineEntry.prototype, "running", void 0);
222
280
  __decorate([
223
281
  state()
224
282
  ], TimelineEntry.prototype, "_hasHeadline", void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"timeline-entry.js","sourceRoot":"","sources":["../src/timeline-entry.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,oBAAoB,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEI,IAAM,aAAa,GAAnB,MAAM,aAAc,SAAQ,UAAU;IAAtC;;QACL,uEAAuE;QAC3D,aAAQ,GAAkB,IAAI,CAAC;QAE3C;;;WAGG;QACS,UAAK,GAAoB,SAAS,CAAC;QAE/C;;;WAGG;QACyC,YAAO,GAAG,KAAK,CAAC;QAE5D,gEAAgE;QAC/C,iBAAY,GAAG,KAAK,CAAC;IAmKxC,CAAC;aAjKiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA4HF;KACF,AA/HqB,CA+HpB;IAEO,iBAAiB;QACxB,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAC1B,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;YAAE,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACxE,CAAC;IAED,6EAA6E;IACrE,qBAAqB,CAAC,KAAY;QACxC,IAAI,CAAC,YAAY,GAAI,KAAK,CAAC,MAA0B,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IACpG,CAAC;IAEQ,MAAM;QACb,OAAO,IAAI,CAAA;;;;;6BAKc,IAAI,CAAC,KAAK;;;;oCAIH,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO;kDAClB,IAAI,CAAC,qBAAqB;;cAE9D,IAAI,CAAC,QAAQ;YACb,CAAC,CAAC,IAAI,CAAA,wCAAwC,IAAI,CAAC,QAAQ,mBAAmB;YAC9E,CAAC,CAAC,OAAO;;;;;KAKlB,CAAC;IACJ,CAAC;CACF,CAAA;AAlLa;IAAX,QAAQ,EAAE;+CAAgC;AAM/B;IAAX,QAAQ,EAAE;4CAAoC;AAMH;IAA3C,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;8CAAiB;AAG3C;IAAhB,KAAK,EAAE;mDAA8B;AAjB3B,aAAa;IADzB,aAAa,CAAC,gBAAgB,CAAC;GACnB,aAAa,CAoLzB","sourcesContent":["import { LitElement, css, html, nothing } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport \"./relative-time.js\";\nimport { tokens } from \"./tokens.js\";\nimport type { StatusPillColor } from \"./status-pill.js\";\n\n/**\n * One event on a `timeline-container`: a dot on the vertical line, an optional\n * headline, a relative timestamp (\"3 hours ago\"), and freely nested content.\n * The connecting line is drawn here — its segment above the dot is hidden on\n * the first entry and the segment below is hidden on the last, so the line caps\n * exactly at the first and last dots. Only meaningful inside a\n * `timeline-container`; demonstrated through it.\n *\n * `datetime` is the entry's one timestamp. A nested element that has its own\n * timestamp prop (e.g. `chat-message`'s `timestamp`) should leave it unset —\n * setting both renders the same time twice.\n *\n * The dot's `color` types the entry using the shared status-pill palette —\n * `primary` by default, plus `neutral`, `info`, `success`, `warning`, and\n * `danger`.\n *\n * Set `compact` for dense, one-line system-status entries (running spinners,\n * state changes): it tightens the vertical spacing and renders the content\n * smaller and muted.\n *\n * @element timeline-entry\n * @slot headline - Optional headline/title for the event.\n * @slot - The event content; nest any elements here.\n */\n@customElement(\"timeline-entry\")\nexport class TimelineEntry extends LitElement {\n /** ISO 8601 or SQLite datetime string, rendered as a relative time. */\n @property() datetime: string | null = null;\n\n /**\n * Visual type of the entry's dot, from the shared status-pill palette:\n * `primary` (default), `neutral`, `info`, `success`, `warning`, or `danger`.\n */\n @property() color: StatusPillColor = \"primary\";\n\n /**\n * Dense, one-line presentation for system-status entries: tighter vertical\n * spacing and smaller, muted content.\n */\n @property({ type: Boolean, reflect: true }) compact = false;\n\n /** Whether the headline slot currently has assigned content. */\n @state() private _hasHeadline = false;\n\n static override styles = [\n tokens,\n css`\n :host {\n display: block;\n font-family: var(\n --ui-font,\n ui-sans-serif,\n system-ui,\n sans-serif,\n \"Apple Color Emoji\",\n \"Segoe UI Emoji\",\n \"Segoe UI Symbol\",\n \"Noto Color Emoji\"\n );\n }\n .entry {\n display: flex;\n gap: 0.75rem;\n }\n .rail {\n position: relative;\n flex: 0 0 auto;\n width: 0.75rem;\n }\n .line {\n position: absolute;\n left: 50%;\n width: 2px;\n transform: translateX(-50%);\n background: var(--ui-border, #e2e8f0);\n }\n .line-top {\n top: 0;\n height: 0.5rem;\n }\n .line-bottom {\n top: 0.5rem;\n bottom: 0;\n }\n .dot {\n position: absolute;\n top: 0.125rem;\n left: 50%;\n width: 0.75rem;\n height: 0.75rem;\n transform: translateX(-50%);\n border-radius: 9999px;\n /* Same lighter-on-top token gradient as map-circle/user-avatar; the\n base color is swapped per the color property via the private\n --_dot var. */\n background: linear-gradient(\n to bottom,\n color-mix(in srgb, var(--_dot, var(--ui-primary, #4f46e5)) 70%, #ffffff) 0%,\n color-mix(in srgb, var(--_dot, var(--ui-primary, #4f46e5)) 70%, #000000) 100%\n );\n }\n .dot.neutral {\n --_dot: var(--ui-text-muted, #64748b);\n }\n .dot.info {\n --_dot: var(--ui-info, #0ea5e9);\n }\n .dot.primary {\n --_dot: var(--ui-primary, #4f46e5);\n }\n .dot.success {\n --_dot: var(--ui-success, #16a34a);\n }\n .dot.warning {\n --_dot: var(--ui-warning, #d97706);\n }\n .dot.danger {\n --_dot: var(--ui-danger, #dc2626);\n }\n :host(:first-child) .line-top {\n display: none;\n }\n :host(:last-child) .line-bottom {\n display: none;\n }\n .body {\n flex: 1 1 auto;\n min-width: 0;\n padding-bottom: 1.5rem;\n }\n :host(:last-child) .body {\n padding-bottom: 0;\n }\n .head {\n display: flex;\n align-items: baseline;\n gap: 0.5rem;\n flex-wrap: wrap;\n }\n .headline {\n font-weight: var(--ui-font-weight-semibold, 600);\n font-size: var(--ui-font-size, 0.875rem);\n line-height: var(--ui-line-height-tight, 1.25);\n color: var(--ui-text, #0f172a);\n }\n .headline.empty {\n display: none;\n }\n .time {\n font-size: var(--ui-font-size-sm, 0.75rem);\n line-height: var(--ui-line-height-tight, 1.25);\n color: var(--ui-text-muted, #64748b);\n }\n .content {\n margin-top: 0.25rem;\n font-size: var(--ui-font-size, 0.875rem);\n line-height: var(--ui-line-height-normal, 1.5);\n color: var(--ui-text, #0f172a);\n }\n :host([compact]) .body {\n padding-bottom: 0.5rem;\n }\n :host([compact]:last-child) .body {\n padding-bottom: 0;\n }\n :host([compact]) .content {\n margin-top: 0.125rem;\n font-size: var(--ui-font-size-sm, 0.75rem);\n color: var(--ui-text-muted, #64748b);\n }\n `,\n ];\n\n override connectedCallback(): void {\n super.connectedCallback();\n if (!this.hasAttribute(\"role\")) this.setAttribute(\"role\", \"listitem\");\n }\n\n /** Collapses the headline when nothing is slotted so the time sits alone. */\n private _onHeadlineSlotChange(event: Event): void {\n this._hasHeadline = (event.target as HTMLSlotElement).assignedNodes({ flatten: true }).length > 0;\n }\n\n override render() {\n return html`\n <div class=\"entry\">\n <div class=\"rail\" aria-hidden=\"true\">\n <span class=\"line line-top\"></span>\n <span class=\"line line-bottom\"></span>\n <span class=\"dot ${this.color}\"></span>\n </div>\n <div class=\"body\">\n <div class=\"head\">\n <span class=\"headline ${this._hasHeadline ? \"\" : \"empty\"}\">\n <slot name=\"headline\" @slotchange=${this._onHeadlineSlotChange}></slot>\n </span>\n ${this.datetime\n ? html`<relative-time class=\"time\" datetime=${this.datetime}></relative-time>`\n : nothing}\n </div>\n <div class=\"content\"><slot></slot></div>\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"timeline-entry\": TimelineEntry;\n }\n}\n"]}
1
+ {"version":3,"file":"timeline-entry.js","sourceRoot":"","sources":["../src/timeline-entry.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,oBAAoB,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEI,IAAM,aAAa,GAAnB,MAAM,aAAc,SAAQ,UAAU;IAAtC;;QACL,uEAAuE;QAC3D,aAAQ,GAAkB,IAAI,CAAC;QAE3C;;;WAGG;QACS,UAAK,GAAoB,SAAS,CAAC;QAE/C;;;WAGG;QACyC,YAAO,GAAG,KAAK,CAAC;QAE5D;;;;WAIG;QACyC,YAAO,GAAG,KAAK,CAAC;QAE5D,gEAAgE;QAC/C,iBAAY,GAAG,KAAK,CAAC;IAiNxC,CAAC;aA/MiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAmKF;KACF,AAtKqB,CAsKpB;IAEO,iBAAiB;QACxB,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAC1B,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;YAAE,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACxE,CAAC;IAED,6EAA6E;IACrE,qBAAqB,CAAC,KAAY;QACxC,IAAI,CAAC,YAAY,GAAI,KAAK,CAAC,MAA0B,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IACpG,CAAC;IAEQ,MAAM;QACb,OAAO,IAAI,CAAA;;;;;YAKH,IAAI,CAAC,OAAO;YACZ,CAAC,CAAC,IAAI,CAAA;;;;;eAKH;YACH,CAAC,CAAC,IAAI,CAAA,oBAAoB,IAAI,CAAC,KAAK,WAAW;;;;oCAIvB,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO;kDAClB,IAAI,CAAC,qBAAqB;;cAE9D,IAAI,CAAC,QAAQ;YACb,CAAC,CAAC,IAAI,CAAA,wCAAwC,IAAI,CAAC,QAAQ,mBAAmB;YAC9E,CAAC,CAAC,OAAO;;;;;KAKlB,CAAC;IACJ,CAAC;CACF,CAAA;AAvOa;IAAX,QAAQ,EAAE;+CAAgC;AAM/B;IAAX,QAAQ,EAAE;4CAAoC;AAMH;IAA3C,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;8CAAiB;AAOhB;IAA3C,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;8CAAiB;AAG3C;IAAhB,KAAK,EAAE;mDAA8B;AAxB3B,aAAa;IADzB,aAAa,CAAC,gBAAgB,CAAC;GACnB,aAAa,CAyOzB","sourcesContent":["import { LitElement, css, html, nothing } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport \"./relative-time.js\";\nimport { tokens } from \"./tokens.js\";\nimport type { StatusPillColor } from \"./status-pill.js\";\n\n/**\n * One event on a `timeline-container`: a dot on the vertical line, an optional\n * headline, a relative timestamp (\"3 hours ago\"), and freely nested content.\n * The connecting line is drawn here — its segment above the dot is hidden on\n * the first entry and the segment below is hidden on the last, so the line caps\n * exactly at the first and last dots. Only meaningful inside a\n * `timeline-container`; demonstrated through it.\n *\n * `datetime` is the entry's one timestamp. A nested element that has its own\n * timestamp prop (e.g. `chat-message`'s `timestamp`) should leave it unset —\n * setting both renders the same time twice.\n *\n * The dot's `color` types the entry using the shared status-pill palette —\n * `primary` by default, plus `neutral`, `info`, `success`, `warning`, and\n * `danger`.\n *\n * Set `compact` for dense, one-line system-status entries: it tightens the\n * vertical spacing and renders the content smaller and muted.\n *\n * Set `running` to replace the dot with an animated gray ring spinner,\n * indicating the entry represents in-progress work; `color` has no visible\n * effect while `running` is set.\n *\n * @element timeline-entry\n * @slot headline - Optional headline/title for the event.\n * @slot - The event content; nest any elements here.\n */\n@customElement(\"timeline-entry\")\nexport class TimelineEntry extends LitElement {\n /** ISO 8601 or SQLite datetime string, rendered as a relative time. */\n @property() datetime: string | null = null;\n\n /**\n * Visual type of the entry's dot, from the shared status-pill palette:\n * `primary` (default), `neutral`, `info`, `success`, `warning`, or `danger`.\n */\n @property() color: StatusPillColor = \"primary\";\n\n /**\n * Dense, one-line presentation for system-status entries: tighter vertical\n * spacing and smaller, muted content.\n */\n @property({ type: Boolean, reflect: true }) compact = false;\n\n /**\n * Shows an animated ring spinner in place of the dot, indicating the entry\n * represents in-progress work. Overrides `color` while set — the spinner\n * always uses the muted/gray palette.\n */\n @property({ type: Boolean, reflect: true }) running = false;\n\n /** Whether the headline slot currently has assigned content. */\n @state() private _hasHeadline = false;\n\n static override styles = [\n tokens,\n css`\n :host {\n display: block;\n font-family: var(\n --ui-font,\n ui-sans-serif,\n system-ui,\n sans-serif,\n \"Apple Color Emoji\",\n \"Segoe UI Emoji\",\n \"Segoe UI Symbol\",\n \"Noto Color Emoji\"\n );\n }\n .entry {\n display: flex;\n gap: 0.75rem;\n }\n .rail {\n position: relative;\n flex: 0 0 auto;\n width: 0.75rem;\n }\n .line {\n position: absolute;\n left: 50%;\n width: 2px;\n transform: translateX(-50%);\n background: var(--ui-border, #e2e8f0);\n }\n .line-top {\n top: 0;\n height: 0.5rem;\n }\n .line-bottom {\n top: 0.5rem;\n bottom: 0;\n }\n .dot {\n position: absolute;\n top: 0.125rem;\n left: 50%;\n width: 0.75rem;\n height: 0.75rem;\n transform: translateX(-50%);\n border-radius: 9999px;\n /* Same lighter-on-top token gradient as map-circle/user-avatar; the\n base color is swapped per the color property via the private\n --_dot var. */\n background: linear-gradient(\n to bottom,\n color-mix(in srgb, var(--_dot, var(--ui-primary, #4f46e5)) 70%, #ffffff) 0%,\n color-mix(in srgb, var(--_dot, var(--ui-primary, #4f46e5)) 70%, #000000) 100%\n );\n }\n .dot.neutral {\n --_dot: var(--ui-text-muted, #64748b);\n }\n .dot.info {\n --_dot: var(--ui-info, #0ea5e9);\n }\n .dot.primary {\n --_dot: var(--ui-primary, #4f46e5);\n }\n .dot.success {\n --_dot: var(--ui-success, #16a34a);\n }\n .dot.warning {\n --_dot: var(--ui-warning, #d97706);\n }\n .dot.danger {\n --_dot: var(--ui-danger, #dc2626);\n }\n .spinner {\n position: absolute;\n top: 0.125rem;\n left: 50%;\n width: 0.75rem;\n height: 0.75rem;\n transform: translateX(-50%);\n animation: spin 0.8s linear infinite;\n }\n .spinner-track {\n fill: none;\n stroke: color-mix(in srgb, var(--ui-text-muted, #64748b) 25%, transparent);\n stroke-width: 3;\n }\n .spinner-arc {\n fill: none;\n stroke: var(--ui-text-muted, #64748b);\n stroke-width: 3;\n stroke-linecap: round;\n stroke-dasharray: 42 100;\n }\n @keyframes spin {\n to {\n transform: rotate(360deg);\n }\n }\n @media (prefers-reduced-motion: reduce) {\n .spinner {\n animation: none;\n }\n }\n @media (forced-colors: active) {\n .spinner-track {\n stroke: GrayText;\n }\n .spinner-arc {\n stroke: CanvasText;\n }\n }\n :host(:first-child) .line-top {\n display: none;\n }\n :host(:last-child) .line-bottom {\n display: none;\n }\n .body {\n flex: 1 1 auto;\n min-width: 0;\n padding-bottom: 1.5rem;\n }\n :host(:last-child) .body {\n padding-bottom: 0;\n }\n .head {\n display: flex;\n align-items: baseline;\n gap: 0.5rem;\n flex-wrap: wrap;\n }\n .headline {\n font-weight: var(--ui-font-weight-semibold, 600);\n font-size: var(--ui-font-size, 0.875rem);\n line-height: var(--ui-line-height-tight, 1.25);\n color: var(--ui-text, #0f172a);\n }\n .headline.empty {\n display: none;\n }\n .time {\n font-size: var(--ui-font-size-sm, 0.75rem);\n line-height: var(--ui-line-height-tight, 1.25);\n color: var(--ui-text-muted, #64748b);\n }\n .content {\n margin-top: 0.25rem;\n font-size: var(--ui-font-size, 0.875rem);\n line-height: var(--ui-line-height-normal, 1.5);\n color: var(--ui-text, #0f172a);\n }\n :host([compact]) .body {\n padding-bottom: 0.5rem;\n }\n :host([compact]:last-child) .body {\n padding-bottom: 0;\n }\n :host([compact]) .content {\n margin-top: 0.125rem;\n font-size: var(--ui-font-size-sm, 0.75rem);\n color: var(--ui-text-muted, #64748b);\n }\n `,\n ];\n\n override connectedCallback(): void {\n super.connectedCallback();\n if (!this.hasAttribute(\"role\")) this.setAttribute(\"role\", \"listitem\");\n }\n\n /** Collapses the headline when nothing is slotted so the time sits alone. */\n private _onHeadlineSlotChange(event: Event): void {\n this._hasHeadline = (event.target as HTMLSlotElement).assignedNodes({ flatten: true }).length > 0;\n }\n\n override render() {\n return html`\n <div class=\"entry\">\n <div class=\"rail\" aria-hidden=\"true\">\n <span class=\"line line-top\"></span>\n <span class=\"line line-bottom\"></span>\n ${this.running\n ? html`\n <svg class=\"spinner\" viewBox=\"0 0 24 24\">\n <circle class=\"spinner-track\" cx=\"12\" cy=\"12\" r=\"9\"></circle>\n <circle class=\"spinner-arc\" cx=\"12\" cy=\"12\" r=\"9\"></circle>\n </svg>\n `\n : html`<span class=\"dot ${this.color}\"></span>`}\n </div>\n <div class=\"body\">\n <div class=\"head\">\n <span class=\"headline ${this._hasHeadline ? \"\" : \"empty\"}\">\n <slot name=\"headline\" @slotchange=${this._onHeadlineSlotChange}></slot>\n </span>\n ${this.datetime\n ? html`<relative-time class=\"time\" datetime=${this.datetime}></relative-time>`\n : nothing}\n </div>\n <div class=\"content\"><slot></slot></div>\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"timeline-entry\": TimelineEntry;\n }\n}\n"]}
@@ -15,9 +15,12 @@ The dot's `color` types the entry using the shared status-pill palette —
15
15
  `primary` by default, plus `neutral`, `info`, `success`, `warning`, and
16
16
  `danger`.
17
17
 
18
- Set `compact` for dense, one-line system-status entries (running spinners,
19
- state changes): it tightens the vertical spacing and renders the content
20
- smaller and muted.
18
+ Set `compact` for dense, one-line system-status entries: it tightens the
19
+ vertical spacing and renders the content smaller and muted.
20
+
21
+ Set `running` to replace the dot with an animated gray ring spinner,
22
+ indicating the entry represents in-progress work; `color` has no visible
23
+ effect while `running` is set.
21
24
 
22
25
  ## Install
23
26
 
@@ -41,6 +44,7 @@ import "@f-ewald/components/timeline-entry.js";
41
44
  | `datetime` | `datetime` | `string | null` | `null` | ISO 8601 or SQLite datetime string, rendered as a relative time. |
42
45
  | `color` | `color` | `StatusPillColor` | `"primary"` | Visual type of the entry's dot, from the shared status-pill palette: `primary` (default), `neutral`, `info`, `success`, `warning`, or `danger`. |
43
46
  | `compact` | `compact` | `boolean` | `false` | Dense, one-line presentation for system-status entries: tighter vertical spacing and smaller, muted content. |
47
+ | `running` | `running` | `boolean` | `false` | Shows an animated ring spinner in place of the dot, indicating the entry represents in-progress work. Overrides `color` while set — the spinner always uses the muted/gray palette. |
44
48
 
45
49
  ## Events
46
50
 
package/llms.txt CHANGED
@@ -1893,13 +1893,16 @@ The dot's `color` types the entry using the shared status-pill palette —
1893
1893
  `primary` by default, plus `neutral`, `info`, `success`, `warning`, and
1894
1894
  `danger`.
1895
1895
 
1896
- Set `compact` for dense, one-line system-status entries (running spinners,
1897
- state changes): it tightens the vertical spacing and renders the content
1898
- smaller and muted.
1896
+ Set `compact` for dense, one-line system-status entries: it tightens the
1897
+ vertical spacing and renders the content smaller and muted.
1898
+
1899
+ Set `running` to replace the dot with an animated gray ring spinner,
1900
+ indicating the entry represents in-progress work; `color` has no visible
1901
+ effect while `running` is set.
1899
1902
 
1900
1903
  Import: `import "@f-ewald/components/timeline-entry.js";`
1901
1904
 
1902
- Properties: `datetime` (attribute `datetime`) : string | null, default null; `color` (attribute `color`) : StatusPillColor, default "primary"; `compact` (attribute `compact`) : boolean, default false
1905
+ Properties: `datetime` (attribute `datetime`) : string | null, default null; `color` (attribute `color`) : StatusPillColor, default "primary"; `compact` (attribute `compact`) : boolean, default false; `running` (attribute `running`) : boolean, default false
1903
1906
  Events: none
1904
1907
  CSS custom properties: `--ui-border`, `--ui-danger`, `--ui-font`, `--ui-font-size`, `--ui-font-size-sm`, `--ui-font-weight-semibold`, `--ui-info`, `--ui-line-height-normal`, `--ui-line-height-tight`, `--ui-primary`, `--ui-success`, `--ui-text`, `--ui-text-muted`, `--ui-warning`
1905
1908
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@f-ewald/components",
3
3
  "private": false,
4
- "version": "1.21.1",
4
+ "version": "1.22.0",
5
5
  "description": "A collection of universally usable web components for various tasks.",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",