@justeattakeaway/pie-list 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +123 -21
- package/custom-elements.json +53 -6
- package/dist/{PieElement-CGtUo9tK.js → PieElement-wRsAkkYC.js} +1 -1
- package/dist/index-DpLh8onF.js +344 -0
- package/dist/index.js +14 -14
- package/dist/pie-list-item/defs.d.ts +21 -1
- package/dist/pie-list-item/defs.d.ts.map +1 -1
- package/dist/pie-list-item/index.d.ts +25 -14
- package/dist/pie-list-item/index.d.ts.map +1 -1
- package/dist/pie-list-item/index.js +4 -3
- package/dist/pie-list-item/react.js +2 -2
- package/package.json +3 -3
- package/src/list.scss +0 -2
- package/src/pie-list-item/defs.ts +24 -2
- package/src/pie-list-item/index.ts +57 -18
- package/src/pie-list-item/list-item.scss +68 -15
- package/dist/index-BMp2PPeH.js +0 -307
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
} from 'lit';
|
|
4
4
|
import { property, state } from 'lit/decorators.js';
|
|
5
5
|
import { classMap } from 'lit/directives/class-map.js';
|
|
6
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
6
7
|
import { consume, ContextProvider } from '@lit/context';
|
|
7
8
|
import {
|
|
8
9
|
safeCustomElement,
|
|
@@ -50,6 +51,12 @@ export class PieListItem extends PieElement implements ListItemProps {
|
|
|
50
51
|
@property({ type: Boolean })
|
|
51
52
|
disabled = defaultProps.disabled;
|
|
52
53
|
|
|
54
|
+
@property({ type: Boolean })
|
|
55
|
+
hasDivider = defaultProps.hasDivider;
|
|
56
|
+
|
|
57
|
+
@property({ type: Object })
|
|
58
|
+
aria: ListItemProps['aria'];
|
|
59
|
+
|
|
53
60
|
// Whether a disabling ancestor (e.g. `pie-radio-group`) has provided its disabled state.
|
|
54
61
|
// Defaults to false when there is no provider (a standalone item or a static list).
|
|
55
62
|
@consume({ context: parentDisabledContext, subscribe: true })
|
|
@@ -83,8 +90,8 @@ export class PieListItem extends PieElement implements ListItemProps {
|
|
|
83
90
|
private _consumerDescribedLink = false;
|
|
84
91
|
|
|
85
92
|
// A selection row hosts a slotted control (radio/checkbox/switch) and makes the whole row a
|
|
86
|
-
// toggle target. `link`
|
|
87
|
-
// selection
|
|
93
|
+
// toggle target. `link`/`button` are interactive too, but they are single navigation/action
|
|
94
|
+
// targets rather than selection controls, so they are deliberately excluded here.
|
|
88
95
|
private get _isSelectable (): boolean {
|
|
89
96
|
return this.interactionType === 'radio' ||
|
|
90
97
|
this.interactionType === 'checkbox' ||
|
|
@@ -95,10 +102,21 @@ export class PieListItem extends PieElement implements ListItemProps {
|
|
|
95
102
|
return this.interactionType === 'link';
|
|
96
103
|
}
|
|
97
104
|
|
|
98
|
-
|
|
99
|
-
|
|
105
|
+
private get _isButtonRow (): boolean {
|
|
106
|
+
return this.interactionType === 'button';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// A link and a button row share the same visual mechanic: an empty element stretched over the
|
|
110
|
+
// whole row as the interactive target, named by the item from its own text. They differ in who
|
|
111
|
+
// owns that element - a link is a consumer-slotted anchor, a button is one the item renders.
|
|
112
|
+
private get _isOverlayRow (): boolean {
|
|
113
|
+
return this._isLinkRow || this._isButtonRow;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// True when the item lends its text as an interactive element's accessible name/description:
|
|
117
|
+
// selectable rows (radio/checkbox/switch) and overlay rows (link anchor / action button).
|
|
100
118
|
private get _providesAria (): boolean {
|
|
101
|
-
return this._isSelectable || this.
|
|
119
|
+
return this._isSelectable || this._isOverlayRow;
|
|
102
120
|
}
|
|
103
121
|
|
|
104
122
|
// radio/checkbox are owned by a selection group, which is why the item becomes `presentation`
|
|
@@ -141,9 +159,9 @@ export class PieListItem extends PieElement implements ListItemProps {
|
|
|
141
159
|
|
|
142
160
|
/**
|
|
143
161
|
* Sets the item's role from `interactionType`: `presentation` for radio/checkbox (so the group
|
|
144
|
-
* owns those controls directly), otherwise `listitem` (static items, switches
|
|
145
|
-
* set explicitly by the consumer is left untouched, even if it is added or
|
|
146
|
-
* after the element connects.
|
|
162
|
+
* owns those controls directly), otherwise `listitem` (static items, switches, links and
|
|
163
|
+
* buttons). A role set explicitly by the consumer is left untouched, even if it is added or
|
|
164
|
+
* removed dynamically after the element connects.
|
|
147
165
|
*/
|
|
148
166
|
private _applyRole (): void {
|
|
149
167
|
const currentRole = this.getAttribute('role');
|
|
@@ -156,12 +174,10 @@ export class PieListItem extends PieElement implements ListItemProps {
|
|
|
156
174
|
}
|
|
157
175
|
|
|
158
176
|
/**
|
|
159
|
-
* The accessible name and description this item provides to its
|
|
160
|
-
* its text.
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
* `aria-label` there would be invalid). Context reaches across the shadow boundary that
|
|
164
|
-
* `aria-labelledby` cannot.
|
|
177
|
+
* The accessible name and description this item provides to its interactive element, stitched
|
|
178
|
+
* from its text. Selection controls consume it via `ariaContext` (each applying it to the
|
|
179
|
+
* element that carries its role); the action button of a `button` row takes it directly as
|
|
180
|
+
* `aria-label`/`aria-description` in the template; a link anchor takes it via `_applyLinkAria`.
|
|
165
181
|
*/
|
|
166
182
|
private get _providedAria (): ContextualAria | undefined {
|
|
167
183
|
if (!this._providesAria) return undefined;
|
|
@@ -186,11 +202,11 @@ export class PieListItem extends PieElement implements ListItemProps {
|
|
|
186
202
|
};
|
|
187
203
|
|
|
188
204
|
/**
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
205
|
+
* On a link row, names the slotted (empty) anchor from the item's own text: the parent owns the
|
|
206
|
+
* child's aria. The anchor is plain light-DOM, so it cannot reference the shadow-DOM text via
|
|
207
|
+
* `aria-labelledby`; and it is not a PIE control that consumes `ariaContext`, so we set the
|
|
192
208
|
* attributes imperatively. The visible text is `aria-hidden` in the template, so nothing is
|
|
193
|
-
* announced twice.
|
|
209
|
+
* announced twice. (A `button` row names its own rendered element in the template instead.)
|
|
194
210
|
*
|
|
195
211
|
* A consumer-provided name/description always wins: we capture (once) whether the anchor already
|
|
196
212
|
* carries `aria-label`/`aria-labelledby` or `aria-description`/`aria-describedby` and, if so,
|
|
@@ -261,6 +277,26 @@ export class PieListItem extends PieElement implements ListItemProps {
|
|
|
261
277
|
return html`<div class="c-listItem-trailing"><slot name="trailing"></slot></div>`;
|
|
262
278
|
}
|
|
263
279
|
|
|
280
|
+
/**
|
|
281
|
+
* The interactive target for a `button` row: an empty, native `<button>` stretched invisibly
|
|
282
|
+
* over the whole row (see the SCSS). The item renders and owns it - rather than the consumer
|
|
283
|
+
* slotting one - so it can name it from its own text and keep its attributes private (it is just
|
|
284
|
+
* an action box, not a configurable control). Being a real button, activation is native: pointer
|
|
285
|
+
* clicks and keyboard (Enter/Space) both fire a `click` that bubbles out, so consumers listen for
|
|
286
|
+
* `click` on the `pie-list-item`. It is `type="button"`, so it never submits a form.
|
|
287
|
+
*/
|
|
288
|
+
_renderActionButton () {
|
|
289
|
+
const aria = this._providedAria;
|
|
290
|
+
|
|
291
|
+
return html`<button
|
|
292
|
+
class="c-listItem-action"
|
|
293
|
+
type="button"
|
|
294
|
+
?disabled=${this._isDisabled}
|
|
295
|
+
aria-label=${ifDefined(aria?.label)}
|
|
296
|
+
aria-description=${ifDefined(aria?.description)}
|
|
297
|
+
aria-haspopup=${ifDefined(this.aria?.button?.haspopup)}></button>`;
|
|
298
|
+
}
|
|
299
|
+
|
|
264
300
|
render () {
|
|
265
301
|
const { primaryText } = this;
|
|
266
302
|
|
|
@@ -269,14 +305,17 @@ export class PieListItem extends PieElement implements ListItemProps {
|
|
|
269
305
|
'is-compact': this.isCompact,
|
|
270
306
|
'is-bold': this.isBold,
|
|
271
307
|
'has-media': this.hasMedia,
|
|
308
|
+
'has-divider': this.hasDivider,
|
|
272
309
|
'is-selectable': this._isSelectable,
|
|
273
310
|
'is-link': this._isLinkRow,
|
|
311
|
+
'is-button': this._isButtonRow,
|
|
274
312
|
'is-disabled': this._isDisabled,
|
|
275
313
|
};
|
|
276
314
|
|
|
277
315
|
return html`
|
|
278
316
|
<div class=${classMap(containerClasses)}>
|
|
279
317
|
${this._isLinkRow ? html`<slot name="link"></slot>` : nothing}
|
|
318
|
+
${this._isButtonRow ? this._renderActionButton() : nothing}
|
|
280
319
|
<div class="c-listItem-leading">
|
|
281
320
|
<slot name="leading"></slot>
|
|
282
321
|
</div>
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
// host declaration wins over a value inherited from an ancestor.
|
|
7
7
|
--list-item-inline-padding: var(--dt-spacing-d);
|
|
8
8
|
--list-item-alignment: flex-start;
|
|
9
|
-
--list-item-border: none;
|
|
10
9
|
|
|
11
10
|
// Using contents to remove the host from the box model so that the internal container element fully acts as the component shell
|
|
12
11
|
display: block;
|
|
@@ -16,21 +15,24 @@
|
|
|
16
15
|
.c-listItem-container {
|
|
17
16
|
--list-item-block-padding: var(--dt-spacing-d);
|
|
18
17
|
|
|
19
|
-
// The list component will add a border to each list-item that follows another. This ensures the height accounts for the added border.
|
|
20
|
-
--list-item-block-end-padding: calc(var(--list-item-block-padding) - var(--list-item-size-modifier));
|
|
21
|
-
|
|
22
18
|
padding-inline-start: var(--list-item-inline-padding);
|
|
23
19
|
padding-inline-end: var(--list-item-inline-padding);
|
|
24
20
|
|
|
25
21
|
padding-block-start: var(--list-item-block-padding);
|
|
26
|
-
padding-block-end: var(--list-item-block-
|
|
22
|
+
padding-block-end: var(--list-item-block-padding);
|
|
27
23
|
|
|
28
24
|
// Layout
|
|
29
25
|
display: flex;
|
|
30
26
|
align-items: var(--list-item-alignment);
|
|
31
27
|
gap: var(--dt-spacing-c);
|
|
32
28
|
min-height: 56px;
|
|
33
|
-
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// When hasDivider is true, show a bottom border and subtract 1px from padding-block-end so the
|
|
32
|
+
// total item height is unchanged whether the divider is present or not.
|
|
33
|
+
.c-listItem-container.has-divider {
|
|
34
|
+
padding-block-end: calc(var(--list-item-block-padding) - 1px);
|
|
35
|
+
border-bottom: 1px solid var(--dt-color-divider-default);
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
// Two lines of text (primary + secondary) need top-alignment so leading/trailing
|
|
@@ -49,9 +51,10 @@
|
|
|
49
51
|
min-height: 48px;
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
//
|
|
53
|
-
// slotted anchor can stretch over it.
|
|
54
|
-
.c-listItem-container.is-link
|
|
54
|
+
// Overlay rows (link/button): the whole item is a single interactive target. The container is
|
|
55
|
+
// positioned so the slotted anchor/button can stretch over it.
|
|
56
|
+
.c-listItem-container.is-link,
|
|
57
|
+
.c-listItem-container.is-button {
|
|
55
58
|
position: relative;
|
|
56
59
|
}
|
|
57
60
|
|
|
@@ -62,7 +65,8 @@
|
|
|
62
65
|
// defaults use the standard PIE interactive overlay tokens; override `--list-item-hover-bg` /
|
|
63
66
|
// `--list-item-active-bg` if design specifies dedicated list tokens.
|
|
64
67
|
.c-listItem-container.is-selectable:not(.is-disabled),
|
|
65
|
-
.c-listItem-container.is-link
|
|
68
|
+
.c-listItem-container.is-link,
|
|
69
|
+
.c-listItem-container.is-button:not(.is-disabled) {
|
|
66
70
|
--list-item-hover-bg: hsl(var(--dt-color-black-h), var(--dt-color-black-s), var(--dt-color-black-l), var(--dt-color-hover-01));
|
|
67
71
|
--list-item-active-bg: hsl(var(--dt-color-black-h), var(--dt-color-black-s), var(--dt-color-black-l), var(--dt-color-active-01));
|
|
68
72
|
|
|
@@ -84,6 +88,9 @@
|
|
|
84
88
|
--interactive-container-tint: var(--list-item-hover-bg);
|
|
85
89
|
}
|
|
86
90
|
|
|
91
|
+
// The pressed tint for a pointer press: the container is an ancestor of the pressed element, so
|
|
92
|
+
// it matches `:active`. (A keyboard press is handled by the `:has()` rule below, because keyboard
|
|
93
|
+
// `:active` does not propagate up to the container.)
|
|
87
94
|
&:active {
|
|
88
95
|
background-color: var(--list-item-active-bg);
|
|
89
96
|
|
|
@@ -91,17 +98,49 @@
|
|
|
91
98
|
}
|
|
92
99
|
}
|
|
93
100
|
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
|
|
101
|
+
// Pressed tint for a `button` row's keyboard press. A native `<button>` is `:active` while Space is
|
|
102
|
+
// held, but that state does not propagate to the container, so `&:active` above misses it. The
|
|
103
|
+
// action button is a real shadow-DOM descendant (not slotted), so `:has()` can react to its
|
|
104
|
+
// `:active` directly - no JS needed. A pointer press satisfies both this and `&:active` with the
|
|
105
|
+
// same value, so there is no double up.
|
|
106
|
+
.c-listItem-container.is-button:has(.c-listItem-action:active) {
|
|
107
|
+
background-color: var(--list-item-active-bg);
|
|
108
|
+
|
|
109
|
+
--interactive-container-tint: var(--list-item-active-bg);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Stretch the interactive target over the entire row so the whole block is the click/touch target.
|
|
113
|
+
// For a link it is the consumer's slotted anchor; for a button it is the native <button> the item
|
|
114
|
+
// renders. Both anchor to the container (positioned above).
|
|
115
|
+
slot[name="link"]::slotted(a),
|
|
116
|
+
.c-listItem-action {
|
|
97
117
|
position: absolute;
|
|
98
118
|
inset: 0;
|
|
99
119
|
z-index: 1;
|
|
100
120
|
border-radius: inherit; // so the focus ring follows any rounding applied to the row
|
|
101
121
|
}
|
|
102
122
|
|
|
103
|
-
//
|
|
104
|
-
|
|
123
|
+
// The action element of a `button` row is a native <button> the item renders. Strip its chrome so
|
|
124
|
+
// it is invisible (the row provides the styling); it stays a real, focusable button with native
|
|
125
|
+
// pointer/keyboard activation and screen-reader semantics.
|
|
126
|
+
.c-listItem-action {
|
|
127
|
+
padding: 0;
|
|
128
|
+
border: none;
|
|
129
|
+
background: transparent;
|
|
130
|
+
appearance: none;
|
|
131
|
+
font: inherit;
|
|
132
|
+
cursor: pointer;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// A disabled action button shows the default cursor (it already ignores clicks and is unfocusable
|
|
136
|
+
// natively via the `disabled` attribute).
|
|
137
|
+
.c-listItem-action:disabled {
|
|
138
|
+
cursor: default;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Standard PIE focus ring. The anchor/action is row-sized, so keyboard focus rings the whole item.
|
|
142
|
+
slot[name="link"]::slotted(a:focus-visible),
|
|
143
|
+
.c-listItem-action:focus-visible {
|
|
105
144
|
@include p.focus;
|
|
106
145
|
}
|
|
107
146
|
|
|
@@ -147,6 +186,20 @@ slot[name="trailing"]::slotted(*),
|
|
|
147
186
|
--icon-size-override: 24px
|
|
148
187
|
}
|
|
149
188
|
|
|
189
|
+
// Disabled button row: mute text and icons. Icons use `fill: currentColor`, so setting `color`
|
|
190
|
+
// on the host element cascades through the shadow boundary and changes their rendered fill.
|
|
191
|
+
.c-listItem-container.is-button.is-disabled {
|
|
192
|
+
.c-listItem-primaryText,
|
|
193
|
+
.c-listItem-secondaryText,
|
|
194
|
+
.c-listItem-metaText {
|
|
195
|
+
color: var(--dt-color-content-disabled);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.c-listItem-container.is-button.is-disabled ::slotted(.c-pieIcon) {
|
|
200
|
+
color: var(--dt-color-disabled-01);
|
|
201
|
+
}
|
|
202
|
+
|
|
150
203
|
// typography
|
|
151
204
|
|
|
152
205
|
.c-listItem-primaryText {
|
package/dist/index-BMp2PPeH.js
DELETED
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
import { unsafeCSS as f, isServer as x, html as p, nothing as b } from "lit";
|
|
2
|
-
import { property as c, state as y } from "lit/decorators.js";
|
|
3
|
-
import { classMap as k } from "lit/directives/class-map.js";
|
|
4
|
-
import { validPropertyValues as _, safeCustomElement as I, ariaContext as T, parentDisabledContext as C } from "@justeattakeaway/pie-webc-core";
|
|
5
|
-
import { P as w } from "./PieElement-CGtUo9tK.js";
|
|
6
|
-
/**
|
|
7
|
-
* @license
|
|
8
|
-
* Copyright 2021 Google LLC
|
|
9
|
-
* SPDX-License-Identifier: BSD-3-Clause
|
|
10
|
-
*/
|
|
11
|
-
let g = class extends Event {
|
|
12
|
-
constructor(t, e, o, s) {
|
|
13
|
-
super("context-request", { bubbles: !0, composed: !0 }), this.context = t, this.contextTarget = e, this.callback = o, this.subscribe = s ?? !1;
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
/**
|
|
17
|
-
* @license
|
|
18
|
-
* Copyright 2021 Google LLC
|
|
19
|
-
* SPDX-License-Identifier: BSD-3-Clause
|
|
20
|
-
*/
|
|
21
|
-
let v = class {
|
|
22
|
-
constructor(t, e, o, s) {
|
|
23
|
-
if (this.subscribe = !1, this.provided = !1, this.value = void 0, this.t = (n, r) => {
|
|
24
|
-
this.unsubscribe && (this.unsubscribe !== r && (this.provided = !1, this.unsubscribe()), this.subscribe || this.unsubscribe()), this.value = n, this.host.requestUpdate(), this.provided && !this.subscribe || (this.provided = !0, this.callback && this.callback(n, r)), this.unsubscribe = r;
|
|
25
|
-
}, this.host = t, e.context !== void 0) {
|
|
26
|
-
const n = e;
|
|
27
|
-
this.context = n.context, this.callback = n.callback, this.subscribe = n.subscribe ?? !1;
|
|
28
|
-
} else this.context = e, this.callback = o, this.subscribe = s ?? !1;
|
|
29
|
-
this.host.addController(this);
|
|
30
|
-
}
|
|
31
|
-
hostConnected() {
|
|
32
|
-
this.dispatchRequest();
|
|
33
|
-
}
|
|
34
|
-
hostDisconnected() {
|
|
35
|
-
this.unsubscribe && (this.unsubscribe(), this.unsubscribe = void 0);
|
|
36
|
-
}
|
|
37
|
-
dispatchRequest() {
|
|
38
|
-
this.host.dispatchEvent(new g(this.context, this.host, this.t, this.subscribe));
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
/**
|
|
42
|
-
* @license
|
|
43
|
-
* Copyright 2021 Google LLC
|
|
44
|
-
* SPDX-License-Identifier: BSD-3-Clause
|
|
45
|
-
*/
|
|
46
|
-
class A {
|
|
47
|
-
get value() {
|
|
48
|
-
return this.o;
|
|
49
|
-
}
|
|
50
|
-
set value(t) {
|
|
51
|
-
this.setValue(t);
|
|
52
|
-
}
|
|
53
|
-
setValue(t, e = !1) {
|
|
54
|
-
const o = e || !Object.is(t, this.o);
|
|
55
|
-
this.o = t, o && this.updateObservers();
|
|
56
|
-
}
|
|
57
|
-
constructor(t) {
|
|
58
|
-
this.subscriptions = /* @__PURE__ */ new Map(), this.updateObservers = () => {
|
|
59
|
-
for (const [e, { disposer: o }] of this.subscriptions) e(this.o, o);
|
|
60
|
-
}, t !== void 0 && (this.value = t);
|
|
61
|
-
}
|
|
62
|
-
addCallback(t, e, o) {
|
|
63
|
-
if (!o) return void t(this.value);
|
|
64
|
-
this.subscriptions.has(t) || this.subscriptions.set(t, { disposer: () => {
|
|
65
|
-
this.subscriptions.delete(t);
|
|
66
|
-
}, consumerHost: e });
|
|
67
|
-
const { disposer: s } = this.subscriptions.get(t);
|
|
68
|
-
t(this.value, s);
|
|
69
|
-
}
|
|
70
|
-
clearCallbacks() {
|
|
71
|
-
this.subscriptions.clear();
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* @license
|
|
76
|
-
* Copyright 2021 Google LLC
|
|
77
|
-
* SPDX-License-Identifier: BSD-3-Clause
|
|
78
|
-
*/
|
|
79
|
-
class L extends Event {
|
|
80
|
-
constructor(t, e) {
|
|
81
|
-
super("context-provider", { bubbles: !0, composed: !0 }), this.context = t, this.contextTarget = e;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
class P extends A {
|
|
85
|
-
constructor(t, e, o) {
|
|
86
|
-
var s, n;
|
|
87
|
-
super(e.context !== void 0 ? e.initialValue : o), this.onContextRequest = (r) => {
|
|
88
|
-
if (r.context !== this.context) return;
|
|
89
|
-
const h = r.contextTarget ?? r.composedPath()[0];
|
|
90
|
-
h !== this.host && (r.stopPropagation(), this.addCallback(r.callback, h, r.subscribe));
|
|
91
|
-
}, this.onProviderRequest = (r) => {
|
|
92
|
-
if (r.context !== this.context || (r.contextTarget ?? r.composedPath()[0]) === this.host) return;
|
|
93
|
-
const h = /* @__PURE__ */ new Set();
|
|
94
|
-
for (const [u, { consumerHost: m }] of this.subscriptions) h.has(u) || (h.add(u), m.dispatchEvent(new g(this.context, m, u, !0)));
|
|
95
|
-
r.stopPropagation();
|
|
96
|
-
}, this.host = t, e.context !== void 0 ? this.context = e.context : this.context = e, this.attachListeners(), (n = (s = this.host).addController) == null || n.call(s, this);
|
|
97
|
-
}
|
|
98
|
-
attachListeners() {
|
|
99
|
-
this.host.addEventListener("context-request", this.onContextRequest), this.host.addEventListener("context-provider", this.onProviderRequest);
|
|
100
|
-
}
|
|
101
|
-
hostConnected() {
|
|
102
|
-
this.host.dispatchEvent(new L(this.context, this.host));
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* @license
|
|
107
|
-
* Copyright 2022 Google LLC
|
|
108
|
-
* SPDX-License-Identifier: BSD-3-Clause
|
|
109
|
-
*/
|
|
110
|
-
function R({ context: i, subscribe: t }) {
|
|
111
|
-
return (e, o) => {
|
|
112
|
-
typeof o == "object" ? o.addInitializer((function() {
|
|
113
|
-
new v(this, { context: i, callback: (s) => {
|
|
114
|
-
e.set.call(this, s);
|
|
115
|
-
}, subscribe: t });
|
|
116
|
-
})) : e.constructor.addInitializer(((s) => {
|
|
117
|
-
new v(s, { context: i, callback: (n) => {
|
|
118
|
-
s[o] = n;
|
|
119
|
-
}, subscribe: t });
|
|
120
|
-
}));
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
const S = ["none", "radio", "checkbox", "switch", "link"], d = {
|
|
124
|
-
isCompact: !1,
|
|
125
|
-
isBold: !1,
|
|
126
|
-
hasMedia: !1,
|
|
127
|
-
interactionType: "none",
|
|
128
|
-
disabled: !1
|
|
129
|
-
}, D = "*,*:after,*:before{box-sizing:inherit}:host{--list-item-inline-padding: var(--dt-spacing-d);--list-item-alignment: flex-start;--list-item-border: none;display:block}.c-listItem-container{--list-item-block-padding: var(--dt-spacing-d);--list-item-block-end-padding: calc(var(--list-item-block-padding) - var(--list-item-size-modifier));padding-inline-start:var(--list-item-inline-padding);padding-inline-end:var(--list-item-inline-padding);padding-block-start:var(--list-item-block-padding);padding-block-end:var(--list-item-block-end-padding, var(--list-item-block-padding));display:flex;align-items:var(--list-item-alignment);gap:var(--dt-spacing-c);min-height:56px;border-bottom:var(--list-item-border)}.c-listItem-container:has(.c-listItem-secondaryText){align-items:var(--list-item-alignment);min-height:76px}.c-listItem-container.is-compact{--list-item-block-padding: var(--dt-spacing-c);--list-item-alignment: center;align-items:var(--list-item-alignment);min-height:48px}.c-listItem-container.is-link{position:relative}.c-listItem-container.is-selectable:not(.is-disabled),.c-listItem-container.is-link{--list-item-hover-bg: hsl(var(--dt-color-black-h), var(--dt-color-black-s), var(--dt-color-black-l), var(--dt-color-hover-01));--list-item-active-bg: hsl(var(--dt-color-black-h), var(--dt-color-black-s), var(--dt-color-black-l), var(--dt-color-active-01))}@supports (background-color: color-mix(in srgb,black,white)){.c-listItem-container.is-selectable:not(.is-disabled),.c-listItem-container.is-link{--list-item-hover-bg: color-mix(in srgb, var(--dt-color-hover-01-bg) var(--dt-color-hover-01), transparent);--list-item-active-bg: color-mix(in srgb, var(--dt-color-active-01-bg) var(--dt-color-active-01), transparent)}}.c-listItem-container.is-selectable:not(.is-disabled),.c-listItem-container.is-link{cursor:pointer}.c-listItem-container.is-selectable:not(.is-disabled):hover,.c-listItem-container.is-link:hover{background-color:var(--list-item-hover-bg);--interactive-container-tint: var(--list-item-hover-bg)}.c-listItem-container.is-selectable:not(.is-disabled):active,.c-listItem-container.is-link:active{background-color:var(--list-item-active-bg);--interactive-container-tint: var(--list-item-active-bg)}slot[name=link]::slotted(a){position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;border-radius:inherit}slot[name=link]::slotted(a:focus-visible){box-shadow:0 0 0 2px var(--dt-color-focus-inner),0 0 0 4px var(--dt-color-focus-outer);outline:none}.c-listItem-container.has-media:has(.c-listItem-primaryText:only-child){--list-item-block-padding: var(--dt-spacing-b)}.c-listItem-text{flex:1}slot[name=trailing]::slotted(*),.c-listItem-metaText{padding-inline-start:var(--dt-spacing-a)}.c-listItem-leading,slot[name=leading],.c-listItem-trailing,slot[name=trailing]{display:contents}.c-listItem-text>*{display:block}.c-listItem-trailing{margin-inline-start:auto}.c-listItem-metaText{display:block;max-width:33.3333333333%}::slotted(.c-pieIcon){--icon-size-override: 24px }.c-listItem-primaryText{font-family:var(--dt-font-body-l-family);font-weight:var(--dt-font-body-l-weight);font-size:calc(var(--dt-font-body-l-size) * 1px);line-height:calc(var(--dt-font-body-l-line-height) * 1px);color:var(--dt-color-content-default)}.c-listItem-container.is-bold .c-listItem-primaryText{font-family:var(--dt-font-body-strong-l-family);font-weight:var(--dt-font-body-strong-l-weight);font-size:calc(var(--dt-font-body-strong-l-size) * 1px);line-height:calc(var(--dt-font-body-strong-l-line-height) * 1px)}.c-listItem-secondaryText,.c-listItem-metaText{font-family:var(--dt-font-body-s-family);font-weight:var(--dt-font-body-s-weight);font-size:calc(var(--dt-font-body-s-size) * 1px);line-height:calc(var(--dt-font-body-s-line-height) * 1px);color:var(--dt-color-content-subdued)}";
|
|
130
|
-
var q = Object.defineProperty, z = Object.getOwnPropertyDescriptor, l = (i, t, e, o) => {
|
|
131
|
-
for (var s = o > 1 ? void 0 : o ? z(t, e) : t, n = i.length - 1, r; n >= 0; n--)
|
|
132
|
-
(r = i[n]) && (s = (o ? r(t, e, s) : r(s)) || s);
|
|
133
|
-
return o && s && q(t, e, s), s;
|
|
134
|
-
};
|
|
135
|
-
const B = "pie-list-item";
|
|
136
|
-
let a = class extends w {
|
|
137
|
-
constructor() {
|
|
138
|
-
super(...arguments), this.isCompact = d.isCompact, this.isBold = d.isBold, this.hasMedia = d.hasMedia, this.interactionType = d.interactionType, this.disabled = d.disabled, this._parentDisabled = !1, this._ariaProvider = x ? void 0 : new P(this, { context: T }), this._managedRole = null, this._linkAriaCaptured = !1, this._consumerNamedLink = !1, this._consumerDescribedLink = !1, this._handleHostClick = (i) => {
|
|
139
|
-
if (!this._isSelectable || this._isDisabled) return;
|
|
140
|
-
const t = this._control;
|
|
141
|
-
!t || i.composedPath().includes(t) || (t.click(), t.focus());
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
// A selection row hosts a slotted control (radio/checkbox/switch) and makes the whole row a
|
|
145
|
-
// toggle target. `link` is interactive too, but it is a navigation target rather than a
|
|
146
|
-
// selection control, so it is deliberately excluded here.
|
|
147
|
-
get _isSelectable() {
|
|
148
|
-
return this.interactionType === "radio" || this.interactionType === "checkbox" || this.interactionType === "switch";
|
|
149
|
-
}
|
|
150
|
-
get _isLinkRow() {
|
|
151
|
-
return this.interactionType === "link";
|
|
152
|
-
}
|
|
153
|
-
// True when the item lends its text to a slotted control as that control's accessible
|
|
154
|
-
// name/description: selectable rows (radio/checkbox/switch) and link rows (the slotted anchor).
|
|
155
|
-
get _providesAria() {
|
|
156
|
-
return this._isSelectable || this._isLinkRow;
|
|
157
|
-
}
|
|
158
|
-
// radio/checkbox are owned by a selection group, which is why the item becomes `presentation`
|
|
159
|
-
// (so the group owns the controls directly) and the radio is named on its host. A switch has
|
|
160
|
-
// no group, so the item stays a `listitem`.
|
|
161
|
-
get _ownedByGroup() {
|
|
162
|
-
return this.interactionType === "radio" || this.interactionType === "checkbox";
|
|
163
|
-
}
|
|
164
|
-
// True when the row should be treated as disabled: either its own `disabled` prop is set, or the
|
|
165
|
-
// containing group is disabled (provided via context). Declarative and reactive, so the styling
|
|
166
|
-
// and row-click guard react without observing the slotted control.
|
|
167
|
-
get _isDisabled() {
|
|
168
|
-
return this.disabled || this._parentDisabled;
|
|
169
|
-
}
|
|
170
|
-
// The interactive control (radio/checkbox/switch) slotted into this item, if any. Used only by
|
|
171
|
-
// the client-side row-click handler.
|
|
172
|
-
get _control() {
|
|
173
|
-
return this.querySelector("pie-radio, pie-checkbox, pie-switch");
|
|
174
|
-
}
|
|
175
|
-
connectedCallback() {
|
|
176
|
-
super.connectedCallback(), this._abortController = new AbortController(), this.addEventListener("click", this._handleHostClick, { signal: this._abortController.signal });
|
|
177
|
-
}
|
|
178
|
-
disconnectedCallback() {
|
|
179
|
-
super.disconnectedCallback(), this._abortController.abort();
|
|
180
|
-
}
|
|
181
|
-
updated() {
|
|
182
|
-
var i;
|
|
183
|
-
this._applyRole(), (i = this._ariaProvider) == null || i.setValue(this._providedAria), this._applyLinkAria();
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Sets the item's role from `interactionType`: `presentation` for radio/checkbox (so the group
|
|
187
|
-
* owns those controls directly), otherwise `listitem` (static items, switches and links). A role
|
|
188
|
-
* set explicitly by the consumer is left untouched, even if it is added or removed dynamically
|
|
189
|
-
* after the element connects.
|
|
190
|
-
*/
|
|
191
|
-
_applyRole() {
|
|
192
|
-
const i = this.getAttribute("role");
|
|
193
|
-
if (i !== null && i !== this._managedRole) return;
|
|
194
|
-
const t = this._ownedByGroup ? "presentation" : "listitem";
|
|
195
|
-
this._managedRole = t, this.setAttribute("role", t);
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* The accessible name and description this item provides to its slotted control, stitched from
|
|
199
|
-
* its text. Only provided in a selectable list. Each control consumes this via `ariaContext`
|
|
200
|
-
* and decides how to apply it: `pie-radio` names its host (which carries `role="radio"`),
|
|
201
|
-
* while `pie-checkbox` / `pie-switch` name their internal input (their host is role-less, so
|
|
202
|
-
* `aria-label` there would be invalid). Context reaches across the shadow boundary that
|
|
203
|
-
* `aria-labelledby` cannot.
|
|
204
|
-
*/
|
|
205
|
-
get _providedAria() {
|
|
206
|
-
if (!this._providesAria) return;
|
|
207
|
-
const i = [this.secondaryText, this.metaText].filter(Boolean).join(". ");
|
|
208
|
-
return { label: this.primaryText, description: i || void 0 };
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* When the item is a link, names the slotted (empty) anchor from the item's own text: the parent
|
|
212
|
-
* owns the child's aria. The anchor is plain light-DOM, so it cannot reference the shadow-DOM text
|
|
213
|
-
* via `aria-labelledby`; and it is not a PIE control that consumes `ariaContext`, so we set the
|
|
214
|
-
* attributes imperatively. The visible text is `aria-hidden` in the template, so nothing is
|
|
215
|
-
* announced twice.
|
|
216
|
-
*
|
|
217
|
-
* A consumer-provided name/description always wins: we capture (once) whether the anchor already
|
|
218
|
-
* carries `aria-label`/`aria-labelledby` or `aria-description`/`aria-describedby` and, if so,
|
|
219
|
-
* leave that attribute untouched. Otherwise we keep our generated value in sync with the text.
|
|
220
|
-
*/
|
|
221
|
-
_applyLinkAria() {
|
|
222
|
-
if (!this._isLinkRow) {
|
|
223
|
-
if (this._linkAriaCaptured) {
|
|
224
|
-
const e = this.querySelector('a[slot="link"]');
|
|
225
|
-
e && (this._consumerNamedLink || e.removeAttribute("aria-label"), this._consumerDescribedLink || e.removeAttribute("aria-description")), this._linkAriaCaptured = !1, this._consumerNamedLink = !1, this._consumerDescribedLink = !1;
|
|
226
|
-
}
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
const i = this.querySelector('a[slot="link"]');
|
|
230
|
-
if (!i) return;
|
|
231
|
-
this._linkAriaCaptured || (this._consumerNamedLink = i.hasAttribute("aria-label") || i.hasAttribute("aria-labelledby"), this._consumerDescribedLink = i.hasAttribute("aria-description") || i.hasAttribute("aria-describedby"), this._linkAriaCaptured = !0);
|
|
232
|
-
const t = this._providedAria;
|
|
233
|
-
this._consumerNamedLink || (t != null && t.label ? i.setAttribute("aria-label", t.label) : i.removeAttribute("aria-label")), this._consumerDescribedLink || (t != null && t.description ? i.setAttribute("aria-description", t.description) : i.removeAttribute("aria-description"));
|
|
234
|
-
}
|
|
235
|
-
_renderSecondaryText() {
|
|
236
|
-
const { secondaryText: i } = this;
|
|
237
|
-
return i ? p`<span class="c-listItem-secondaryText">${i}</span>` : b;
|
|
238
|
-
}
|
|
239
|
-
// metaText is another form of trailing content. The component can only ever display either slotted trailing content or the metaText string
|
|
240
|
-
_renderTrailingContent() {
|
|
241
|
-
const { metaText: i } = this;
|
|
242
|
-
return i ? p`<span class="c-listItem-metaText c-listItem-trailing" aria-hidden=${this._providesAria ? "true" : b}>${i}</span>` : p`<div class="c-listItem-trailing"><slot name="trailing"></slot></div>`;
|
|
243
|
-
}
|
|
244
|
-
render() {
|
|
245
|
-
const { primaryText: i } = this, t = {
|
|
246
|
-
"c-listItem-container": !0,
|
|
247
|
-
"is-compact": this.isCompact,
|
|
248
|
-
"is-bold": this.isBold,
|
|
249
|
-
"has-media": this.hasMedia,
|
|
250
|
-
"is-selectable": this._isSelectable,
|
|
251
|
-
"is-link": this._isLinkRow,
|
|
252
|
-
"is-disabled": this._isDisabled
|
|
253
|
-
};
|
|
254
|
-
return p`
|
|
255
|
-
<div class=${k(t)}>
|
|
256
|
-
${this._isLinkRow ? p`<slot name="link"></slot>` : b}
|
|
257
|
-
<div class="c-listItem-leading">
|
|
258
|
-
<slot name="leading"></slot>
|
|
259
|
-
</div>
|
|
260
|
-
|
|
261
|
-
<div class="c-listItem-text" aria-hidden=${this._providesAria ? "true" : b}>
|
|
262
|
-
<span class="c-listItem-primaryText">${i}</span>
|
|
263
|
-
${this._renderSecondaryText()}
|
|
264
|
-
</div>
|
|
265
|
-
|
|
266
|
-
${this._renderTrailingContent()}
|
|
267
|
-
</div>`;
|
|
268
|
-
}
|
|
269
|
-
};
|
|
270
|
-
a.styles = f(D);
|
|
271
|
-
l([
|
|
272
|
-
c({ type: String })
|
|
273
|
-
], a.prototype, "primaryText", 2);
|
|
274
|
-
l([
|
|
275
|
-
c({ type: String })
|
|
276
|
-
], a.prototype, "secondaryText", 2);
|
|
277
|
-
l([
|
|
278
|
-
c({ type: String })
|
|
279
|
-
], a.prototype, "metaText", 2);
|
|
280
|
-
l([
|
|
281
|
-
c({ type: Boolean })
|
|
282
|
-
], a.prototype, "isCompact", 2);
|
|
283
|
-
l([
|
|
284
|
-
c({ type: Boolean })
|
|
285
|
-
], a.prototype, "isBold", 2);
|
|
286
|
-
l([
|
|
287
|
-
c({ type: Boolean })
|
|
288
|
-
], a.prototype, "hasMedia", 2);
|
|
289
|
-
l([
|
|
290
|
-
c({ type: String }),
|
|
291
|
-
_(B, S, d.interactionType)
|
|
292
|
-
], a.prototype, "interactionType", 2);
|
|
293
|
-
l([
|
|
294
|
-
c({ type: Boolean })
|
|
295
|
-
], a.prototype, "disabled", 2);
|
|
296
|
-
l([
|
|
297
|
-
R({ context: C, subscribe: !0 }),
|
|
298
|
-
y()
|
|
299
|
-
], a.prototype, "_parentDisabled", 2);
|
|
300
|
-
a = l([
|
|
301
|
-
I("pie-list-item")
|
|
302
|
-
], a);
|
|
303
|
-
export {
|
|
304
|
-
a as P,
|
|
305
|
-
d,
|
|
306
|
-
S as i
|
|
307
|
-
};
|