UNPKG

@spectrum-web-components/overlay

Version:

An `<sp-overlay>` element is used to decorate content that you would like to present to your visitors as "overlaid" on the rest of the application. This includes dialogs (modal and not), pickers, tooltips, context menus, et al.

23 lines (22 loc) 10.6 kB
"use strict";var y=Object.defineProperty;var E=Object.getOwnPropertyDescriptor;var r=(m,l,e,t)=>{for(var i=t>1?void 0:t?E(l,e):l,s=m.length-1,a;s>=0;s--)(a=m[s])&&(i=(t?a(l,e,i):a(i))||i);return t&&i&&y(l,e,i),i};import{html as c}from"@spectrum-web-components/base";import{property as n,query as g,queryAssignedElements as w,state as C}from"@spectrum-web-components/base/src/decorators.js";import{ElementResolutionController as T,elementResolverUpdatedSymbol as P}from"@spectrum-web-components/reactive-controllers/src/ElementResolution.js";import{ifDefined as d,styleMap as S}from"@spectrum-web-components/base/src/directives.js";import{randomID as O}from"@spectrum-web-components/shared/src/random-id.js";import{AbstractOverlay as f,nextFrame as u}from"./AbstractOverlay.js";import{OverlayPopover as L}from"./OverlayPopover.js";import{OverlayNoPopover as R}from"./OverlayNoPopover.js";import{overlayStack as v}from"./OverlayStack.js";import{VirtualTrigger as k}from"./VirtualTrigger.js";import{PlacementController as H}from"./PlacementController.js";export{LONGPRESS_INSTRUCTIONS}from"./LongpressController.js";import{strategies as M}from"./strategies.js";import{removeSlottableRequest as F,SlottableRequestEvent as N}from"./slottable-request-event.js";import $ from"./overlay.css.js";const q="showPopover"in document.createElement("div");let b=L(f);q||(b=R(f));const o=class o extends b{constructor(){super(...arguments);this._delayed=!1;this._disabled=!1;this.offset=0;this._open=!1;this.lastRequestSlottableState=!1;this.receivesFocus="auto";this.allowOutsideClick=!1;this._state="closed";this.triggerElement=null;this.type="auto";this.wasOpen=!1;this._focusTrap=null;this.closeOnFocusOut=e=>{if(!e.relatedTarget)return;const t=new Event("overlay-relation-query",{bubbles:!0,composed:!0});e.relatedTarget.addEventListener(t.type,i=>{i.composedPath().some(p=>p===this)||(this.open=!1)}),e.relatedTarget.dispatchEvent(t)};this.closeOnCancelEvent=()=>{this.open=!1}}get delayed(){var e;return((e=this.elements.at(-1))==null?void 0:e.hasAttribute("delayed"))||this._delayed}set delayed(e){this._delayed=e}get disabled(){return this._disabled}set disabled(e){var t;this._disabled=e,e?((t=this.strategy)==null||t.abort(),this.wasOpen=this.open,this.open=!1):(this.bindEvents(),this.open=this.open||this.wasOpen,this.wasOpen=!1)}get hasNonVirtualTrigger(){return!!this.triggerElement&&!(this.triggerElement instanceof k)}get placementController(){return this._placementController||(this._placementController=new H(this)),this._placementController}get open(){return this._open}set open(e){var t;e&&this.disabled||e!==this.open&&((t=this.strategy)!=null&&t.activelyOpening&&!e||(this._open=e,this.open&&(o.openCount+=1),this.requestUpdate("open",!this.open),this.open&&this.requestSlottable()))}get state(){return this._state}set state(e){var i;if(e===this.state)return;const t=this.state;this._state=e,(this.state==="opened"||this.state==="closed")&&((i=this.strategy)==null||i.shouldCompleteOpen()),this.requestUpdate("state",t)}get elementResolver(){return this._elementResolver||(this._elementResolver=new T(this)),this._elementResolver}get popoverValue(){if("popover"in this)switch(this.type){case"modal":return"auto";case"page":return"manual";case"hint":return"manual";default:return this.type}}get requiresPositioning(){return!(this.type==="page"||!this.open||!this.triggerElement||!this.placement&&this.type!=="hint")}managePosition(){if(!this.requiresPositioning||!this.open)return;const e=this.offset||0,t=this.triggerElement,i=this.placement||"right",s=this.tipPadding;this.placementController.placeOverlay(this.dialogEl,{offset:e,placement:i,tipPadding:s,trigger:t,type:this.type})}async managePopoverOpen(){super.managePopoverOpen();const e=this.open;if(this.open!==e||(await this.manageDelay(e),this.open!==e)||(this.triggerInteraction==="longpress"&&await u(),await this.ensureOnDOM(e),this.open!==e))return;const t=await this.makeTransition(e);if(this.open===e){if(e){const i=await import("focus-trap");this._focusTrap=i.createFocusTrap(this.dialogEl,{initialFocus:t||void 0,tabbableOptions:{getShadowRoot:!0},fallbackFocus:()=>(this.dialogEl.setAttribute("tabIndex","-1"),this.dialogEl),escapeDeactivates:!1,allowOutsideClick:this.allowOutsideClick}),(this.type==="modal"||this.type==="page")&&this._focusTrap.activate()}await this.applyFocus(e,t)}}async applyFocus(e,t){if(!(this.receivesFocus==="false"||this.type==="hint")){if(await u(),await u(),e===this.open&&!this.open){this.hasNonVirtualTrigger&&this.contains(this.getRootNode().activeElement)&&this.triggerElement.focus();return}t==null||t.focus()}}returnFocus(){var t;if(this.open||this.type==="hint")return;const e=()=>{var a,p;const i=[];let s=document.activeElement;for(;(a=s==null?void 0:s.shadowRoot)!=null&&a.activeElement;)s=s.shadowRoot.activeElement;for(;s;){const h=s.assignedSlot||s.parentElement||((p=s.getRootNode())==null?void 0:p.host);h&&i.push(h),s=h}return i};this.receivesFocus!=="false"&&((t=this.triggerElement)!=null&&t.focus)&&(this.contains(this.getRootNode().activeElement)||e().includes(this)||document.activeElement===document.body)&&this.triggerElement.focus()}async manageOpen(e){var i;if(!this.isConnected&&this.open)return;this.hasUpdated||await this.updateComplete,this.open?(v.add(this),this.willPreventClose&&(document.addEventListener("pointerup",()=>{this.dialogEl.classList.toggle("not-immediately-closable",!1),this.willPreventClose=!1},{once:!0}),this.dialogEl.classList.toggle("not-immediately-closable",!0))):(e&&((i=this._focusTrap)==null||i.deactivate(),this._focusTrap=null,this.dispose()),v.remove(this)),this.open&&this.state!=="opened"?this.state="opening":!this.open&&this.state!=="closed"&&(this.state="closing"),this.managePopoverOpen();const t=this.getRootNode();this.type==="auto"&&(this.open?t.addEventListener("focusout",this.closeOnFocusOut,{capture:!0}):t.removeEventListener("focusout",this.closeOnFocusOut,{capture:!0})),(this.type==="modal"||this.type==="page")&&(this.open?t.addEventListener("cancel",this.closeOnCancelEvent,{capture:!0}):t.removeEventListener("cancel",this.closeOnCancelEvent,{capture:!0}))}bindEvents(){var e;(e=this.strategy)==null||e.abort(),this.strategy=void 0,this.hasNonVirtualTrigger&&this.triggerInteraction&&(this.strategy=new M[this.triggerInteraction](this.triggerElement,{overlay:this}))}handleBeforetoggle(e){e.newState!=="open"&&this.handleBrowserClose(e)}handleBrowserClose(e){var t;if(e.stopPropagation(),!((t=this.strategy)!=null&&t.activelyOpening)){this.open=!1;return}this.manuallyKeepOpen()}manuallyKeepOpen(){this.open=!0,this.placementController.allowPlacementUpdate=!0,this.manageOpen(!1)}handleSlotchange(){var e,t;this.elements.length?this.hasNonVirtualTrigger&&((t=this.strategy)==null||t.prepareDescription(this.triggerElement)):(e=this.strategy)==null||e.releaseDescription()}shouldPreventClose(){const e=this.willPreventClose;return this.willPreventClose=!1,e}requestSlottable(){this.lastRequestSlottableState!==this.open&&(this.open||document.body.offsetHeight,this.dispatchEvent(new N("overlay-content",this.open?{}:F)),this.lastRequestSlottableState=this.open)}willUpdate(e){var i;if(this.hasAttribute("id")||this.setAttribute("id",`${this.tagName.toLowerCase()}-${O()}`),e.has("allowOutsideClick")&&this.allowOutsideClick&&console.warn(`[${this.localName}] The "allow-outside-click" attribute has been deprecated and will be removed in a future release. We do not recommend using this attribute for accessibility reasons. It allows clicks outside the overlay to close it, which can cause unexpected behavior and accessibility issues.`),e.has("open")&&(this.hasUpdated||this.open)&&this.manageOpen(e.get("open")),e.has("trigger")){const[s,a]=((i=this.trigger)==null?void 0:i.split("@"))||[];this.elementResolver.selector=s?`#${s}`:"",this.triggerInteraction=a}let t=!1;e.has(P)&&(t=this.triggerElement,this.triggerElement=this.elementResolver.element),e.has("triggerElement")&&(t=e.get("triggerElement")),t!==!1&&this.bindEvents()}updated(e){super.updated(e),e.has("placement")&&(this.placement?this.dialogEl.setAttribute("actual-placement",this.placement):this.dialogEl.removeAttribute("actual-placement"),this.open&&typeof e.get("placement")!="undefined"&&this.placementController.resetOverlayPosition()),e.has("state")&&this.state==="closed"&&typeof e.get("state")!="undefined"&&this.placementController.clearOverlayPosition()}renderContent(){return c` <slot @slotchange=${this.handleSlotchange}></slot> `}get dialogStyleMap(){return{"--swc-overlay-open-count":o.openCount.toString()}}renderPopover(){return c` <div class="dialog" part="dialog" role=${d(this.type==="modal"||this.type==="page"?"dialog":void 0)} aria-modal=${d(this.type==="modal"||this.type==="page"?"true":void 0)} placement=${d(this.requiresPositioning?this.placement||"right":void 0)} popover=${d(this.popoverValue)} style=${S(this.dialogStyleMap)} @beforetoggle=${this.handleBeforetoggle} @close=${this.handleBrowserClose} ?is-visible=${this.state!=="closed"} > ${this.renderContent()} </div> `}render(){return c` ${this.renderPopover()} <slot name="longpress-describedby-descriptor"></slot> `}connectedCallback(){super.connectedCallback(),this.addEventListener("close",()=>{this.open=!1}),this.hasUpdated&&this.bindEvents()}disconnectedCallback(){var e;(e=this.strategy)==null||e.releaseDescription(),this.open=!1,super.disconnectedCallback()}};o.styles=[$],o.openCount=1,r([n({type:Boolean})],o.prototype,"delayed",1),r([g(".dialog")],o.prototype,"dialogEl",2),r([n({type:Boolean})],o.prototype,"disabled",1),r([w({flatten:!0,selector:':not([slot="longpress-describedby-descriptor"], slot)'})],o.prototype,"elements",2),r([n({type:Number})],o.prototype,"offset",2),r([n({type:Boolean,reflect:!0})],o.prototype,"open",1),r([n()],o.prototype,"placement",2),r([n({attribute:"receives-focus"})],o.prototype,"receivesFocus",2),r([n({type:Boolean,attribute:"allow-outside-click"})],o.prototype,"allowOutsideClick",2),r([g("slot")],o.prototype,"slotEl",2),r([C()],o.prototype,"state",1),r([n({type:Number,attribute:"tip-padding"})],o.prototype,"tipPadding",2),r([n()],o.prototype,"trigger",2),r([n({attribute:!1})],o.prototype,"triggerElement",2),r([n({attribute:!1})],o.prototype,"triggerInteraction",2),r([n()],o.prototype,"type",2);export let Overlay=o; //# sourceMappingURL=Overlay.js.map