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' ;
916import { ARIAMixinStrict } from '../../../../internal/aria/aria.js' ;
1017import { mixinDelegatesAria } from '../../../../internal/aria/delegate.js' ;
1118import { ripple } from '../ripple/ripple.js' ;
19+ import { hasSlotted } from '../shared/has-slotted.js' ;
1220
1321import 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