@justeattakeaway/pie-list 0.2.0 → 0.4.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 +157 -20
- package/custom-elements.json +72 -12
- package/dist/{PieElement-B3g_8vKy.js → PieElement-CGtUo9tK.js} +1 -1
- package/dist/index-BMp2PPeH.js +307 -0
- package/dist/index.js +1 -1
- package/dist/pie-list-item/defs.d.ts +12 -9
- package/dist/pie-list-item/defs.d.ts.map +1 -1
- package/dist/pie-list-item/index.d.ts +24 -5
- package/dist/pie-list-item/index.d.ts.map +1 -1
- package/dist/pie-list-item/index.js +2 -2
- package/dist/pie-list-item/react.js +8 -8
- package/package.json +3 -3
- package/src/pie-list-item/defs.ts +14 -10
- package/src/pie-list-item/index.ts +107 -18
- package/src/pie-list-item/list-item.scss +31 -1
- package/dist/index-KnN5fPmj.js +0 -265
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
type ContextualAria,
|
|
13
13
|
} from '@justeattakeaway/pie-webc-core';
|
|
14
14
|
import { PieElement } from '@justeattakeaway/pie-webc-core/src/internals/PieElement';
|
|
15
|
-
import { type ListItemProps, defaultProps,
|
|
15
|
+
import { type ListItemProps, defaultProps, interactionTypes } from './defs';
|
|
16
16
|
import styles from './list-item.scss?inline';
|
|
17
17
|
|
|
18
18
|
const componentSelector = 'pie-list-item';
|
|
@@ -21,6 +21,7 @@ const componentSelector = 'pie-list-item';
|
|
|
21
21
|
* @tagname pie-list-item
|
|
22
22
|
* @slot leading - Content shown at the start of the item (for example an icon, avatar, or a slotted radio/checkbox).
|
|
23
23
|
* @slot trailing - Content shown at the end of the item. Mutually exclusive with `metaText`.
|
|
24
|
+
* @slot link - Rendered only when `interactionType="link"`. Slot one empty anchor (e.g. `<a slot="link" href="/x">`) stretched over the whole row as the link target. Must contain no text.
|
|
24
25
|
*/
|
|
25
26
|
@safeCustomElement('pie-list-item')
|
|
26
27
|
export class PieListItem extends PieElement implements ListItemProps {
|
|
@@ -42,9 +43,9 @@ export class PieListItem extends PieElement implements ListItemProps {
|
|
|
42
43
|
@property({ type: Boolean })
|
|
43
44
|
hasMedia = defaultProps.hasMedia;
|
|
44
45
|
|
|
45
|
-
@property({ type: String
|
|
46
|
-
@validPropertyValues(componentSelector,
|
|
47
|
-
|
|
46
|
+
@property({ type: String })
|
|
47
|
+
@validPropertyValues(componentSelector, interactionTypes, defaultProps.interactionType)
|
|
48
|
+
interactionType = defaultProps.interactionType;
|
|
48
49
|
|
|
49
50
|
@property({ type: Boolean })
|
|
50
51
|
disabled = defaultProps.disabled;
|
|
@@ -69,17 +70,42 @@ export class PieListItem extends PieElement implements ListItemProps {
|
|
|
69
70
|
|
|
70
71
|
private _abortController!: AbortController;
|
|
71
72
|
|
|
72
|
-
|
|
73
|
+
// The last role value we wrote ourselves, so `_applyRole` can tell apart our own managed role
|
|
74
|
+
// from one the consumer set (and therefore should not overwrite).
|
|
75
|
+
private _managedRole: string | null = null;
|
|
76
|
+
|
|
77
|
+
// Captured once (in `_applyLinkAria`): whether the consumer already named/described the slotted
|
|
78
|
+
// link anchor themselves. If so, their value always wins and we never touch it.
|
|
79
|
+
private _linkAriaCaptured = false;
|
|
80
|
+
|
|
81
|
+
private _consumerNamedLink = false;
|
|
73
82
|
|
|
83
|
+
private _consumerDescribedLink = false;
|
|
84
|
+
|
|
85
|
+
// A selection row hosts a slotted control (radio/checkbox/switch) and makes the whole row a
|
|
86
|
+
// toggle target. `link` is interactive too, but it is a navigation target rather than a
|
|
87
|
+
// selection control, so it is deliberately excluded here.
|
|
74
88
|
private get _isSelectable (): boolean {
|
|
75
|
-
return this.
|
|
89
|
+
return this.interactionType === 'radio' ||
|
|
90
|
+
this.interactionType === 'checkbox' ||
|
|
91
|
+
this.interactionType === 'switch';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private get _isLinkRow (): boolean {
|
|
95
|
+
return this.interactionType === 'link';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// True when the item lends its text to a slotted control as that control's accessible
|
|
99
|
+
// name/description: selectable rows (radio/checkbox/switch) and link rows (the slotted anchor).
|
|
100
|
+
private get _providesAria (): boolean {
|
|
101
|
+
return this._isSelectable || this._isLinkRow;
|
|
76
102
|
}
|
|
77
103
|
|
|
78
104
|
// radio/checkbox are owned by a selection group, which is why the item becomes `presentation`
|
|
79
105
|
// (so the group owns the controls directly) and the radio is named on its host. A switch has
|
|
80
106
|
// no group, so the item stays a `listitem`.
|
|
81
107
|
private get _ownedByGroup (): boolean {
|
|
82
|
-
return this.
|
|
108
|
+
return this.interactionType === 'radio' || this.interactionType === 'checkbox';
|
|
83
109
|
}
|
|
84
110
|
|
|
85
111
|
// True when the row should be treated as disabled: either its own `disabled` prop is set, or the
|
|
@@ -98,9 +124,6 @@ export class PieListItem extends PieElement implements ListItemProps {
|
|
|
98
124
|
connectedCallback () {
|
|
99
125
|
super.connectedCallback();
|
|
100
126
|
|
|
101
|
-
// Respect a role the consumer set explicitly; otherwise we manage it from `selectionType`.
|
|
102
|
-
this._hasExplicitRole = this.hasAttribute('role');
|
|
103
|
-
|
|
104
127
|
this._abortController = new AbortController();
|
|
105
128
|
this.addEventListener('click', this._handleHostClick, { signal: this._abortController.signal });
|
|
106
129
|
}
|
|
@@ -113,16 +136,23 @@ export class PieListItem extends PieElement implements ListItemProps {
|
|
|
113
136
|
protected updated () {
|
|
114
137
|
this._applyRole();
|
|
115
138
|
this._ariaProvider?.setValue(this._providedAria);
|
|
139
|
+
this._applyLinkAria();
|
|
116
140
|
}
|
|
117
141
|
|
|
118
142
|
/**
|
|
119
|
-
* Sets the item's role from `
|
|
120
|
-
* owns those controls directly), otherwise `listitem` (static items and
|
|
121
|
-
* explicitly by the consumer is left untouched
|
|
143
|
+
* Sets the item's role from `interactionType`: `presentation` for radio/checkbox (so the group
|
|
144
|
+
* owns those controls directly), otherwise `listitem` (static items, switches and links). A role
|
|
145
|
+
* set explicitly by the consumer is left untouched, even if it is added or removed dynamically
|
|
146
|
+
* after the element connects.
|
|
122
147
|
*/
|
|
123
148
|
private _applyRole (): void {
|
|
124
|
-
|
|
125
|
-
|
|
149
|
+
const currentRole = this.getAttribute('role');
|
|
150
|
+
// Only manage the role if it is absent or was last set by us — not a consumer-supplied value.
|
|
151
|
+
if (currentRole !== null && currentRole !== this._managedRole) return;
|
|
152
|
+
|
|
153
|
+
const nextRole = this._ownedByGroup ? 'presentation' : 'listitem';
|
|
154
|
+
this._managedRole = nextRole;
|
|
155
|
+
this.setAttribute('role', nextRole);
|
|
126
156
|
}
|
|
127
157
|
|
|
128
158
|
/**
|
|
@@ -134,7 +164,7 @@ export class PieListItem extends PieElement implements ListItemProps {
|
|
|
134
164
|
* `aria-labelledby` cannot.
|
|
135
165
|
*/
|
|
136
166
|
private get _providedAria (): ContextualAria | undefined {
|
|
137
|
-
if (!this.
|
|
167
|
+
if (!this._providesAria) return undefined;
|
|
138
168
|
|
|
139
169
|
const description = [this.secondaryText, this.metaText].filter(Boolean).join('. ');
|
|
140
170
|
|
|
@@ -155,6 +185,63 @@ export class PieListItem extends PieElement implements ListItemProps {
|
|
|
155
185
|
control.focus();
|
|
156
186
|
};
|
|
157
187
|
|
|
188
|
+
/**
|
|
189
|
+
* When the item is a link, names the slotted (empty) anchor from the item's own text: the parent
|
|
190
|
+
* owns the child's aria. The anchor is plain light-DOM, so it cannot reference the shadow-DOM text
|
|
191
|
+
* via `aria-labelledby`; and it is not a PIE control that consumes `ariaContext`, so we set the
|
|
192
|
+
* attributes imperatively. The visible text is `aria-hidden` in the template, so nothing is
|
|
193
|
+
* announced twice.
|
|
194
|
+
*
|
|
195
|
+
* A consumer-provided name/description always wins: we capture (once) whether the anchor already
|
|
196
|
+
* carries `aria-label`/`aria-labelledby` or `aria-description`/`aria-describedby` and, if so,
|
|
197
|
+
* leave that attribute untouched. Otherwise we keep our generated value in sync with the text.
|
|
198
|
+
*/
|
|
199
|
+
private _applyLinkAria (): void {
|
|
200
|
+
if (!this._isLinkRow) {
|
|
201
|
+
// When transitioning away from `link`, remove any attributes we previously set so they
|
|
202
|
+
// are not left orphaned on the anchor. Reset capture state so a future transition back
|
|
203
|
+
// to `link` re-reads the consumer's attrs from the (possibly different) anchor.
|
|
204
|
+
if (this._linkAriaCaptured) {
|
|
205
|
+
const anchor = this.querySelector('a[slot="link"]');
|
|
206
|
+
if (anchor) {
|
|
207
|
+
if (!this._consumerNamedLink) anchor.removeAttribute('aria-label');
|
|
208
|
+
if (!this._consumerDescribedLink) anchor.removeAttribute('aria-description');
|
|
209
|
+
}
|
|
210
|
+
this._linkAriaCaptured = false;
|
|
211
|
+
this._consumerNamedLink = false;
|
|
212
|
+
this._consumerDescribedLink = false;
|
|
213
|
+
}
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const anchor = this.querySelector('a[slot="link"]');
|
|
218
|
+
if (!anchor) return;
|
|
219
|
+
|
|
220
|
+
if (!this._linkAriaCaptured) {
|
|
221
|
+
this._consumerNamedLink = anchor.hasAttribute('aria-label') || anchor.hasAttribute('aria-labelledby');
|
|
222
|
+
this._consumerDescribedLink = anchor.hasAttribute('aria-description') || anchor.hasAttribute('aria-describedby');
|
|
223
|
+
this._linkAriaCaptured = true;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const aria = this._providedAria;
|
|
227
|
+
|
|
228
|
+
if (!this._consumerNamedLink) {
|
|
229
|
+
if (aria?.label) {
|
|
230
|
+
anchor.setAttribute('aria-label', aria.label);
|
|
231
|
+
} else {
|
|
232
|
+
anchor.removeAttribute('aria-label');
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (!this._consumerDescribedLink) {
|
|
237
|
+
if (aria?.description) {
|
|
238
|
+
anchor.setAttribute('aria-description', aria.description);
|
|
239
|
+
} else {
|
|
240
|
+
anchor.removeAttribute('aria-description');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
158
245
|
_renderSecondaryText () {
|
|
159
246
|
const { secondaryText } = this;
|
|
160
247
|
if (secondaryText) {
|
|
@@ -168,7 +255,7 @@ export class PieListItem extends PieElement implements ListItemProps {
|
|
|
168
255
|
_renderTrailingContent () {
|
|
169
256
|
const { metaText } = this;
|
|
170
257
|
if (metaText) {
|
|
171
|
-
return html`<span class="c-listItem-metaText c-listItem-trailing" aria-hidden=${this.
|
|
258
|
+
return html`<span class="c-listItem-metaText c-listItem-trailing" aria-hidden=${this._providesAria ? 'true' : nothing}>${metaText}</span>`;
|
|
172
259
|
}
|
|
173
260
|
|
|
174
261
|
return html`<div class="c-listItem-trailing"><slot name="trailing"></slot></div>`;
|
|
@@ -183,16 +270,18 @@ export class PieListItem extends PieElement implements ListItemProps {
|
|
|
183
270
|
'is-bold': this.isBold,
|
|
184
271
|
'has-media': this.hasMedia,
|
|
185
272
|
'is-selectable': this._isSelectable,
|
|
273
|
+
'is-link': this._isLinkRow,
|
|
186
274
|
'is-disabled': this._isDisabled,
|
|
187
275
|
};
|
|
188
276
|
|
|
189
277
|
return html`
|
|
190
278
|
<div class=${classMap(containerClasses)}>
|
|
279
|
+
${this._isLinkRow ? html`<slot name="link"></slot>` : nothing}
|
|
191
280
|
<div class="c-listItem-leading">
|
|
192
281
|
<slot name="leading"></slot>
|
|
193
282
|
</div>
|
|
194
283
|
|
|
195
|
-
<div class="c-listItem-text" aria-hidden=${this.
|
|
284
|
+
<div class="c-listItem-text" aria-hidden=${this._providesAria ? 'true' : nothing}>
|
|
196
285
|
<span class="c-listItem-primaryText">${primaryText}</span>
|
|
197
286
|
${this._renderSecondaryText()}
|
|
198
287
|
</div>
|
|
@@ -49,13 +49,20 @@
|
|
|
49
49
|
min-height: 48px;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
// Link rows: the whole item is a single navigation target. The container is positioned so the
|
|
53
|
+
// slotted anchor can stretch over it.
|
|
54
|
+
.c-listItem-container.is-link {
|
|
55
|
+
position: relative;
|
|
56
|
+
}
|
|
57
|
+
|
|
52
58
|
// Interactive row states. When the item is inside a radio/checkbox group the whole row
|
|
53
59
|
// is a click target (it forwards the click to the slotted control), so it shows hover and
|
|
54
60
|
// active surface tints, matching the control's own interactive states. These are suppressed
|
|
55
61
|
// when the slotted control is disabled (the item reflects this as `is-disabled`). The
|
|
56
62
|
// defaults use the standard PIE interactive overlay tokens; override `--list-item-hover-bg` /
|
|
57
63
|
// `--list-item-active-bg` if design specifies dedicated list tokens.
|
|
58
|
-
.c-listItem-container.is-selectable:not(.is-disabled)
|
|
64
|
+
.c-listItem-container.is-selectable:not(.is-disabled),
|
|
65
|
+
.c-listItem-container.is-link {
|
|
59
66
|
--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));
|
|
60
67
|
--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));
|
|
61
68
|
|
|
@@ -66,15 +73,38 @@
|
|
|
66
73
|
|
|
67
74
|
cursor: pointer;
|
|
68
75
|
|
|
76
|
+
// Expose the current interaction tint to slotted controls via an inherited custom property (it
|
|
77
|
+
// flows through the `<slot>` into slotted content). This is a generic contract for any control
|
|
78
|
+
// inside an interactive container: a slotted `pie-switch` layers it over its track so the track
|
|
79
|
+
// co-tints with the container; radio/checkbox instead let it show through their transparent
|
|
80
|
+
// fills. Unset when not interacting, so a control outside such a container sees nothing.
|
|
69
81
|
&:hover {
|
|
70
82
|
background-color: var(--list-item-hover-bg);
|
|
83
|
+
|
|
84
|
+
--interactive-container-tint: var(--list-item-hover-bg);
|
|
71
85
|
}
|
|
72
86
|
|
|
73
87
|
&:active {
|
|
74
88
|
background-color: var(--list-item-active-bg);
|
|
89
|
+
|
|
90
|
+
--interactive-container-tint: var(--list-item-active-bg);
|
|
75
91
|
}
|
|
76
92
|
}
|
|
77
93
|
|
|
94
|
+
// Stretch the empty slotted anchor over the entire row so the whole block is the link's
|
|
95
|
+
// click/touch target. It anchors to the container (positioned above).
|
|
96
|
+
slot[name="link"]::slotted(a) {
|
|
97
|
+
position: absolute;
|
|
98
|
+
inset: 0;
|
|
99
|
+
z-index: 1;
|
|
100
|
+
border-radius: inherit; // so the focus ring follows any rounding applied to the row
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Standard PIE focus ring. The anchor is row-sized, so keyboard focus rings the whole item.
|
|
104
|
+
slot[name="link"]::slotted(a:focus-visible) {
|
|
105
|
+
@include p.focus;
|
|
106
|
+
}
|
|
107
|
+
|
|
78
108
|
// Larger slotted media (e.g. pie-thumbnail) reduces the block padding, but only
|
|
79
109
|
// when the primary text stands alone (`:only-child` == no secondary text). With
|
|
80
110
|
// secondary text the default padding is kept so two lines of text sit correctly.
|
package/dist/index-KnN5fPmj.js
DELETED
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
import { unsafeCSS as x, isServer as f, html as p, nothing as m } 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 T, ariaContext as I, parentDisabledContext as C } from "@justeattakeaway/pie-webc-core";
|
|
5
|
-
import { P as w } from "./PieElement-B3g_8vKy.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 = (r, a) => {
|
|
24
|
-
this.unsubscribe && (this.unsubscribe !== a && (this.provided = !1, this.unsubscribe()), this.subscribe || this.unsubscribe()), this.value = r, this.host.requestUpdate(), this.provided && !this.subscribe || (this.provided = !0, this.callback && this.callback(r, a)), this.unsubscribe = a;
|
|
25
|
-
}, this.host = t, e.context !== void 0) {
|
|
26
|
-
const r = e;
|
|
27
|
-
this.context = r.context, this.callback = r.callback, this.subscribe = r.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 P {
|
|
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 S extends Event {
|
|
80
|
-
constructor(t, e) {
|
|
81
|
-
super("context-provider", { bubbles: !0, composed: !0 }), this.context = t, this.contextTarget = e;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
class E extends P {
|
|
85
|
-
constructor(t, e, o) {
|
|
86
|
-
var s, r;
|
|
87
|
-
super(e.context !== void 0 ? e.initialValue : o), this.onContextRequest = (a) => {
|
|
88
|
-
if (a.context !== this.context) return;
|
|
89
|
-
const h = a.contextTarget ?? a.composedPath()[0];
|
|
90
|
-
h !== this.host && (a.stopPropagation(), this.addCallback(a.callback, h, a.subscribe));
|
|
91
|
-
}, this.onProviderRequest = (a) => {
|
|
92
|
-
if (a.context !== this.context || (a.contextTarget ?? a.composedPath()[0]) === this.host) return;
|
|
93
|
-
const h = /* @__PURE__ */ new Set();
|
|
94
|
-
for (const [b, { consumerHost: u }] of this.subscriptions) h.has(b) || (h.add(b), u.dispatchEvent(new g(this.context, u, b, !0)));
|
|
95
|
-
a.stopPropagation();
|
|
96
|
-
}, this.host = t, e.context !== void 0 ? this.context = e.context : this.context = e, this.attachListeners(), (r = (s = this.host).addController) == null || r.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 S(this.context, this.host));
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* @license
|
|
107
|
-
* Copyright 2022 Google LLC
|
|
108
|
-
* SPDX-License-Identifier: BSD-3-Clause
|
|
109
|
-
*/
|
|
110
|
-
function B({ 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: (r) => {
|
|
118
|
-
s[o] = r;
|
|
119
|
-
}, subscribe: t });
|
|
120
|
-
}));
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
const z = ["none", "radio", "checkbox", "switch"], d = {
|
|
124
|
-
isCompact: !1,
|
|
125
|
-
isBold: !1,
|
|
126
|
-
hasMedia: !1,
|
|
127
|
-
selectionType: "none",
|
|
128
|
-
disabled: !1
|
|
129
|
-
}, R = "*,*: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-selectable:not(.is-disabled){--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){--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){cursor:pointer}.c-listItem-container.is-selectable:not(.is-disabled):hover{background-color:var(--list-item-hover-bg)}.c-listItem-container.is-selectable:not(.is-disabled):active{background-color:var(--list-item-active-bg)}.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, D = Object.getOwnPropertyDescriptor, l = (i, t, e, o) => {
|
|
131
|
-
for (var s = o > 1 ? void 0 : o ? D(t, e) : t, r = i.length - 1, a; r >= 0; r--)
|
|
132
|
-
(a = i[r]) && (s = (o ? a(t, e, s) : a(s)) || s);
|
|
133
|
-
return o && s && q(t, e, s), s;
|
|
134
|
-
};
|
|
135
|
-
const $ = "pie-list-item";
|
|
136
|
-
let n = class extends w {
|
|
137
|
-
constructor() {
|
|
138
|
-
super(...arguments), this.isCompact = d.isCompact, this.isBold = d.isBold, this.hasMedia = d.hasMedia, this.selectionType = d.selectionType, this.disabled = d.disabled, this._parentDisabled = !1, this._ariaProvider = f ? void 0 : new E(this, { context: I }), this._hasExplicitRole = !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
|
-
get _isSelectable() {
|
|
145
|
-
return this.selectionType !== "none";
|
|
146
|
-
}
|
|
147
|
-
// radio/checkbox are owned by a selection group, which is why the item becomes `presentation`
|
|
148
|
-
// (so the group owns the controls directly) and the radio is named on its host. A switch has
|
|
149
|
-
// no group, so the item stays a `listitem`.
|
|
150
|
-
get _ownedByGroup() {
|
|
151
|
-
return this.selectionType === "radio" || this.selectionType === "checkbox";
|
|
152
|
-
}
|
|
153
|
-
// True when the row should be treated as disabled: either its own `disabled` prop is set, or the
|
|
154
|
-
// containing group is disabled (provided via context). Declarative and reactive, so the styling
|
|
155
|
-
// and row-click guard react without observing the slotted control.
|
|
156
|
-
get _isDisabled() {
|
|
157
|
-
return this.disabled || this._parentDisabled;
|
|
158
|
-
}
|
|
159
|
-
// The interactive control (radio/checkbox/switch) slotted into this item, if any. Used only by
|
|
160
|
-
// the client-side row-click handler.
|
|
161
|
-
get _control() {
|
|
162
|
-
return this.querySelector("pie-radio, pie-checkbox, pie-switch");
|
|
163
|
-
}
|
|
164
|
-
connectedCallback() {
|
|
165
|
-
super.connectedCallback(), this._hasExplicitRole = this.hasAttribute("role"), this._abortController = new AbortController(), this.addEventListener("click", this._handleHostClick, { signal: this._abortController.signal });
|
|
166
|
-
}
|
|
167
|
-
disconnectedCallback() {
|
|
168
|
-
super.disconnectedCallback(), this._abortController.abort();
|
|
169
|
-
}
|
|
170
|
-
updated() {
|
|
171
|
-
var i;
|
|
172
|
-
this._applyRole(), (i = this._ariaProvider) == null || i.setValue(this._providedAria);
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Sets the item's role from `selectionType`: `presentation` for radio/checkbox (so the group
|
|
176
|
-
* owns those controls directly), otherwise `listitem` (static items and switches). A role set
|
|
177
|
-
* explicitly by the consumer is left untouched.
|
|
178
|
-
*/
|
|
179
|
-
_applyRole() {
|
|
180
|
-
this._hasExplicitRole || this.setAttribute("role", this._ownedByGroup ? "presentation" : "listitem");
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* The accessible name and description this item provides to its slotted control, stitched from
|
|
184
|
-
* its text. Only provided in a selectable list. Each control consumes this via `ariaContext`
|
|
185
|
-
* and decides how to apply it: `pie-radio` names its host (which carries `role="radio"`),
|
|
186
|
-
* while `pie-checkbox` / `pie-switch` name their internal input (their host is role-less, so
|
|
187
|
-
* `aria-label` there would be invalid). Context reaches across the shadow boundary that
|
|
188
|
-
* `aria-labelledby` cannot.
|
|
189
|
-
*/
|
|
190
|
-
get _providedAria() {
|
|
191
|
-
if (!this._isSelectable) return;
|
|
192
|
-
const i = [this.secondaryText, this.metaText].filter(Boolean).join(". ");
|
|
193
|
-
return { label: this.primaryText, description: i || void 0 };
|
|
194
|
-
}
|
|
195
|
-
_renderSecondaryText() {
|
|
196
|
-
const { secondaryText: i } = this;
|
|
197
|
-
return i ? p`<span class="c-listItem-secondaryText">${i}</span>` : m;
|
|
198
|
-
}
|
|
199
|
-
// metaText is another form of trailing content. The component can only ever display either slotted trailing content or the metaText string
|
|
200
|
-
_renderTrailingContent() {
|
|
201
|
-
const { metaText: i } = this;
|
|
202
|
-
return i ? p`<span class="c-listItem-metaText c-listItem-trailing" aria-hidden=${this._isSelectable ? "true" : m}>${i}</span>` : p`<div class="c-listItem-trailing"><slot name="trailing"></slot></div>`;
|
|
203
|
-
}
|
|
204
|
-
render() {
|
|
205
|
-
const { primaryText: i } = this, t = {
|
|
206
|
-
"c-listItem-container": !0,
|
|
207
|
-
"is-compact": this.isCompact,
|
|
208
|
-
"is-bold": this.isBold,
|
|
209
|
-
"has-media": this.hasMedia,
|
|
210
|
-
"is-selectable": this._isSelectable,
|
|
211
|
-
"is-disabled": this._isDisabled
|
|
212
|
-
};
|
|
213
|
-
return p`
|
|
214
|
-
<div class=${k(t)}>
|
|
215
|
-
<div class="c-listItem-leading">
|
|
216
|
-
<slot name="leading"></slot>
|
|
217
|
-
</div>
|
|
218
|
-
|
|
219
|
-
<div class="c-listItem-text" aria-hidden=${this._isSelectable ? "true" : m}>
|
|
220
|
-
<span class="c-listItem-primaryText">${i}</span>
|
|
221
|
-
${this._renderSecondaryText()}
|
|
222
|
-
</div>
|
|
223
|
-
|
|
224
|
-
${this._renderTrailingContent()}
|
|
225
|
-
</div>`;
|
|
226
|
-
}
|
|
227
|
-
};
|
|
228
|
-
n.styles = x(R);
|
|
229
|
-
l([
|
|
230
|
-
c({ type: String })
|
|
231
|
-
], n.prototype, "primaryText", 2);
|
|
232
|
-
l([
|
|
233
|
-
c({ type: String })
|
|
234
|
-
], n.prototype, "secondaryText", 2);
|
|
235
|
-
l([
|
|
236
|
-
c({ type: String })
|
|
237
|
-
], n.prototype, "metaText", 2);
|
|
238
|
-
l([
|
|
239
|
-
c({ type: Boolean })
|
|
240
|
-
], n.prototype, "isCompact", 2);
|
|
241
|
-
l([
|
|
242
|
-
c({ type: Boolean })
|
|
243
|
-
], n.prototype, "isBold", 2);
|
|
244
|
-
l([
|
|
245
|
-
c({ type: Boolean })
|
|
246
|
-
], n.prototype, "hasMedia", 2);
|
|
247
|
-
l([
|
|
248
|
-
c({ type: String, attribute: "selection-type" }),
|
|
249
|
-
_($, z, d.selectionType)
|
|
250
|
-
], n.prototype, "selectionType", 2);
|
|
251
|
-
l([
|
|
252
|
-
c({ type: Boolean })
|
|
253
|
-
], n.prototype, "disabled", 2);
|
|
254
|
-
l([
|
|
255
|
-
B({ context: C, subscribe: !0 }),
|
|
256
|
-
y()
|
|
257
|
-
], n.prototype, "_parentDisabled", 2);
|
|
258
|
-
n = l([
|
|
259
|
-
T("pie-list-item")
|
|
260
|
-
], n);
|
|
261
|
-
export {
|
|
262
|
-
n as P,
|
|
263
|
-
d,
|
|
264
|
-
z as s
|
|
265
|
-
};
|