@@ -28,6 +28,8 @@ import {
2828import { openOverlay } from './loader.js' ;
2929import overlayTriggerStyles from './overlay-trigger.css.js' ;
3030
31+ export type OverlayContentTypes = 'click' | 'hover' | 'longpress' ;
32+
3133/**
3234 * A overlay trigger component for displaying overlays relative to other content.
3335 * @element overlay-trigger
@@ -42,6 +44,7 @@ import overlayTriggerStyles from './overlay-trigger.css.js';
4244 */
4345export class OverlayTrigger extends LitElement {
4446 private closeClickOverlay ?: ( ) => void ;
47+ private closeLongpressOverlay ?: ( ) => void ;
4548 private closeHoverOverlay ?: ( ) => void ;
4649
4750 public static get styles ( ) : CSSResultArray {
@@ -61,6 +64,9 @@ export class OverlayTrigger extends LitElement {
6164 @property ( { type : Number , reflect : true } )
6265 public offset = 6 ;
6366
67+ @property ( { reflect : true } )
68+ public open ?: OverlayContentTypes ;
69+
6470 @property ( { type : Boolean , reflect : true } )
6571 public disabled = false ;
6672
@@ -69,8 +75,8 @@ export class OverlayTrigger extends LitElement {
6975 private hoverContent ?: HTMLElement ;
7076 private targetContent ?: HTMLElement ;
7177
72- public overloadTarget ( target : HTMLElement ) : void {
73- this . targetContent = target ;
78+ private handleClose ( ) : void {
79+ this . removeAttribute ( 'open' ) ;
7480 }
7581
7682 protected render ( ) : TemplateResult {
@@ -85,6 +91,7 @@ export class OverlayTrigger extends LitElement {
8591 @mouseleave=${ this . onTrigger }
8692 @focusin=${ this . onTrigger }
8793 @focusout=${ this . onTrigger }
94+ @sp-closed=${ this . handleClose }
8895 >
8996 < slot
9097 @slotchange =${ this . onTargetSlotChange }
@@ -118,6 +125,50 @@ export class OverlayTrigger extends LitElement {
118125 ) {
119126 this . closeClickOverlay ( ) ;
120127 }
128+ if ( changes . has ( 'open' ) ) {
129+ this . manageOpen ( changes . get ( 'open' ) as OverlayContentTypes ) ;
130+ }
131+ }
132+
133+ private manageOpen ( previous ?: OverlayContentTypes ) : void {
134+ switch ( this . open ) {
135+ case 'click' :
136+ if ( ! this . closeClickOverlay ) {
137+ this . onTriggerClick ( ) ;
138+ }
139+ break ;
140+ case 'hover' :
141+ if ( ! this . closeHoverOverlay ) {
142+ this . onTriggerMouseEnter ( ) ;
143+ }
144+ break ;
145+ case 'longpress' :
146+ if ( ! this . closeLongpressOverlay ) {
147+ this . onTriggerLongpress ( ) ;
148+ }
149+ break ;
150+ default :
151+ switch ( previous ) {
152+ case 'click' :
153+ if ( this . closeClickOverlay ) {
154+ this . closeClickOverlay ( ) ;
155+ delete this . closeClickOverlay ;
156+ }
157+ break ;
158+ case 'longpress' :
159+ if ( this . closeLongpressOverlay ) {
160+ this . closeLongpressOverlay ( ) ;
161+ delete this . closeLongpressOverlay ;
162+ }
163+ break ;
164+ case 'hover' :
165+ this . onTriggerMouseLeave ( ) ;
166+ break ;
167+ default :
168+ break ;
169+ }
170+ break ;
171+ }
121172 }
122173
123174 public static openOverlay = async (
@@ -143,61 +194,69 @@ export class OverlayTrigger extends LitElement {
143194 return ;
144195 }
145196 switch ( event . type ) {
146- case 'click' :
147- this . onTriggerClick ( ) ;
148- return ;
149197 case 'mouseenter' :
150198 case 'focusin' :
151- this . onTriggerMouseEnter ( ) ;
199+ if ( this . hoverContent ) {
200+ this . open = 'hover' ;
201+ }
152202 return ;
153203 case 'mouseleave' :
154204 case 'focusout' :
155- this . onTriggerMouseLeave ( ) ;
205+ if ( this . open === 'hover' ) {
206+ this . handleClose ( ) ;
207+ }
208+ return ;
209+ case 'click' :
210+ if ( this . clickContent ) {
211+ this . open = event . type ;
212+ }
156213 return ;
157214 case 'longpress' :
158- this . onTriggerLongpress ( event ) ;
215+ if ( this . longpressContent ) {
216+ this . open = event . type ;
217+ }
159218 return ;
160219 }
161220 }
162221
222+ private prepareToFocusOverlayContent ( overlayContent : HTMLElement ) : void {
223+ if ( this . type !== 'modal' ) {
224+ return ;
225+ }
226+ const firstFocusable = overlayContent . querySelector (
227+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"]), [focusable]'
228+ ) as HTMLElement ;
229+ if ( ! firstFocusable ) {
230+ overlayContent . tabIndex = 0 ;
231+ }
232+ }
233+
163234 public async onTriggerClick ( ) : Promise < void > {
164- if ( this . targetContent && this . clickContent ) {
165- if ( this . type === 'modal' ) {
166- const firstFocusable = this . querySelector (
167- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"]), [focusable]'
168- ) as HTMLElement ;
169- if ( ! firstFocusable ) {
170- this . clickContent . tabIndex = 0 ;
171- }
172- }
173- const { targetContent, clickContent } = this ;
174- this . closeClickOverlay = await OverlayTrigger . openOverlay (
175- targetContent ,
176- this . type ? this . type : 'click' ,
177- clickContent ,
178- this . overlayOptions
179- ) ;
235+ if ( ! this . targetContent || ! this . clickContent ) {
236+ return ;
180237 }
238+ const { targetContent, clickContent } = this ;
239+ this . prepareToFocusOverlayContent ( clickContent ) ;
240+ this . closeClickOverlay = await OverlayTrigger . openOverlay (
241+ targetContent ,
242+ this . type ? this . type : 'click' ,
243+ clickContent ,
244+ this . overlayOptions
245+ ) ;
181246 }
182247
183248 private async onTriggerLongpress (
184- event : CustomEvent < LongpressEvent >
249+ event ? : CustomEvent < LongpressEvent >
185250 ) : Promise < void > {
186251 if ( ! this . targetContent || ! this . longpressContent ) {
187252 return ;
188253 }
189254 const { targetContent, longpressContent } = this ;
190- if ( this . type === 'modal' ) {
191- const firstFocusable = this . querySelector (
192- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"]), [focusable]'
193- ) as HTMLElement ;
194- if ( ! firstFocusable ) {
195- longpressContent . tabIndex = 0 ;
196- }
197- }
198- const type = event . detail . source === 'keyboard' ? 'click' : 'longpress' ;
255+ this . prepareToFocusOverlayContent ( longpressContent ) ;
256+ const type =
257+ event && event . detail . source === 'keyboard' ? 'click' : 'longpress' ;
199258 const interaction = this . type ? this . type : type || 'longpress' ;
200- this . closeClickOverlay = await OverlayTrigger . openOverlay (
259+ this . closeLongpressOverlay = await OverlayTrigger . openOverlay (
201260 targetContent ,
202261 interaction ,
203262 longpressContent ,
@@ -211,22 +270,23 @@ export class OverlayTrigger extends LitElement {
211270 private hoverOverlayReady = Promise . resolve ( ) ;
212271
213272 public async onTriggerMouseEnter ( ) : Promise < void > {
214- if ( this . targetContent && this . hoverContent ) {
215- let overlayReady : ( ) => void = ( ) => {
216- return ;
217- } ;
218- this . hoverOverlayReady = new Promise ( ( res ) => {
219- overlayReady = res ;
220- } ) ;
221- const { targetContent, hoverContent } = this ;
222- this . closeHoverOverlay = await OverlayTrigger . openOverlay (
223- targetContent ,
224- 'hover' ,
225- hoverContent ,
226- this . overlayOptions
227- ) ;
228- overlayReady ( ) ;
273+ if ( ! this . targetContent || ! this . hoverContent ) {
274+ return ;
229275 }
276+ let overlayReady : ( ) => void = ( ) => {
277+ return ;
278+ } ;
279+ this . hoverOverlayReady = new Promise ( ( res ) => {
280+ overlayReady = res ;
281+ } ) ;
282+ const { targetContent, hoverContent } = this ;
283+ this . closeHoverOverlay = await OverlayTrigger . openOverlay (
284+ targetContent ,
285+ 'hover' ,
286+ hoverContent ,
287+ this . overlayOptions
288+ ) ;
289+ overlayReady ( ) ;
230290 }
231291
232292 public async onTriggerMouseLeave ( ) : Promise < void > {
@@ -240,29 +300,28 @@ export class OverlayTrigger extends LitElement {
240300 private onClickSlotChange (
241301 event : Event & { target : HTMLSlotElement }
242302 ) : void {
243- const content = this . extractSlotContentFromEvent ( event ) ;
244- this . clickContent = content ;
303+ this . clickContent = this . extractSlotContentFromEvent ( event ) ;
304+ this . manageOpen ( ) ;
245305 }
246306
247307 private onLongpressSlotChange (
248308 event : Event & { target : HTMLSlotElement }
249309 ) : void {
250- const content = this . extractSlotContentFromEvent ( event ) ;
251- this . longpressContent = content ;
310+ this . longpressContent = this . extractSlotContentFromEvent ( event ) ;
311+ this . manageOpen ( ) ;
252312 }
253313
254314 private onHoverSlotChange (
255315 event : Event & { target : HTMLSlotElement }
256316 ) : void {
257- const content = this . extractSlotContentFromEvent ( event ) ;
258- this . hoverContent = content ;
317+ this . hoverContent = this . extractSlotContentFromEvent ( event ) ;
318+ this . manageOpen ( ) ;
259319 }
260320
261321 private onTargetSlotChange (
262322 event : Event & { target : HTMLSlotElement }
263323 ) : void {
264- const content = this . extractSlotContentFromEvent ( event ) ;
265- this . targetContent = content ;
324+ this . targetContent = this . extractSlotContentFromEvent ( event ) ;
266325 }
267326
268327 private extractSlotContentFromEvent ( event : Event ) : HTMLElement | undefined {
@@ -276,6 +335,10 @@ export class OverlayTrigger extends LitElement {
276335 this . closeClickOverlay ( ) ;
277336 delete this . closeClickOverlay ;
278337 }
338+ if ( this . closeHoverOverlay ) {
339+ this . closeHoverOverlay ( ) ;
340+ delete this . closeClickOverlay ;
341+ }
279342 super . disconnectedCallback ( ) ;
280343 }
281344}
0 commit comments