@aceshooting/lyra-ui 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +1 -1
  2. package/custom-elements.json +9289 -8457
  3. package/dist/components/chart/chart.d.ts +11 -0
  4. package/dist/components/chart/chart.js +54 -2
  5. package/dist/components/chart/lite-chart.d.ts +9 -0
  6. package/dist/components/chart/lite-chart.js +41 -1
  7. package/dist/components/chart/lite-chart.stories.d.ts +2 -0
  8. package/dist/components/chart/lite-chart.stories.js +16 -0
  9. package/dist/components/combobox/combobox.js +1 -1
  10. package/dist/components/flag/flag.d.ts +26 -5
  11. package/dist/components/flag/flag.js +44 -3
  12. package/dist/components/flag/flag.stories.d.ts +1 -0
  13. package/dist/components/flag/flag.stories.js +14 -0
  14. package/dist/components/heatmap/heatmap.d.ts +15 -3
  15. package/dist/components/heatmap/heatmap.js +24 -7
  16. package/dist/components/heatmap/heatmap.stories.d.ts +5 -0
  17. package/dist/components/heatmap/heatmap.stories.js +25 -0
  18. package/dist/components/select/select.d.ts +3 -0
  19. package/dist/components/select/select.js +6 -42
  20. package/dist/components/select/select.stories.d.ts +2 -0
  21. package/dist/components/select/select.stories.js +16 -0
  22. package/dist/components/select/select.styles.js +26 -2
  23. package/dist/components/stat/stat.d.ts +16 -0
  24. package/dist/components/stat/stat.js +41 -1
  25. package/dist/components/stat/stat.stories.d.ts +1 -0
  26. package/dist/components/stat/stat.stories.js +17 -0
  27. package/dist/components/stat/stat.styles.js +20 -0
  28. package/dist/components/widget/widget.d.ts +10 -0
  29. package/dist/components/widget/widget.js +24 -1
  30. package/dist/components/widget/widget.stories.d.ts +2 -0
  31. package/dist/components/widget/widget.stories.js +33 -0
  32. package/dist/components/widget/widget.styles.js +9 -2
  33. package/dist/components/word-cloud/word-cloud.styles.js +1 -0
  34. package/llms-full.txt +90 -20
  35. package/package.json +3 -3
@@ -14,47 +14,6 @@ import { chevronIcon } from '../../internal/icons.js';
14
14
  import { styles } from './select.styles.js';
15
15
  import { LyraOption } from '../combobox/option.js';
16
16
  import '../combobox/option.js';
