Skip to content

Commit ed72d96

Browse files
feat(labs): add container slot support for card
PiperOrigin-RevId: 930106532
1 parent d59dd9d commit ed72d96

1 file changed

Lines changed: 104 additions & 2 deletions

File tree

labs/gb/components/card/md-card.ts

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,19 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
import {css, CSSResultOrNative, html, LitElement, nothing} from 'lit';
8-
import {customElement, property} from 'lit/decorators.js';
7+
import {
8+
css,
9+
CSSResultOrNative,
10+
html,
11+
LitElement,
12+
nothing,
13+
PropertyValues,
14+
} from 'lit';
15+
import {customElement, property, state} from 'lit/decorators.js';
916
import {ARIAMixinStrict} from '../../../../internal/aria/aria.js';
1017
import {mixinDelegatesAria} from '../../../../internal/aria/delegate.js';
1118
import {ripple} from '../ripple/ripple.js';
19+
import {hasSlotted} from '../shared/has-slotted.js';
1220

1321
import focusRingStyles from '../focus/focus-ring.css' with {type: 'css'}; // github-only
1422
// import focusRingStyles from '../focus/focus-ring.cssresult.js'; // google3-only
@@ -47,13 +55,40 @@ export class Card extends baseClass {
4755
css`
4856
:host {
4957
display: inline-flex;
58+
isolation: isolate;
5059
}
5160
.card {
5261
flex: 1;
62+
position: relative;
63+
}
64+
.card:has([name='container'].has-slotted) {
65+
background-color: transparent;
66+
}
67+
.card.card-hide-outline {
68+
--outline-color: transparent;
69+
--container-elevation: var(--md-sys-elevation-shadow-0);
70+
}
71+
slot[name='container'] {
72+
display: block;
73+
position: absolute;
74+
inset: 0;
75+
border-radius: inherit;
76+
pointer-events: none;
77+
--color: var(--container-color);
78+
z-index: -1;
79+
transition: inherit;
80+
}
81+
slot[name='container']::slotted(*) {
82+
width: 100%;
83+
height: 100%;
5384
}
5485
`,
5586
];
5687

88+
@state() private hideOutline = false;
89+
90+
private lastFiredEnabledState?: boolean;
91+
5792
/** The color of the card. */
5893
@property() color: CardColor = 'outlined';
5994

@@ -63,6 +98,51 @@ export class Card extends baseClass {
6398
/** Whether the card is interactive. */
6499
@property({type: Boolean}) interactive = false;
65100

101+
override connectedCallback() {
102+
super.connectedCallback();
103+
this.addEventListener('md-gb:set-show-outline', this.handleSetShowOutline);
104+
}
105+
106+
override disconnectedCallback() {
107+
super.disconnectedCallback();
108+
this.removeEventListener(
109+
'md-gb:set-show-outline',
110+
this.handleSetShowOutline,
111+
);
112+
}
113+
114+
private readonly handleSetShowOutline = (event: Event) => {
115+
const customEvent = event as CustomEvent<{shown: boolean}>;
116+
this.hideOutline = !customEvent.detail.shown;
117+
};
118+
119+
protected override updated(changedProperties: PropertyValues) {
120+
super.updated(changedProperties);
121+
if (changedProperties.has('disabled')) {
122+
this.dispatchSetEnabledEvent();
123+
}
124+
}
125+
126+
private dispatchSetEnabledEvent() {
127+
const enabled = !this.disabled;
128+
129+
if (this.lastFiredEnabledState === enabled) return;
130+
131+
const slot = this.shadowRoot?.querySelector(
132+
'slot[name="container"]',
133+
) as HTMLSlotElement;
134+
if (slot) {
135+
for (const element of slot.assignedElements({flatten: true})) {
136+
element.dispatchEvent(
137+
new CustomEvent('md-gb:set-enabled', {
138+
detail: {enabled},
139+
}),
140+
);
141+
}
142+
}
143+
this.lastFiredEnabledState = enabled;
144+
}
145+
66146
protected override render() {
67147
const {ariaLabel} = this as ARIAMixinStrict;
68148
return html`<div
@@ -71,6 +151,9 @@ export class Card extends baseClass {
71151
color: this.color,
72152
disabled: this.disabled,
73153
interactive: this.interactive,
154+
classes: {
155+
'card-hide-outline': this.hideOutline,
156+
},
74157
})}">
75158
${this.interactive
76159
? html`<button
@@ -81,6 +164,25 @@ export class Card extends baseClass {
81164
aria-label="${ariaLabel || nothing}"></button>`
82165
: nothing}
83166
<slot></slot>
167+
<slot
168+
name="container"
169+
${hasSlotted()}
170+
@slotchange=${this.handleContainerSlotChange}></slot>
84171
</div>`;
85172
}
173+
174+
private handleContainerSlotChange(event: Event) {
175+
const slot = event.target as HTMLSlotElement;
176+
177+
const enabled = !this.disabled;
178+
179+
for (const element of slot.assignedElements({flatten: true})) {
180+
element.dispatchEvent(new CustomEvent('md-gb:change-container-slot'));
181+
element.dispatchEvent(
182+
new CustomEvent('md-gb:set-enabled', {
183+
detail: {enabled},
184+
}),
185+
);
186+
}
187+
}
86188
}

0 commit comments

Comments
 (0)