@fusion.dev/helix-headless 0.153.0 → 0.154.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.
@@ -27,8 +27,16 @@ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
27
27
  * a glyph that came later is drawn. A glyph registered after the element
28
28
  * rendered is drawn then.
29
29
  *
30
- * A name with no match renders nothing, and the element fires `error`, so a
31
- * typo in a name can be heard. It waits until the page has loaded and painted
30
+ * **Or point `src` at an SVG file**, such as `src="/icons/rocket.svg"`, and
31
+ * the element fetches it and draws its shapes in `currentColor` where the file
32
+ * does not color them. A file is fetched once a page, however many icons name
33
+ * it, and `load` fires when it is drawn. Scripts, event handlers and
34
+ * `javascript:` links in the file are taken out before it is drawn. A server
35
+ * fetches nothing, so a served icon with a `src` is drawn once the script
36
+ * runs.
37
+ *
38
+ * A name with no match, or a `src` that is no SVG, renders nothing, and the
39
+ * element fires `error`, so a typo in a name can be heard. It waits until the page has loaded and painted
32
40
  * first, so a library a later module registers is not taken for a typo.
33
41
  *
34
42
  * An icon is decorative by default and hidden from assistive technology. Set
@@ -47,8 +55,8 @@ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
47
55
  * @aliases Glyph, Symbol
48
56
  * @documentation https://fusion.dev/helix/components/icon
49
57
  *
50
- * @event {Event} error - `name` matches no registered icon and no library answers it, so nothing is drawn.
51
- * @event {Event} load - A glyph a library gave later for `name` is drawn.
58
+ * @event {Event} error - `name` matches no registered icon and no library answers it, or `src` is no SVG, so nothing is drawn.
59
+ * @event {Event} load - A glyph a library gave later for `name`, or the file at `src`, is drawn.
52
60
  *
53
61
  * @csspart svg - The rendered SVG.
54
62
  */
