@aceshooting/lyra-ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +98 -0
- package/dist/components/combobox/combobox.d.ts +90 -0
- package/dist/components/combobox/combobox.js +398 -0
- package/dist/components/combobox/combobox.styles.d.ts +1 -0
- package/dist/components/combobox/combobox.styles.js +148 -0
- package/dist/components/combobox/option.d.ts +32 -0
- package/dist/components/combobox/option.js +62 -0
- package/dist/components/date-picker/calendar-core.d.ts +20 -0
- package/dist/components/date-picker/calendar-core.js +71 -0
- package/dist/components/date-picker/date-input.d.ts +57 -0
- package/dist/components/date-picker/date-input.js +233 -0
- package/dist/components/date-picker/date-input.styles.d.ts +1 -0
- package/dist/components/date-picker/date-input.styles.js +67 -0
- package/dist/components/date-picker/date-picker.d.ts +64 -0
- package/dist/components/date-picker/date-picker.js +310 -0
- package/dist/components/date-picker/date-picker.styles.d.ts +1 -0
- package/dist/components/date-picker/date-picker.styles.js +101 -0
- package/dist/components/flag/flag.d.ts +35 -0
- package/dist/components/flag/flag.js +91 -0
- package/dist/components/flag/flag.styles.d.ts +1 -0
- package/dist/components/flag/flag.styles.js +27 -0
- package/dist/components/flag/language-map.d.ts +11 -0
- package/dist/components/flag/language-map.js +62 -0
- package/dist/components/sparkline/sparkline.d.ts +30 -0
- package/dist/components/sparkline/sparkline.js +74 -0
- package/dist/components/sparkline/sparkline.styles.d.ts +1 -0
- package/dist/components/sparkline/sparkline.styles.js +31 -0
- package/dist/components/toast/toast-item.d.ts +49 -0
- package/dist/components/toast/toast-item.js +123 -0
- package/dist/components/toast/toast-item.styles.d.ts +1 -0
- package/dist/components/toast/toast-item.styles.js +81 -0
- package/dist/components/toast/toast.d.ts +32 -0
- package/dist/components/toast/toast.js +48 -0
- package/dist/components/toast/toast.styles.d.ts +1 -0
- package/dist/components/toast/toast.styles.js +52 -0
- package/dist/components/toast/toaster.d.ts +26 -0
- package/dist/components/toast/toaster.js +41 -0
- package/dist/internal/a11y.d.ts +4 -0
- package/dist/internal/a11y.js +18 -0
- package/dist/internal/form-associated.d.ts +26 -0
- package/dist/internal/form-associated.js +56 -0
- package/dist/internal/lyra-element.d.ts +11 -0
- package/dist/internal/lyra-element.js +21 -0
- package/dist/internal/positioner.d.ts +10 -0
- package/dist/internal/positioner.js +21 -0
- package/dist/internal/prefix.d.ts +4 -0
- package/dist/internal/prefix.js +8 -0
- package/dist/internal/tokens.styles.d.ts +1 -0
- package/dist/internal/tokens.styles.js +35 -0
- package/dist/lyra.d.ts +26 -0
- package/dist/lyra.js +23 -0
- package/package.json +64 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default mapping from a language subtag to a representative country flag
|
|
3
|
+
* (ISO 3166-1 alpha-2). Languages don't map 1:1 to countries; these are the
|
|
4
|
+
* conventional choices for language pickers. Override per-app as needed.
|
|
5
|
+
*/
|
|
6
|
+
export const LANGUAGE_TO_COUNTRY = {
|
|
7
|
+
en: 'gb',
|
|
8
|
+
fr: 'fr',
|
|
9
|
+
de: 'de',
|
|
10
|
+
es: 'es',
|
|
11
|
+
it: 'it',
|
|
12
|
+
pt: 'pt',
|
|
13
|
+
nl: 'nl',
|
|
14
|
+
ar: 'sa',
|
|
15
|
+
zh: 'cn',
|
|
16
|
+
ja: 'jp',
|
|
17
|
+
ko: 'kr',
|
|
18
|
+
ru: 'ru',
|
|
19
|
+
pl: 'pl',
|
|
20
|
+
tr: 'tr',
|
|
21
|
+
sv: 'se',
|
|
22
|
+
da: 'dk',
|
|
23
|
+
fi: 'fi',
|
|
24
|
+
no: 'no',
|
|
25
|
+
nb: 'no',
|
|
26
|
+
cs: 'cz',
|
|
27
|
+
sk: 'sk',
|
|
28
|
+
el: 'gr',
|
|
29
|
+
he: 'il',
|
|
30
|
+
hi: 'in',
|
|
31
|
+
th: 'th',
|
|
32
|
+
vi: 'vn',
|
|
33
|
+
id: 'id',
|
|
34
|
+
ms: 'my',
|
|
35
|
+
uk: 'ua',
|
|
36
|
+
ro: 'ro',
|
|
37
|
+
hu: 'hu',
|
|
38
|
+
bg: 'bg',
|
|
39
|
+
hr: 'hr',
|
|
40
|
+
sr: 'rs',
|
|
41
|
+
sl: 'si',
|
|
42
|
+
et: 'ee',
|
|
43
|
+
lv: 'lv',
|
|
44
|
+
lt: 'lt',
|
|
45
|
+
fa: 'ir',
|
|
46
|
+
ur: 'pk',
|
|
47
|
+
bn: 'bd',
|
|
48
|
+
ta: 'in',
|
|
49
|
+
ca: 'es',
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Resolve a BCP-47-ish language tag to a flag country code.
|
|
53
|
+
* A region subtag wins (`en-US` → `us`); otherwise the base language is mapped.
|
|
54
|
+
*/
|
|
55
|
+
export function languageToCountry(language) {
|
|
56
|
+
const parts = language.toLowerCase().split(/[-_]/);
|
|
57
|
+
const base = parts[0];
|
|
58
|
+
const region = parts[1];
|
|
59
|
+
if (region && region.length === 2)
|
|
60
|
+
return region;
|
|
61
|
+
return LANGUAGE_TO_COUNTRY[base];
|
|
62
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type TemplateResult } from 'lit';
|
|
2
|
+
import { LyraElement } from '../../internal/lyra-element.js';
|
|
3
|
+
/**
|
|
4
|
+
* `<lyra-sparkline>` — a zero-dependency inline SVG trend chart.
|
|
5
|
+
* Mirrors the Web Awesome `<wa-sparkline>` API under the `lyra-` prefix.
|
|
6
|
+
*
|
|
7
|
+
* @customElement lyra-sparkline
|
|
8
|
+
* @csspart line - The line path (type="line").
|
|
9
|
+
* @csspart area - The filled area under the line (type="area").
|
|
10
|
+
* @csspart bar - Each bar rectangle (type="bar").
|
|
11
|
+
*/
|
|
12
|
+
export declare class LyraSparkline extends LyraElement {
|
|
13
|
+
static styles: import("lit").CSSResultGroup[];
|
|
14
|
+
/** The data series to plot. */
|
|
15
|
+
values: number[];
|
|
16
|
+
/** Rendering style. */
|
|
17
|
+
type: 'line' | 'bar' | 'area';
|
|
18
|
+
/** Lower bound of the value scale (defaults to the data minimum). */
|
|
19
|
+
min?: number;
|
|
20
|
+
/** Upper bound of the value scale (defaults to the data maximum). */
|
|
21
|
+
max?: number;
|
|
22
|
+
private points;
|
|
23
|
+
protected willUpdate(): void;
|
|
24
|
+
render(): TemplateResult;
|
|
25
|
+
}
|
|
26
|
+
declare global {
|
|
27
|
+
interface HTMLElementTagNameMap {
|
|
28
|
+
'lyra-sparkline': LyraSparkline;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
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, svg } 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 { styles } from './sparkline.styles.js';
|
|
12
|
+
const VIEW = 100;
|
|
13
|
+
/**
|
|
14
|
+
* `<lyra-sparkline>` — a zero-dependency inline SVG trend chart.
|
|
15
|
+
* Mirrors the Web Awesome `<wa-sparkline>` API under the `lyra-` prefix.
|
|
16
|
+
*
|
|
17
|
+
* @customElement lyra-sparkline
|
|
18
|
+
* @csspart line - The line path (type="line").
|
|
19
|
+
* @csspart area - The filled area under the line (type="area").
|
|
20
|
+
* @csspart bar - Each bar rectangle (type="bar").
|
|
21
|
+
*/
|
|
22
|
+
export class LyraSparkline extends LyraElement {
|
|
23
|
+
constructor() {
|
|
24
|
+
super(...arguments);
|
|
25
|
+
/** The data series to plot. */
|
|
26
|
+
this.values = [];
|
|
27
|
+
/** Rendering style. */
|
|
28
|
+
this.type = 'line';
|
|
29
|
+
}
|
|
30
|
+
static { this.styles = [LyraElement.styles, styles]; }
|
|
31
|
+
points() {
|
|
32
|
+
const v = this.values;
|
|
33
|
+
const lo = this.min ?? Math.min(...v);
|
|
34
|
+
const hi = this.max ?? Math.max(...v);
|
|
35
|
+
const span = hi - lo || 1;
|
|
36
|
+
return v.map((n, i) => {
|
|
37
|
+
const x = v.length > 1 ? (i / (v.length - 1)) * VIEW : VIEW / 2;
|
|
38
|
+
const y = VIEW - ((n - lo) / span) * VIEW;
|
|
39
|
+
return [x, y];
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
willUpdate() {
|
|
43
|
+
const v = this.values;
|
|
44
|
+
this.setAttribute('role', 'img');
|
|
45
|
+
this.setAttribute('aria-label', v.length ? `Trend of ${v.length} values, last ${v.at(-1)}` : 'No data');
|
|
46
|
+
}
|
|
47
|
+
render() {
|
|
48
|
+
const v = this.values;
|
|
49
|
+
if (!v.length)
|
|
50
|
+
return html `<svg viewBox="0 0 ${VIEW} ${VIEW}"></svg>`;
|
|
51
|
+
const pts = this.points();
|
|
52
|
+
const d = pts.map(([x, y], i) => `${i ? 'L' : 'M'}${x},${y}`).join(' ');
|
|
53
|
+
const shape = this.type === 'bar'
|
|
54
|
+
? pts.map(([x, y]) => svg `<rect part="bar" x="${x - 2}" y="${y}" width="4" height="${VIEW - y}"></rect>`)
|
|
55
|
+
: svg `<path part="line" d="${d}"></path>`;
|
|
56
|
+
return html `<svg viewBox="0 0 ${VIEW} ${VIEW}" preserveAspectRatio="none">
|
|
57
|
+
${this.type === 'area' ? svg `<path part="area" d="${d} L${VIEW},${VIEW} L0,${VIEW} Z"></path>` : ''}
|
|
58
|
+
${shape}
|
|
59
|
+
</svg>`;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
__decorate([
|
|
63
|
+
property({ type: Array })
|
|
64
|
+
], LyraSparkline.prototype, "values", void 0);
|
|
65
|
+
__decorate([
|
|
66
|
+
property()
|
|
67
|
+
], LyraSparkline.prototype, "type", void 0);
|
|
68
|
+
__decorate([
|
|
69
|
+
property({ type: Number })
|
|
70
|
+
], LyraSparkline.prototype, "min", void 0);
|
|
71
|
+
__decorate([
|
|
72
|
+
property({ type: Number })
|
|
73
|
+
], LyraSparkline.prototype, "max", void 0);
|
|
74
|
+
defineElement('sparkline', LyraSparkline);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const styles: import("lit").CSSResult;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
export const styles = css `
|
|
3
|
+
:host {
|
|
4
|
+
display: inline-block;
|
|
5
|
+
inline-size: 6em;
|
|
6
|
+
block-size: 1.5em;
|
|
7
|
+
vertical-align: middle;
|
|
8
|
+
}
|
|
9
|
+
svg {
|
|
10
|
+
display: block;
|
|
11
|
+
inline-size: 100%;
|
|
12
|
+
block-size: 100%;
|
|
13
|
+
overflow: visible;
|
|
14
|
+
}
|
|
15
|
+
[part='line'] {
|
|
16
|
+
fill: none;
|
|
17
|
+
stroke: var(--lyra-color-brand);
|
|
18
|
+
stroke-width: 1.5;
|
|
19
|
+
stroke-linejoin: round;
|
|
20
|
+
stroke-linecap: round;
|
|
21
|
+
vector-effect: non-scaling-stroke;
|
|
22
|
+
}
|
|
23
|
+
[part='area'] {
|
|
24
|
+
fill: var(--lyra-color-brand);
|
|
25
|
+
opacity: 0.15;
|
|
26
|
+
stroke: none;
|
|
27
|
+
}
|
|
28
|
+
[part='bar'] {
|
|
29
|
+
fill: var(--lyra-color-brand);
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type TemplateResult } from 'lit';
|
|
2
|
+
import { LyraElement } from '../../internal/lyra-element.js';
|
|
3
|
+
export type ToastVariant = 'brand' | 'success' | 'warning' | 'danger' | 'neutral';
|
|
4
|
+
export type ToastSize = 'xs' | 's' | 'm' | 'l' | 'xl';
|
|
5
|
+
/**
|
|
6
|
+
* `<lyra-toast-item>` — a single toast notification.
|
|
7
|
+
* Mirrors the Web Awesome `<wa-toast-item>` API under the `lyra-` prefix.
|
|
8
|
+
*
|
|
9
|
+
* @customElement lyra-toast-item
|
|
10
|
+
* @slot - The message content.
|
|
11
|
+
* @slot icon - Optional icon shown at the start.
|
|
12
|
+
* @event lyra-show - Fired when the item begins showing.
|
|
13
|
+
* @event lyra-after-show - Fired after the show animation completes.
|
|
14
|
+
* @event lyra-hide - Fired when the item begins hiding.
|
|
15
|
+
* @event lyra-after-hide - Fired after the hide animation completes (item then removes itself).
|
|
16
|
+
* @csspart toast-item - The outer container.
|
|
17
|
+
* @csspart accent - The colored accent bar.
|
|
18
|
+
* @csspart icon - The icon wrapper.
|
|
19
|
+
* @csspart content - The message wrapper.
|
|
20
|
+
* @csspart close-button - The dismiss button.
|
|
21
|
+
*/
|
|
22
|
+
export declare class LyraToastItem extends LyraElement {
|
|
23
|
+
static styles: import("lit").CSSResultGroup[];
|
|
24
|
+
/** Auto-dismiss delay in ms. Set to `Infinity` (or <= 0) to disable. */
|
|
25
|
+
duration: number;
|
|
26
|
+
/** Visual size. */
|
|
27
|
+
size: ToastSize;
|
|
28
|
+
/** Severity/variant. */
|
|
29
|
+
variant: ToastVariant;
|
|
30
|
+
/** Show the icon slot. */
|
|
31
|
+
withIcon: boolean;
|
|
32
|
+
private timer?;
|
|
33
|
+
private remaining;
|
|
34
|
+
private startedAt;
|
|
35
|
+
firstUpdated(): void;
|
|
36
|
+
disconnectedCallback(): void;
|
|
37
|
+
private startTimer;
|
|
38
|
+
private resumeTimer;
|
|
39
|
+
private pauseTimer;
|
|
40
|
+
private clearTimer;
|
|
41
|
+
/** Hide with animation, then remove from the DOM. */
|
|
42
|
+
hide(): Promise<void>;
|
|
43
|
+
render(): TemplateResult;
|
|
44
|
+
}
|
|
45
|
+
declare global {
|
|
46
|
+
interface HTMLElementTagNameMap {
|
|
47
|
+
'lyra-toast-item': LyraToastItem;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
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 { styles } from './toast-item.styles.js';
|
|
12
|
+
const ANIM_MS = 250;
|
|
13
|
+
/**
|
|
14
|
+
* `<lyra-toast-item>` — a single toast notification.
|
|
15
|
+
* Mirrors the Web Awesome `<wa-toast-item>` API under the `lyra-` prefix.
|
|
16
|
+
*
|
|
17
|
+
* @customElement lyra-toast-item
|
|
18
|
+
* @slot - The message content.
|
|
19
|
+
* @slot icon - Optional icon shown at the start.
|
|
20
|
+
* @event lyra-show - Fired when the item begins showing.
|
|
21
|
+
* @event lyra-after-show - Fired after the show animation completes.
|
|
22
|
+
* @event lyra-hide - Fired when the item begins hiding.
|
|
23
|
+
* @event lyra-after-hide - Fired after the hide animation completes (item then removes itself).
|
|
24
|
+
* @csspart toast-item - The outer container.
|
|
25
|
+
* @csspart accent - The colored accent bar.
|
|
26
|
+
* @csspart icon - The icon wrapper.
|
|
27
|
+
* @csspart content - The message wrapper.
|
|
28
|
+
* @csspart close-button - The dismiss button.
|
|
29
|
+
*/
|
|
30
|
+
export class LyraToastItem extends LyraElement {
|
|
31
|
+
constructor() {
|
|
32
|
+
super(...arguments);
|
|
33
|
+
/** Auto-dismiss delay in ms. Set to `Infinity` (or <= 0) to disable. */
|
|
34
|
+
this.duration = 5000;
|
|
35
|
+
/** Visual size. */
|
|
36
|
+
this.size = 'm';
|
|
37
|
+
/** Severity/variant. */
|
|
38
|
+
this.variant = 'neutral';
|
|
39
|
+
/** Show the icon slot. */
|
|
40
|
+
this.withIcon = false;
|
|
41
|
+
this.remaining = 0;
|
|
42
|
+
this.startedAt = 0;
|
|
43
|
+
this.resumeTimer = () => {
|
|
44
|
+
if (!isFinite(this.duration) || this.duration <= 0 || this.remaining <= 0)
|
|
45
|
+
return;
|
|
46
|
+
this.startedAt = performance.now();
|
|
47
|
+
this.timer = window.setTimeout(() => this.hide(), this.remaining);
|
|
48
|
+
};
|
|
49
|
+
this.pauseTimer = () => {
|
|
50
|
+
if (this.timer !== undefined) {
|
|
51
|
+
clearTimeout(this.timer);
|
|
52
|
+
this.timer = undefined;
|
|
53
|
+
this.remaining -= performance.now() - this.startedAt;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
static { this.styles = [LyraElement.styles, styles]; }
|
|
58
|
+
firstUpdated() {
|
|
59
|
+
// Assertive for actionable severities, polite otherwise.
|
|
60
|
+
this.setAttribute('role', this.variant === 'danger' || this.variant === 'warning' ? 'alert' : 'status');
|
|
61
|
+
requestAnimationFrame(() => {
|
|
62
|
+
this.setAttribute('data-visible', '');
|
|
63
|
+
this.emit('lyra-show');
|
|
64
|
+
window.setTimeout(() => this.emit('lyra-after-show'), ANIM_MS);
|
|
65
|
+
this.startTimer();
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
disconnectedCallback() {
|
|
69
|
+
super.disconnectedCallback();
|
|
70
|
+
this.clearTimer();
|
|
71
|
+
}
|
|
72
|
+
startTimer() {
|
|
73
|
+
if (!isFinite(this.duration) || this.duration <= 0)
|
|
74
|
+
return;
|
|
75
|
+
this.remaining = this.duration;
|
|
76
|
+
this.resumeTimer();
|
|
77
|
+
}
|
|
78
|
+
clearTimer() {
|
|
79
|
+
if (this.timer !== undefined)
|
|
80
|
+
clearTimeout(this.timer);
|
|
81
|
+
this.timer = undefined;
|
|
82
|
+
}
|
|
83
|
+
/** Hide with animation, then remove from the DOM. */
|
|
84
|
+
async hide() {
|
|
85
|
+
this.clearTimer();
|
|
86
|
+
this.emit('lyra-hide');
|
|
87
|
+
this.removeAttribute('data-visible');
|
|
88
|
+
await new Promise((r) => window.setTimeout(r, ANIM_MS));
|
|
89
|
+
this.emit('lyra-after-hide');
|
|
90
|
+
this.remove();
|
|
91
|
+
}
|
|
92
|
+
render() {
|
|
93
|
+
return html `
|
|
94
|
+
<div
|
|
95
|
+
part="toast-item"
|
|
96
|
+
@pointerenter=${this.pauseTimer}
|
|
97
|
+
@pointerleave=${this.resumeTimer}
|
|
98
|
+
@focusin=${this.pauseTimer}
|
|
99
|
+
@focusout=${this.resumeTimer}
|
|
100
|
+
>
|
|
101
|
+
<span part="accent" aria-hidden="true"></span>
|
|
102
|
+
${this.withIcon ? html `<span part="icon"><slot name="icon"></slot></span>` : ''}
|
|
103
|
+
<div part="content"><slot></slot></div>
|
|
104
|
+
<button part="close-button" type="button" aria-label="Close" @click=${() => this.hide()}>
|
|
105
|
+
×
|
|
106
|
+
</button>
|
|
107
|
+
</div>
|
|
108
|
+
`;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
__decorate([
|
|
112
|
+
property({ type: Number })
|
|
113
|
+
], LyraToastItem.prototype, "duration", void 0);
|
|
114
|
+
__decorate([
|
|
115
|
+
property({ reflect: true })
|
|
116
|
+
], LyraToastItem.prototype, "size", void 0);
|
|
117
|
+
__decorate([
|
|
118
|
+
property({ reflect: true })
|
|
119
|
+
], LyraToastItem.prototype, "variant", void 0);
|
|
120
|
+
__decorate([
|
|
121
|
+
property({ type: Boolean, attribute: 'with-icon' })
|
|
122
|
+
], LyraToastItem.prototype, "withIcon", void 0);
|
|
123
|
+
defineElement('toast-item', LyraToastItem);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const styles: import("lit").CSSResult;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
export const styles = css `
|
|
3
|
+
:host {
|
|
4
|
+
display: block;
|
|
5
|
+
--accent-width: 4px;
|
|
6
|
+
--show-duration: 250ms;
|
|
7
|
+
--hide-duration: 250ms;
|
|
8
|
+
--padding: var(--lyra-space-m);
|
|
9
|
+
--accent-color: var(--lyra-color-border);
|
|
10
|
+
}
|
|
11
|
+
:host([variant='brand']) {
|
|
12
|
+
--accent-color: var(--lyra-color-brand);
|
|
13
|
+
}
|
|
14
|
+
:host([variant='success']) {
|
|
15
|
+
--accent-color: var(--lyra-color-success);
|
|
16
|
+
}
|
|
17
|
+
:host([variant='warning']) {
|
|
18
|
+
--accent-color: var(--lyra-color-warning);
|
|
19
|
+
}
|
|
20
|
+
:host([variant='danger']) {
|
|
21
|
+
--accent-color: var(--lyra-color-danger);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
[part='toast-item'] {
|
|
25
|
+
position: relative;
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: start;
|
|
28
|
+
gap: var(--lyra-space-s);
|
|
29
|
+
inline-size: 100%;
|
|
30
|
+
padding: var(--padding);
|
|
31
|
+
padding-inline-start: calc(var(--padding) + var(--accent-width));
|
|
32
|
+
background: var(--lyra-color-surface);
|
|
33
|
+
color: var(--lyra-color-text);
|
|
34
|
+
border: 1px solid var(--lyra-color-border);
|
|
35
|
+
border-radius: var(--lyra-radius);
|
|
36
|
+
box-shadow: var(--lyra-shadow);
|
|
37
|
+
opacity: 0;
|
|
38
|
+
transform: translateY(-8px);
|
|
39
|
+
transition:
|
|
40
|
+
opacity var(--show-duration) ease,
|
|
41
|
+
transform var(--show-duration) ease;
|
|
42
|
+
}
|
|
43
|
+
:host([data-visible]) [part='toast-item'] {
|
|
44
|
+
opacity: 1;
|
|
45
|
+
transform: none;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
[part='accent'] {
|
|
49
|
+
position: absolute;
|
|
50
|
+
inset-block: 0;
|
|
51
|
+
inset-inline-start: 0;
|
|
52
|
+
inline-size: var(--accent-width);
|
|
53
|
+
background: var(--accent-color);
|
|
54
|
+
border-start-start-radius: var(--lyra-radius);
|
|
55
|
+
border-end-start-radius: var(--lyra-radius);
|
|
56
|
+
}
|
|
57
|
+
[part='icon'] {
|
|
58
|
+
display: inline-flex;
|
|
59
|
+
flex: 0 0 auto;
|
|
60
|
+
color: var(--accent-color);
|
|
61
|
+
}
|
|
62
|
+
[part='content'] {
|
|
63
|
+
flex: 1 1 auto;
|
|
64
|
+
min-inline-size: 0;
|
|
65
|
+
}
|
|
66
|
+
[part='close-button'] {
|
|
67
|
+
flex: 0 0 auto;
|
|
68
|
+
margin-inline-start: auto;
|
|
69
|
+
background: none;
|
|
70
|
+
border: none;
|
|
71
|
+
cursor: pointer;
|
|
72
|
+
color: var(--lyra-color-text-quiet);
|
|
73
|
+
font-size: 1rem;
|
|
74
|
+
line-height: 1;
|
|
75
|
+
padding: var(--lyra-space-xs);
|
|
76
|
+
border-radius: var(--lyra-radius);
|
|
77
|
+
}
|
|
78
|
+
[part='close-button']:hover {
|
|
79
|
+
color: var(--lyra-color-text);
|
|
80
|
+
}
|
|
81
|
+
`;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type TemplateResult } from 'lit';
|
|
2
|
+
import { LyraElement } from '../../internal/lyra-element.js';
|
|
3
|
+
import { LyraToastItem, type ToastVariant, type ToastSize } from './toast-item.js';
|
|
4
|
+
import './toast-item.js';
|
|
5
|
+
export type ToastPlacement = 'top-start' | 'top-center' | 'top-end' | 'bottom-start' | 'bottom-center' | 'bottom-end';
|
|
6
|
+
export interface ToastCreateOptions {
|
|
7
|
+
variant?: ToastVariant;
|
|
8
|
+
duration?: number;
|
|
9
|
+
size?: ToastSize;
|
|
10
|
+
withIcon?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* `<lyra-toast>` — the stacking toast region. One per page is recommended.
|
|
14
|
+
* Mirrors the Web Awesome `<wa-toast>` API under the `lyra-` prefix.
|
|
15
|
+
*
|
|
16
|
+
* @customElement lyra-toast
|
|
17
|
+
* @slot - `<lyra-toast-item>` elements.
|
|
18
|
+
* @csspart stack - The fl-column container holding the items.
|
|
19
|
+
*/
|
|
20
|
+
export declare class LyraToast extends LyraElement {
|
|
21
|
+
static styles: import("lit").CSSResultGroup[];
|
|
22
|
+
/** Where the stack anchors on screen. */
|
|
23
|
+
placement: ToastPlacement;
|
|
24
|
+
/** Create and append a toast item programmatically; resolves to the item. */
|
|
25
|
+
create(message: string, options?: ToastCreateOptions): Promise<LyraToastItem>;
|
|
26
|
+
render(): TemplateResult;
|
|
27
|
+
}
|
|
28
|
+
declare global {
|
|
29
|
+
interface HTMLElementTagNameMap {
|
|
30
|
+
'lyra-toast': LyraToast;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
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 { styles } from './toast.styles.js';
|
|
12
|
+
import { LyraToastItem } from './toast-item.js';
|
|
13
|
+
import './toast-item.js';
|
|
14
|
+
/**
|
|
15
|
+
* `<lyra-toast>` — the stacking toast region. One per page is recommended.
|
|
16
|
+
* Mirrors the Web Awesome `<wa-toast>` API under the `lyra-` prefix.
|
|
17
|
+
*
|
|
18
|
+
* @customElement lyra-toast
|
|
19
|
+
* @slot - `<lyra-toast-item>` elements.
|
|
20
|
+
* @csspart stack - The fl-column container holding the items.
|
|
21
|
+
*/
|
|
22
|
+
export class LyraToast extends LyraElement {
|
|
23
|
+
constructor() {
|
|
24
|
+
super(...arguments);
|
|
25
|
+
/** Where the stack anchors on screen. */
|
|
26
|
+
this.placement = 'top-end';
|
|
27
|
+
}
|
|
28
|
+
static { this.styles = [LyraElement.styles, styles]; }
|
|
29
|
+
/** Create and append a toast item programmatically; resolves to the item. */
|
|
30
|
+
async create(message, options = {}) {
|
|
31
|
+
const item = document.createElement('lyra-toast-item');
|
|
32
|
+
item.variant = options.variant ?? 'neutral';
|
|
33
|
+
item.duration = options.duration ?? 5000;
|
|
34
|
+
item.size = options.size ?? 'm';
|
|
35
|
+
item.withIcon = options.withIcon ?? false;
|
|
36
|
+
item.textContent = message;
|
|
37
|
+
this.appendChild(item);
|
|
38
|
+
await item.updateComplete;
|
|
39
|
+
return item;
|
|
40
|
+
}
|
|
41
|
+
render() {
|
|
42
|
+
return html `<div part="stack" role="status" aria-live="polite"><slot></slot></div>`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
__decorate([
|
|
46
|
+
property({ reflect: true })
|
|
47
|
+
], LyraToast.prototype, "placement", void 0);
|
|
48
|
+
defineElement('toast', LyraToast);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const styles: import("lit").CSSResult;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
export const styles = css `
|
|
3
|
+
:host {
|
|
4
|
+
position: fixed;
|
|
5
|
+
z-index: 9999;
|
|
6
|
+
display: block;
|
|
7
|
+
--gap: var(--lyra-space-s);
|
|
8
|
+
--width: 28rem;
|
|
9
|
+
pointer-events: none;
|
|
10
|
+
}
|
|
11
|
+
[part='stack'] {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
gap: var(--gap);
|
|
15
|
+
inline-size: var(--width);
|
|
16
|
+
max-inline-size: calc(100vw - 2rem);
|
|
17
|
+
}
|
|
18
|
+
[part='stack']::slotted(*),
|
|
19
|
+
::slotted(*) {
|
|
20
|
+
pointer-events: auto;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
:host([placement^='bottom']) [part='stack'] {
|
|
24
|
+
flex-direction: column-reverse;
|
|
25
|
+
}
|
|
26
|
+
:host([placement='top-start']) {
|
|
27
|
+
inset-block-start: 1rem;
|
|
28
|
+
inset-inline-start: 1rem;
|
|
29
|
+
}
|
|
30
|
+
:host([placement='top-end']) {
|
|
31
|
+
inset-block-start: 1rem;
|
|
32
|
+
inset-inline-end: 1rem;
|
|
33
|
+
}
|
|
34
|
+
:host([placement='top-center']) {
|
|
35
|
+
inset-block-start: 1rem;
|
|
36
|
+
inset-inline: 0;
|
|
37
|
+
margin-inline: auto;
|
|
38
|
+
}
|
|
39
|
+
:host([placement='bottom-start']) {
|
|
40
|
+
inset-block-end: 1rem;
|
|
41
|
+
inset-inline-start: 1rem;
|
|
42
|
+
}
|
|
43
|
+
:host([placement='bottom-end']) {
|
|
44
|
+
inset-block-end: 1rem;
|
|
45
|
+
inset-inline-end: 1rem;
|
|
46
|
+
}
|
|
47
|
+
:host([placement='bottom-center']) {
|
|
48
|
+
inset-block-end: 1rem;
|
|
49
|
+
inset-inline: 0;
|
|
50
|
+
margin-inline: auto;
|
|
51
|
+
}
|
|
52
|
+
`;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ToastCreateOptions, type ToastPlacement } from './toast.js';
|
|
2
|
+
import type { LyraToastItem } from './toast-item.js';
|
|
3
|
+
import './toast.js';
|
|
4
|
+
export interface ToastOptions extends ToastCreateOptions {
|
|
5
|
+
message: string;
|
|
6
|
+
placement?: ToastPlacement;
|
|
7
|
+
/** Optional action button rendered after the message. */
|
|
8
|
+
action?: {
|
|
9
|
+
label: string;
|
|
10
|
+
onClick: (item: LyraToastItem) => void;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export interface ToastHandle {
|
|
14
|
+
/** Resolves to the created toast item. */
|
|
15
|
+
item: Promise<LyraToastItem>;
|
|
16
|
+
/** Dismiss the toast early. */
|
|
17
|
+
dismiss: () => void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Show a toast. Ergonomic convenience over `<lyra-toast>.create()` that mounts
|
|
21
|
+
* and reuses a single page-level region — the drop-in for `react-hot-toast`.
|
|
22
|
+
*
|
|
23
|
+
* @example toast('Saved');
|
|
24
|
+
* @example toast({ message: 'Deleted', variant: 'danger', action: { label: 'Undo', onClick: undo } });
|
|
25
|
+
*/
|
|
26
|
+
export declare function toast(input: ToastOptions | string): ToastHandle;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { LyraToast } from './toast.js';
|
|
2
|
+
import './toast.js';
|
|
3
|
+
let region;
|
|
4
|
+
/** Get (or lazily mount) the singleton toast region on `document.body`. */
|
|
5
|
+
function getRegion(placement) {
|
|
6
|
+
if (!region || !region.isConnected) {
|
|
7
|
+
region = document.createElement('lyra-toast');
|
|
8
|
+
document.body.appendChild(region);
|
|
9
|
+
}
|
|
10
|
+
if (placement)
|
|
11
|
+
region.placement = placement;
|
|
12
|
+
return region;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Show a toast. Ergonomic convenience over `<lyra-toast>.create()` that mounts
|
|
16
|
+
* and reuses a single page-level region — the drop-in for `react-hot-toast`.
|
|
17
|
+
*
|
|
18
|
+
* @example toast('Saved');
|
|
19
|
+
* @example toast({ message: 'Deleted', variant: 'danger', action: { label: 'Undo', onClick: undo } });
|
|
20
|
+
*/
|
|
21
|
+
export function toast(input) {
|
|
22
|
+
const opts = typeof input === 'string' ? { message: input } : input;
|
|
23
|
+
const item = getRegion(opts.placement)
|
|
24
|
+
.create(opts.message, opts)
|
|
25
|
+
.then((el) => {
|
|
26
|
+
if (opts.action) {
|
|
27
|
+
const btn = document.createElement('button');
|
|
28
|
+
btn.type = 'button';
|
|
29
|
+
btn.textContent = opts.action.label;
|
|
30
|
+
btn.addEventListener('click', () => opts.action.onClick(el));
|
|
31
|
+
el.appendChild(btn);
|
|
32
|
+
}
|
|
33
|
+
return el;
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
item,
|
|
37
|
+
dismiss: () => {
|
|
38
|
+
void item.then((el) => el.hide());
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|