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 { redispatchEvent } from '../../../../internal/events/redispatch-event.js' ;
@@ -17,6 +24,9 @@ import focusRingStyles from '../focus/focus-ring.css' with {type: 'css'}; // git
1724// import focusRingStyles from '../focus/focus-ring.cssresult.js'; // google3-only
1825import rippleStyles from '../ripple/ripple.css' with { type : 'css' } ; // github-only
1926// import rippleStyles from '../ripple/ripple.cssresult.js'; // google3-only
27+
28+ import { hasSlotted } from '../shared/has-slotted.js' ;
29+
2030import buttonStyles from './button.css' with { type : 'css' } ; // github-only
2131// import buttonStyles from './button.cssresult.js'; // google3-only
2232
@@ -52,13 +62,37 @@ export class Button extends baseClass {
5262 css `
5363 :host {
5464 display: inline-flex;
65+ isolation: isolate;
5566 }
5667 .btn {
5768 flex: 1;
69+ position: relative;
70+ }
71+ .btn:has([name='container'].has-slotted) {
72+ background-color: transparent;
73+ }
74+ .btn.btn-hide-outline {
75+ --outline-color: transparent;
76+ }
77+ slot[name='container'] {
78+ display: block;
79+ position: absolute;
80+ inset: 0;
81+ border-radius: inherit;
82+ pointer-events: none;
83+ --color: var(--container-color);
84+ z-index: -1;
85+ transition: inherit;
86+ }
87+ slot[name='container']::slotted(*) {
88+ width: 100%;
89+ height: 100%;
5890 }
5991 ` ,
6092 ] ;
6193
94+ @state ( ) private hideOutline = false ;
95+
6296 /**
6397 * The color of the button.
6498 */
@@ -129,13 +163,66 @@ export class Button extends baseClass {
129163 */
130164 @property ( ) target : '_blank' | '_parent' | '_self' | '_top' | '' = '' ;
131165
166+ private lastFiredEnabledState ?: boolean ;
167+
168+ override connectedCallback ( ) {
169+ super . connectedCallback ( ) ;
170+ this . addEventListener ( 'md-gb:set-show-outline' , this . handleSetShowOutline ) ;
171+ }
172+
173+ override disconnectedCallback ( ) {
174+ super . disconnectedCallback ( ) ;
175+ this . removeEventListener (
176+ 'md-gb:set-show-outline' ,
177+ this . handleSetShowOutline ,
178+ ) ;
179+ }
180+
181+ private readonly handleSetShowOutline = ( event : Event ) => {
182+ const customEvent = event as CustomEvent < { shown : boolean } > ;
183+ this . hideOutline = ! customEvent . detail . shown ;
184+ } ;
185+
186+ protected override updated ( changedProperties : PropertyValues ) {
187+ super . updated ( changedProperties ) ;
188+ if (
189+ changedProperties . has ( 'disabled' ) ||
190+ changedProperties . has ( 'softDisabled' )
191+ ) {
192+ this . dispatchSetEnabledEvent ( ) ;
193+ }
194+ }
195+
196+ private dispatchSetEnabledEvent ( ) {
197+ const enabled = ! ( this . disabled || this . softDisabled ) ;
198+
199+ if ( this . lastFiredEnabledState === enabled ) return ;
200+
201+ const slot = this . shadowRoot ?. querySelector (
202+ 'slot[name="container"]' ,
203+ ) as HTMLSlotElement ;
204+ if ( slot ) {
205+ for ( const element of slot . assignedElements ( { flatten : true } ) ) {
206+ element . dispatchEvent (
207+ new CustomEvent ( 'md-gb:set-enabled' , {
208+ detail : { enabled} ,
209+ } ) ,
210+ ) ;
211+ }
212+ }
213+ this . lastFiredEnabledState = enabled ;
214+ }
215+
132216 protected override render ( ) {
133217 const classes = button ( {
134218 color : this . color ,
135219 size : this . size ,
136220 square : this . square ,
137221 // Emulate `:disabled` when soft-disabled
138222 disabled : this . softDisabled ,
223+ classes : {
224+ 'btn-hide-outline' : this . hideOutline ,
225+ } ,
139226 } ) ;
140227
141228 // Needed for closure conformance
@@ -153,6 +240,10 @@ export class Button extends baseClass {
153240 aria-disabled=${ this . disabled || this . softDisabled || nothing }
154241 tabindex=${ this . disabled && ! this . softDisabled ? - 1 : nothing } >
155242 < slot > </ slot >
243+ < slot
244+ name ="container "
245+ ${ hasSlotted ( ) }
246+ @slotchange =${ this . handleContainerSlotChange } > </ slot >
156247 </ a > ` ;
157248 }
158249
@@ -167,9 +258,28 @@ export class Button extends baseClass {
167258 aria-expanded=${ ariaExpanded || nothing }
168259 @change=${ this . handleChange } >
169260 < slot > </ slot >
261+ < slot
262+ name ="container "
263+ ${ hasSlotted ( ) }
264+ @slotchange =${ this . handleContainerSlotChange } > </ slot >
170265 </ button > ` ;
171266 }
172267
268+ private handleContainerSlotChange ( event : Event ) {
269+ const slot = event . target as HTMLSlotElement ;
270+
271+ const enabled = ! ( this . disabled || this . softDisabled ) ;
272+
273+ for ( const element of slot . assignedElements ( { flatten : true } ) ) {
274+ element . dispatchEvent ( new CustomEvent ( 'md-gb:change-container-slot' ) ) ;
275+ element . dispatchEvent (
276+ new CustomEvent ( 'md-gb:set-enabled' , {
277+ detail : { enabled} ,
278+ } ) ,
279+ ) ;
280+ }
281+ }
282+
173283 private handleChange ( event : Event ) {
174284 this . selected = ( event . target as HTMLElement ) . ariaPressed === 'true' ;
175285 redispatchEvent ( this , event ) ;
0 commit comments