@@ -97,6 +105,13 @@ export declare class HelixHeadlessIcon extends HelixElement {
97
105
  * prefix.
98
106
  */
99
107
  name: string;
108
+ /**
109
+ * The URL of an SVG file to draw, fetched once a page and drawn in
110
+ * `currentColor` where the file does not color its shapes.
111
+ *
112
+ * Wins over `name`; `icon` wins over both. Absent draws `name`.
113
+ */
114
+ src: string;
100
115
  /**
101
116
  * Hears an `aria-label` written on the element, which no property mirrors.
102
117
  *
@@ -120,11 +135,18 @@ export declare class HelixHeadlessIcon extends HelixElement {
120
135
  private arrived;
121
136
  /** Stops listening to the registry. */
122
137
  private unwatch;
138
+ /** What the glyph is asked for by: `src` where it is set, and `name` otherwise. */
139
+ private get asked();
123
140
  connectedCallback(): void;
124
141
  disconnectedCallback(): void;
125
- /** What the registry gives for `name` now, starting the wait for a late glyph. */
142
+ /**
143
+ * What `src` or the registry gives now, starting the wait for a late glyph.
144
+ * A file is fetched only where the page can be read: a server fetches
145
+ * nothing, and a hydrating render draws what the server sent.
146
+ */
126
147
  private lookup;
127
148
  protected willUpdate(changed: PropertyValues): void;
149
+ protected firstUpdated(changed: PropertyValues): void;
128
150
  protected updated(changed: PropertyValues): void;
129
151
  /** Hands the drawn `<svg>` to the mutator of the library `name` asks, where it has one. */
130
152
  private mutate;
@@ -8,6 +8,7 @@ import { auditName, customElement, HelixElement, hostAria, reportRuntime, } from
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
11
+ import { fetchIcon } from '../fetch.js';
11
12
  import { iconMutator, resolveIcon, watchIcons } from '../registry.js';
12
13
  import { afterPageSettles } from '../settle.js';
13
14
  let HelixHeadlessIcon = class HelixHeadlessIcon extends HelixElement {
@@ -16,6 +17,7 @@ let HelixHeadlessIcon = class HelixHeadlessIcon extends HelixElement {
16
17
  this.label = '';
17
18
  this.mirror = false;
18
19
  this.name = '';
20
+ this.src = '';
19
21
  this.missed = '';
20
22
  this.waiting = false;
21
23
  this.arrived = '';
@@ -30,10 +32,13 @@ let HelixHeadlessIcon = class HelixHeadlessIcon extends HelixElement {
30
32
  this.requestUpdate();
31
33
  }
32
34
  }
35
+ get asked() {
36
+ return this.src || this.name;
37
+ }
33
38
  connectedCallback() {
34
39
  super.connectedCallback();
35
40
  this.unwatch = watchIcons(() => {
36
- if (this.icon === undefined && this.name !== '') {
41
+ if (this.icon === undefined && this.src === '' && this.name !== '') {
37
42
  this.requestUpdate();
38
43
  }
39
44
  });
@@ -44,15 +49,21 @@ let HelixHeadlessIcon = class HelixHeadlessIcon extends HelixElement {
44
49
  this.unwatch = undefined;
45
50
  }
46
51
  lookup() {
47
- const found = this.name === '' ? undefined : resolveIcon(this.name);
48
- this.waiting = found instanceof Promise;
52
+ const asked = this.asked;
53
+ const found = this.src !== ''
54
+ ? this.childrenKnown
55
+ ? fetchIcon(this.src)
56
+ : undefined
57
+ : this.name === ''
58
+ ? undefined
59
+ : resolveIcon(this.name);
60
+ this.waiting = found instanceof Promise || (this.src !== '' && !this.childrenKnown);
49
61
  if (!(found instanceof Promise)) {
50
62
  return found;
51
63
  }
52
- const name = this.name;
53
64
  void found.then(() => {
54
- if (this.name === name && this.isConnected) {
55
- this.arrived = name;
65
+ if (this.asked === asked && this.isConnected) {
66
+ this.arrived = asked;
56
67
  this.requestUpdate();
57
68
  }
58
69
  });
@@ -64,19 +75,26 @@ let HelixHeadlessIcon = class HelixHeadlessIcon extends HelixElement {
64
75
  hostAria(this.internals, { role: named ? 'img' : '' });
65
76
  this.drawn = this.icon ?? this.lookup();
66
77
  }
78
+ firstUpdated(changed) {
79
+ super.firstUpdated(changed);
80
+ if (this.src !== '') {
81
+ this.finishHydration();
82
+ }
83
+ }
67
84
  updated(changed) {
68
85
  super.updated(changed);
69
86
  if (this.drawn !== undefined) {
70
87
  this.mutate();
71
88
  this.auditControl();
72
89
  }
73
- if (this.arrived === this.name && this.drawn !== undefined) {
90
+ const asked = this.asked;
91
+ if (this.arrived === asked && this.drawn !== undefined) {
74
92
  this.dispatchEvent(new Event('load', { bubbles: false, cancelable: false, composed: false }));
75
93
  }
76
94
  this.arrived = '';
77
- if (this.drawn === undefined && this.name !== '' && !this.waiting) {
78
- if (this.missed !== this.name && this.pending !== this.name) {
79
- this.pending = this.name;
95
+ if (this.drawn === undefined && asked !== '' && !this.waiting) {
96
+ if (this.missed !== asked && this.pending !== asked) {
97
+ this.pending = asked;
80
98
  afterPageSettles(() => this.reportMissing());
81
99
  }
82
100
  }
@@ -86,7 +104,7 @@ let HelixHeadlessIcon = class HelixHeadlessIcon extends HelixElement {
86
104
  }
87
105
  mutate() {
88
106
  const svg = this.renderRoot.querySelector('svg');
89
- const mutator = this.icon === undefined ? iconMutator(this.name) : undefined;
107
+ const mutator = this.icon === undefined && this.src === '' ? iconMutator(this.name) : undefined;
90
108
  if (svg && mutator) {
91
109
  mutator(svg);
92
110
  }
@@ -102,18 +120,21 @@ let HelixHeadlessIcon = class HelixHeadlessIcon extends HelixElement {
102
120
  }
103
121
  }
104
122
  reportMissing() {
105
- const name = this.pending;
123
+ const asked = this.pending;
106
124
  this.pending = '';
107
- if (this.name !== name || this.drawn !== undefined || this.waiting || !this.isConnected) {
125
+ if (this.asked !== asked || this.drawn !== undefined || this.waiting || !this.isConnected) {
108
126
  return;
109
127
  }
110
- this.missed = name;
128
+ this.missed = asked;
111
129
  this.dispatchEvent(new Event('error', { bubbles: false, cancelable: false, composed: false }));
112
130
  reportRuntime({
113
131
  kind: 'unknown-icon',
114
- message: `<${this.localName} name="${name}"> names no registered icon, so it draws ` +
115
- 'nothing. Check the name, register the glyph with `registerIcons`, or import it ' +
116
- 'and pass it as `icon`.',
132
+ message: this.src === ''
133
+ ? `<${this.localName} name="${asked}"> names no registered icon, so it draws ` +
134
+ 'nothing. Check the name, register the glyph with `registerIcons`, or import it ' +
135
+ 'and pass it as `icon`.'
136
+ : `<${this.localName} src="${asked}"> could not be fetched as an SVG file, so it ` +
137
+ 'draws nothing. Check the URL, and that the file is an `<svg>`.',
117
138
  tag: this.localName,
118
139
  });
119
140
  }
@@ -148,6 +169,9 @@ __decorate([
148
169
  __decorate([
149
170
  property()
150
171
  ], HelixHeadlessIcon.prototype, "name", void 0);
172
+ __decorate([
173
+ property()
174
+ ], HelixHeadlessIcon.prototype, "src", void 0);
151
175
  HelixHeadlessIcon = __decorate([
152
176
  customElement('helix-headless-icon')
153
177
  ], HelixHeadlessIcon);
@@ -1,5 +1,5 @@
1
1
  import { HelixElement } from '@fusion.dev/helix-core';
2
- import { type PropertyValues, type TemplateResult } from 'lit';
2
+ import { type CSSResultGroup, type PropertyValues, type TemplateResult } from 'lit';
3
3
  /**
4
4
  * @summary A short quotation, set inline with the text around it.
5
5
  *
@@ -12,6 +12,12 @@ import { type PropertyValues, type TemplateResult } from 'lit';
12
12
  * element, and its marks follow it: `lang="de"` sets „…“ and `lang="fr"` «…»
13
13
  * inside an English page.
14
14
  *
15
+ * **`no-marks` leaves the marks off**, for a quotation set where the page draws
16
+ * its own, such as a pull quote with a large decorative mark beside it or a
17
+ * table of sayings. The quotation still counts toward nesting, so a quote
18
+ * inside it takes the inner marks it would have had. Text with marks typed
19
+ * into it raises no warning under `no-marks`.
20
+ *
15
21
  * For a passage set off from the surrounding text, use a blockquote.
16
22
  *
17
23
  * @since 0.1.0
@@ -25,10 +31,20 @@ import { type PropertyValues, type TemplateResult } from 'lit';
25
31
  * @csspart quote - The quotation.
26
32
  */
27
33
  export declare class HelixHeadlessQuote extends HelixElement {
34
+ static styles: CSSResultGroup;
28
35
  /**
29
36
  * URL identifying where the quotation came from.
30
37
  */
31
38
  cite: string;
39
+ /**
40
+ * Whether to leave the quotation marks off, for a quotation set where the
41
+ * page draws its own.
42
+ *
43
+ * Negative, because the marks are on by default and a boolean attribute is
44
+ * true whenever it is present. Reflected, because a stylesheet takes the
45
+ * marks off on it.
46
+ */
47
+ noMarks: boolean;
32
48
  /** Whether this quote has said its text carries typed marks, so it says so once. */
33
49
  private warned;
34
50
  /**
@@ -5,20 +5,21 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import { customElement, HelixElement, reportRuntime } from '@fusion.dev/helix-core';
8
- import { html, nothing } from 'lit';
8
+ import { css, html, nothing, } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { typedMarks } from '../marks.js';
11
11
  let HelixHeadlessQuote = class HelixHeadlessQuote extends HelixElement {
12
12
  constructor() {
13
13
  super(...arguments);
14
14
  this.cite = '';
15
+ this.noMarks = false;
15
16
  this.warned = false;
16
17
  this.readText = () => {
17
18
  const text = [...this.childNodes]
18
19
  .filter((node) => !(node instanceof Element) || !node.slot)
19
20
  .map((node) => node.textContent ?? '')
20
21
  .join('');
21
- if (this.warned || !typedMarks(text)) {
22
+ if (this.warned || this.noMarks || !typedMarks(text)) {
22
23
  return;
23
24
  }
24
25
  this.warned = true;
@@ -29,6 +30,15 @@ let HelixHeadlessQuote = class HelixHeadlessQuote extends HelixElement {
29
30
  });
30
31
  };
31
32
  }
33
+ static { this.styles = css `
34
+ :host([no-marks]) [part='quote']::before {
35
+ content: no-open-quote;
36
+ }
37
+
38
+ :host([no-marks]) [part='quote']::after {
39
+ content: no-close-quote;
40
+ }
41
+ `; }
32
42
  firstUpdated(changed) {
33
43
  super.firstUpdated(changed);
34
44
  this.readText();
@@ -41,6 +51,9 @@ let HelixHeadlessQuote = class HelixHeadlessQuote extends HelixElement {
41
51
  __decorate([
42
52
  property()
43
53
  ], HelixHeadlessQuote.prototype, "cite", void 0);
54
+ __decorate([
55
+ property({ attribute: 'no-marks', reflect: true, type: Boolean })
56
+ ], HelixHeadlessQuote.prototype, "noMarks", void 0);
44
57
  HelixHeadlessQuote = __decorate([
45
58
  customElement('helix-headless-quote')
46
59
  ], HelixHeadlessQuote);
@@ -61,6 +61,13 @@ export declare class HelixHeadlessSkeleton extends HelixElement {
61
61
  protected willUpdate(changed: PropertyValues): void;
62
62
  protected firstUpdated(changed: PropertyValues): void;
63
63
  protected render(): TemplateResult;
64
+ /**
65
+ * The placeholder shape and the sheen inside it.
66
+ *
67
+ * A method, so a tier above can draw the placeholder as several shapes, as
68
+ * the styled tier draws a paragraph of lines.
69
+ */
70
+ protected renderShape(): TemplateResult;
64
71
  }
65
72
  declare global {
66
73
  interface HTMLElementTagNameMap {
@@ -38,7 +38,10 @@ let HelixHeadlessSkeleton = class HelixHeadlessSkeleton extends HelixElement {
38
38
  render() {
39
39
  return html `<span part="content" ?inert=${this.loading}
40
40
  ><slot @slotchange=${() => this.requestUpdate()}></slot></span
41
- ><span part="skeleton"><span part="sheen"></span></span>`;
41
+ >${this.renderShape()}`;
42
+ }
43
+ renderShape() {
44
+ return html `<span part="skeleton"><span part="sheen"></span></span>`;
42
45
  }
43
46
  };
44
47
  __decorate([
@@ -4,9 +4,12 @@ import { type PropertyValues, type TemplateResult } from 'lit';
4
4
  * @summary A row of related measurements.
5
5
  *
6
6
  * @description
7
- * Screen readers announce the stats as a group. The figures are read together
8
- * and their order has no meaning, so the group does not announce positions such
9
- * as "1 of 4".
7
+ * **A named group is a group**, so a screen reader announces the stats
8
+ * together under its name, such as "Usage, group". Name it with `label`,
9
+ * `aria-label` or `aria-labelledby`. An unnamed one has no role, since a group
10
+ * with no name only adds "group" to what a reader says, as an unnamed Card
11
+ * has none. The figures are read together and their order has no meaning, so
12
+ * the group does not announce positions such as "1 of 4".
10
13
  *
11
14
  * @since 0.1.0
12
15
  * @status stable
@@ -17,9 +20,25 @@ import { type PropertyValues, type TemplateResult } from 'lit';
17
20
  */
18
21
  export declare class HelixHeadlessStatGroup extends HelixElement {
19
22
  /**
20
- * Accessible name for the group.
23
+ * Accessible name for the group, which makes it a group.
24
+ *
25
+ * Absent leaves the stats with no group around them, unless `aria-label` or
26
+ * `aria-labelledby` names it.
21
27
  */
22
28
  label: string;
29
+ /**
30
+ * Hears a name written on the element, which no property mirrors.
31
+ *
32
+ * Lit observes the attributes of its own properties only, and `ariaLabel`
33
+ * and `ariaLabelledByElements` belong to the platform.
34
+ */
35
+ static get observedAttributes(): string[];
36
+ /**
37
+ * Called by the browser when an observed attribute changes. A name written
38
+ * or taken off asks for an update, because it decides whether the stats are
39
+ * a group.
40
+ */
41
+ attributeChangedCallback(name: string, old: string | null, value: string | null): void;
23
42
  protected willUpdate(changed: PropertyValues): void;
24
43
  protected render(): TemplateResult;
25
44
  }
@@ -12,9 +12,21 @@ let HelixHeadlessStatGroup = class HelixHeadlessStatGroup extends HelixElement {
12
12
  super(...arguments);
13
13
  this.label = '';
14
14
  }
15
+ static get observedAttributes() {
16
+ return [...super.observedAttributes, 'aria-label', 'aria-labelledby'];
17
+ }
18
+ attributeChangedCallback(name, old, value) {
19
+ super.attributeChangedCallback(name, old, value);
20
+ if (name === 'aria-label' || name === 'aria-labelledby') {
21
+ this.requestUpdate();
22
+ }
23
+ }
15
24
  willUpdate(changed) {
16
25
  super.willUpdate(changed);
17
- hostAria(this.internals, { label: this.label, role: 'group' });
26
+ const named = this.label.trim() !== '' ||
27
+ Boolean(this.getAttribute?.('aria-label')?.trim()) ||
28
+ Boolean(this.getAttribute?.('aria-labelledby')?.trim());
29
+ hostAria(this.internals, { label: this.label, role: named ? 'group' : '' });
18
30
  }
19
31
  render() {
20
32
  return html `<slot></slot>`;
@@ -19,6 +19,19 @@ import { type PropertyValues, type TemplateResult } from 'lit';
19
19
  * attribute; a stat with no label has no role. Put a `Trend` in the `trend`
20
20
  * slot to have it read inside the group, after the figure it qualifies.
21
21
  *
22
+ * **Or give the figure as a number.** `value` is written with
23
+ * `Intl.NumberFormat`, in `locale` and with `format-options`, so
24
+ * `value="1234567" format-options='{"notation":"compact"}'` shows `1.2M`, and
25
+ * a server writes it as the browser does. A number in `value` takes the place
26
+ * of a slotted figure.
27
+ *
28
+ * **Each slot is a part of the same name**, and `value` for the figure, so a
29
+ * page reaches the label, the unit and the trend from outside. A slot draws no
30
+ * box, so a part passes inherited properties to what is slotted. **The stat
31
+ * says which pieces it holds**, as `:state(has-label)`, `:state(has-unit)` and
32
+ * `:state(has-trend)`, read in the browser, so a served stat carries none
33
+ * until the script runs.
34
+ *
22
35
  * @since 0.1.0
23
36
  * @status stable
24
37
  * @category data
@@ -30,7 +43,14 @@ import { type PropertyValues, type TemplateResult } from 'lit';
30
43
  * @slot trend - How the value moved, usually a `Trend`, read after the value inside the stat's group.
31
44
  * @slot unit - The unit, shown beside the value.
32
45
  *
33
- * @csspart value - The slot the value is written in. A slot draws no box, so it takes the inherited properties, such as color and font, and passes them to the value.
46
+ * @csspart label - The slot the label arrives through, drawing no box, as `value` does.
47
+ * @csspart trend - The slot the trend arrives through, drawing no box, as `value` does.
48
+ * @csspart unit - The slot the unit arrives through, drawing no box, as `value` does.
49
+ * @csspart value - The slot the value is written in, or the figure `value` writes. A slot draws no box, so it takes the inherited properties, such as color and font, and passes them to the value.
50
+ *
51
+ * @cssstate has-label - Something is slotted into `label`.
52
+ * @cssstate has-trend - Something is slotted into `trend`.
53
+ * @cssstate has-unit - Something is slotted into `unit`.
34
54
  */
35
55
  export declare class HelixHeadlessStat extends HelixElement {
36
56
  /**
@@ -61,9 +81,34 @@ export declare class HelixHeadlessStat extends HelixElement {
61
81
  * carries nothing nobody wrote.
62
82
  */
63
83
  direction?: 'column-reverse' | 'row' | 'row-reverse';
84
+ /**
85
+ * `Intl.NumberFormat` options `value` is written with, such as
86
+ * `{"notation":"compact"}` for `1.2M` or `{"style":"percent"}` for `98%`,
87
+ * written as JSON in the attribute.
88
+ *
89
+ * Absent writes the number as the locale writes any number.
90
+ */
91
+ formatOptions?: Intl.NumberFormatOptions;
92
+ /**
93
+ * The locale `value` is written in, such as `de` for `1.234.567`.
94
+ *
95
+ * Absent reads the nearest `lang`, then the runtime's own. Set it on a page
96
+ * rendered on a server, so the server and the browser write the figure
97
+ * alike.
98
+ */
99
+ locale: string;
100
+ /**
101
+ * The figure as a number, written with `format-options` in `locale` in
102
+ * place of a slotted value.
103
+ *
104
+ * Absent leaves the figure to the slot.
105
+ */
106
+ value?: number;
64
107
  protected willUpdate(changed: PropertyValues): void;
65
108
  protected firstUpdated(changed: PropertyValues): void;
66
109
  protected updated(changed: PropertyValues): void;
110
+ /** Says which pieces the stat holds, one call apiece so each pairs with its `@cssstate`. */
111
+ private readonly readPieces;
67
112
  protected render(): TemplateResult;
68
113
  }
69
114
  declare global {
@@ -4,10 +4,20 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
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
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { customElement, HelixElement, hostAria, reportRuntime, slottedWords, } from '@fusion.dev/helix-core';
7
+ import { customElement, formatNumber, HelixElement, hostAria, reportRuntime, slotFilled, slottedWords, wordsLocale, } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  let HelixHeadlessStat = class HelixHeadlessStat extends HelixElement {
11
+ constructor() {
12
+ super(...arguments);
13
+ this.locale = '';
14
+ this.readPieces = () => {
15
+ const filled = (name) => slotFilled(this.renderRoot?.querySelector(`slot[name="${name}"]`));
16
+ this.toggleState('has-label', filled('label'));
17
+ this.toggleState('has-unit', filled('unit'));
18
+ this.toggleState('has-trend', filled('trend'));
19
+ };
20
+ }
11
21
  willUpdate(changed) {
12
22
  super.willUpdate(changed);
13
23
  const labels = this.knownChildren.filter((child) => child.slot === 'label');
@@ -25,6 +35,7 @@ let HelixHeadlessStat = class HelixHeadlessStat extends HelixElement {
25
35
  }
26
36
  updated(changed) {
27
37
  super.updated(changed);
38
+ this.readPieces();
28
39
  const given = this.direction;
29
40
  if (changed.has('direction') && (given === 'up' || given === 'down' || given === 'neutral')) {
30
41
  reportRuntime({
@@ -37,15 +48,28 @@ let HelixHeadlessStat = class HelixHeadlessStat extends HelixElement {
37
48
  }
38
49
  }
39
50
  render() {
40
- return html `<slot name="label" @slotchange=${() => this.requestUpdate()}></slot>
41
- <slot part="value"></slot>
42
- <slot name="unit"></slot>
43
- <slot name="trend"></slot>`;
51
+ const value = this.value;
52
+ const figure = value === undefined || !Number.isFinite(value)
53
+ ? ''
54
+ : formatNumber(value, wordsLocale(this), this.formatOptions);
55
+ return html `<slot name="label" part="label" @slotchange=${() => this.requestUpdate()}></slot>
56
+ ${figure === '' ? html `<slot part="value"></slot>` : html `<span part="value">${figure}</span>`}
57
+ <slot name="unit" part="unit" @slotchange=${this.readPieces}></slot>
58
+ <slot name="trend" part="trend" @slotchange=${this.readPieces}></slot>`;
44
59
  }
45
60
  };
46
61
  __decorate([
47
62
  property({ reflect: true })
48
63
  ], HelixHeadlessStat.prototype, "direction", void 0);
64
+ __decorate([
65
+ property({ attribute: 'format-options', type: Object })
66
+ ], HelixHeadlessStat.prototype, "formatOptions", void 0);
67
+ __decorate([
68
+ property()
69
+ ], HelixHeadlessStat.prototype, "locale", void 0);
70
+ __decorate([
71
+ property({ type: Number })
72
+ ], HelixHeadlessStat.prototype, "value", void 0);
49
73
  HelixHeadlessStat = __decorate([
50
74
  customElement('helix-headless-stat')
51
75
  ], HelixHeadlessStat);