@fusion.dev/helix-core 0.0.0-dev

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 (115) hide show
  1. package/LICENSE +21 -0
  2. package/custom-elements.json +8005 -0
  3. package/dist/controllers/aria-forward-controller/aria-forward-controller.d.ts +67 -0
  4. package/dist/controllers/aria-forward-controller/aria-forward-controller.js +146 -0
  5. package/dist/controllers/chart-pointer-controller/chart-pointer-controller.d.ts +79 -0
  6. package/dist/controllers/chart-pointer-controller/chart-pointer-controller.js +140 -0
  7. package/dist/controllers/command-controller/command-controller.d.ts +83 -0
  8. package/dist/controllers/command-controller/command-controller.js +93 -0
  9. package/dist/controllers/copy-controller/copy-controller.d.ts +58 -0
  10. package/dist/controllers/copy-controller/copy-controller.js +128 -0
  11. package/dist/controllers/disabled-cascade-controller/disabled-cascade-controller.d.ts +76 -0
  12. package/dist/controllers/disabled-cascade-controller/disabled-cascade-controller.js +67 -0
  13. package/dist/controllers/dismiss-controller/dismiss-controller.d.ts +58 -0
  14. package/dist/controllers/dismiss-controller/dismiss-controller.js +165 -0
  15. package/dist/controllers/drilldown-controller/drilldown-controller.d.ts +127 -0
  16. package/dist/controllers/drilldown-controller/drilldown-controller.js +171 -0
  17. package/dist/controllers/floating-controller/floating-controller.d.ts +123 -0
  18. package/dist/controllers/floating-controller/floating-controller.js +227 -0
  19. package/dist/controllers/focus-trap-controller/focus-trap-controller.d.ts +26 -0
  20. package/dist/controllers/focus-trap-controller/focus-trap-controller.js +93 -0
  21. package/dist/controllers/indicator-controller/indicator-controller.d.ts +97 -0
  22. package/dist/controllers/indicator-controller/indicator-controller.js +164 -0
  23. package/dist/controllers/keyboard-navigation-controller/keyboard-navigation-controller.d.ts +113 -0
  24. package/dist/controllers/keyboard-navigation-controller/keyboard-navigation-controller.js +117 -0
  25. package/dist/controllers/popover-controller/popover-controller.d.ts +132 -0
  26. package/dist/controllers/popover-controller/popover-controller.js +150 -0
  27. package/dist/controllers/source-controller/source-controller.d.ts +55 -0
  28. package/dist/controllers/source-controller/source-controller.js +94 -0
  29. package/dist/controllers/token-color-controller/token-color-controller.d.ts +63 -0
  30. package/dist/controllers/token-color-controller/token-color-controller.js +105 -0
  31. package/dist/controllers/virtualize-controller/virtualize-controller.d.ts +127 -0
  32. package/dist/controllers/virtualize-controller/virtualize-controller.js +216 -0
  33. package/dist/events/element-events.d.ts +4 -0
  34. package/dist/events/element-events.js +34 -0
  35. package/dist/events/events.d.ts +743 -0
  36. package/dist/events/events.js +799 -0
  37. package/dist/helix-element/helix-element.d.ts +201 -0
  38. package/dist/helix-element/helix-element.js +695 -0
  39. package/dist/helix-form-element/helix-form-element.d.ts +152 -0
  40. package/dist/helix-form-element/helix-form-element.js +194 -0
  41. package/dist/index.d.ts +54 -0
  42. package/dist/index.js +54 -0
  43. package/dist/mixins/mixin-chart-source/mixin-chart-source.d.ts +137 -0
  44. package/dist/mixins/mixin-chart-source/mixin-chart-source.js +140 -0
  45. package/dist/mixins/mixin-collection/index.d.ts +1 -0
  46. package/dist/mixins/mixin-collection/index.js +1 -0
  47. package/dist/mixins/mixin-collection/mixin-collection.d.ts +58 -0
  48. package/dist/mixins/mixin-collection/mixin-collection.js +154 -0
  49. package/dist/mixins/mixin-controller-active/mixin-controller-active.d.ts +53 -0
  50. package/dist/mixins/mixin-controller-active/mixin-controller-active.js +155 -0
  51. package/dist/mixins/mixin-controller-checked/mixin-controller-checked.d.ts +54 -0
  52. package/dist/mixins/mixin-controller-checked/mixin-controller-checked.js +157 -0
  53. package/dist/mixins/mixin-controller-open/mixin-controller-open.d.ts +49 -0
  54. package/dist/mixins/mixin-controller-open/mixin-controller-open.js +176 -0
  55. package/dist/mixins/mixin-controller-value/mixin-controller-value.d.ts +73 -0
  56. package/dist/mixins/mixin-controller-value/mixin-controller-value.js +173 -0
  57. package/dist/mixins/mixin-dismissible/mixin-dismissible.d.ts +41 -0
  58. package/dist/mixins/mixin-dismissible/mixin-dismissible.js +79 -0
  59. package/dist/mixins/mixin-field/mixin-field.d.ts +145 -0
  60. package/dist/mixins/mixin-field/mixin-field.js +423 -0
  61. package/dist/utils/anchor-element/anchor-element.d.ts +23 -0
  62. package/dist/utils/anchor-element/anchor-element.js +39 -0
  63. package/dist/utils/capture-pointer/capture-pointer.d.ts +22 -0
  64. package/dist/utils/capture-pointer/capture-pointer.js +30 -0
  65. package/dist/utils/closest-instance/closest-instance.d.ts +25 -0
  66. package/dist/utils/closest-instance/closest-instance.js +34 -0
  67. package/dist/utils/create-sheet/create-sheet.d.ts +16 -0
  68. package/dist/utils/create-sheet/create-sheet.js +16 -0
  69. package/dist/utils/custom-element/custom-element.d.ts +26 -0
  70. package/dist/utils/custom-element/custom-element.js +114 -0
  71. package/dist/utils/draw-icon/draw-icon.d.ts +38 -0
  72. package/dist/utils/draw-icon/draw-icon.js +39 -0
  73. package/dist/utils/draw-tooltip/draw-tooltip.d.ts +56 -0
  74. package/dist/utils/draw-tooltip/draw-tooltip.js +89 -0
  75. package/dist/utils/elapsed-time/elapsed-time.d.ts +10 -0
  76. package/dist/utils/elapsed-time/elapsed-time.js +117 -0
  77. package/dist/utils/focusable-elements/focusable-elements.d.ts +40 -0
  78. package/dist/utils/focusable-elements/focusable-elements.js +88 -0
  79. package/dist/utils/format-numbers/format-numbers.d.ts +1 -0
  80. package/dist/utils/format-numbers/format-numbers.js +29 -0
  81. package/dist/utils/host-aria/host-aria.d.ts +38 -0
  82. package/dist/utils/host-aria/host-aria.js +41 -0
  83. package/dist/utils/inherit-disabled/inherit-disabled.d.ts +33 -0
  84. package/dist/utils/inherit-disabled/inherit-disabled.js +55 -0
  85. package/dist/utils/line-path/line-path.d.ts +23 -0
  86. package/dist/utils/line-path/line-path.js +107 -0
  87. package/dist/utils/on-unless-false/on-unless-false.d.ts +28 -0
  88. package/dist/utils/on-unless-false/on-unless-false.js +32 -0
  89. package/dist/utils/read-series/read-series.d.ts +78 -0
  90. package/dist/utils/read-series/read-series.js +187 -0
  91. package/dist/utils/relative-time/relative-time.d.ts +24 -0
  92. package/dist/utils/relative-time/relative-time.js +84 -0
  93. package/dist/utils/request-change/request-change.d.ts +48 -0
  94. package/dist/utils/request-change/request-change.js +7 -0
  95. package/dist/utils/resolve-active-item/resolve-active-item.d.ts +28 -0
  96. package/dist/utils/resolve-active-item/resolve-active-item.js +21 -0
  97. package/dist/utils/restore-value/restore-value.d.ts +32 -0
  98. package/dist/utils/restore-value/restore-value.js +84 -0
  99. package/dist/utils/scale/scale.d.ts +23 -0
  100. package/dist/utils/scale/scale.js +32 -0
  101. package/dist/utils/scale/ticks.d.ts +27 -0
  102. package/dist/utils/scale/ticks.js +52 -0
  103. package/dist/utils/scroll-item-into-view/scroll-item-into-view.d.ts +10 -0
  104. package/dist/utils/scroll-item-into-view/scroll-item-into-view.js +98 -0
  105. package/dist/utils/slot-filled/slot-filled.d.ts +26 -0
  106. package/dist/utils/slot-filled/slot-filled.js +26 -0
  107. package/dist/utils/tell-disclosure/tell-disclosure.d.ts +51 -0
  108. package/dist/utils/tell-disclosure/tell-disclosure.js +76 -0
  109. package/dist/utils/ticker/ticker.d.ts +8 -0
  110. package/dist/utils/ticker/ticker.js +55 -0
  111. package/dist/utils/tone-marks/tone-marks.d.ts +52 -0
  112. package/dist/utils/tone-marks/tone-marks.js +36 -0
  113. package/dist/utils/value-as/value-as.d.ts +64 -0
  114. package/dist/utils/value-as/value-as.js +105 -0
  115. package/package.json +58 -0
