@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.
- package/README.md +77 -6
- package/custom-elements.json +12015 -0
- package/dist/components/chart/bar-chart.d.ts +14 -0
- package/dist/components/chart/bar-chart.js +14 -0
- package/dist/components/chart/box-plot.d.ts +46 -0
- package/dist/components/chart/box-plot.js +145 -0
- package/dist/components/chart/box-plot.styles.d.ts +1 -0
- package/dist/components/chart/box-plot.styles.js +22 -0
- package/dist/components/chart/bubble-chart.d.ts +19 -0
- package/dist/components/chart/bubble-chart.js +18 -0
- package/dist/components/chart/chart-loader.d.ts +8 -0
- package/dist/components/chart/chart-loader.js +26 -0
- package/dist/components/chart/chart.d.ts +91 -0
- package/dist/components/chart/chart.js +275 -0
- package/dist/components/chart/chart.styles.d.ts +1 -0
- package/dist/components/chart/chart.styles.js +33 -0
- package/dist/components/chart/doughnut-chart.d.ts +15 -0
- package/dist/components/chart/doughnut-chart.js +14 -0
- package/dist/components/chart/histogram-bin.d.ts +6 -0
- package/dist/components/chart/histogram-bin.js +19 -0
- package/dist/components/chart/histogram.d.ts +20 -0
- package/dist/components/chart/histogram.js +70 -0
- package/dist/components/chart/histogram.styles.d.ts +1 -0
- package/dist/components/chart/histogram.styles.js +1 -0
- package/dist/components/chart/line-chart.d.ts +14 -0
- package/dist/components/chart/line-chart.js +14 -0
- package/dist/components/chart/pie-chart.d.ts +16 -0
- package/dist/components/chart/pie-chart.js +15 -0
- package/dist/components/chart/polar-area-chart.d.ts +15 -0
- package/dist/components/chart/polar-area-chart.js +14 -0
- package/dist/components/chart/radar-chart.d.ts +15 -0
- package/dist/components/chart/radar-chart.js +14 -0
- package/dist/components/chart/scatter-chart.d.ts +15 -0
- package/dist/components/chart/scatter-chart.js +15 -0
- package/dist/components/combobox/combobox.d.ts +16 -0
- package/dist/components/combobox/combobox.js +113 -6
- package/dist/components/combobox/combobox.styles.js +68 -7
- package/dist/components/combobox/option.d.ts +2 -2
- package/dist/components/combobox/option.js +11 -7
- package/dist/components/date-picker/date-input.d.ts +12 -1
- package/dist/components/date-picker/date-input.js +68 -6
- package/dist/components/date-picker/date-input.styles.js +58 -6
- package/dist/components/date-picker/date-picker.js +5 -2
- package/dist/components/date-picker/date-picker.styles.js +9 -2
- package/dist/components/empty/empty.d.ts +32 -0
- package/dist/components/empty/empty.js +90 -0
- package/dist/components/empty/empty.styles.d.ts +1 -0
- package/dist/components/empty/empty.styles.js +42 -0
- package/dist/components/export-button/csv.d.ts +10 -0
- package/dist/components/export-button/csv.js +28 -0
- package/dist/components/export-button/export-button.d.ts +41 -0
- package/dist/components/export-button/export-button.js +151 -0
- package/dist/components/export-button/export-button.styles.d.ts +1 -0
- package/dist/components/export-button/export-button.styles.js +79 -0
- package/dist/components/file-input/accept.d.ts +14 -0
- package/dist/components/file-input/accept.js +30 -0
- package/dist/components/file-input/file-input.d.ts +47 -0
- package/dist/components/file-input/file-input.js +182 -0
- package/dist/components/file-input/file-input.styles.d.ts +1 -0
- package/dist/components/file-input/file-input.styles.js +44 -0
- package/dist/components/flag/flag.d.ts +4 -0
- package/dist/components/flag/flag.js +24 -1
- package/dist/components/flag/flag.styles.js +1 -1
- package/dist/components/gauge/gauge.d.ts +29 -0
- package/dist/components/gauge/gauge.js +133 -0
- package/dist/components/gauge/gauge.styles.d.ts +1 -0
- package/dist/components/gauge/gauge.styles.js +60 -0
- package/dist/components/graph/graph.d.ts +68 -0
- package/dist/components/graph/graph.js +276 -0
- package/dist/components/graph/graph.styles.d.ts +1 -0
- package/dist/components/graph/graph.styles.js +34 -0
- package/dist/components/heatmap/heatmap-scale.d.ts +8 -0
- package/dist/components/heatmap/heatmap-scale.js +17 -0
- package/dist/components/heatmap/heatmap.d.ts +66 -0
- package/dist/components/heatmap/heatmap.js +252 -0
- package/dist/components/heatmap/heatmap.styles.d.ts +1 -0
- package/dist/components/heatmap/heatmap.styles.js +39 -0
- package/dist/components/map/map-loader.d.ts +8 -0
- package/dist/components/map/map-loader.js +17 -0
- package/dist/components/map/map.d.ts +49 -0
- package/dist/components/map/map.js +189 -0
- package/dist/components/map/map.styles.d.ts +1 -0
- package/dist/components/map/map.styles.js +48 -0
- package/dist/components/playback/playback.d.ts +51 -0
- package/dist/components/playback/playback.js +146 -0
- package/dist/components/playback/playback.styles.d.ts +1 -0
- package/dist/components/playback/playback.styles.js +40 -0
- package/dist/components/skeleton/skeleton.d.ts +26 -0
- package/dist/components/skeleton/skeleton.js +65 -0
- package/dist/components/skeleton/skeleton.styles.d.ts +1 -0
- package/dist/components/skeleton/skeleton.styles.js +57 -0
- package/dist/components/split/split.d.ts +41 -0
- package/dist/components/split/split.js +171 -0
- package/dist/components/split/split.styles.d.ts +1 -0
- package/dist/components/split/split.styles.js +51 -0
- package/dist/components/stat/stat.d.ts +36 -0
- package/dist/components/stat/stat.js +117 -0
- package/dist/components/stat/stat.styles.d.ts +1 -0
- package/dist/components/stat/stat.styles.js +86 -0
- package/dist/components/table/table.d.ts +54 -0
- package/dist/components/table/table.js +201 -0
- package/dist/components/table/table.styles.d.ts +1 -0
- package/dist/components/table/table.styles.js +98 -0
- package/dist/components/time-range/time-range.d.ts +40 -0
- package/dist/components/time-range/time-range.js +219 -0
- package/dist/components/time-range/time-range.styles.d.ts +1 -0
- package/dist/components/time-range/time-range.styles.js +78 -0
- package/dist/components/toast/toast-item.js +10 -2
- package/dist/components/toast/toast-item.styles.js +13 -4
- package/dist/components/toast/toast.styles.js +0 -1
- package/dist/components/tree/tree-node.d.ts +50 -0
- package/dist/components/tree/tree-node.js +196 -0
- package/dist/components/tree/tree.d.ts +65 -0
- package/dist/components/tree/tree.js +227 -0
- package/dist/components/tree/tree.styles.d.ts +1 -0
- package/dist/components/tree/tree.styles.js +13 -0
- package/dist/internal/form-associated.js +49 -1
- package/dist/internal/icons.d.ts +11 -0
- package/dist/internal/icons.js +64 -0
- package/dist/internal/tokens.styles.js +41 -1
- package/dist/lyra.d.ts +67 -0
- package/dist/lyra.js +55 -0
- package/llms-full.txt +1425 -0
- package/llms.txt +47 -0
- 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
|
+
}
|