@fusion.dev/helix-core 0.0.0-dev → 0.0.3
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/custom-elements.json +156 -124
- package/dist/controllers/aria-forward-controller/aria-forward-controller.d.ts +2 -0
- package/dist/controllers/aria-forward-controller/aria-forward-controller.js +44 -17
- package/dist/controllers/chart-pointer-controller/chart-pointer-controller.d.ts +5 -6
- package/dist/controllers/chart-pointer-controller/chart-pointer-controller.js +5 -6
- package/dist/controllers/command-controller/command-controller.d.ts +5 -7
- package/dist/controllers/command-controller/command-controller.js +5 -7
- package/dist/controllers/copy-controller/copy-controller.d.ts +2 -4
- package/dist/controllers/copy-controller/copy-controller.js +7 -9
- package/dist/controllers/disabled-cascade-controller/disabled-cascade-controller.d.ts +10 -17
- package/dist/controllers/disabled-cascade-controller/disabled-cascade-controller.js +11 -18
- package/dist/controllers/dismiss-controller/dismiss-controller.d.ts +44 -6
- package/dist/controllers/dismiss-controller/dismiss-controller.js +77 -10
- package/dist/controllers/drilldown-controller/drilldown-controller.d.ts +3 -6
- package/dist/controllers/drilldown-controller/drilldown-controller.js +3 -6
- package/dist/controllers/floating-controller/floating-controller.d.ts +7 -8
- package/dist/controllers/floating-controller/floating-controller.js +69 -38
- package/dist/controllers/focus-trap-controller/focus-trap-controller.js +7 -10
- package/dist/controllers/indicator-controller/indicator-controller.d.ts +17 -29
- package/dist/controllers/indicator-controller/indicator-controller.js +11 -21
- package/dist/controllers/keyboard-navigation-controller/keyboard-navigation-controller.d.ts +3 -13
- package/dist/controllers/keyboard-navigation-controller/keyboard-navigation-controller.js +11 -24
- package/dist/controllers/popover-controller/popover-controller.d.ts +2 -7
- package/dist/controllers/popover-controller/popover-controller.js +3 -9
- package/dist/controllers/source-controller/source-controller.d.ts +2 -5
- package/dist/controllers/source-controller/source-controller.js +2 -5
- package/dist/controllers/token-color-controller/token-color-controller.d.ts +2 -4
- package/dist/controllers/token-color-controller/token-color-controller.js +2 -4
- package/dist/controllers/virtualize-controller/virtualize-controller.d.ts +1 -1
- package/dist/controllers/virtualize-controller/virtualize-controller.js +15 -15
- package/dist/events/element-events.js +0 -5
- package/dist/events/events.d.ts +55 -93
- package/dist/events/events.js +68 -105
- package/dist/helix-element/helix-element.d.ts +26 -27
- package/dist/helix-element/helix-element.js +83 -100
- package/dist/helix-form-element/helix-form-element.d.ts +11 -17
- package/dist/helix-form-element/helix-form-element.js +15 -21
- package/dist/mixins/mixin-chart-source/mixin-chart-source.d.ts +6 -14
- package/dist/mixins/mixin-chart-source/mixin-chart-source.js +12 -25
- package/dist/mixins/mixin-collection/mixin-collection.d.ts +1 -7
- package/dist/mixins/mixin-collection/mixin-collection.js +16 -23
- package/dist/mixins/mixin-controller-active/mixin-controller-active.d.ts +15 -16
- package/dist/mixins/mixin-controller-active/mixin-controller-active.js +28 -42
- package/dist/mixins/mixin-controller-checked/mixin-controller-checked.d.ts +15 -17
- package/dist/mixins/mixin-controller-checked/mixin-controller-checked.js +33 -45
- package/dist/mixins/mixin-controller-open/mixin-controller-open.d.ts +4 -4
- package/dist/mixins/mixin-controller-open/mixin-controller-open.js +29 -45
- package/dist/mixins/mixin-controller-value/mixin-controller-value.d.ts +10 -18
- package/dist/mixins/mixin-controller-value/mixin-controller-value.js +14 -31
- package/dist/mixins/mixin-dismissible/mixin-dismissible.d.ts +10 -15
- package/dist/mixins/mixin-dismissible/mixin-dismissible.js +10 -15
- package/dist/mixins/mixin-field/mixin-field.d.ts +10 -12
- package/dist/mixins/mixin-field/mixin-field.js +48 -64
- package/dist/utils/capture-pointer/capture-pointer.d.ts +1 -4
- package/dist/utils/capture-pointer/capture-pointer.js +1 -4
- package/dist/utils/closest-instance/closest-instance.d.ts +6 -10
- package/dist/utils/closest-instance/closest-instance.js +6 -10
- package/dist/utils/create-sheet/create-sheet.d.ts +1 -1
- package/dist/utils/create-sheet/create-sheet.js +1 -1
- package/dist/utils/custom-element/custom-element.d.ts +0 -21
- package/dist/utils/custom-element/custom-element.js +1 -1
- package/dist/utils/draw-icon/draw-icon.d.ts +3 -3
- package/dist/utils/draw-icon/draw-icon.js +3 -3
- package/dist/utils/elapsed-time/elapsed-time.js +1 -3
- package/dist/utils/focusable-elements/focusable-elements.d.ts +3 -4
- package/dist/utils/focusable-elements/focusable-elements.js +9 -11
- package/dist/utils/format-numbers/format-numbers.js +1 -4
- package/dist/utils/host-aria/host-aria.d.ts +2 -2
- package/dist/utils/host-aria/host-aria.js +2 -2
- package/dist/utils/line-path/line-path.js +1 -1
- package/dist/utils/on-unless-false/on-unless-false.d.ts +0 -5
- package/dist/utils/on-unless-false/on-unless-false.js +0 -5
- package/dist/utils/read-series/read-series.d.ts +26 -0
- package/dist/utils/read-series/read-series.js +26 -29
- package/dist/utils/relative-time/relative-time.js +5 -6
- package/dist/utils/request-change/request-change.d.ts +9 -20
- package/dist/utils/resolve-active-item/resolve-active-item.d.ts +3 -3
- package/dist/utils/resolve-active-item/resolve-active-item.js +3 -3
- package/dist/utils/restore-value/restore-value.js +1 -6
- package/dist/utils/scroll-item-into-view/scroll-item-into-view.js +4 -5
- package/dist/utils/slot-filled/slot-filled.d.ts +2 -4
- package/dist/utils/slot-filled/slot-filled.js +2 -4
- package/dist/utils/tell-disclosure/tell-disclosure.d.ts +15 -18
- package/dist/utils/tell-disclosure/tell-disclosure.js +15 -18
- package/dist/utils/tone-marks/tone-marks.d.ts +10 -16
- package/dist/utils/tone-marks/tone-marks.js +6 -10
- package/dist/utils/value-as/value-as.d.ts +0 -28
- package/dist/utils/value-as/value-as.js +5 -10
- package/package.json +6 -6
|
@@ -12,9 +12,7 @@ import { property, state } from 'lit/decorators.js';
|
|
|
12
12
|
* **Empty and loading are different answers and a reader can tell them apart.**
|
|
13
13
|
* "No results" under a spinner is a lie for as long as the request is in
|
|
14
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.
|
|
16
|
-
* and none of them said either thing, so every consumer wrote their own and
|
|
17
|
-
* wrote it differently.
|
|
15
|
+
* a list is empty, still arriving, or broken.
|
|
18
16
|
*
|
|
19
17
|
* **`aria-busy` on the collection, not on the message.** A reader with a screen
|
|
20
18
|
* reader is told the region is changing and then told once, politely, what it
|
|
@@ -26,10 +24,6 @@ import { property, state } from 'lit/decorators.js';
|
|
|
26
24
|
* which question it is answering, and this is the point in a component's life
|
|
27
25
|
* when a consumer is most likely to be looking at stale content.
|
|
28
26
|
*
|
|
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
27
|
* **`empty` and `loading` are documented on each of the five families that
|
|
34
28
|
* apply this, rather than here.** See `MixinField` for why a class-level tag on
|
|
35
29
|
* a mixin reaches no manifest.
|
|
@@ -38,6 +32,10 @@ export const MixinCollection = (Base) => {
|
|
|
38
32
|
class Collection extends Base {
|
|
39
33
|
constructor() {
|
|
40
34
|
super(...arguments);
|
|
35
|
+
/**
|
|
36
|
+
* What is said where there are none, and none are coming.
|
|
37
|
+
*/
|
|
38
|
+
this.emptyText = 'Nothing to show';
|
|
41
39
|
/**
|
|
42
40
|
* Whether the rows are still on their way.
|
|
43
41
|
*
|
|
@@ -50,10 +48,6 @@ export const MixinCollection = (Base) => {
|
|
|
50
48
|
* What is said while they are on their way.
|
|
51
49
|
*/
|
|
52
50
|
this.loadingText = 'Loading';
|
|
53
|
-
/**
|
|
54
|
-
* What is said where there are none, and none are coming.
|
|
55
|
-
*/
|
|
56
|
-
this.emptyText = 'Nothing to show';
|
|
57
51
|
/**
|
|
58
52
|
* Whether the rows have been looked at yet.
|
|
59
53
|
*/
|
|
@@ -61,14 +55,13 @@ export const MixinCollection = (Base) => {
|
|
|
61
55
|
/**
|
|
62
56
|
* How many there were, the last time anyone looked.
|
|
63
57
|
*
|
|
64
|
-
* **
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* found this before it happened.
|
|
58
|
+
* **Never name this `rows`.** TypeScript's `private` is a compile-time
|
|
59
|
+
* claim and the runtime field is whatever it is named, so a subclass
|
|
60
|
+
* declaring `@property() rows` for the data a consumer hands it would share
|
|
61
|
+
* one field with this counter: the slot count would overwrite the
|
|
62
|
+
* consumer's array on every `updated`, and `hasRows` would read the array's
|
|
63
|
+
* length as a slot count. Nothing would throw. `helix-data-table` takes
|
|
64
|
+
* exactly that property.
|
|
72
65
|
*/
|
|
73
66
|
this.slotRows = 0;
|
|
74
67
|
}
|
|
@@ -79,7 +72,7 @@ export const MixinCollection = (Base) => {
|
|
|
79
72
|
* count. `slotchange` never fires there, so `counted` is false exactly when
|
|
80
73
|
* the answer is unknown, and an unknown answer is not "empty": a list that
|
|
81
74
|
* shipped "Nothing to show" over markup about to have rows in it is the one
|
|
82
|
-
* thing an empty state must never do.
|
|
75
|
+
* thing an empty state must never do.
|
|
83
76
|
*/
|
|
84
77
|
countRows(event) {
|
|
85
78
|
this.slotCounted = true;
|
|
@@ -135,15 +128,15 @@ export const MixinCollection = (Base) => {
|
|
|
135
128
|
return html `<p part="empty">${this.emptyText}</p>`;
|
|
136
129
|
}
|
|
137
130
|
}
|
|
131
|
+
__decorate([
|
|
132
|
+
property({ attribute: 'empty-text' })
|
|
133
|
+
], Collection.prototype, "emptyText", void 0);
|
|
138
134
|
__decorate([
|
|
139
135
|
property({ reflect: true, type: Boolean })
|
|
140
136
|
], Collection.prototype, "loading", void 0);
|
|
141
137
|
__decorate([
|
|
142
138
|
property({ attribute: 'loading-text' })
|
|
143
139
|
], Collection.prototype, "loadingText", void 0);
|
|
144
|
-
__decorate([
|
|
145
|
-
property({ attribute: 'empty-text' })
|
|
146
|
-
], Collection.prototype, "emptyText", void 0);
|
|
147
140
|
__decorate([
|
|
148
141
|
state()
|
|
149
142
|
], Collection.prototype, "slotCounted", void 0);
|
|
@@ -11,8 +11,9 @@ export declare class MixinControllerActiveInterface {
|
|
|
11
11
|
/**
|
|
12
12
|
* Which item is current, by its `value`.
|
|
13
13
|
*
|
|
14
|
-
* Reading gives the real one
|
|
15
|
-
*
|
|
14
|
+
* Reading gives the real one. Writing always applies, and is a seed and a
|
|
15
|
+
* correction rather than a claim of ownership: a consumer who wants the
|
|
16
|
+
* decision refuses the `helix-before-activate`.
|
|
16
17
|
*/
|
|
17
18
|
active: string;
|
|
18
19
|
/** Initial item for uncontrolled use. Ignored once `active` is written. */
|
|
@@ -22,8 +23,8 @@ export declare class MixinControllerActiveInterface {
|
|
|
22
23
|
/**
|
|
23
24
|
* Which item is current, given what there is to choose from.
|
|
24
25
|
*
|
|
25
|
-
* The first enabled one when `
|
|
26
|
-
* consumer has chosen and a set whose current item has just been removed.
|
|
26
|
+
* The first enabled one when `active` names nothing, which is a set before
|
|
27
|
+
* its consumer has chosen and a set whose current item has just been removed.
|
|
27
28
|
*/
|
|
28
29
|
protected resolveActive<Item extends ActivatableItem>(items: Item[]): Item | undefined;
|
|
29
30
|
}
|
|
@@ -35,19 +36,17 @@ export declare class MixinControllerActiveInterface {
|
|
|
35
36
|
* is submitted and belongs to `MixinControllerValue`, which is why these are
|
|
36
37
|
* two mixins and not one with a flag.
|
|
37
38
|
*
|
|
38
|
-
* **Announcing before applying is the part worth sharing.**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* refused should not depend on which component a reader is looking at.
|
|
39
|
+
* **Announcing before applying is the part worth sharing.** Writing the current
|
|
40
|
+
* value straight from the item's event leaves a consumer no way to refuse a
|
|
41
|
+
* change, and whether a selection can be refused must not depend on which
|
|
42
|
+
* component a reader is looking at.
|
|
43
43
|
*
|
|
44
|
-
* **The fallback is the other half.**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* first item showed it as current.
|
|
44
|
+
* **The fallback is the other half.** `items.find(match) ?? items[0]` is not
|
|
45
|
+
* the obvious line it looks like: it is what makes a set whose `active` names
|
|
46
|
+
* nothing still show something, which is every set before its consumer has set
|
|
47
|
+
* one and every set whose current item has just been removed. `resolveActive`
|
|
48
|
+
* also skips disabled items in that fallback, or a set opening on a disabled
|
|
49
|
+
* first item shows it as current.
|
|
51
50
|
*/
|
|
52
51
|
export declare const MixinControllerActive: <T extends Constructor<HelixElement>>(Base: T) => Constructor<MixinControllerActiveInterface> & T;
|
|
53
52
|
export {};
|
|
@@ -16,19 +16,17 @@ import { resolveActiveItem, } from '../../utils/resolve-active-item/resolve-acti
|
|
|
16
16
|
* is submitted and belongs to `MixinControllerValue`, which is why these are
|
|
17
17
|
* two mixins and not one with a flag.
|
|
18
18
|
*
|
|
19
|
-
* **Announcing before applying is the part worth sharing.**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* refused should not depend on which component a reader is looking at.
|
|
19
|
+
* **Announcing before applying is the part worth sharing.** Writing the current
|
|
20
|
+
* value straight from the item's event leaves a consumer no way to refuse a
|
|
21
|
+
* change, and whether a selection can be refused must not depend on which
|
|
22
|
+
* component a reader is looking at.
|
|
24
23
|
*
|
|
25
|
-
* **The fallback is the other half.**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* first item showed it as current.
|
|
24
|
+
* **The fallback is the other half.** `items.find(match) ?? items[0]` is not
|
|
25
|
+
* the obvious line it looks like: it is what makes a set whose `active` names
|
|
26
|
+
* nothing still show something, which is every set before its consumer has set
|
|
27
|
+
* one and every set whose current item has just been removed. `resolveActive`
|
|
28
|
+
* also skips disabled items in that fallback, or a set opening on a disabled
|
|
29
|
+
* first item shows it as current.
|
|
32
30
|
*/
|
|
33
31
|
export const MixinControllerActive = (Base) => {
|
|
34
32
|
// JSDoc is duplicated from the interface deliberately. The interface is what
|
|
@@ -47,27 +45,19 @@ export const MixinControllerActive = (Base) => {
|
|
|
47
45
|
* The state, whoever set it, and `undefined` until somebody has.
|
|
48
46
|
*
|
|
49
47
|
* One field and not two. Writing always applies, the way a native control
|
|
50
|
-
* does when a script assigns to it.
|
|
51
|
-
* which mode the element is in.
|
|
48
|
+
* does when a script assigns to it.
|
|
52
49
|
*/
|
|
53
50
|
this.activeState = undefined;
|
|
54
51
|
}
|
|
55
52
|
/**
|
|
56
53
|
* Which item is current, by its `value`.
|
|
57
54
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
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
|
-
*
|
|
55
|
+
* Not `option.active`, which is the option the keyboard is on: that is a
|
|
56
|
+
* different question from which one is chosen.
|
|
70
57
|
*
|
|
58
|
+
* Writing always applies, so `<helix-tabs active="one">` starts on that
|
|
59
|
+
* tab, and `default-active` is how markup says where to start without
|
|
60
|
+
* naming the current one.
|
|
71
61
|
*/
|
|
72
62
|
get active() {
|
|
73
63
|
return this.activeState ?? this.defaultActive;
|
|
@@ -85,7 +75,7 @@ export const MixinControllerActive = (Base) => {
|
|
|
85
75
|
* by the time this runs, so refusing is simply not writing: there is
|
|
86
76
|
* nothing to put back. Nobody refusing means the set updates itself,
|
|
87
77
|
* whether or not a consumer declared `active`, a declared property being a
|
|
88
|
-
* seed and a correction.
|
|
78
|
+
* seed and a correction.
|
|
89
79
|
*/
|
|
90
80
|
requestActive(next) {
|
|
91
81
|
if (next === this.active) {
|
|
@@ -109,25 +99,21 @@ export const MixinControllerActive = (Base) => {
|
|
|
109
99
|
/**
|
|
110
100
|
* Says the current child moved, once it has.
|
|
111
101
|
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
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.
|
|
102
|
+
* `helix-activate` rather than a native word: nothing on the platform means
|
|
103
|
+
* "a set chose a different member", and borrowing `change` would be a lie
|
|
104
|
+
* about what moved.
|
|
117
105
|
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
* item is *chosen* and not when `active` is assigned, and `Steps` and
|
|
125
|
-
* `Video Playlist` announced nothing at all.
|
|
106
|
+
* A consumer who only wants to know which child is current cannot use
|
|
107
|
+
* `helix-before-activate` for it, that one firing *before* `apply`, so
|
|
108
|
+
* `currentTarget.active` is the previous value inside the handler and
|
|
109
|
+
* `detail.value` is a proposal another listener may refuse. `helix-select`
|
|
110
|
+
* is no substitute either: it fires when an item is *chosen* and not when
|
|
111
|
+
* `active` is assigned.
|
|
126
112
|
*
|
|
127
113
|
* **After `apply`, which is the whole reason it is a second event**, so the
|
|
128
114
|
* state is on the element by the time a handler reads it. Not cancelable:
|
|
129
115
|
* the decision was taken by the event before this one, and two refusable
|
|
130
|
-
* events for one transition are two answers to one question.
|
|
116
|
+
* events for one transition are two answers to one question.
|
|
131
117
|
*/
|
|
132
118
|
announceActivated(value) {
|
|
133
119
|
this.dispatchEvent(new HelixActivateEvent({ value }));
|
|
@@ -135,7 +121,7 @@ export const MixinControllerActive = (Base) => {
|
|
|
135
121
|
/**
|
|
136
122
|
* Which item is current, given what there is to choose from.
|
|
137
123
|
*
|
|
138
|
-
* The first enabled one when `
|
|
124
|
+
* The first enabled one when `active` names nothing, which is a set before
|
|
139
125
|
* its consumer has chosen and a set whose current item has just been
|
|
140
126
|
* removed. Disabled items are skipped in the fallback but not in the match:
|
|
141
127
|
* a consumer who names a disabled item meant it, and quietly moving to
|
|
@@ -11,9 +11,9 @@ export declare class MixinControllerCheckedInterface {
|
|
|
11
11
|
/**
|
|
12
12
|
* Whether the control is checked.
|
|
13
13
|
*
|
|
14
|
-
* Reading gives the real state,
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* Reading gives the real state. Writing always applies, and is a seed and a
|
|
15
|
+
* correction rather than a claim of ownership: a consumer who wants the
|
|
16
|
+
* decision refuses the `helix-before-toggle`.
|
|
17
17
|
*/
|
|
18
18
|
checked: boolean;
|
|
19
19
|
/** Initial checked state for uncontrolled use. Ignored once `checked` is set. */
|
|
@@ -28,27 +28,25 @@ export declare class MixinControllerCheckedInterface {
|
|
|
28
28
|
/** Requests the opposite of the current state. */
|
|
29
29
|
toggleChecked(): boolean;
|
|
30
30
|
/**
|
|
31
|
-
* Drops the
|
|
31
|
+
* Drops the recorded state, so the control reads `defaultChecked` again.
|
|
32
32
|
*
|
|
33
|
-
* A form reset has to return a control to what its markup declared
|
|
34
|
-
* `checked`
|
|
35
|
-
*
|
|
36
|
-
* and would still be what `checked` reports.
|
|
33
|
+
* A form reset has to return a control to what its markup declared, and
|
|
34
|
+
* assigning `checked` cannot do it: any assignment records a state, and the
|
|
35
|
+
* recorded state is private to the mixin. This is the only way to clear it.
|
|
37
36
|
*/
|
|
38
37
|
resetChecked(): void;
|
|
39
38
|
}
|
|
40
39
|
/**
|
|
41
|
-
* Grafts
|
|
40
|
+
* Grafts checked state onto a base class.
|
|
42
41
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* Unset, and the element owns it and `defaultChecked` says where it starts.
|
|
42
|
+
* `checked` is a seed and a correction rather than a claim: the element applies
|
|
43
|
+
* its own changes from wherever markup started it, `defaultChecked` is what a
|
|
44
|
+
* reset returns to, and a consumer takes the decision by refusing the
|
|
45
|
+
* `helix-before-toggle`.
|
|
48
46
|
*
|
|
49
|
-
* Records `:state(checked)` whenever the element is checked
|
|
50
|
-
*
|
|
51
|
-
*
|
|
47
|
+
* Records `:state(checked)` whenever the element is checked. Styled tiers
|
|
48
|
+
* select on that rather than on `[checked]`, which is absent until somebody
|
|
49
|
+
* writes it.
|
|
52
50
|
*/
|
|
53
51
|
export declare const MixinControllerChecked: <T extends Constructor<HelixElement>>(Base: T) => Constructor<MixinControllerCheckedInterface> & T;
|
|
54
52
|
export {};
|
|
@@ -8,17 +8,16 @@ import { property } from 'lit/decorators.js';
|
|
|
8
8
|
import { HelixBeforeToggleEvent } from '../../events/events.js';
|
|
9
9
|
import { requestChange } from '../../utils/request-change/request-change.js';
|
|
10
10
|
/**
|
|
11
|
-
* Grafts
|
|
11
|
+
* Grafts checked state onto a base class.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* Unset, and the element owns it and `defaultChecked` says where it starts.
|
|
13
|
+
* `checked` is a seed and a correction rather than a claim: the element applies
|
|
14
|
+
* its own changes from wherever markup started it, `defaultChecked` is what a
|
|
15
|
+
* reset returns to, and a consumer takes the decision by refusing the
|
|
16
|
+
* `helix-before-toggle`.
|
|
18
17
|
*
|
|
19
|
-
* Records `:state(checked)` whenever the element is checked
|
|
20
|
-
*
|
|
21
|
-
*
|
|
18
|
+
* Records `:state(checked)` whenever the element is checked. Styled tiers
|
|
19
|
+
* select on that rather than on `[checked]`, which is absent until somebody
|
|
20
|
+
* writes it.
|
|
22
21
|
*/
|
|
23
22
|
export const MixinControllerChecked = (Base) => {
|
|
24
23
|
// JSDoc is duplicated from MixinControllerCheckedInterface deliberately. The
|
|
@@ -35,25 +34,19 @@ export const MixinControllerChecked = (Base) => {
|
|
|
35
34
|
* The state, whoever set it, and `undefined` until somebody has.
|
|
36
35
|
*
|
|
37
36
|
* One field and not two. Writing always applies, the way a native control
|
|
38
|
-
* does when a script assigns to it.
|
|
39
|
-
* which mode the element is in.
|
|
37
|
+
* does when a script assigns to it.
|
|
40
38
|
*/
|
|
41
39
|
this.checkedState = undefined;
|
|
42
40
|
}
|
|
43
41
|
/**
|
|
44
42
|
* Whether the control is checked.
|
|
45
43
|
*
|
|
46
|
-
* One property rather than two.
|
|
47
|
-
*
|
|
48
|
-
* is what the attribute in `<helix-checkbox checked>` does as well. The
|
|
49
|
-
* same shape all four of these mixins have, for the reason given on
|
|
50
|
-
* `MixinControllerOpen.open`.
|
|
44
|
+
* One property rather than two. Writing always applies;
|
|
45
|
+
* `<helix-checkbox checked>` is the same statement in markup.
|
|
51
46
|
*
|
|
52
47
|
* Not reflected: the attribute is the consumer's input and stays as they
|
|
53
48
|
* wrote it, and `:state(checked)` carries the resolved state for
|
|
54
49
|
* stylesheets.
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
50
|
*/
|
|
58
51
|
get checked() {
|
|
59
52
|
return this.checkedState ?? this.defaultChecked;
|
|
@@ -66,19 +59,15 @@ export const MixinControllerChecked = (Base) => {
|
|
|
66
59
|
/**
|
|
67
60
|
* Records the resolved state as the custom state `checked`.
|
|
68
61
|
*
|
|
69
|
-
* **A state and
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
62
|
+
* **A custom state and never `data-checked`.** An attribute written here
|
|
63
|
+
* means `<helix-checkbox default-checked>` comes back from a consumer's
|
|
64
|
+
* server without it and gains it the moment the definition lands, which
|
|
65
|
+
* React lists as a hydration mismatch and does not patch up.
|
|
66
|
+
* `HelixElement.toggleState` says why the host is the wrong place to keep a
|
|
67
|
+
* component's own bookkeeping.
|
|
75
68
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* SSR calls `willUpdate` and not `connectedCallback`, so the attribute
|
|
79
|
-
* reached server output: true, and no longer a reason for anything, a
|
|
80
|
-
* custom state being a fact about a running element that a server has
|
|
81
|
-
* nothing to say about.
|
|
69
|
+
* In `willUpdate`, which is where the resolved state is first known and is
|
|
70
|
+
* what a subclass rendering from it expects.
|
|
82
71
|
*/
|
|
83
72
|
willUpdate(changed) {
|
|
84
73
|
super.willUpdate(changed);
|
|
@@ -88,10 +77,11 @@ export const MixinControllerChecked = (Base) => {
|
|
|
88
77
|
* Requests a checked change, and reports whether it was applied.
|
|
89
78
|
*
|
|
90
79
|
* Announces before it acts, so `preventDefault()` on the
|
|
91
|
-
* `helix-before-toggle` is a refusal and nothing moves. Nobody refusing
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
80
|
+
* `helix-before-toggle` is a refusal and nothing moves. Nobody refusing
|
|
81
|
+
* means the element updates itself, whether or not a consumer declared the
|
|
82
|
+
* property: a declared property is a seed and a correction, the way
|
|
83
|
+
* `<input value>` is, and a consumer who wants the change for themselves
|
|
84
|
+
* refuses it and assigns their own.
|
|
95
85
|
*/
|
|
96
86
|
requestChecked(next) {
|
|
97
87
|
if (this.checked === next) {
|
|
@@ -122,24 +112,22 @@ export const MixinControllerChecked = (Base) => {
|
|
|
122
112
|
/**
|
|
123
113
|
* Restores the control when the browser brings a form back.
|
|
124
114
|
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* mixin owns `checked`, so the mixin owns putting it back.
|
|
115
|
+
* The mixin owns `checked`, so the mixin owns putting it back: written on
|
|
116
|
+
* one consumer and not the other, a reader who navigates away and comes
|
|
117
|
+
* back to a restored form finds that control flipped off.
|
|
129
118
|
*
|
|
130
|
-
* Through
|
|
131
|
-
*
|
|
119
|
+
* Through the public accessor rather than the private field, so a restore
|
|
120
|
+
* takes the same path a script assignment does.
|
|
132
121
|
*/
|
|
133
122
|
formStateRestoreCallback(state) {
|
|
134
123
|
this.checked = state !== null;
|
|
135
124
|
}
|
|
136
125
|
/**
|
|
137
|
-
* Drops the
|
|
138
|
-
* again.
|
|
126
|
+
* Drops the recorded state, so the control reads `defaultChecked` again.
|
|
139
127
|
*
|
|
140
|
-
* A form reset has to return a control to what its markup declared, and
|
|
141
|
-
*
|
|
142
|
-
*
|
|
128
|
+
* A form reset has to return a control to what its markup declared, and no
|
|
129
|
+
* assignment to `checked` can do that: any assignment records a state. The
|
|
130
|
+
* recorded state lives here, so this is where it is dropped.
|
|
143
131
|
*/
|
|
144
132
|
resetChecked() {
|
|
145
133
|
const previous = this.checked;
|
|
@@ -13,9 +13,9 @@ export declare class MixinControllerOpenInterface {
|
|
|
13
13
|
/**
|
|
14
14
|
* Whether the element is open.
|
|
15
15
|
*
|
|
16
|
-
* Reading gives the real state,
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* Reading gives the real state. Writing always applies, and is a seed and a
|
|
17
|
+
* correction rather than a claim of ownership: a consumer who wants the
|
|
18
|
+
* decision refuses the `helix-before-open` or `helix-before-close`.
|
|
19
19
|
*/
|
|
20
20
|
open: boolean;
|
|
21
21
|
/**
|
|
@@ -36,7 +36,7 @@ export declare class MixinControllerOpenInterface {
|
|
|
36
36
|
* `toggle` announces it afterwards, once the state has actually gone, carrying
|
|
37
37
|
* `newState` and `oldState` the way the platform does on `<details>` and on a
|
|
38
38
|
* popover. The first is how a consumer takes the decision; the second is what
|
|
39
|
-
* they bind when they only want to know.
|
|
39
|
+
* they bind when they only want to know.
|
|
40
40
|
*
|
|
41
41
|
* `open` is a seed and a correction rather than a claim: the element applies
|
|
42
42
|
* its own changes from wherever markup started it, and `defaultOpen` is what a
|
|
@@ -15,7 +15,7 @@ import { requestChange } from '../../utils/request-change/request-change.js';
|
|
|
15
15
|
* `toggle` announces it afterwards, once the state has actually gone, carrying
|
|
16
16
|
* `newState` and `oldState` the way the platform does on `<details>` and on a
|
|
17
17
|
* popover. The first is how a consumer takes the decision; the second is what
|
|
18
|
-
* they bind when they only want to know.
|
|
18
|
+
* they bind when they only want to know.
|
|
19
19
|
*
|
|
20
20
|
* `open` is a seed and a correction rather than a claim: the element applies
|
|
21
21
|
* its own changes from wherever markup started it, and `defaultOpen` is what a
|
|
@@ -45,37 +45,24 @@ export const MixinControllerOpen = (Base) => {
|
|
|
45
45
|
* silently overwrites the first. Measured, as a select whose `value` came
|
|
46
46
|
* back holding its `open` boolean.
|
|
47
47
|
*
|
|
48
|
-
* One field and not two. Writing always applies,
|
|
49
|
-
*
|
|
50
|
-
* imperative `element.open = true` should have whatever mode the element is
|
|
51
|
-
* in. What the write does *not* do is change which mode that is.
|
|
48
|
+
* One field and not two. Writing always applies, the way a native control
|
|
49
|
+
* does when a script assigns to it.
|
|
52
50
|
*/
|
|
53
51
|
this.openState = undefined;
|
|
54
52
|
}
|
|
55
53
|
/**
|
|
56
54
|
* Whether the element is open.
|
|
57
55
|
*
|
|
58
|
-
* One property, not two.
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* attribute in `<helix-dialog open>` does as well: from then on the element
|
|
62
|
-
* announces changes and applies none of them.
|
|
63
|
-
*
|
|
64
|
-
* It reads as an accessor pair rather than a field because those are two
|
|
65
|
-
* different questions sharing one name, and the alternative was a second
|
|
66
|
-
* property answering the first. That worked for a boolean, where `isOpen`
|
|
67
|
-
* reads well; there is no such word for a value or an active item, so all
|
|
68
|
-
* four of these mixins now have this shape instead of two shapes.
|
|
56
|
+
* One property, not two, so `dialog.open` answers the question anyone
|
|
57
|
+
* asking it meant. Writing always applies; `<helix-dialog open>` is the
|
|
58
|
+
* same statement in markup.
|
|
69
59
|
*
|
|
70
60
|
* **Not reflected, and that is required rather than tidy.** Reflection
|
|
71
|
-
* writes the resolved state back as an attribute,
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* back holding whatever the last reader did rather than what the author
|
|
77
|
-
* wrote. `:state(open)` carries the resolved state for stylesheets, which
|
|
78
|
-
* is what the mirror has always been for.
|
|
61
|
+
* writes the resolved state back as an attribute, so an element that opened
|
|
62
|
+
* itself would put `open` on its own host: cloned, serialized for
|
|
63
|
+
* hydration, or diffed by a framework, it comes back holding whatever the
|
|
64
|
+
* last reader did rather than what the author wrote. `:state(open)` carries
|
|
65
|
+
* the resolved state for stylesheets, which is what the mirror is for.
|
|
79
66
|
*/
|
|
80
67
|
get open() {
|
|
81
68
|
return this.openState ?? this.defaultOpen;
|
|
@@ -88,14 +75,14 @@ export const MixinControllerOpen = (Base) => {
|
|
|
88
75
|
/**
|
|
89
76
|
* Records the resolved state as the custom state `open`.
|
|
90
77
|
*
|
|
91
|
-
* **A state and
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
78
|
+
* **A custom state and never `data-open`.** An attribute mirror is in the
|
|
79
|
+
* DOM, so an `<helix-accordion-item default-open>` served without it gains
|
|
80
|
+
* it on upgrade, which is a hydration mismatch a React consumer reports.
|
|
81
|
+
* See `HelixElement.toggleState`, and see `Toast.Item` for the other answer
|
|
82
|
+
* to the same fault: inverting the hook so the write falls on the side that
|
|
83
|
+
* only happens after somebody acts. Inversion works where the resting state
|
|
84
|
+
* is the loud one and cannot help here, `checked` and `open` being true
|
|
85
|
+
* only because a consumer asked for them.
|
|
99
86
|
*/
|
|
100
87
|
willUpdate(changed) {
|
|
101
88
|
super.willUpdate(changed);
|
|
@@ -109,7 +96,6 @@ export const MixinControllerOpen = (Base) => {
|
|
|
109
96
|
* element opens or closes itself, whether or not a consumer declared
|
|
110
97
|
* `open`: a declared property is a seed and a correction, and a consumer
|
|
111
98
|
* who wants the decision for themselves refuses it and assigns their own.
|
|
112
|
-
* See 294.
|
|
113
99
|
*/
|
|
114
100
|
requestOpen(next) {
|
|
115
101
|
if (this.open === next) {
|
|
@@ -133,19 +119,17 @@ export const MixinControllerOpen = (Base) => {
|
|
|
133
119
|
/**
|
|
134
120
|
* Says the state moved, under the platform's own name and in its shape.
|
|
135
121
|
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* `TreeItem` are `aria-expanded` and `hidden` written by hand, which is
|
|
143
|
-
* `<details>`'s job done manually.
|
|
122
|
+
* **A native `toggle` from inside a shadow root reaches nobody**, because
|
|
123
|
+
* it neither bubbles nor composes, so the ten families showing a
|
|
124
|
+
* `popover="manual"` panel and the two rendering a real `<dialog>` announce
|
|
125
|
+
* nothing to a consumer. The disclosures have no native element to fire
|
|
126
|
+
* anything at all: `AccordionItem` and `TreeItem` are `aria-expanded` and
|
|
127
|
+
* `hidden` written by hand, which is `<details>`'s job done manually.
|
|
144
128
|
*
|
|
145
|
-
* **From here rather than from `PopoverController
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
129
|
+
* **From here rather than from `PopoverController`.** The controller covers
|
|
130
|
+
* eleven families and the mixin covers sixteen, so announcing from both
|
|
131
|
+
* would announce twice on the ten they share. What `toggle` reports is the
|
|
132
|
+
* *state*, and the state is this mixin's; the controller is a way of
|
|
149
133
|
* putting a panel on the screen.
|
|
150
134
|
*
|
|
151
135
|
* **`newState` and `oldState`, because that is the platform's shape**, so a
|