@aceshooting/lyra-ui 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/README.md +77 -6
  2. package/custom-elements.json +12015 -0
  3. package/dist/components/chart/bar-chart.d.ts +14 -0
  4. package/dist/components/chart/bar-chart.js +14 -0
  5. package/dist/components/chart/box-plot.d.ts +46 -0
  6. package/dist/components/chart/box-plot.js +145 -0
  7. package/dist/components/chart/box-plot.styles.d.ts +1 -0
  8. package/dist/components/chart/box-plot.styles.js +22 -0
  9. package/dist/components/chart/bubble-chart.d.ts +19 -0
  10. package/dist/components/chart/bubble-chart.js +18 -0
  11. package/dist/components/chart/chart-loader.d.ts +8 -0
  12. package/dist/components/chart/chart-loader.js +26 -0
  13. package/dist/components/chart/chart.d.ts +91 -0
  14. package/dist/components/chart/chart.js +275 -0
  15. package/dist/components/chart/chart.styles.d.ts +1 -0
  16. package/dist/components/chart/chart.styles.js +33 -0
  17. package/dist/components/chart/doughnut-chart.d.ts +15 -0
  18. package/dist/components/chart/doughnut-chart.js +14 -0
  19. package/dist/components/chart/histogram-bin.d.ts +6 -0
  20. package/dist/components/chart/histogram-bin.js +19 -0
  21. package/dist/components/chart/histogram.d.ts +20 -0
  22. package/dist/components/chart/histogram.js +70 -0
  23. package/dist/components/chart/histogram.styles.d.ts +1 -0
  24. package/dist/components/chart/histogram.styles.js +1 -0
  25. package/dist/components/chart/line-chart.d.ts +14 -0
  26. package/dist/components/chart/line-chart.js +14 -0
  27. package/dist/components/chart/pie-chart.d.ts +16 -0
  28. package/dist/components/chart/pie-chart.js +15 -0
  29. package/dist/components/chart/polar-area-chart.d.ts +15 -0
  30. package/dist/components/chart/polar-area-chart.js +14 -0
  31. package/dist/components/chart/radar-chart.d.ts +15 -0
  32. package/dist/components/chart/radar-chart.js +14 -0
  33. package/dist/components/chart/scatter-chart.d.ts +15 -0
  34. package/dist/components/chart/scatter-chart.js +15 -0
  35. package/dist/components/combobox/combobox.d.ts +16 -0
  36. package/dist/components/combobox/combobox.js +113 -6
  37. package/dist/components/combobox/combobox.styles.js +68 -7
  38. package/dist/components/combobox/option.d.ts +2 -2
  39. package/dist/components/combobox/option.js +11 -7
  40. package/dist/components/date-picker/date-input.d.ts +12 -1
  41. package/dist/components/date-picker/date-input.js +68 -6
  42. package/dist/components/date-picker/date-input.styles.js +58 -6
  43. package/dist/components/date-picker/date-picker.js +5 -2
  44. package/dist/components/date-picker/date-picker.styles.js +9 -2
  45. package/dist/components/empty/empty.d.ts +32 -0
  46. package/dist/components/empty/empty.js +90 -0
  47. package/dist/components/empty/empty.styles.d.ts +1 -0
  48. package/dist/components/empty/empty.styles.js +42 -0
  49. package/dist/components/export-button/csv.d.ts +10 -0
  50. package/dist/components/export-button/csv.js +28 -0
  51. package/dist/components/export-button/export-button.d.ts +41 -0
  52. package/dist/components/export-button/export-button.js +151 -0
  53. package/dist/components/export-button/export-button.styles.d.ts +1 -0
  54. package/dist/components/export-button/export-button.styles.js +79 -0
  55. package/dist/components/file-input/accept.d.ts +14 -0
  56. package/dist/components/file-input/accept.js +30 -0
  57. package/dist/components/file-input/file-input.d.ts +47 -0
  58. package/dist/components/file-input/file-input.js +182 -0
  59. package/dist/components/file-input/file-input.styles.d.ts +1 -0
  60. package/dist/components/file-input/file-input.styles.js +44 -0
  61. package/dist/components/flag/flag.d.ts +4 -0
  62. package/dist/components/flag/flag.js +24 -1
  63. package/dist/components/flag/flag.styles.js +1 -1
  64. package/dist/components/gauge/gauge.d.ts +29 -0
  65. package/dist/components/gauge/gauge.js +133 -0
  66. package/dist/components/gauge/gauge.styles.d.ts +1 -0
  67. package/dist/components/gauge/gauge.styles.js +60 -0
  68. package/dist/components/graph/graph.d.ts +68 -0
  69. package/dist/components/graph/graph.js +276 -0
  70. package/dist/components/graph/graph.styles.d.ts +1 -0
  71. package/dist/components/graph/graph.styles.js +34 -0
  72. package/dist/components/heatmap/heatmap-scale.d.ts +8 -0
  73. package/dist/components/heatmap/heatmap-scale.js +17 -0
  74. package/dist/components/heatmap/heatmap.d.ts +66 -0
  75. package/dist/components/heatmap/heatmap.js +252 -0
  76. package/dist/components/heatmap/heatmap.styles.d.ts +1 -0
  77. package/dist/components/heatmap/heatmap.styles.js +39 -0
  78. package/dist/components/map/map-loader.d.ts +8 -0
  79. package/dist/components/map/map-loader.js +17 -0
  80. package/dist/components/map/map.d.ts +49 -0
  81. package/dist/components/map/map.js +189 -0
  82. package/dist/components/map/map.styles.d.ts +1 -0
  83. package/dist/components/map/map.styles.js +48 -0
  84. package/dist/components/playback/playback.d.ts +51 -0
  85. package/dist/components/playback/playback.js +146 -0
  86. package/dist/components/playback/playback.styles.d.ts +1 -0
  87. package/dist/components/playback/playback.styles.js +40 -0
  88. package/dist/components/skeleton/skeleton.d.ts +26 -0
  89. package/dist/components/skeleton/skeleton.js +65 -0
  90. package/dist/components/skeleton/skeleton.styles.d.ts +1 -0
  91. package/dist/components/skeleton/skeleton.styles.js +57 -0
  92. package/dist/components/split/split.d.ts +41 -0
  93. package/dist/components/split/split.js +171 -0
  94. package/dist/components/split/split.styles.d.ts +1 -0
  95. package/dist/components/split/split.styles.js +51 -0
  96. package/dist/components/stat/stat.d.ts +36 -0
  97. package/dist/components/stat/stat.js +117 -0
  98. package/dist/components/stat/stat.styles.d.ts +1 -0
  99. package/dist/components/stat/stat.styles.js +86 -0
  100. package/dist/components/table/table.d.ts +54 -0
  101. package/dist/components/table/table.js +201 -0
  102. package/dist/components/table/table.styles.d.ts +1 -0
  103. package/dist/components/table/table.styles.js +98 -0
  104. package/dist/components/time-range/time-range.d.ts +40 -0
  105. package/dist/components/time-range/time-range.js +219 -0
  106. package/dist/components/time-range/time-range.styles.d.ts +1 -0
  107. package/dist/components/time-range/time-range.styles.js +78 -0
  108. package/dist/components/toast/toast-item.js +10 -2
  109. package/dist/components/toast/toast-item.styles.js +13 -4
  110. package/dist/components/toast/toast.styles.js +0 -1
  111. package/dist/components/tree/tree-node.d.ts +50 -0
  112. package/dist/components/tree/tree-node.js +196 -0
  113. package/dist/components/tree/tree.d.ts +65 -0
  114. package/dist/components/tree/tree.js +227 -0
  115. package/dist/components/tree/tree.styles.d.ts +1 -0
  116. package/dist/components/tree/tree.styles.js +13 -0
  117. package/dist/internal/form-associated.js +49 -1
  118. package/dist/internal/icons.d.ts +11 -0
  119. package/dist/internal/icons.js +64 -0
  120. package/dist/internal/tokens.styles.js +41 -1
  121. package/dist/lyra.d.ts +67 -0
  122. package/dist/lyra.js +55 -0
  123. package/llms-full.txt +1425 -0
  124. package/llms.txt +47 -0
  125. package/package.json +60 -11
