@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.
- package/LICENSE +21 -0
- package/custom-elements.json +8005 -0
- package/dist/controllers/aria-forward-controller/aria-forward-controller.d.ts +67 -0
- package/dist/controllers/aria-forward-controller/aria-forward-controller.js +146 -0
- package/dist/controllers/chart-pointer-controller/chart-pointer-controller.d.ts +79 -0
- package/dist/controllers/chart-pointer-controller/chart-pointer-controller.js +140 -0
- package/dist/controllers/command-controller/command-controller.d.ts +83 -0
- package/dist/controllers/command-controller/command-controller.js +93 -0
- package/dist/controllers/copy-controller/copy-controller.d.ts +58 -0
- package/dist/controllers/copy-controller/copy-controller.js +128 -0
- package/dist/controllers/disabled-cascade-controller/disabled-cascade-controller.d.ts +76 -0
- package/dist/controllers/disabled-cascade-controller/disabled-cascade-controller.js +67 -0
- package/dist/controllers/dismiss-controller/dismiss-controller.d.ts +58 -0
- package/dist/controllers/dismiss-controller/dismiss-controller.js +165 -0
- package/dist/controllers/drilldown-controller/drilldown-controller.d.ts +127 -0
- package/dist/controllers/drilldown-controller/drilldown-controller.js +171 -0
- package/dist/controllers/floating-controller/floating-controller.d.ts +123 -0
- package/dist/controllers/floating-controller/floating-controller.js +227 -0
- package/dist/controllers/focus-trap-controller/focus-trap-controller.d.ts +26 -0
- package/dist/controllers/focus-trap-controller/focus-trap-controller.js +93 -0
- package/dist/controllers/indicator-controller/indicator-controller.d.ts +97 -0
- package/dist/controllers/indicator-controller/indicator-controller.js +164 -0
- package/dist/controllers/keyboard-navigation-controller/keyboard-navigation-controller.d.ts +113 -0
- package/dist/controllers/keyboard-navigation-controller/keyboard-navigation-controller.js +117 -0
- package/dist/controllers/popover-controller/popover-controller.d.ts +132 -0
- package/dist/controllers/popover-controller/popover-controller.js +150 -0
- package/dist/controllers/source-controller/source-controller.d.ts +55 -0
- package/dist/controllers/source-controller/source-controller.js +94 -0
- package/dist/controllers/token-color-controller/token-color-controller.d.ts +63 -0
- package/dist/controllers/token-color-controller/token-color-controller.js +105 -0
- package/dist/controllers/virtualize-controller/virtualize-controller.d.ts +127 -0
- package/dist/controllers/virtualize-controller/virtualize-controller.js +216 -0
- package/dist/events/element-events.d.ts +4 -0
- package/dist/events/element-events.js +34 -0
- package/dist/events/events.d.ts +743 -0
- package/dist/events/events.js +799 -0
- package/dist/helix-element/helix-element.d.ts +201 -0
- package/dist/helix-element/helix-element.js +695 -0
- package/dist/helix-form-element/helix-form-element.d.ts +152 -0
- package/dist/helix-form-element/helix-form-element.js +194 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.js +54 -0
- package/dist/mixins/mixin-chart-source/mixin-chart-source.d.ts +137 -0
- package/dist/mixins/mixin-chart-source/mixin-chart-source.js +140 -0
- package/dist/mixins/mixin-collection/index.d.ts +1 -0
- package/dist/mixins/mixin-collection/index.js +1 -0
- package/dist/mixins/mixin-collection/mixin-collection.d.ts +58 -0
- package/dist/mixins/mixin-collection/mixin-collection.js +154 -0
- package/dist/mixins/mixin-controller-active/mixin-controller-active.d.ts +53 -0
- package/dist/mixins/mixin-controller-active/mixin-controller-active.js +155 -0
- package/dist/mixins/mixin-controller-checked/mixin-controller-checked.d.ts +54 -0
- package/dist/mixins/mixin-controller-checked/mixin-controller-checked.js +157 -0
- package/dist/mixins/mixin-controller-open/mixin-controller-open.d.ts +49 -0
- package/dist/mixins/mixin-controller-open/mixin-controller-open.js +176 -0
- package/dist/mixins/mixin-controller-value/mixin-controller-value.d.ts +73 -0
- package/dist/mixins/mixin-controller-value/mixin-controller-value.js +173 -0
- package/dist/mixins/mixin-dismissible/mixin-dismissible.d.ts +41 -0
- package/dist/mixins/mixin-dismissible/mixin-dismissible.js +79 -0
- package/dist/mixins/mixin-field/mixin-field.d.ts +145 -0
- package/dist/mixins/mixin-field/mixin-field.js +423 -0
- package/dist/utils/anchor-element/anchor-element.d.ts +23 -0
- package/dist/utils/anchor-element/anchor-element.js +39 -0
- package/dist/utils/capture-pointer/capture-pointer.d.ts +22 -0
- package/dist/utils/capture-pointer/capture-pointer.js +30 -0
- package/dist/utils/closest-instance/closest-instance.d.ts +25 -0
- package/dist/utils/closest-instance/closest-instance.js +34 -0
- package/dist/utils/create-sheet/create-sheet.d.ts +16 -0
- package/dist/utils/create-sheet/create-sheet.js +16 -0
- package/dist/utils/custom-element/custom-element.d.ts +26 -0
- package/dist/utils/custom-element/custom-element.js +114 -0
- package/dist/utils/draw-icon/draw-icon.d.ts +38 -0
- package/dist/utils/draw-icon/draw-icon.js +39 -0
- package/dist/utils/draw-tooltip/draw-tooltip.d.ts +56 -0
- package/dist/utils/draw-tooltip/draw-tooltip.js +89 -0
- package/dist/utils/elapsed-time/elapsed-time.d.ts +10 -0
- package/dist/utils/elapsed-time/elapsed-time.js +117 -0
- package/dist/utils/focusable-elements/focusable-elements.d.ts +40 -0
- package/dist/utils/focusable-elements/focusable-elements.js +88 -0
- package/dist/utils/format-numbers/format-numbers.d.ts +1 -0
- package/dist/utils/format-numbers/format-numbers.js +29 -0
- package/dist/utils/host-aria/host-aria.d.ts +38 -0
- package/dist/utils/host-aria/host-aria.js +41 -0
- package/dist/utils/inherit-disabled/inherit-disabled.d.ts +33 -0
- package/dist/utils/inherit-disabled/inherit-disabled.js +55 -0
- package/dist/utils/line-path/line-path.d.ts +23 -0
- package/dist/utils/line-path/line-path.js +107 -0
- package/dist/utils/on-unless-false/on-unless-false.d.ts +28 -0
- package/dist/utils/on-unless-false/on-unless-false.js +32 -0
- package/dist/utils/read-series/read-series.d.ts +78 -0
- package/dist/utils/read-series/read-series.js +187 -0
- package/dist/utils/relative-time/relative-time.d.ts +24 -0
- package/dist/utils/relative-time/relative-time.js +84 -0
- package/dist/utils/request-change/request-change.d.ts +48 -0
- package/dist/utils/request-change/request-change.js +7 -0
- package/dist/utils/resolve-active-item/resolve-active-item.d.ts +28 -0
- package/dist/utils/resolve-active-item/resolve-active-item.js +21 -0
- package/dist/utils/restore-value/restore-value.d.ts +32 -0
- package/dist/utils/restore-value/restore-value.js +84 -0
- package/dist/utils/scale/scale.d.ts +23 -0
- package/dist/utils/scale/scale.js +32 -0
- package/dist/utils/scale/ticks.d.ts +27 -0
- package/dist/utils/scale/ticks.js +52 -0
- package/dist/utils/scroll-item-into-view/scroll-item-into-view.d.ts +10 -0
- package/dist/utils/scroll-item-into-view/scroll-item-into-view.js +98 -0
- package/dist/utils/slot-filled/slot-filled.d.ts +26 -0
- package/dist/utils/slot-filled/slot-filled.js +26 -0
- package/dist/utils/tell-disclosure/tell-disclosure.d.ts +51 -0
- package/dist/utils/tell-disclosure/tell-disclosure.js +76 -0
- package/dist/utils/ticker/ticker.d.ts +8 -0
- package/dist/utils/ticker/ticker.js +55 -0
- package/dist/utils/tone-marks/tone-marks.d.ts +52 -0
- package/dist/utils/tone-marks/tone-marks.js +36 -0
- package/dist/utils/value-as/value-as.d.ts +64 -0
- package/dist/utils/value-as/value-as.js +105 -0
- 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 {};
|