@spectrum-web-components/overlay
Advanced tools
Comparing version
{ | ||
"name": "@spectrum-web-components/overlay", | ||
"version": "1.2.0-beta.16", | ||
"version": "1.2.0-beta.17", | ||
"publishConfig": { | ||
@@ -163,7 +163,7 @@ "access": "public" | ||
"@floating-ui/utils": "^0.2.1", | ||
"@spectrum-web-components/action-button": "^1.2.0-beta.16", | ||
"@spectrum-web-components/base": "^1.2.0-beta.16", | ||
"@spectrum-web-components/reactive-controllers": "^1.2.0-beta.16", | ||
"@spectrum-web-components/shared": "^1.2.0-beta.16", | ||
"@spectrum-web-components/theme": "^1.2.0-beta.16" | ||
"@spectrum-web-components/action-button": "^1.2.0-beta.17", | ||
"@spectrum-web-components/base": "^1.2.0-beta.17", | ||
"@spectrum-web-components/reactive-controllers": "^1.2.0-beta.17", | ||
"@spectrum-web-components/shared": "^1.2.0-beta.17", | ||
"@spectrum-web-components/theme": "^1.2.0-beta.17" | ||
}, | ||
@@ -180,3 +180,3 @@ "types": "./src/index.d.ts", | ||
], | ||
"gitHead": "c5ff77aaedeae8c87b1798471551b098c3878a84" | ||
"gitHead": "2beef5b4a5d94174350dc1946f8f357ba99f3bc4" | ||
} |
@@ -5,18 +5,43 @@ import { CSSResultArray, PropertyValues, SpectrumElement, TemplateResult } from '@spectrum-web-components/base'; | ||
import type { OverlayTriggerInteractions } from './overlay-types'; | ||
import '@spectrum-web-components/overlay/sp-overlay.js'; | ||
export type OverlayContentTypes = 'click' | 'hover' | 'longpress'; | ||
type Combinations<T extends string, U extends string = T> = T extends string ? T | `${T} ${Combinations<Exclude<U, T>>}` : never; | ||
export type TriggeredByType = Combinations<OverlayContentTypes>; | ||
/** | ||
* @element overlay-trigger | ||
* | ||
* A component that manages overlay content triggered by different interactions. | ||
* Supports click, hover, and longpress triggered overlays with configurable | ||
* placement and behavior. | ||
* | ||
* @slot trigger - The content that will trigger the various overlays | ||
* @slot hover-content - The content that will be displayed on hover | ||
* @slot click-content - The content that will be displayed on click | ||
* @slot longpress-content - The content that will be displayed on click | ||
* @slot longpress-content - The content that will be displayed on longpress | ||
* @slot longpress-describedby-descriptor - Description for longpress content | ||
* | ||
* @fires sp-opened - Announces that the overlay has been opened | ||
* @fires sp-closed - Announces that the overlay has been closed | ||
* | ||
* @attr {string} placement - The placement of the overlay relative to the trigger | ||
* @attr {number} offset - The distance between the overlay and the trigger | ||
* @attr {boolean} disabled - Whether the overlay trigger is disabled | ||
* @attr {string} receives-focus - How focus should be handled ('true'|'false'|'auto') | ||
*/ | ||
export declare class OverlayTrigger extends SpectrumElement { | ||
static get styles(): CSSResultArray; | ||
content: string; | ||
/** | ||
* Optional property to optimize performance and prevent race conditions. | ||
* | ||
* By explicitly declaring which content types are used (e.g. "click", "longpress hover"), | ||
* we can avoid: | ||
* 1. Extra renders from unnecessary slot reparenting | ||
* 2. Potential infinite render loops during content detection | ||
* 3. Race conditions during slot assignment | ||
* | ||
* By only returning overlay wrappers for explicitly declared content types, | ||
* we minimize unecessary DOM nodes, operations and ensure a more stable rendering behavior. | ||
*/ | ||
triggeredBy?: TriggeredByType; | ||
/** | ||
* @type {"top" | "top-start" | "top-end" | "right" | "right-start" | "right-end" | "bottom" | "bottom-start" | "bottom-end" | "left" | "left-start" | "left-end"} | ||
@@ -51,4 +76,5 @@ * @attr | ||
protected render(): TemplateResult; | ||
protected updated(changes: PropertyValues): void; | ||
protected updated(changedProperties: PropertyValues): void; | ||
protected getUpdateComplete(): Promise<boolean>; | ||
} | ||
export {}; |
@@ -21,2 +21,3 @@ "use strict"; | ||
} from "@spectrum-web-components/base/src/decorators.js"; | ||
import "@spectrum-web-components/overlay/sp-overlay.js"; | ||
import overlayTriggerStyles from "./overlay-trigger.css.js"; | ||
@@ -26,3 +27,2 @@ export class OverlayTrigger extends SpectrumElement { | ||
super(...arguments); | ||
this.content = "click hover longpress"; | ||
this.offset = 6; | ||
@@ -107,8 +107,5 @@ this.disabled = false; | ||
renderClickOverlay() { | ||
import("@spectrum-web-components/overlay/sp-overlay.js"); | ||
var _a; | ||
const slot = this.renderSlot("click-content"); | ||
if (!this.clickContent.length) { | ||
return slot; | ||
} | ||
return html` | ||
const clickOverlay = html` | ||
<sp-overlay | ||
@@ -129,10 +126,15 @@ id="click-overlay" | ||
`; | ||
if ((_a = this.triggeredBy) == null ? void 0 : _a.includes("click")) { | ||
return clickOverlay; | ||
} | ||
if (!this.clickContent.length) { | ||
return slot; | ||
} else { | ||
return clickOverlay; | ||
} | ||
} | ||
renderHoverOverlay() { | ||
import("@spectrum-web-components/overlay/sp-overlay.js"); | ||
var _a; | ||
const slot = this.renderSlot("hover-content"); | ||
if (!this.hoverContent.length) { | ||
return slot; | ||
} | ||
return html` | ||
const hoverOverlay = html` | ||
<sp-overlay | ||
@@ -153,10 +155,15 @@ id="hover-overlay" | ||
`; | ||
if ((_a = this.triggeredBy) == null ? void 0 : _a.includes("hover")) { | ||
return hoverOverlay; | ||
} | ||
if (!this.hoverContent.length) { | ||
return slot; | ||
} else { | ||
return hoverOverlay; | ||
} | ||
} | ||
renderLongpressOverlay() { | ||
import("@spectrum-web-components/overlay/sp-overlay.js"); | ||
var _a; | ||
const slot = this.renderSlot("longpress-content"); | ||
if (!this.longpressContent.length) { | ||
return slot; | ||
} | ||
return html` | ||
const longpressOverlay = html` | ||
<sp-overlay | ||
@@ -178,5 +185,12 @@ id="longpress-overlay" | ||
`; | ||
if ((_a = this.triggeredBy) == null ? void 0 : _a.includes("longpress")) { | ||
return longpressOverlay; | ||
} | ||
if (!this.longpressContent.length) { | ||
return slot; | ||
} else { | ||
return longpressOverlay; | ||
} | ||
} | ||
render() { | ||
const content = this.content.split(" "); | ||
return html` | ||
@@ -189,11 +203,24 @@ <slot | ||
${[ | ||
content.includes("click") ? this.renderClickOverlay() : html``, | ||
content.includes("hover") ? this.renderHoverOverlay() : html``, | ||
content.includes("longpress") ? this.renderLongpressOverlay() : html`` | ||
this.renderClickOverlay(), | ||
this.renderHoverOverlay(), | ||
this.renderLongpressOverlay() | ||
]} | ||
`; | ||
} | ||
updated(changes) { | ||
super.updated(changes); | ||
if (this.disabled && changes.has("disabled")) { | ||
updated(changedProperties) { | ||
super.updated(changedProperties); | ||
if (!this.triggeredBy) { | ||
const issues = [ | ||
"You have not specified the `triggeredBy` property. For optimal performance, consider explicitly declaring which overlay types you plan to use.", | ||
'Example: triggered-by="click hover"', | ||
"This helps avoid unnecessary DOM operations and potential race conditions." | ||
]; | ||
window.__swc.warn( | ||
this, | ||
"Performance optimization available for <overlay-trigger>:", | ||
"https://opensource.adobe.com/spectrum-web-components/components/overlay-trigger/#performance-optimization", | ||
{ issues } | ||
); | ||
} | ||
if (this.disabled && changedProperties.has("disabled")) { | ||
this.open = void 0; | ||
@@ -209,4 +236,4 @@ return; | ||
__decorateClass([ | ||
property() | ||
], OverlayTrigger.prototype, "content", 2); | ||
property({ attribute: "triggered-by" }) | ||
], OverlayTrigger.prototype, "triggeredBy", 2); | ||
__decorateClass([ | ||
@@ -213,0 +240,0 @@ property({ reflect: true }) |
@@ -1,4 +0,4 @@ | ||
"use strict";var g=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var n=(p,a,e,o)=>{for(var t=o>1?void 0:o?v(a,e):a,l=p.length-1,i;l>=0;l--)(i=p[l])&&(t=(o?i(a,e,t):i(t))||t);return o&&t&&g(a,e,t),t};import{html as r,SpectrumElement as u}from"@spectrum-web-components/base";import{property as s,query as h,state as c}from"@spectrum-web-components/base/src/decorators.js";import f from"./overlay-trigger.css.js";export class OverlayTrigger extends u{constructor(){super(...arguments);this.content="click hover longpress";this.offset=6;this.disabled=!1;this.receivesFocus="auto";this.clickContent=[];this.longpressContent=[];this.hoverContent=[];this.targetContent=[]}static get styles(){return[f]}getAssignedElementsFromSlot(e){return e.assignedElements({flatten:!0})}handleTriggerContent(e){this.targetContent=this.getAssignedElementsFromSlot(e.target)}handleSlotContent(e){switch(e.target.name){case"click-content":this.clickContent=this.getAssignedElementsFromSlot(e.target);break;case"longpress-content":this.longpressContent=this.getAssignedElementsFromSlot(e.target);break;case"hover-content":this.hoverContent=this.getAssignedElementsFromSlot(e.target);break}}handleBeforetoggle(e){const{target:o}=e;let t;if(o===this.clickOverlayElement)t="click";else if(o===this.longpressOverlayElement)t="longpress";else if(o===this.hoverOverlayElement)t="hover";else return;e.newState==="open"?this.open=t:this.open===t&&(this.open=void 0)}update(e){var o,t,l,i,d,m;e.has("clickContent")&&(this.clickPlacement=((o=this.clickContent[0])==null?void 0:o.getAttribute("placement"))||((t=this.clickContent[0])==null?void 0:t.getAttribute("direction"))||void 0),e.has("hoverContent")&&(this.hoverPlacement=((l=this.hoverContent[0])==null?void 0:l.getAttribute("placement"))||((i=this.hoverContent[0])==null?void 0:i.getAttribute("direction"))||void 0),e.has("longpressContent")&&(this.longpressPlacement=((d=this.longpressContent[0])==null?void 0:d.getAttribute("placement"))||((m=this.longpressContent[0])==null?void 0:m.getAttribute("direction"))||void 0),super.update(e)}renderSlot(e){return r` | ||
"use strict";var m=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var n=(p,l,e,r)=>{for(var t=r>1?void 0:r?u(l,e):l,s=p.length-1,i;s>=0;s--)(i=p[s])&&(t=(r?i(l,e,t):i(t))||t);return r&&t&&m(l,e,t),t};import{html as a,SpectrumElement as v}from"@spectrum-web-components/base";import{property as o,query as h,state as c}from"@spectrum-web-components/base/src/decorators.js";import"@spectrum-web-components/overlay/sp-overlay.js";import y from"./overlay-trigger.css.js";export class OverlayTrigger extends v{constructor(){super(...arguments);this.offset=6;this.disabled=!1;this.receivesFocus="auto";this.clickContent=[];this.longpressContent=[];this.hoverContent=[];this.targetContent=[]}static get styles(){return[y]}getAssignedElementsFromSlot(e){return e.assignedElements({flatten:!0})}handleTriggerContent(e){this.targetContent=this.getAssignedElementsFromSlot(e.target)}handleSlotContent(e){switch(e.target.name){case"click-content":this.clickContent=this.getAssignedElementsFromSlot(e.target);break;case"longpress-content":this.longpressContent=this.getAssignedElementsFromSlot(e.target);break;case"hover-content":this.hoverContent=this.getAssignedElementsFromSlot(e.target);break}}handleBeforetoggle(e){const{target:r}=e;let t;if(r===this.clickOverlayElement)t="click";else if(r===this.longpressOverlayElement)t="longpress";else if(r===this.hoverOverlayElement)t="hover";else return;e.newState==="open"?this.open=t:this.open===t&&(this.open=void 0)}update(e){var r,t,s,i,d,g;e.has("clickContent")&&(this.clickPlacement=((r=this.clickContent[0])==null?void 0:r.getAttribute("placement"))||((t=this.clickContent[0])==null?void 0:t.getAttribute("direction"))||void 0),e.has("hoverContent")&&(this.hoverPlacement=((s=this.hoverContent[0])==null?void 0:s.getAttribute("placement"))||((i=this.hoverContent[0])==null?void 0:i.getAttribute("direction"))||void 0),e.has("longpressContent")&&(this.longpressPlacement=((d=this.longpressContent[0])==null?void 0:d.getAttribute("placement"))||((g=this.longpressContent[0])==null?void 0:g.getAttribute("direction"))||void 0),super.update(e)}renderSlot(e){return a` | ||
<slot name=${e} @slotchange=${this.handleSlotContent}></slot> | ||
`}renderClickOverlay(){import("@spectrum-web-components/overlay/sp-overlay.js");const e=this.renderSlot("click-content");return this.clickContent.length?r` | ||
`}renderClickOverlay(){var t;const e=this.renderSlot("click-content"),r=a` | ||
<sp-overlay | ||
@@ -18,3 +18,3 @@ id="click-overlay" | ||
</sp-overlay> | ||
`:e}renderHoverOverlay(){import("@spectrum-web-components/overlay/sp-overlay.js");const e=this.renderSlot("hover-content");return this.hoverContent.length?r` | ||
`;return(t=this.triggeredBy)!=null&&t.includes("click")||this.clickContent.length?r:e}renderHoverOverlay(){var t;const e=this.renderSlot("hover-content"),r=a` | ||
<sp-overlay | ||
@@ -34,3 +34,3 @@ id="hover-overlay" | ||
</sp-overlay> | ||
`:e}renderLongpressOverlay(){import("@spectrum-web-components/overlay/sp-overlay.js");const e=this.renderSlot("longpress-content");return this.longpressContent.length?r` | ||
`;return(t=this.triggeredBy)!=null&&t.includes("hover")||this.hoverContent.length?r:e}renderLongpressOverlay(){var t;const e=this.renderSlot("longpress-content"),r=a` | ||
<sp-overlay | ||
@@ -51,3 +51,3 @@ id="longpress-overlay" | ||
<slot name="longpress-describedby-descriptor"></slot> | ||
`:e}render(){const e=this.content.split(" ");return r` | ||
`;return(t=this.triggeredBy)!=null&&t.includes("longpress")||this.longpressContent.length?r:e}render(){return a` | ||
<slot | ||
@@ -58,4 +58,4 @@ id="trigger" | ||
></slot> | ||
${[e.includes("click")?this.renderClickOverlay():r``,e.includes("hover")?this.renderHoverOverlay():r``,e.includes("longpress")?this.renderLongpressOverlay():r``]} | ||
`}updated(e){if(super.updated(e),this.disabled&&e.has("disabled")){this.open=void 0;return}}async getUpdateComplete(){return await super.getUpdateComplete()}}n([s()],OverlayTrigger.prototype,"content",2),n([s({reflect:!0})],OverlayTrigger.prototype,"placement",2),n([s()],OverlayTrigger.prototype,"type",2),n([s({type:Number})],OverlayTrigger.prototype,"offset",2),n([s({reflect:!0})],OverlayTrigger.prototype,"open",2),n([s({type:Boolean,reflect:!0})],OverlayTrigger.prototype,"disabled",2),n([s({attribute:"receives-focus"})],OverlayTrigger.prototype,"receivesFocus",2),n([c()],OverlayTrigger.prototype,"clickContent",2),n([c()],OverlayTrigger.prototype,"longpressContent",2),n([c()],OverlayTrigger.prototype,"hoverContent",2),n([c()],OverlayTrigger.prototype,"targetContent",2),n([h("#click-overlay",!0)],OverlayTrigger.prototype,"clickOverlayElement",2),n([h("#longpress-overlay",!0)],OverlayTrigger.prototype,"longpressOverlayElement",2),n([h("#hover-overlay",!0)],OverlayTrigger.prototype,"hoverOverlayElement",2); | ||
${[this.renderClickOverlay(),this.renderHoverOverlay(),this.renderLongpressOverlay()]} | ||
`}updated(e){if(super.updated(e),this.disabled&&e.has("disabled")){this.open=void 0;return}}async getUpdateComplete(){return await super.getUpdateComplete()}}n([o({attribute:"triggered-by"})],OverlayTrigger.prototype,"triggeredBy",2),n([o({reflect:!0})],OverlayTrigger.prototype,"placement",2),n([o()],OverlayTrigger.prototype,"type",2),n([o({type:Number})],OverlayTrigger.prototype,"offset",2),n([o({reflect:!0})],OverlayTrigger.prototype,"open",2),n([o({type:Boolean,reflect:!0})],OverlayTrigger.prototype,"disabled",2),n([o({attribute:"receives-focus"})],OverlayTrigger.prototype,"receivesFocus",2),n([c()],OverlayTrigger.prototype,"clickContent",2),n([c()],OverlayTrigger.prototype,"longpressContent",2),n([c()],OverlayTrigger.prototype,"hoverContent",2),n([c()],OverlayTrigger.prototype,"targetContent",2),n([h("#click-overlay",!0)],OverlayTrigger.prototype,"clickOverlayElement",2),n([h("#longpress-overlay",!0)],OverlayTrigger.prototype,"longpressOverlayElement",2),n([h("#hover-overlay",!0)],OverlayTrigger.prototype,"hoverOverlayElement",2); | ||
//# sourceMappingURL=OverlayTrigger.js.map |
@@ -128,3 +128,3 @@ "use strict"; | ||
<overlay-trigger | ||
content="click hover" | ||
triggered-by="click hover" | ||
id="trigger" | ||
@@ -823,3 +823,3 @@ placement="${placement}" | ||
return html` | ||
<overlay-trigger placement="right-start"> | ||
<overlay-trigger triggered-by="longpress" placement="right-start"> | ||
<sp-action-button slot="trigger" hold-affordance> | ||
@@ -1414,2 +1414,59 @@ <sp-icon-magnify slot="icon"></sp-icon-magnify> | ||
}; | ||
export const triggeredByOptimization = () => { | ||
return html` | ||
<h2>"triggered-by" attribute optimization</h2> | ||
<p> | ||
This demo shows different ways to trigger overlays using the | ||
<code>triggered-by</code> | ||
attribute. | ||
</p> | ||
<p> | ||
<strong>Pro tip:</strong> | ||
Inspect the DOM to verify that only the respective overlay elements | ||
are being rendered into the DOM based on the | ||
<code>triggered-by</code> | ||
value. | ||
</p> | ||
<p> | ||
Unused interaction types aren't rendered. This improves performance, | ||
reduces the number of unecessary DOM nodes and avoids race | ||
conditions in slot reparenting. | ||
</p> | ||
<div style="display: flex; gap: 20px; flex-direction: column;"> | ||
<!-- Click and hover only --> | ||
<overlay-trigger triggered-by="click hover"> | ||
<sp-button slot="trigger">Click and hover trigger</sp-button> | ||
<sp-popover slot="click-content" direction="right" tip> | ||
<sp-dialog size="s" no-divider>Click content</sp-dialog> | ||
</sp-popover> | ||
<sp-tooltip slot="hover-content">Hover content</sp-tooltip> | ||
</overlay-trigger> | ||
<!-- Longpress only --> | ||
<overlay-trigger triggered-by="longpress"> | ||
<sp-button slot="trigger">Longpress trigger</sp-button> | ||
<sp-popover slot="longpress-content" direction="right" tip> | ||
<sp-dialog size="s" no-divider>Longpress content</sp-dialog> | ||
</sp-popover> | ||
<div slot="longpress-describedby-descriptor"> | ||
Press and hold to reveal more options | ||
</div> | ||
</overlay-trigger> | ||
<!-- Click only --> | ||
<overlay-trigger triggered-by="click"> | ||
<sp-button slot="trigger">Click only trigger</sp-button> | ||
<sp-popover slot="click-content" direction="right" tip> | ||
<sp-dialog size="s" no-divider>Click content</sp-dialog> | ||
</sp-popover> | ||
</overlay-trigger> | ||
<!-- Hover only --> | ||
<overlay-trigger triggered-by="hover"> | ||
<sp-button slot="trigger">Hover only trigger</sp-button> | ||
<sp-tooltip slot="hover-content">Hover content</sp-tooltip> | ||
</overlay-trigger> | ||
</div> | ||
`; | ||
}; | ||
//# sourceMappingURL=overlay.stories.js.map |
@@ -16,3 +16,3 @@ "use strict"; | ||
); | ||
el.content = "click"; | ||
el.triggeredBy = "click"; | ||
await elementUpdated(item); | ||
@@ -19,0 +19,0 @@ const opened = oneEvent(el, "sp-opened"); |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 9 instances in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 8 instances in 1 package
1561266
1.94%211
0.96%16383
1.57%82
1.23%