17
- /**
18
- * `<lyra-select>` — a plain closed-list dropdown: a direct `<lyra-*>`
19
- * counterpart to `<wa-select>`/`<wa-option>`. Trigger is a button (not a text
20
- * input) -- click/Enter/Space/ArrowDown opens it, there's no typing-to-filter.
21
- * A printable keypress instead jumps (or, while closed, directly selects) the
22
- * next option whose label starts with what's been typed, like a native
23
- * `<select>`'s type-ahead.
24
- *
25
- * Options are `<lyra-option value>` children, the same element `<lyra-combobox>`
26
- * uses. Unlike `lyra-combobox` this is single-select only, with no filter/
27
- * source/with-clear/max-options-visible/empty-text/max-render/multiple surface
28
- * -- see `<lyra-combobox>` for the filterable/multi-select case.
29
- *
30
- * Reuses `lyra-combobox`'s popup positioning (`internal/positioner.js`) and
31
- * click-outside/Escape/Home/End/Arrow-key listbox navigation patterns,
32
- * adapted to a trigger button that keeps DOM focus throughout (the listbox's
33
- * "active" row is conveyed via `aria-activedescendant`, never actual focus),
34
- * matching the WAI-ARIA "select-only combobox" pattern.
35
- *
36
- * @customElement lyra-select
37
- * @slot - `<lyra-option>` elements.
38
- * @slot label - Custom label content.
39
- * @slot hint - Custom hint content.
40
- * @slot error - Custom error content.
41
- * @event change - The selection changed.
42
- * @event input - Fired alongside `change` on every selection change (native
43
- * `<select>` doesn't meaningfully distinguish the two either).
44
- * @event lyra-show - The listbox opened.
45
- * @event lyra-hide - The listbox closed.
46
- * @csspart form-control - The outer wrapper around label, trigger, listbox, error and hint.
47
- * @csspart form-control-label - The `<label>` element.
48
- * @csspart trigger - The trigger button (positioning anchor).
49
- * @csspart listbox - The options popover.
50
- * @csspart option - An option row.
51
- * @csspart option-dot - An option row's leading status dot (when `dot-color` is set).
52
- * @csspart option-label - An option row's label/sub wrapper.
53
- * @csspart option-sub - An option row's secondary line (when `sub` is set).
54
- * @csspart expand-icon - The dropdown indicator.
55
- * @csspart error - The error message.
56
- * @csspart hint - The hint message.
57
- */
58
17
  export class LyraSelect extends LyraElement {
59
18
  static { this.formAssociated = true; }
60
19
  static { this.styles = [LyraElement.styles, styles]; }
@@ -71,6 +30,8 @@ export class LyraSelect extends LyraElement {
71
30
  this.hint = '';
72
31
  this.errorText = '';
73
32
  this.open = false;
33
+ /** Visual size — same `xs`–`xl` scale as `lyra-toast-item`'s `size`. */
34
+ this.size = 'm';
74
35
  this.activeIndex = -1;
75
36
  this.options = [];
76
37
  // Set on the trigger button's first `blur`; gates the `data-invalid`
@@ -438,7 +399,7 @@ export class LyraSelect extends LyraElement {
438
399
  aria-expanded=${this.open ? 'true' : 'false'}
439
400
  aria-controls=${this.listId}
440
401
  aria-activedescendant=${activeId}
441
- aria-label=${this.label || this.placeholder || 'Select'}
402
+ aria-label=${this.getAttribute('aria-label') || this.label || this.placeholder || 'Select'}
442
403
  aria-required=${this.required ? 'true' : 'false'}
443
404
  aria-invalid=${this.touched && !this.internals.validity.valid ? 'true' : 'false'}
444
405
  ?disabled=${this.disabled}
@@ -495,6 +456,9 @@ __decorate([
495
456
  __decorate([
496
457
  property({ type: Boolean, reflect: true })
497
458
  ], LyraSelect.prototype, "open", void 0);
459
+ __decorate([
460
+ property({ reflect: true })
461
+ ], LyraSelect.prototype, "size", void 0);
498
462
  __decorate([
499
463
  state()
500
464
  ], LyraSelect.prototype, "activeIndex", void 0);
@@ -11,6 +11,8 @@ export declare const PreSelectedValue: Story;
11
11
  * rich-row support as `<lyra-combobox>`.
12
12
  */
13
13
  export declare const RichRows: Story;
14
+ /** `size` spans the same `xs`–`xl` scale as `lyra-toast-item`, default `m`. */
15
+ export declare const Sizes: Story;
14
16
  export declare const Disabled: Story;
15
17
  /**
16
18
  * `required` blocks form submission while empty; `error-text` renders a
@@ -46,6 +46,22 @@ export const RichRows = {
46
46
  </lyra-select>
47
47
  `,
48
48
  };
49
+ /** `size` spans the same `xs`–`xl` scale as `lyra-toast-item`, default `m`. */
50
+ export const Sizes = {
51
+ render: () => {
52
+ const sizes = ['xs', 's', 'm', 'l', 'xl'];
53
+ return html `
54
+ <div style="display: flex; flex-direction: column; gap: 1rem; max-width: 20rem">
55
+ ${sizes.map((size) => html `
56
+ <lyra-select size=${size} placeholder=${`Size "${size}"`}>
57
+ <lyra-option value="a">Apple</lyra-option>
58
+ <lyra-option value="b">Banana</lyra-option>
59
+ </lyra-select>
60
+ `)}
61
+ </div>
62
+ `;
63
+ },
64
+ };
49
65
  export const Disabled = {
50
66
  render: () => html `
51
67
  <lyra-select label="Disabled" disabled placeholder="Can't touch this" style="max-width: 20rem">
@@ -2,6 +2,29 @@ import { css } from 'lit';
2
2
  export const styles = css `
3
3
  :host {
4
4
  display: block;
5
+ --lyra-select-trigger-padding: var(--lyra-space-xs) var(--lyra-space-s);
6
+ --lyra-select-trigger-min-height: 2.5rem;
7
+ --lyra-select-font-size: 1rem;
8
+ }
9
+ :host([size='xs']) {
10
+ --lyra-select-trigger-padding: 0.125rem var(--lyra-space-xs);
11
+ --lyra-select-trigger-min-height: 1.5rem;
12
+ --lyra-select-font-size: 0.75rem;
13
+ }
14
+ :host([size='s']) {
15
+ --lyra-select-trigger-padding: var(--lyra-space-xs) var(--lyra-space-xs);
16
+ --lyra-select-trigger-min-height: 1.875rem;
17
+ --lyra-select-font-size: 0.8125rem;
18
+ }
19
+ :host([size='l']) {
20
+ --lyra-select-trigger-padding: var(--lyra-space-s) var(--lyra-space-m);
21
+ --lyra-select-trigger-min-height: 3rem;
22
+ --lyra-select-font-size: 1.125rem;
23
+ }
24
+ :host([size='xl']) {
25
+ --lyra-select-trigger-padding: var(--lyra-space-m) var(--lyra-space-l);
26
+ --lyra-select-trigger-min-height: 3.5rem;
27
+ --lyra-select-font-size: 1.25rem;
5
28
  }
6
29
  [part='form-control-label'] {
7
30
  display: block;
@@ -30,13 +53,14 @@ export const styles = css `
30
53
  justify-content: space-between;
31
54
  gap: var(--lyra-space-xs);
32
55
  inline-size: 100%;
33
- min-block-size: 2.5rem;
34
- padding: var(--lyra-space-xs) var(--lyra-space-s);
56
+ min-block-size: var(--lyra-select-trigger-min-height);
57
+ padding: var(--lyra-select-trigger-padding);
35
58
  border: 1px solid var(--lyra-color-border);
36
59
  border-radius: var(--lyra-radius);
37
60
  background: var(--lyra-color-surface);
38
61
  color: inherit;
39
62
  font: inherit;
63
+ font-size: var(--lyra-select-font-size);
40
64
  text-align: start;
41
65
  cursor: pointer;
42
66
  }
@@ -15,6 +15,7 @@ export interface StatRow {
15
15
  * @slot caption - Rich caption content (overrides the `caption` attribute).
16
16
  * @slot spark - A sparkline (e.g. `<lyra-sparkline>`) or other compact trend
17
17
  * visual. `lyra-stat` only reserves the slot; it doesn't render one itself.
18
+ * @slot sub - Rich sub-line content (overrides the `sub` attribute).
18
19
  * @csspart base - The component's root wrapper.
19
20
  * @csspart icon - Container for the leading icon slot.
20
21
  * @csspart label - The label text.
@@ -22,6 +23,7 @@ export interface StatRow {
22
23
  * @csspart value - The value text.
23
24
  * @csspart unit - The unit text.
24
25
  * @csspart trend - The trend pill.
26
+ * @csspart sub - Container for the `sub` attribute/slot.
25
27
  * @csspart spark - Container for the `spark` slot.
26
28
  * @csspart caption - Container for the caption attribute/slot.
27
29
  * @csspart rows - Container for the `rows` breakdown list.
@@ -46,13 +48,27 @@ export declare class LyraStat extends LyraElement {
46
48
  * the status `variant`; see the `[part='value']` selector below for how
47
49
  * the two combine. */
48
50
  emphasis: boolean;
51
+ /** Exact value shown as a hover/focus tooltip on the headline `value` (e.g. `value="$1.2K"
52
+ * exact-value="$1,204.37"`). Also makes `[part='value']` keyboard-focusable so the tooltip is
53
+ * reachable without a pointer. */
54
+ exactValue: string;
55
+ /** A secondary line distinct from `caption` (e.g. a comparison-period label), rendered between the
56
+ * trend pill and the caption. */
57
+ sub: string;
58
+ /** Renders `value` as smaller/lighter prose (e.g. a loading/status message) instead of the bold
59
+ * numeric headline style, and hides `unit`. */
60
+ prose: boolean;
61
+ /** Tighter padding for constrained spaces — same convention as `lyra-empty`'s `compact`. */
62
+ compact: boolean;
49
63
  private hasIcon;
50
64
  private hasCaptionSlot;
51
65
  private hasSparkSlot;
66
+ private hasSubSlot;
52
67
  protected willUpdate(): void;
53
68
  private onIconSlotChange;
54
69
  private onCaptionSlotChange;
55
70
  private onSparkSlotChange;
71
+ private onSubSlotChange;
56
72
  render(): TemplateResult;
57
73
  }
58
74
  declare global {
@@ -20,6 +20,7 @@ import { styles } from './stat.styles.js';
20
20
  * @slot caption - Rich caption content (overrides the `caption` attribute).
21
21
  * @slot spark - A sparkline (e.g. `<lyra-sparkline>`) or other compact trend
22
22
  * visual. `lyra-stat` only reserves the slot; it doesn't render one itself.
23
+ * @slot sub - Rich sub-line content (overrides the `sub` attribute).
23
24
  * @csspart base - The component's root wrapper.
24
25
  * @csspart icon - Container for the leading icon slot.
25
26
  * @csspart label - The label text.
@@ -27,6 +28,7 @@ import { styles } from './stat.styles.js';
27
28
  * @csspart value - The value text.
28
29
  * @csspart unit - The unit text.
29
30
  * @csspart trend - The trend pill.
31
+ * @csspart sub - Container for the `sub` attribute/slot.
30
32
  * @csspart spark - Container for the `spark` slot.
31
33
  * @csspart caption - Container for the caption attribute/slot.
32
34
  * @csspart rows - Container for the `rows` breakdown list.
@@ -52,12 +54,25 @@ export class LyraStat extends LyraElement {
52
54
  * the status `variant`; see the `[part='value']` selector below for how
53
55
  * the two combine. */
54
56
  this.emphasis = false;
57
+ /** Exact value shown as a hover/focus tooltip on the headline `value` (e.g. `value="$1.2K"
58
+ * exact-value="$1,204.37"`). Also makes `[part='value']` keyboard-focusable so the tooltip is
59
+ * reachable without a pointer. */
60
+ this.exactValue = '';
61
+ /** A secondary line distinct from `caption` (e.g. a comparison-period label), rendered between the
62
+ * trend pill and the caption. */
63
+ this.sub = '';
64
+ /** Renders `value` as smaller/lighter prose (e.g. a loading/status message) instead of the bold
65
+ * numeric headline style, and hides `unit`. */
66
+ this.prose = false;
67
+ /** Tighter padding for constrained spaces — same convention as `lyra-empty`'s `compact`. */
68
+ this.compact = false;
55
69
  // Same fix `lyra-empty` already established: `[part]:empty` never matches
56
70
  // because the part always contains a literal `<slot>` child. Track real
57
71
  // slot assignment in JS instead.
58
72
  this.hasIcon = false;
59
73
  this.hasCaptionSlot = false;
60
74
  this.hasSparkSlot = false;
75
+ this.hasSubSlot = false;
61
76
  this.onIconSlotChange = (e) => {
62
77
  this.hasIcon = e.target.assignedElements({ flatten: true }).length > 0;
63
78
  };
@@ -67,6 +82,9 @@ export class LyraStat extends LyraElement {
67
82
  this.onSparkSlotChange = (e) => {
68
83
  this.hasSparkSlot = e.target.assignedElements({ flatten: true }).length > 0;
69
84
  };
85
+ this.onSubSlotChange = (e) => {
86
+ this.hasSubSlot = e.target.assignedElements({ flatten: true }).length > 0;
87
+ };
70
88
  }
71
89
  static { this.styles = [LyraElement.styles, styles, srOnly]; }
72
90
  willUpdate() {
@@ -74,6 +92,7 @@ export class LyraStat extends LyraElement {
74
92
  this.hasIcon = Array.from(this.children).some((el) => !el.hasAttribute('slot'));
75
93
  this.hasCaptionSlot = Array.from(this.children).some((el) => el.getAttribute('slot') === 'caption');
76
94
  this.hasSparkSlot = Array.from(this.children).some((el) => el.getAttribute('slot') === 'spark');
95
+ this.hasSubSlot = Array.from(this.children).some((el) => el.getAttribute('slot') === 'sub');
77
96
  }
78
97
  }
79
98
  render() {
@@ -86,6 +105,7 @@ export class LyraStat extends LyraElement {
86
105
  // [part='trend'].
87
106
  const arrow = rawDirection === 'flat' ? '–' : chevronIcon();
88
107
  const hasCaption = this.hasCaptionSlot || this.caption.length > 0;
108
+ const hasSub = this.hasSubSlot || this.sub.length > 0;
89
109
  // The visible pill only ever shows the icon rotation + color to convey
90
110
  // direction and good/bad polarity; both are invisible to screen readers,
91
111
  // so mirror them into a plain-language sr-only announcement.
@@ -99,7 +119,9 @@ export class LyraStat extends LyraElement {
99
119
  ></span>
100
120
  <span part="label">${this.label}</span>
101
121
  <div part="value-row">
102
- <span part="value">${this.value}</span>
122
+ <span part="value" title=${this.exactValue || nothing} tabindex=${this.exactValue ? '0' : nothing}
123
+ >${this.value}</span
124
+ >
103
125
  <span part="unit">${this.unit}</span>
104
126
  </div>
105
127
  ${hasTrend
@@ -112,6 +134,9 @@ export class LyraStat extends LyraElement {
112
134
  <span class="sr-only">${trendAnnouncement}</span>
113
135
  </span>`
114
136
  : nothing}
137
+ <div part="sub" ?hidden=${!hasSub}>
138
+ ${this.sub}<slot name="sub" @slotchange=${this.onSubSlotChange}></slot>
139
+ </div>
115
140
  <div part="spark" ?hidden=${!this.hasSparkSlot}>
116
141
  <slot name="spark" @slotchange=${this.onSparkSlotChange}></slot>
117
142
  </div>
@@ -157,6 +182,18 @@ __decorate([
157
182
  __decorate([
158
183
  property({ type: Boolean, reflect: true })
159
184
  ], LyraStat.prototype, "emphasis", void 0);
185
+ __decorate([
186
+ property({ attribute: 'exact-value' })
187
+ ], LyraStat.prototype, "exactValue", void 0);
188
+ __decorate([
189
+ property()
190
+ ], LyraStat.prototype, "sub", void 0);
191
+ __decorate([
192
+ property({ type: Boolean, reflect: true })
193
+ ], LyraStat.prototype, "prose", void 0);
194
+ __decorate([
195
+ property({ type: Boolean, reflect: true })
196
+ ], LyraStat.prototype, "compact", void 0);
160
197
  __decorate([
161
198
  state()
162
199
  ], LyraStat.prototype, "hasIcon", void 0);
@@ -166,4 +203,7 @@ __decorate([
166
203
  __decorate([
167
204
  state()
168
205
  ], LyraStat.prototype, "hasSparkSlot", void 0);
206
+ __decorate([
207
+ state()
208
+ ], LyraStat.prototype, "hasSubSlot", void 0);
169
209
  defineElement('stat', LyraStat);
@@ -6,3 +6,4 @@ export declare const Gallery: Story;
6
6
  export declare const SparkAndBreakdown: Story;
7
7
  export declare const BreakdownRows: Story;
8
8
  export declare const Emphasis: Story;
9
+ export declare const ExactValueSubProseCompact: Story;
@@ -62,3 +62,20 @@ export const Emphasis = {
62
62
  </div>
63
63
  `,
64
64
  };
65
+ export const ExactValueSubProseCompact = {
66
+ render: () => html `
67
+ <div class="flex flex-wrap gap-4">
68
+ <lyra-stat
69
+ label="Revenue"
70
+ value="$1.2K"
71
+ exact-value="$1,204.37"
72
+ sub="vs. last week"
73
+ caption="Updated 2h ago"
74
+ trend="3.2"
75
+ variant="success"
76
+ ></lyra-stat>
77
+ <lyra-stat label="Status" prose value="Waiting for the next sync…"></lyra-stat>
78
+ <lyra-stat label="Sessions" value="9,204" sub="+312 today" compact></lyra-stat>
79
+ </div>
80
+ `,
81
+ };
@@ -84,6 +84,13 @@ export const styles = css `
84
84
  [part='spark'][hidden] {
85
85
  display: none;
86
86
  }
87
+ [part='sub'] {
88
+ font-size: 0.8125rem;
89
+ font-weight: 600;
90
+ }
91
+ [part='sub'][hidden] {
92
+ display: none;
93
+ }
87
94
  [part='caption'] {
88
95
  font-size: 0.8125rem;
89
96
  color: var(--lyra-color-text-quiet);
@@ -127,4 +134,17 @@ export const styles = css `
127
134
  :host([emphasis][variant='neutral']) [part='value'] {
128
135
  color: var(--lyra-color-brand);
129
136
  }
137
+ :host([prose]) [part='value'] {
138
+ font-size: 0.9375rem;
139
+ font-weight: 400;
140
+ font-family: inherit;
141
+ color: var(--lyra-color-text-quiet);
142
+ }
143
+ :host([prose]) [part='unit'] {
144
+ display: none;
145
+ }
146
+ :host([compact]) [part='base'] {
147
+ padding: var(--lyra-space-s);
148
+ gap: 0.125rem;
149
+ }
130
150
  `;
@@ -21,6 +21,10 @@ import { LyraElement } from '../../internal/lyra-element.js';
21
21
  * @csspart fullscreen-button - The fullscreen toggle button.
22
22
  * @csspart body - The wrapper around the default slot (the panel body).
23
23
  * @csspart backdrop - The fullscreen scrim behind the panel.
24
+ *
25
+ * `fullscreen-inset` overrides the default `var(--lyra-space-l)` inset applied to `[part="base"]`
26
+ * and `[part="backdrop"]` while fullscreen (e.g. `"0 0 0 240px"` to leave a persistent sidebar
27
+ * visible). `compact` tightens header/body padding — same convention as `lyra-empty`'s `compact`.
24
28
  */
25
29
  export declare class LyraWidget extends LyraElement {
26
30
  static styles: import("lit").CSSResultGroup[];
@@ -30,6 +34,12 @@ export declare class LyraWidget extends LyraElement {
30
34
  collapsed: boolean;
31
35
  expandable: boolean;
32
36
  fullscreen: boolean;
37
+ /** Raw CSS `inset` shorthand applied to the fullscreen panel and backdrop instead of the default
38
+ * `var(--lyra-space-l)` on every side — e.g. `"0 0 0 240px"` to leave a 240px persistent sidebar
39
+ * visible while fullscreen. */
40
+ fullscreenInset: string;
41
+ /** Tighter header/body padding for constrained spaces. */
42
+ compact: boolean;
33
43
  private hasActionsSlot;
34
44
  private releaseScrollLock?;
35
45
  private lastTrigger?;
@@ -69,6 +69,10 @@ function isRendered(el) {
69
69
  * @csspart fullscreen-button - The fullscreen toggle button.
70
70
  * @csspart body - The wrapper around the default slot (the panel body).
71
71
  * @csspart backdrop - The fullscreen scrim behind the panel.
72
+ *
73
+ * `fullscreen-inset` overrides the default `var(--lyra-space-l)` inset applied to `[part="base"]`
74
+ * and `[part="backdrop"]` while fullscreen (e.g. `"0 0 0 240px"` to leave a persistent sidebar
75
+ * visible). `compact` tightens header/body padding — same convention as `lyra-empty`'s `compact`.
72
76
  */
73
77
  export class LyraWidget extends LyraElement {
74
78
  constructor() {
@@ -79,6 +83,12 @@ export class LyraWidget extends LyraElement {
79
83
  this.collapsed = false;
80
84
  this.expandable = false;
81
85
  this.fullscreen = false;
86
+ /** Raw CSS `inset` shorthand applied to the fullscreen panel and backdrop instead of the default
87
+ * `var(--lyra-space-l)` on every side — e.g. `"0 0 0 240px"` to leave a 240px persistent sidebar
88
+ * visible while fullscreen. */
89
+ this.fullscreenInset = '';
90
+ /** Tighter header/body padding for constrained spaces. */
91
+ this.compact = false;
82
92
  this.hasActionsSlot = false;
83
93
  this.bodyId = nextId('widget-body');
84
94
  this.onActionsSlotChange = (e) => {
@@ -221,13 +231,20 @@ export class LyraWidget extends LyraElement {
221
231
  const hasLabel = this.label.length > 0;
222
232
  const hasSublabel = this.sublabel.length > 0;
223
233
  return html `
224
- ${this.fullscreen ? html `<div part="backdrop" @click=${this.onBackdropClick}></div>` : nothing}
234
+ ${this.fullscreen
235
+ ? html `<div
236
+ part="backdrop"
237
+ style=${this.fullscreenInset ? `--lyra-widget-fullscreen-inset:${this.fullscreenInset}` : nothing}
238
+ @click=${this.onBackdropClick}
239
+ ></div>`
240
+ : nothing}
225
241
  <div
226
242
  part="base"
227
243
  role=${this.fullscreen ? 'dialog' : nothing}
228
244
  aria-modal=${this.fullscreen ? 'true' : nothing}
229
245
  aria-label=${this.fullscreen ? (hasLabel ? this.label : 'Fullscreen panel') : nothing}
230
246
  tabindex=${this.fullscreen ? '-1' : nothing}
247
+ style=${this.fullscreenInset ? `--lyra-widget-fullscreen-inset:${this.fullscreenInset}` : nothing}
231
248
  >
232
249
  <div part="header">
233
250
  <div part="title">
@@ -288,6 +305,12 @@ __decorate([
288
305
  __decorate([
289
306
  property({ type: Boolean, reflect: true })
290
307
  ], LyraWidget.prototype, "fullscreen", void 0);
308
+ __decorate([
309
+ property({ attribute: 'fullscreen-inset' })
310
+ ], LyraWidget.prototype, "fullscreenInset", void 0);
311
+ __decorate([
312
+ property({ type: Boolean, reflect: true })
313
+ ], LyraWidget.prototype, "compact", void 0);
291
314
  __decorate([
292
315
  state()
293
316
  ], LyraWidget.prototype, "hasActionsSlot", void 0);
@@ -5,3 +5,5 @@ type Story = StoryObj;
5
5
  export declare const Default: Story;
6
6
  export declare const CollapsedInitially: Story;
7
7
  export declare const FullscreenInitially: Story;
8
+ export declare const FullscreenWithSidebarInset: Story;
9
+ export declare const Compact: Story;
@@ -39,3 +39,36 @@ export const FullscreenInitially = {
39
39
  </lyra-widget>
40
40
  `,
41
41
  };
42
+ export const FullscreenWithSidebarInset = {
43
+ render: () => html `
44
+ <div style="position: relative;">
45
+ <div
46
+ style="position: fixed; inset: 0 auto 0 0; inline-size: 240px; background: #1e293b; color: #fff; padding: 1rem; z-index: 1001;"
47
+ >
48
+ Persistent sidebar (stays visible above the widget's fullscreen panel)
49
+ </div>
50
+ <lyra-widget
51
+ label="Load profile"
52
+ sublabel="Last 7 days"
53
+ expandable
54
+ fullscreen
55
+ fullscreen-inset="0 0 0 240px"
56
+ style="max-width: 28rem;"
57
+ >
58
+ <div style="padding: 1rem;">
59
+ <p style="margin: 0;">
60
+ Fullscreen with <code>fullscreen-inset="0 0 0 240px"</code> — the panel and backdrop leave
61
+ room for the 240px sidebar instead of covering it.
62
+ </p>
63
+ </div>
64
+ </lyra-widget>
65
+ </div>
66
+ `,
67
+ };
68
+ export const Compact = {
69
+ render: () => html `
70
+ <lyra-widget label="Alerts" sublabel="3 active" compact collapsible expandable style="max-width: 28rem;">
71
+ <div style="padding: 0.5rem;">Tighter header/body padding for constrained spaces.</div>
72
+ </lyra-widget>
73
+ `,
74
+ };
@@ -6,6 +6,7 @@ export const styles = css `
6
6
  without a raw literal leaking into the public API (no shared
7
7
  --wa-*-overlay token exists in the design system to resolve through). */
8
8
  --lyra-widget-overlay-color: rgb(0 0 0 / 0.5);
9
+ --lyra-widget-fullscreen-inset: var(--lyra-space-l);
9
10
  }
10
11
  [part='base'] {
11
12
  display: flex;
@@ -77,13 +78,13 @@ export const styles = css `
77
78
  }
78
79
  [part='backdrop'] {
79
80
  position: fixed;
80
- inset: 0;
81
+ inset: var(--lyra-widget-fullscreen-inset, 0);
81
82
  background: var(--lyra-widget-overlay-color);
82
83
  z-index: 999;
83
84
  }
84
85
  :host([fullscreen]) [part='base'] {
85
86
  position: fixed;
86
- inset: var(--lyra-space-l);
87
+ inset: var(--lyra-widget-fullscreen-inset);
87
88
  z-index: 1000;
88
89
  box-shadow: var(--lyra-shadow);
89
90
  }
@@ -91,6 +92,12 @@ export const styles = css `
91
92
  overflow: auto;
92
93
  block-size: 100%;
93
94
  }
95
+ :host([compact]) [part='header'] {
96
+ padding: var(--lyra-space-xs) var(--lyra-space-s);
97
+ }
98
+ :host([compact]) [part='body'] {
99
+ padding: var(--lyra-space-s);
100
+ }
94
101
  @media (prefers-reduced-motion: reduce) {
95
102
  [part='collapse-button'],
96
103
  [part='fullscreen-button'] {
@@ -20,6 +20,7 @@ export const styles = css `
20
20
  display: flex;
21
21
  flex-direction: column;
22
22
  gap: var(--lyra-space-xs);
23
+ block-size: 100%;
23
24
  }
24
25
  svg {
25
26
  display: block;