@@ -0,0 +1,146 @@
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 { html } from 'lit';
8
+ import { property } from 'lit/decorators.js';
9
+ import { LyraElement } from '../../internal/lyra-element.js';
10
+ import { defineElement } from '../../internal/prefix.js';
11
+ import { playIcon, pauseIcon } from '../../internal/icons.js';
12
+ import { styles } from './playback.styles.js';
13
+ /**
14
+ * `<lyra-playback>` — steps an index through `[0, length)` on a fixed
15
+ * interval (play/pause), like the manual play-timers duplicated across
16
+ * per-repo time-series dashboards.
17
+ *
18
+ * @customElement lyra-playback
19
+ * @event lyra-play
20
+ * @event lyra-pause
21
+ * @event lyra-step - `detail: { index }`, fired on every tick and manual step.
22
+ * @csspart base, play-button, slider
23
+ */
24
+ export class LyraPlayback extends LyraElement {
25
+ constructor() {
26
+ super(...arguments);
27
+ this.length = 0;
28
+ this.index = 0;
29
+ this.intervalMs = 900;
30
+ this.playing = false;
31
+ this.loop = true;
32
+ /**
33
+ * Re-declared as a Lit reactive property (shadowing the inherited plain
34
+ * `HTMLElement.hidden` IDL property) so that setting `el.hidden = true` —
35
+ * or the `hidden` attribute — actually enters Lit's change-tracking system
36
+ * and the `willUpdate` auto-pause guard below can see it via `changed`.
37
+ * `reflect: true` preserves the native attribute-reflection behavior.
38
+ */
39
+ this.hidden = false;
40
+ }
41
+ static { this.styles = [LyraElement.styles, styles]; }
42
+ disconnectedCallback() {
43
+ super.disconnectedCallback();
44
+ this.pause();
45
+ }
46
+ willUpdate(changed) {
47
+ if (changed.has('hidden') && this.hidden)
48
+ this.pause();
49
+ }
50
+ /** Start playback; no-op if there's nothing to advance through. */
51
+ play() {
52
+ if (this.playing || this.length <= 1)
53
+ return;
54
+ this.playing = true;
55
+ this.emit('lyra-play');
56
+ this.timer = window.setInterval(() => this.tick(), this.intervalMs);
57
+ }
58
+ /** Stop playback. */
59
+ pause() {
60
+ if (!this.playing)
61
+ return;
62
+ window.clearInterval(this.timer);
63
+ this.timer = undefined;
64
+ this.playing = false;
65
+ this.emit('lyra-pause');
66
+ }
67
+ toggle() {
68
+ this.playing ? this.pause() : this.play();
69
+ }
70
+ tick() {
71
+ const next = this.index + 1;
72
+ if (next >= this.length) {
73
+ if (this.loop)
74
+ this.setIndex(0);
75
+ else {
76
+ this.setIndex(this.length - 1);
77
+ this.pause();
78
+ }
79
+ }
80
+ else {
81
+ this.setIndex(next);
82
+ }
83
+ }
84
+ setIndex(i) {
85
+ this.index = i;
86
+ this.emit('lyra-step', { index: this.index });
87
+ }
88
+ /** Advance one step without starting playback. */
89
+ next() {
90
+ if (this.index + 1 < this.length)
91
+ this.setIndex(this.index + 1);
92
+ }
93
+ /** Go back one step without starting playback. */
94
+ previous() {
95
+ if (this.index > 0)
96
+ this.setIndex(this.index - 1);
97
+ }
98
+ /** Jump to an explicit index, pausing playback. */
99
+ goTo(index) {
100
+ this.pause();
101
+ this.setIndex(Math.min(this.length - 1, Math.max(0, index)));
102
+ }
103
+ render() {
104
+ return html `
105
+ <div part="base">
106
+ <button
107
+ part="play-button"
108
+ type="button"
109
+ aria-label=${this.playing ? 'Pause' : 'Play'}
110
+ ?disabled=${this.length <= 1}
111
+ @click=${() => this.toggle()}
112
+ >
113
+ ${this.playing ? pauseIcon() : playIcon()}
114
+ </button>
115
+ <input
116
+ part="slider"
117
+ type="range"
118
+ min="0"
119
+ max=${Math.max(0, this.length - 1)}
120
+ .value=${String(this.index)}
121
+ aria-label="Playback position"
122
+ @input=${(e) => this.goTo(Number(e.target.value))}
123
+ />
124
+ </div>
125
+ `;
126
+ }
127
+ }
128
+ __decorate([
129
+ property({ type: Number })
130
+ ], LyraPlayback.prototype, "length", void 0);
131
+ __decorate([
132
+ property({ type: Number })
133
+ ], LyraPlayback.prototype, "index", void 0);
134
+ __decorate([
135
+ property({ type: Number, attribute: 'interval-ms' })
136
+ ], LyraPlayback.prototype, "intervalMs", void 0);
137
+ __decorate([
138
+ property({ type: Boolean, reflect: true })
139
+ ], LyraPlayback.prototype, "playing", void 0);
140
+ __decorate([
141
+ property({ type: Boolean })
142
+ ], LyraPlayback.prototype, "loop", void 0);
143
+ __decorate([
144
+ property({ type: Boolean, reflect: true })
145
+ ], LyraPlayback.prototype, "hidden", void 0);
146
+ defineElement('playback', LyraPlayback);
@@ -0,0 +1 @@
1
+ export declare const styles: import("lit").CSSResult;
@@ -0,0 +1,40 @@
1
+ import { css } from 'lit';
2
+ export const styles = css `
3
+ :host {
4
+ display: inline-flex;
5
+ align-items: center;
6
+ gap: var(--lyra-space-s);
7
+ }
8
+ [part='base'] {
9
+ display: flex;
10
+ align-items: center;
11
+ gap: var(--lyra-space-s);
12
+ }
13
+ [part='play-button'] {
14
+ inline-size: 2rem;
15
+ block-size: 2rem;
16
+ border-radius: 50%;
17
+ border: 1px solid var(--lyra-color-border);
18
+ background: var(--lyra-color-surface);
19
+ color: var(--lyra-color-text);
20
+ cursor: pointer;
21
+ display: inline-flex;
22
+ align-items: center;
23
+ justify-content: center;
24
+ font-size: 0.875rem;
25
+ }
26
+ [part='play-button']:hover {
27
+ border-color: var(--lyra-color-brand);
28
+ }
29
+ [part='play-button']:disabled {
30
+ opacity: var(--lyra-opacity-disabled);
31
+ cursor: not-allowed;
32
+ }
33
+ [part='play-button']:focus-visible {
34
+ outline: var(--lyra-focus-ring-width) solid var(--lyra-focus-ring-color);
35
+ outline-offset: var(--lyra-focus-ring-offset);
36
+ }
37
+ [part='slider'] {
38
+ accent-color: var(--lyra-color-brand);
39
+ }
40
+ `;
@@ -0,0 +1,26 @@
1
+ import { type TemplateResult, type PropertyValues } from 'lit';
2
+ import { LyraElement } from '../../internal/lyra-element.js';
3
+ export type SkeletonVariant = 'text' | 'circle' | 'rect';
4
+ export type SkeletonEffect = 'pulse' | 'sheen';
5
+ /**
6
+ * `<lyra-skeleton>` — a loading placeholder. First-party invention; every
7
+ * surveyed repo reinvents this as a bespoke `animate-pulse` div.
8
+ *
9
+ * @customElement lyra-skeleton
10
+ * @csspart base - The placeholder shape.
11
+ */
12
+ export declare class LyraSkeleton extends LyraElement {
13
+ static styles: import("lit").CSSResultGroup[];
14
+ variant: SkeletonVariant;
15
+ effect: SkeletonEffect;
16
+ width?: string;
17
+ height?: string;
18
+ protected willUpdate(): void;
19
+ protected updated(changed: PropertyValues): void;
20
+ render(): TemplateResult;
21
+ }
22
+ declare global {
23
+ interface HTMLElementTagNameMap {
24
+ 'lyra-skeleton': LyraSkeleton;
25
+ }
26
+ }
@@ -0,0 +1,65 @@
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 { html } from 'lit';
8
+ import { property } from 'lit/decorators.js';
9
+ import { LyraElement } from '../../internal/lyra-element.js';
10
+ import { defineElement } from '../../internal/prefix.js';
11
+ import { srOnly } from '../../internal/a11y.js';
12
+ import { styles } from './skeleton.styles.js';
13
+ /**
14
+ * `<lyra-skeleton>` — a loading placeholder. First-party invention; every
15
+ * surveyed repo reinvents this as a bespoke `animate-pulse` div.
16
+ *
17
+ * @customElement lyra-skeleton
18
+ * @csspart base - The placeholder shape.
19
+ */
20
+ export class LyraSkeleton extends LyraElement {
21
+ constructor() {
22
+ super(...arguments);
23
+ this.variant = 'text';
24
+ this.effect = 'pulse';
25
+ }
26
+ static { this.styles = [LyraElement.styles, styles, srOnly]; }
27
+ willUpdate() {
28
+ this.setAttribute('role', 'status');
29
+ }
30
+ updated(changed) {
31
+ if (changed.has('width') || changed.has('height')) {
32
+ const base = this.renderRoot.querySelector('[part="base"]');
33
+ if (!base)
34
+ return;
35
+ if (this.width) {
36
+ base.style.setProperty('--lyra-skeleton-w', this.width);
37
+ }
38
+ else {
39
+ base.style.removeProperty('--lyra-skeleton-w');
40
+ }
41
+ if (this.height) {
42
+ base.style.setProperty('--lyra-skeleton-h', this.height);
43
+ }
44
+ else {
45
+ base.style.removeProperty('--lyra-skeleton-h');
46
+ }
47
+ }
48
+ }
49
+ render() {
50
+ return html `<span part="base"><span class="sr-only">Loading…</span></span>`;
51
+ }
52
+ }
53
+ __decorate([
54
+ property({ reflect: true })
55
+ ], LyraSkeleton.prototype, "variant", void 0);
56
+ __decorate([
57
+ property({ reflect: true })
58
+ ], LyraSkeleton.prototype, "effect", void 0);
59
+ __decorate([
60
+ property()
61
+ ], LyraSkeleton.prototype, "width", void 0);
62
+ __decorate([
63
+ property()
64
+ ], LyraSkeleton.prototype, "height", void 0);
65
+ defineElement('skeleton', LyraSkeleton);
@@ -0,0 +1 @@
1
+ export declare const styles: import("lit").CSSResult;
@@ -0,0 +1,57 @@
1
+ import { css } from 'lit';
2
+ export const styles = css `
3
+ :host {
4
+ display: inline-block;
5
+ inline-size: var(--lyra-skeleton-w, 100%);
6
+ block-size: var(--lyra-skeleton-h, 1em);
7
+ }
8
+ [part='base'] {
9
+ inline-size: 100%;
10
+ block-size: 100%;
11
+ background: var(--lyra-color-border);
12
+ }
13
+ :host([variant='text']) [part='base'] {
14
+ border-radius: var(--lyra-radius);
15
+ }
16
+ :host([variant='circle']) [part='base'] {
17
+ border-radius: 50%;
18
+ }
19
+ :host([variant='rect']) [part='base'] {
20
+ border-radius: var(--lyra-radius);
21
+ }
22
+ :host([effect='pulse']) [part='base'] {
23
+ animation: lyra-skeleton-pulse 1.5s ease-in-out infinite;
24
+ }
25
+ :host([effect='sheen']) [part='base'] {
26
+ background-image: linear-gradient(
27
+ 90deg,
28
+ var(--lyra-color-border) 0%,
29
+ var(--lyra-color-surface) 50%,
30
+ var(--lyra-color-border) 100%
31
+ );
32
+ background-size: 200% 100%;
33
+ animation: lyra-skeleton-sheen 1.5s ease-in-out infinite;
34
+ }
35
+ @keyframes lyra-skeleton-pulse {
36
+ 0%,
37
+ 100% {
38
+ opacity: 1;
39
+ }
40
+ 50% {
41
+ opacity: 0.4;
42
+ }
43
+ }
44
+ @keyframes lyra-skeleton-sheen {
45
+ 0% {
46
+ background-position: 200% 0;
47
+ }
48
+ 100% {
49
+ background-position: -200% 0;
50
+ }
51
+ }
52
+ @media (prefers-reduced-motion: reduce) {
53
+ [part='base'] {
54
+ animation: none !important;
55
+ }
56
+ }
57
+ `;
@@ -0,0 +1,41 @@
1
+ import { type TemplateResult, type PropertyValues } from 'lit';
2
+ import { LyraElement } from '../../internal/lyra-element.js';
3
+ /**
4
+ * `<lyra-split>` — resizable panels for dashboard layouts. Direct light-DOM
5
+ * children are the panels; a divider is auto-inserted between each pair.
6
+ *
7
+ * @customElement lyra-split
8
+ * @event lyra-resize - `detail: { sizes }`, fired on every drag step/release
9
+ * and every keyboard step.
10
+ * @csspart base, divider
11
+ */
12
+ export declare class LyraSplit extends LyraElement {
13
+ static styles: import("lit").CSSResultGroup[];
14
+ sizes: number[];
15
+ min: number;
16
+ orientation: 'horizontal' | 'vertical';
17
+ storageKey?: string;
18
+ private panelCount;
19
+ private dragIndex;
20
+ private dragStartPos;
21
+ private dragStartSizes;
22
+ connectedCallback(): void;
23
+ disconnectedCallback(): void;
24
+ private get storageFullKey();
25
+ private loadPersisted;
26
+ private persist;
27
+ private ensureSizes;
28
+ private clampPair;
29
+ private applyDelta;
30
+ private onPointerDown;
31
+ private onPointerMove;
32
+ private onPointerUp;
33
+ private onDividerKeyDown;
34
+ protected updated(changed: PropertyValues): void;
35
+ render(): TemplateResult;
36
+ }
37
+ declare global {
38
+ interface HTMLElementTagNameMap {
39
+ 'lyra-split': LyraSplit;
40
+ }
41
+ }
@@ -0,0 +1,171 @@
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 { html } from 'lit';
8
+ import { property, state } from 'lit/decorators.js';
9
+ import { LyraElement } from '../../internal/lyra-element.js';
10
+ import { defineElement } from '../../internal/prefix.js';
11
+ import { styles } from './split.styles.js';
12
+ const KEYBOARD_STEP = 2;
13
+ /**
14
+ * `<lyra-split>` — resizable panels for dashboard layouts. Direct light-DOM
15
+ * children are the panels; a divider is auto-inserted between each pair.
16
+ *
17
+ * @customElement lyra-split
18
+ * @event lyra-resize - `detail: { sizes }`, fired on every drag step/release
19
+ * and every keyboard step.
20
+ * @csspart base, divider
21
+ */
22
+ export class LyraSplit extends LyraElement {
23
+ constructor() {
24
+ super(...arguments);
25
+ this.sizes = [];
26
+ this.min = 10;
27
+ this.orientation = 'horizontal';
28
+ this.panelCount = 0;
29
+ this.dragIndex = -1;
30
+ this.dragStartPos = 0;
31
+ this.dragStartSizes = [];
32
+ this.onPointerDown = (e, index) => {
33
+ this.dragIndex = index;
34
+ this.dragStartPos = this.orientation === 'vertical' ? e.clientY : e.clientX;
35
+ this.dragStartSizes = [...this.sizes];
36
+ e.target.setPointerCapture(e.pointerId);
37
+ window.addEventListener('pointermove', this.onPointerMove);
38
+ window.addEventListener('pointerup', this.onPointerUp);
39
+ };
40
+ this.onPointerMove = (e) => {
41
+ if (this.dragIndex < 0)
42
+ return;
43
+ const base = this.renderRoot.querySelector('[part="base"]');
44
+ const total = this.orientation === 'vertical' ? base.clientHeight : base.clientWidth;
45
+ const pos = this.orientation === 'vertical' ? e.clientY : e.clientX;
46
+ const deltaPercent = ((pos - this.dragStartPos) / total) * 100;
47
+ this.sizes = this.clampPair(this.dragStartSizes, this.dragIndex, deltaPercent);
48
+ this.emit('lyra-resize', { sizes: [...this.sizes] });
49
+ };
50
+ this.onPointerUp = () => {
51
+ this.dragIndex = -1;
52
+ this.persist();
53
+ window.removeEventListener('pointermove', this.onPointerMove);
54
+ window.removeEventListener('pointerup', this.onPointerUp);
55
+ };
56
+ this.onDividerKeyDown = (e, index) => {
57
+ const forwardKey = this.orientation === 'vertical' ? 'ArrowDown' : 'ArrowRight';
58
+ const backwardKey = this.orientation === 'vertical' ? 'ArrowUp' : 'ArrowLeft';
59
+ if (e.key === forwardKey) {
60
+ e.preventDefault();
61
+ this.applyDelta(index, KEYBOARD_STEP, true);
62
+ }
63
+ else if (e.key === backwardKey) {
64
+ e.preventDefault();
65
+ this.applyDelta(index, -KEYBOARD_STEP, true);
66
+ }
67
+ };
68
+ }
69
+ static { this.styles = [LyraElement.styles, styles]; }
70
+ connectedCallback() {
71
+ super.connectedCallback();
72
+ this.panelCount = this.children.length;
73
+ this.ensureSizes();
74
+ this.loadPersisted();
75
+ }
76
+ disconnectedCallback() {
77
+ super.disconnectedCallback();
78
+ // Clean up any remaining event listeners
79
+ window.removeEventListener('pointermove', this.onPointerMove);
80
+ window.removeEventListener('pointerup', this.onPointerUp);
81
+ }
82
+ get storageFullKey() {
83
+ return this.storageKey ? `lyra-split:${this.storageKey}:${this.panelCount}` : undefined;
84
+ }
85
+ loadPersisted() {
86
+ const key = this.storageFullKey;
87
+ if (!key)
88
+ return;
89
+ const raw = localStorage.getItem(key);
90
+ if (!raw)
91
+ return;
92
+ try {
93
+ const parsed = JSON.parse(raw);
94
+ if (Array.isArray(parsed) && parsed.length === this.panelCount)
95
+ this.sizes = parsed;
96
+ }
97
+ catch {
98
+ /* ignore malformed persisted state */
99
+ }
100
+ }
101
+ persist() {
102
+ const key = this.storageFullKey;
103
+ if (key)
104
+ localStorage.setItem(key, JSON.stringify(this.sizes));
105
+ }
106
+ ensureSizes() {
107
+ if (this.sizes.length === this.panelCount && this.panelCount > 0)
108
+ return;
109
+ const equal = 100 / Math.max(1, this.panelCount);
110
+ this.sizes = Array.from({ length: this.panelCount }, () => equal);
111
+ }
112
+ clampPair(sizes, i, delta) {
113
+ const next = [...sizes];
114
+ const a = next[i] + delta;
115
+ const b = next[i + 1] - delta;
116
+ if (a < this.min || b < this.min)
117
+ return sizes;
118
+ next[i] = a;
119
+ next[i + 1] = b;
120
+ return next;
121
+ }
122
+ applyDelta(index, delta, commit) {
123
+ this.sizes = this.clampPair(this.sizes, index, delta);
124
+ this.emit('lyra-resize', { sizes: [...this.sizes] });
125
+ if (commit)
126
+ this.persist();
127
+ }
128
+ updated(changed) {
129
+ if (changed.has('sizes') || this.sizes.length === 0)
130
+ this.ensureSizes();
131
+ const panels = [...this.children];
132
+ panels.forEach((panel, i) => {
133
+ panel.style.flex = `0 0 ${this.sizes[i] ?? 0}%`;
134
+ panel.style.order = String(i * 2);
135
+ });
136
+ }
137
+ render() {
138
+ const dividers = [];
139
+ for (let i = 0; i < this.panelCount - 1; i++) {
140
+ dividers.push(html `<div
141
+ part="divider"
142
+ role="separator"
143
+ aria-orientation=${this.orientation === 'vertical' ? 'horizontal' : 'vertical'}
144
+ aria-valuenow=${Math.round(this.sizes[i] ?? 0)}
145
+ aria-valuemin=${this.min}
146
+ aria-valuemax=${100 - this.min}
147
+ tabindex="0"
148
+ style=${`order:${i * 2 + 1}`}
149
+ @pointerdown=${(e) => this.onPointerDown(e, i)}
150
+ @keydown=${(e) => this.onDividerKeyDown(e, i)}
151
+ ></div>`);
152
+ }
153
+ return html `<div part="base"><slot></slot>${dividers}</div>`;
154
+ }
155
+ }
156
+ __decorate([
157
+ property({ attribute: false })
158
+ ], LyraSplit.prototype, "sizes", void 0);
159
+ __decorate([
160
+ property({ type: Number })
161
+ ], LyraSplit.prototype, "min", void 0);
162
+ __decorate([
163
+ property({ reflect: true })
164
+ ], LyraSplit.prototype, "orientation", void 0);
165
+ __decorate([
166
+ property({ attribute: 'storage-key' })
167
+ ], LyraSplit.prototype, "storageKey", void 0);
168
+ __decorate([
169
+ state()
170
+ ], LyraSplit.prototype, "panelCount", void 0);
171
+ defineElement('split', LyraSplit);
@@ -0,0 +1 @@
1
+ export declare const styles: import("lit").CSSResult;
@@ -0,0 +1,51 @@
1
+ import { css } from 'lit';
2
+ export const styles = css `
3
+ :host {
4
+ display: block;
5
+ inline-size: 100%;
6
+ block-size: 100%;
7
+ }
8
+ [part='base'] {
9
+ display: flex;
10
+ inline-size: 100%;
11
+ block-size: 100%;
12
+ }
13
+ :host([orientation='vertical']) [part='base'] {
14
+ flex-direction: column;
15
+ }
16
+ [part='divider'] {
17
+ position: relative;
18
+ flex: 0 0 auto;
19
+ inline-size: 3px;
20
+ block-size: auto;
21
+ background: var(--lyra-color-border);
22
+ cursor: col-resize;
23
+ touch-action: none;
24
+ }
25
+ /* Transparent hit-slop: widens the draggable/tappable box along the resize
26
+ axis only, without changing the divider's visible 3px width. Generated
27
+ content is part of the originating element's hit-test box, so pointer
28
+ events here still resolve e.target to [part="divider"] itself. */
29
+ [part='divider']::before {
30
+ content: '';
31
+ position: absolute;
32
+ inset-block: 0;
33
+ inset-inline: -6px;
34
+ }
35
+ :host([orientation='vertical']) [part='divider'] {
36
+ inline-size: auto;
37
+ block-size: 3px;
38
+ cursor: row-resize;
39
+ }
40
+ :host([orientation='vertical']) [part='divider']::before {
41
+ inset-block: -6px;
42
+ inset-inline: 0;
43
+ }
44
+ [part='divider']:hover {
45
+ background: var(--lyra-color-brand);
46
+ }
47
+ [part='divider']:focus-visible {
48
+ outline: var(--lyra-focus-ring-width) solid var(--lyra-focus-ring-color);
49
+ outline-offset: var(--lyra-focus-ring-offset);
50
+ }
51
+ `;
@@ -0,0 +1,36 @@
1
+ import { type TemplateResult } from 'lit';
2
+ import { LyraElement } from '../../internal/lyra-element.js';
3
+ export type StatVariant = 'neutral' | 'success' | 'warning' | 'danger';
4
+ export type StatGoodDirection = 'up' | 'down';
5
+ /**
6
+ * `<lyra-stat>` — a KPI/stat card. First-party invention consolidating the
7
+ * "metric row" / "KPI card" pattern reinvented in every surveyed repo.
8
+ *
9
+ * @customElement lyra-stat
10
+ * @slot - Leading icon.
11
+ * @slot caption - Rich caption content (overrides the `caption` attribute).
12
+ * @csspart base, icon, label, value, unit, trend, caption
13
+ */
14
+ export declare class LyraStat extends LyraElement {
15
+ static styles: import("lit").CSSResultGroup[];
16
+ label: string;
17
+ value: string;
18
+ unit: string;
19
+ variant: StatVariant;
20
+ trend: number;
21
+ caption: string;
22
+ /** Which trend direction counts as "good" — inverts arrow/color polarity for
23
+ * cost/latency/error-rate-style metrics where a decrease is the win. */
24
+ goodDirection: StatGoodDirection;
25
+ private hasIcon;
26
+ private hasCaptionSlot;
27
+ protected willUpdate(): void;
28
+ private onIconSlotChange;
29
+ private onCaptionSlotChange;
30
+ render(): TemplateResult;
31
+ }
32
+ declare global {
33
+ interface HTMLElementTagNameMap {
34
+ 'lyra-stat': LyraStat;
35
+ }
36
+ }