@@ -0,0 +1,140 @@
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 { property } from 'lit/decorators.js';
8
+ import { ChartPointerController } from '../../controllers/chart-pointer-controller/chart-pointer-controller.js';
9
+ import { SourceController } from '../../controllers/source-controller/source-controller.js';
10
+ import { readRows, readSeries } from '../../utils/read-series/read-series.js';
11
+ /**
12
+ * How many colours the series palette has, and so where it starts over.
13
+ *
14
+ * Here rather than in each chart, which is what it was. Four families held the
15
+ * same `const PALETTE = 24` and the legend held a fifth: when the palette went
16
+ * from eight to twenty-four, one of them was missed and drew every ninth series
17
+ * in the first color again while the legend beside it named the ninth. A
18
+ * number two files have to agree on is a number that will stop agreeing.
19
+ */
20
+ export const PALETTE = 24;
21
+ /** The palette slot for the nth series, counting from one and wrapping. */
22
+ export const seriesIndex = (depth) => (depth % PALETTE) + 1;
23
+ /**
24
+ * The half of a chart that is about finding its numbers rather than drawing.
25
+ *
26
+ * Four families had written this out separately, and the row that asked for it
27
+ * set the bar: two copies is a coincidence and three is a pattern. There were
28
+ * five copies of `for`, four of `label` and three of the caption fallback by the
29
+ * time the line and donut charts landed, which is why this arrives now and not
30
+ * when the second chart did.
31
+ *
32
+ * **A mixin rather than a controller**, by the rule the tier notes give: it
33
+ * introduces `for` and `label`, which a consumer writes. `SourceController` is
34
+ * the other half and stays a controller, because resolving an id and watching
35
+ * for changes is behavior nobody calls.
36
+ *
37
+ * **`Legend` deliberately does not take this.** It reads a table and has no
38
+ * drawing to title, so `label` would be API it never uses, and a mixin applied
39
+ * for four fifths of a fit is how a shared thing starts drifting.
40
+ */
41
+ export const MixinChartSource = (Base) => {
42
+ class ChartSourceElement extends Base {
43
+ constructor() {
44
+ super(...arguments);
45
+ /** Resolves `for` and re-renders when the table behind it changes. */
46
+ this.source = new SourceController(this);
47
+ /**
48
+ * Which reading a pointer is nearest, for a tooltip to sit on.
49
+ *
50
+ * Public for the same reason `series` and `named` are: a protected member
51
+ * cannot be cast onto across unrelated classes, so it would be invisible to
52
+ * every chart that needs it. Reading where a pointer is resting is a fair
53
+ * thing to ask a chart anyway.
54
+ */
55
+ this.pointer = new ChartPointerController(this);
56
+ /**
57
+ * The id of the table to read.
58
+ *
59
+ * Resolved through `getRootNode()`, so a chart inside another component's
60
+ * shadow root names a table in that root rather than in the document. A
61
+ * `<table>` or anything built from grid roles, including `helix-table`.
62
+ *
63
+ * **In React this arrives as a property and not as an attribute**, the way
64
+ * `Tooltip.for` does, so it does not reflect and nothing styles on it.
65
+ */
66
+ this.for = '';
67
+ /**
68
+ * The drawing's name, for a pointer resting on it.
69
+ *
70
+ * Absent falls back to the table's own caption, which is usually the right
71
+ * words and is already written.
72
+ */
73
+ this.label = '';
74
+ }
75
+ /**
76
+ * Re-resolves when the chart is pointed at a different table.
77
+ *
78
+ * `SourceController` watches whatever it resolved on connect, and nothing
79
+ * tells it the id changed. `BarChart` had this and `LineChart` and
80
+ * `DonutChart` did not, which is the kind of difference that survives
81
+ * precisely because each file looked complete on its own: pointing a bar
82
+ * chart at a second table worked and pointing a line chart at one left it
83
+ * drawing the first. Extracting the four found it.
84
+ */
85
+ willUpdate(changed) {
86
+ super.willUpdate(changed);
87
+ if (changed.has('for')) {
88
+ this.source.watch();
89
+ }
90
+ }
91
+ /** The columns of the table, one series each, or none if there is no table. */
92
+ get series() {
93
+ const element = this.source.element;
94
+ return element ? readSeries(element) : [];
95
+ }
96
+ /**
97
+ * What to call the drawing, the consumer's word or the table's own.
98
+ *
99
+ * The fallback is the reason this is shared rather than copied: a caption is
100
+ * already written, already the right words, and already the thing a reader
101
+ * of the table sees. A chart that made the consumer say it twice would be
102
+ * asking for the second one to drift.
103
+ */
104
+ get named() {
105
+ return this.label || this.source.element?.querySelector('caption')?.textContent?.trim() || '';
106
+ }
107
+ /**
108
+ * Every row of the table, with each cell's text as well as its number.
109
+ *
110
+ * This is where a tooltip's content comes from, and it is deliberately not
111
+ * `series`. A chart draws numbers; a reader resting a pointer on one wants
112
+ * what the table says, which is `$128k` and not `128000`, and often wants
113
+ * the column beside it that nothing plotted at all.
114
+ *
115
+ * **Not `rows`, and the rename is the whole point of this paragraph.** It
116
+ * is the source table's rows, and `rows` on a chart is a name a chart
117
+ * wants for itself: `helix-heatmap` takes `rows="7"` to mean a calendar
118
+ * week, and declaring that against a base class whose `rows` is a `Row[]`
119
+ * getter is a type error at best. The same fault `MixinCollection` had,
120
+ * where a slot counter called `rows` would have shared one field with a
121
+ * consumer's data property. Name the member for what it holds rather than
122
+ * for the shortest word available.
123
+ */
124
+ get sourceRows() {
125
+ const element = this.source.element;
126
+ return element ? readRows(element) : [];
127
+ }
128
+ /** How many readings there are, which is what the pointer maps onto. */
129
+ get readings() {
130
+ return this.sourceRows.length;
131
+ }
132
+ }
133
+ __decorate([
134
+ property()
135
+ ], ChartSourceElement.prototype, "for", void 0);
136
+ __decorate([
137
+ property()
138
+ ], ChartSourceElement.prototype, "label", void 0);
139
+ return ChartSourceElement;
140
+ };
@@ -0,0 +1 @@
1
+ export { type CollectionElement, MixinCollection } from './mixin-collection.js';
@@ -0,0 +1 @@
1
+ export { MixinCollection } from './mixin-collection.js';
@@ -0,0 +1,58 @@
1
+ import { type LitElement, nothing, type TemplateResult } from 'lit';
2
+ type Constructor<T> = new (...args: any[]) => T;
3
+ /**
4
+ * The API a collection has for the two states that are not its content.
5
+ */
6
+ export declare class CollectionElement {
7
+ /** Whether the rows are still on their way. */
8
+ loading: boolean;
9
+ /** What is said while they are. */
10
+ loadingText: string;
11
+ /** What is said where there are none and none are coming. */
12
+ emptyText: string;
13
+ /**
14
+ * The status line, or nothing where the collection has content to show.
15
+ *
16
+ * Rendered by the component wherever its rows would be.
17
+ */
18
+ protected renderStatus(hasContent: boolean): TemplateResult | typeof nothing;
19
+ /** Binds to a rows slot's `slotchange`, and answers `hasRows` from it. */
20
+ protected countRows(event: Event): void;
21
+ /**
22
+ * Whether there is anything to show, as far as anything can tell.
23
+ *
24
+ * True until the rows have been counted, which is what keeps a server from
25
+ * claiming a list is empty.
26
+ */
27
+ protected get hasRows(): boolean;
28
+ }
29
+ /**
30
+ * Grafts the two states a collection has that are not its content.
31
+ *
32
+ * **Empty and loading are different answers and a reader can tell them apart.**
33
+ * "No results" under a spinner is a lie for as long as the request is in
34
+ * flight, and a blank box is not an answer at all: a reader cannot know whether
35
+ * a list is empty, still arriving, or broken. Six components each rendered rows
36
+ * and none of them said either thing, so every consumer wrote their own and
37
+ * wrote it differently.
38
+ *
39
+ * **`aria-busy` on the collection, not on the message.** A reader with a screen
40
+ * reader is told the region is changing and then told once, politely, what it
41
+ * settled on: a live region that fires on every intermediate state is a reader
42
+ * being read a progress bar.
43
+ *
44
+ * **The status replaces the rows rather than sitting above them.** A list that
45
+ * shows "Loading" over the last request's results is a list that lies about
46
+ * which question it is answering, and this is the point in a component's life
47
+ * when a consumer is most likely to be looking at stale content.
48
+ *
49
+ * Listbox had `empty-text` and nothing else, and Combobox forwarded it. Both
50
+ * keep the name, so nothing a consumer wrote has moved.
51
+ *
52
+ *
53
+ * **`empty` and `loading` are documented on each of the five families that
54
+ * apply this, rather than here.** See `MixinField` for why a class-level tag on
55
+ * a mixin reaches no manifest.
56
+ */
57
+ export declare const MixinCollection: <T extends Constructor<LitElement>>(Base: T) => Constructor<CollectionElement> & T;
58
+ export {};
@@ -0,0 +1,154 @@
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, nothing } from 'lit';
8
+ import { property, state } from 'lit/decorators.js';
9
+ /**
10
+ * Grafts the two states a collection has that are not its content.
11
+ *
12
+ * **Empty and loading are different answers and a reader can tell them apart.**
13
+ * "No results" under a spinner is a lie for as long as the request is in
14
+ * flight, and a blank box is not an answer at all: a reader cannot know whether
15
+ * a list is empty, still arriving, or broken. Six components each rendered rows
16
+ * and none of them said either thing, so every consumer wrote their own and
17
+ * wrote it differently.
18
+ *
19
+ * **`aria-busy` on the collection, not on the message.** A reader with a screen
20
+ * reader is told the region is changing and then told once, politely, what it
21
+ * settled on: a live region that fires on every intermediate state is a reader
22
+ * being read a progress bar.
23
+ *
24
+ * **The status replaces the rows rather than sitting above them.** A list that
25
+ * shows "Loading" over the last request's results is a list that lies about
26
+ * which question it is answering, and this is the point in a component's life
27
+ * when a consumer is most likely to be looking at stale content.
28
+ *
29
+ * Listbox had `empty-text` and nothing else, and Combobox forwarded it. Both
30
+ * keep the name, so nothing a consumer wrote has moved.
31
+ *
32
+ *
33
+ * **`empty` and `loading` are documented on each of the five families that
34
+ * apply this, rather than here.** See `MixinField` for why a class-level tag on
35
+ * a mixin reaches no manifest.
36
+ */
37
+ export const MixinCollection = (Base) => {
38
+ class Collection extends Base {
39
+ constructor() {
40
+ super(...arguments);
41
+ /**
42
+ * Whether the rows are still on their way.
43
+ *
44
+ * Reflected, because a stylesheet needs it: the rows that are already there
45
+ * are dimmed rather than removed while a new set arrives, and only CSS can
46
+ * say that without the component knowing what a row looks like.
47
+ */
48
+ this.loading = false;
49
+ /**
50
+ * What is said while they are on their way.
51
+ */
52
+ this.loadingText = 'Loading';
53
+ /**
54
+ * What is said where there are none, and none are coming.
55
+ */
56
+ this.emptyText = 'Nothing to show';
57
+ /**
58
+ * Whether the rows have been looked at yet.
59
+ */
60
+ this.slotCounted = false;
61
+ /**
62
+ * How many there were, the last time anyone looked.
63
+ *
64
+ * **Not `rows`, and the rename is the whole point of this comment.**
65
+ * TypeScript's `private` is a compile-time claim and the runtime field is
66
+ * whatever it is named, so a subclass declaring `@property() rows` for the
67
+ * data a consumer hands it would share one field with this counter: the
68
+ * slot count would overwrite the consumer's array on every `updated`, and
69
+ * `hasRows` would read the array's length as a slot count. Nothing would
70
+ * throw. `helix-data-table` takes exactly that property, which is what
71
+ * found this before it happened.
72
+ */
73
+ this.slotRows = 0;
74
+ }
75
+ /**
76
+ * Counts what a rows slot was given.
77
+ *
78
+ * Emptiness is a fact about light DOM children and the server has none to
79
+ * count. `slotchange` never fires there, so `counted` is false exactly when
80
+ * the answer is unknown, and an unknown answer is not "empty": a list that
81
+ * shipped "Nothing to show" over markup about to have rows in it is the one
82
+ * thing an empty state must never do. Listbox's spec caught precisely that.
83
+ */
84
+ countRows(event) {
85
+ this.slotCounted = true;
86
+ this.slotRows = event.target.assignedElements().length;
87
+ }
88
+ get hasRows() {
89
+ return this.slotCounted ? this.slotRows > 0 : true;
90
+ }
91
+ /**
92
+ * Counts the rows slot after every render.
93
+ *
94
+ * `slotchange` alone is not enough, and the gap is exactly the case this
95
+ * mixin exists for: the event fires when an assignment *changes*, and a
96
+ * collection that arrives with nothing in it has no change to report. So a
97
+ * list that was empty from the start never learned it was empty and said
98
+ * nothing at all, which is the blank box a reader cannot tell from a broken
99
+ * one. Measured on Table, whose rows slot starts empty.
100
+ *
101
+ * The default slot, by name, because that is where every collection in this
102
+ * library puts its rows: Listbox its options, Tree its items, Table its
103
+ * rows, Menu and Drilldown their pages. A named slot is a header or a
104
+ * footer, and neither is content.
105
+ *
106
+ * `updated` never runs on the server, which is what keeps `counted` false
107
+ * exactly when the answer is unknown.
108
+ */
109
+ updated(changed) {
110
+ super.updated?.(changed);
111
+ const slot = this.renderRoot?.querySelector?.('slot:not([name])');
112
+ if (!slot) {
113
+ return;
114
+ }
115
+ const rows = slot.assignedElements().length;
116
+ this.slotCounted = true;
117
+ if (rows !== this.slotRows) {
118
+ this.slotRows = rows;
119
+ }
120
+ }
121
+ /**
122
+ * The status line, or nothing where there is content to show.
123
+ *
124
+ * Loading wins over empty. A collection that has not answered yet is not
125
+ * empty, and saying so is the most common version of this fault: the reader
126
+ * is told there is nothing a moment before there is something.
127
+ */
128
+ renderStatus(hasContent) {
129
+ if (this.loading) {
130
+ return html `<p part="loading" role="status">${this.loadingText}</p>`;
131
+ }
132
+ if (hasContent) {
133
+ return nothing;
134
+ }
135
+ return html `<p part="empty">${this.emptyText}</p>`;
136
+ }
137
+ }
138
+ __decorate([
139
+ property({ reflect: true, type: Boolean })
140
+ ], Collection.prototype, "loading", void 0);
141
+ __decorate([
142
+ property({ attribute: 'loading-text' })
143
+ ], Collection.prototype, "loadingText", void 0);
144
+ __decorate([
145
+ property({ attribute: 'empty-text' })
146
+ ], Collection.prototype, "emptyText", void 0);
147
+ __decorate([
148
+ state()
149
+ ], Collection.prototype, "slotCounted", void 0);
150
+ __decorate([
151
+ state()
152
+ ], Collection.prototype, "slotRows", void 0);
153
+ return Collection;
154
+ };
@@ -0,0 +1,53 @@
1
+ import type { HelixElement } from '../../helix-element/helix-element.js';
2
+ import { type ActivatableItem } from '../../utils/resolve-active-item/resolve-active-item.js';
3
+ type Constructor<T> = new (...args: any[]) => T;
4
+ /**
5
+ * The public surface `MixinControllerActive` adds.
6
+ *
7
+ * Declared separately because TypeScript cannot emit declarations for an
8
+ * anonymous class that has private members (TS4094).
9
+ */
10
+ export declare class MixinControllerActiveInterface {
11
+ /**
12
+ * Which item is current, by its `value`.
13
+ *
14
+ * Reading gives the real one, in either mode. Writing hands ownership to the
15
+ * consumer: from then on the set announces changes and applies none of them.
16
+ */
17
+ active: string;
18
+ /** Initial item for uncontrolled use. Ignored once `active` is written. */
19
+ defaultActive: string;
20
+ /** Requests a new current item, and reports whether the request stood. */
21
+ requestActive(next: string): boolean;
22
+ /**
23
+ * Which item is current, given what there is to choose from.
24
+ *
25
+ * The first enabled one when `value` names nothing, which is a set before its
26
+ * consumer has chosen and a set whose current item has just been removed.
27
+ */
28
+ protected resolveActive<Item extends ActivatableItem>(items: Item[]): Item | undefined;
29
+ }
30
+ /**
31
+ * Grafts "which one of these is the current one" onto a container.
32
+ *
33
+ * For a set where one member is current and the set is not a form control:
34
+ * tabs, a tree, a nav bar, a carousel. Where it *is* a form control the value
35
+ * is submitted and belongs to `MixinControllerValue`, which is why these are
36
+ * two mixins and not one with a flag.
37
+ *
38
+ * **Announcing before applying is the part worth sharing.** `Tabs` and `Tree`
39
+ * both wrote the current value straight from the item's event, so a consumer
40
+ * had no way to refuse a change, while `Radio.Group` announced first. Neither
41
+ * was wrong on its own and the two together are: whether a selection can be
42
+ * refused should not depend on which component a reader is looking at.
43
+ *
44
+ * **The fallback is the other half.** Four components had written
45
+ * `items.find(match) ?? items[0]`, and it is not the obvious line it looks
46
+ * like: it is what makes a set with a `value` naming nothing still show
47
+ * something, which is every set before its consumer has set one and every set
48
+ * whose current item has just been removed. `resolveActive` also skips disabled
49
+ * items, which the hand-written copies did not, so a set opening on a disabled
50
+ * first item showed it as current.
51
+ */
52
+ export declare const MixinControllerActive: <T extends Constructor<HelixElement>>(Base: T) => Constructor<MixinControllerActiveInterface> & T;
53
+ export {};
@@ -0,0 +1,155 @@
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 { property } from 'lit/decorators.js';
8
+ import { HelixActivateEvent, HelixBeforeActivateEvent } from '../../events/events.js';
9
+ import { requestChange } from '../../utils/request-change/request-change.js';
10
+ import { resolveActiveItem, } from '../../utils/resolve-active-item/resolve-active-item.js';
11
+ /**
12
+ * Grafts "which one of these is the current one" onto a container.
13
+ *
14
+ * For a set where one member is current and the set is not a form control:
15
+ * tabs, a tree, a nav bar, a carousel. Where it *is* a form control the value
16
+ * is submitted and belongs to `MixinControllerValue`, which is why these are
17
+ * two mixins and not one with a flag.
18
+ *
19
+ * **Announcing before applying is the part worth sharing.** `Tabs` and `Tree`
20
+ * both wrote the current value straight from the item's event, so a consumer
21
+ * had no way to refuse a change, while `Radio.Group` announced first. Neither
22
+ * was wrong on its own and the two together are: whether a selection can be
23
+ * refused should not depend on which component a reader is looking at.
24
+ *
25
+ * **The fallback is the other half.** Four components had written
26
+ * `items.find(match) ?? items[0]`, and it is not the obvious line it looks
27
+ * like: it is what makes a set with a `value` naming nothing still show
28
+ * something, which is every set before its consumer has set one and every set
29
+ * whose current item has just been removed. `resolveActive` also skips disabled
30
+ * items, which the hand-written copies did not, so a set opening on a disabled
31
+ * first item showed it as current.
32
+ */
33
+ export const MixinControllerActive = (Base) => {
34
+ // JSDoc is duplicated from the interface deliberately. The interface is what
35
+ // TypeScript consumers see; this class is what the manifest analyzer reads
36
+ // when resolving inherited members into dependent packages.
37
+ class ActiveElement extends Base {
38
+ constructor() {
39
+ super(...arguments);
40
+ /**
41
+ * Initial item for uncontrolled use.
42
+ *
43
+ * Ignored once `active` is written.
44
+ */
45
+ this.defaultActive = '';
46
+ /**
47
+ * The state, whoever set it, and `undefined` until somebody has.
48
+ *
49
+ * One field and not two. Writing always applies, the way a native control
50
+ * does when a script assigns to it. What the write does not do is change
51
+ * which mode the element is in.
52
+ */
53
+ this.activeState = undefined;
54
+ }
55
+ /**
56
+ * Which item is current, by its `value`.
57
+ *
58
+ * Called `active`, which is what this mixin is named for and what it was
59
+ * not: it declared `value`, so the one mixin whose whole subject is which
60
+ * item is active had no such property, and `active` meant something else
61
+ * entirely elsewhere in the library. It still does, and only there now:
62
+ * `option.active` is the option the keyboard is on, which is a different
63
+ * question from which one is chosen.
64
+ *
65
+ * An accessor pair, the same shape as the other three. Reading gives the
66
+ * real item; writing hands ownership to the consumer, so `<helix-tabs
67
+ * active="one">` is a set that stays on that tab and `default-active` is
68
+ * how markup says where to start.
69
+ *
70
+ *
71
+ */
72
+ get active() {
73
+ return this.activeState ?? this.defaultActive;
74
+ }
75
+ set active(next) {
76
+ const previous = this.active;
77
+ this.activeState = next;
78
+ this.requestUpdate('active', previous);
79
+ }
80
+ /**
81
+ * Requests a new current item, and reports whether the request stood.
82
+ *
83
+ * Announces before it acts, so `preventDefault()` on the `helix-before-activate` is
84
+ * a refusal and nothing moves. Unlike a native control, nothing has moved
85
+ * by the time this runs, so refusing is simply not writing: there is
86
+ * nothing to put back. Nobody refusing means the set updates itself,
87
+ * whether or not a consumer declared `active`, a declared property being a
88
+ * seed and a correction. See 294.
89
+ */
90
+ requestActive(next) {
91
+ if (next === this.active) {
92
+ return true;
93
+ }
94
+ const applied = requestChange({
95
+ apply: (value) => {
96
+ const previous = this.active;
97
+ this.activeState = value;
98
+ this.requestUpdate('active', previous);
99
+ },
100
+ event: (value) => new HelixBeforeActivateEvent({ value }),
101
+ host: this,
102
+ next,
103
+ });
104
+ if (applied) {
105
+ this.announceActivated(next);
106
+ }
107
+ return applied;
108
+ }
109
+ /**
110
+ * Says the current child moved, once it has.
111
+ *
112
+ * **This was the only one of the four mixins with no notification**, and the
113
+ * event class said so deliberately: no native word means "a set chose a
114
+ * different member", so borrowing `change` would be a lie about what moved.
115
+ * That half still holds, and is why the pair is `helix-activate`. What did
116
+ * not follow is that a set should therefore announce nothing.
117
+ *
118
+ * The cost is the one 294 fixed for values and left here. A consumer who
119
+ * only wants to know which child is current had one channel and it fires
120
+ * *before* `apply`, so `currentTarget.active` is the previous value inside
121
+ * the handler and `detail.value` is a proposal another listener may refuse.
122
+ * Measured across the four families taking this: `Tabs` and `Tree` looked
123
+ * covered because `helix-select` bubbles up from a child, which fires when an
124
+ * item is *chosen* and not when `active` is assigned, and `Steps` and
125
+ * `Video Playlist` announced nothing at all.
126
+ *
127
+ * **After `apply`, which is the whole reason it is a second event**, so the
128
+ * state is on the element by the time a handler reads it. Not cancelable:
129
+ * the decision was taken by the event before this one, and two refusable
130
+ * events for one transition are two answers to one question. See #325.
131
+ */
132
+ announceActivated(value) {
133
+ this.dispatchEvent(new HelixActivateEvent({ value }));
134
+ }
135
+ /**
136
+ * Which item is current, given what there is to choose from.
137
+ *
138
+ * The first enabled one when `value` names nothing, which is a set before
139
+ * its consumer has chosen and a set whose current item has just been
140
+ * removed. Disabled items are skipped in the fallback but not in the match:
141
+ * a consumer who names a disabled item meant it, and quietly moving to
142
+ * another would be the component overruling them.
143
+ */
144
+ resolveActive(items) {
145
+ return resolveActiveItem(this.active, items);
146
+ }
147
+ }
148
+ __decorate([
149
+ property()
150
+ ], ActiveElement.prototype, "active", null);
151
+ __decorate([
152
+ property({ attribute: 'default-active' })
153
+ ], ActiveElement.prototype, "defaultActive", void 0);
154
+ return ActiveElement;
155
+ };
@@ -0,0 +1,54 @@
1
+ import type { HelixElement } from '../../helix-element/helix-element.js';
2
+ type Constructor<T> = new (...args: any[]) => T;
3
+ /**
4
+ * The public surface `MixinControllerChecked` adds.
5
+ *
6
+ * Declared separately because TypeScript cannot emit declarations for an
7
+ * anonymous class that has private members (TS4094). Casting the mixin's return
8
+ * to this interface keeps the internals private and the build working.
9
+ */
10
+ export declare class MixinControllerCheckedInterface {
11
+ /**
12
+ * Whether the control is checked.
13
+ *
14
+ * Reading gives the real state, in either mode. Writing hands ownership to
15
+ * the consumer: from then on the control announces changes and applies none
16
+ * of them.
17
+ */
18
+ checked: boolean;
19
+ /** Initial checked state for uncontrolled use. Ignored once `checked` is set. */
20
+ defaultChecked: boolean;
21
+ /**
22
+ * Requests a checked change, and reports whether it was applied.
23
+ *
24
+ * Announces before it acts: a listener calling `preventDefault()` on the
25
+ * `helix-before-toggle` refuses the change, and nothing moves.
26
+ */
27
+ requestChecked(next: boolean): boolean;
28
+ /** Requests the opposite of the current state. */
29
+ toggleChecked(): boolean;
30
+ /**
31
+ * Drops the uncontrolled state, so the control reads `defaultChecked` again.
32
+ *
33
+ * A form reset has to return a control to what its markup declared. Clearing
34
+ * `checked` alone cannot do it: that only leaves controlled mode, and the
35
+ * uncontrolled value the element has been keeping is private to the mixin
36
+ * and would still be what `checked` reports.
37
+ */
38
+ resetChecked(): void;
39
+ }
40
+ /**
41
+ * Grafts controlled and uncontrolled checked state onto a base class.
42
+ *
43
+ * Which mode it is in is read once, at the first update, from whether anything
44
+ * had set `checked` by then: markup, a hydrating server's attributes and a
45
+ * framework's initial properties have all arrived, and nothing else has
46
+ * happened yet. Set, and the consumer owns it and the element only announces.
47
+ * Unset, and the element owns it and `defaultChecked` says where it starts.
48
+ *
49
+ * Records `:state(checked)` whenever the element is checked, in either mode.
50
+ * Styled tiers select on that rather than on `[checked]`, which is absent for
51
+ * uncontrolled consumers.
52
+ */
53
+ export declare const MixinControllerChecked: <T extends Constructor<HelixElement>>(Base: T) => Constructor<MixinControllerCheckedInterface> & T;
54
+ export {};