diff --git a/src/components.d.ts b/src/components.d.ts index bea93282..ef219e38 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -14,7 +14,7 @@ export namespace Components { /** * Hides the alert */ - "hide": () => Promise<boolean>; + "hide": () => Promise<void>; /** * Indicates whether or not the alert is open. You can use this in lieu of the show/hide methods. */ @@ -22,7 +22,7 @@ export namespace Components { /** * Shows the alert. */ - "show": () => Promise<boolean>; + "show": () => Promise<void>; /** * The type of alert. */ @@ -276,7 +276,7 @@ export namespace Components { /** * Hides the alert */ - "hide": () => Promise<boolean>; + "hide": () => Promise<void>; /** * Indicates whether or not the details is open. You can use this in lieu of the show/hide methods. */ @@ -284,7 +284,7 @@ export namespace Components { /** * Shows the alert. */ - "show": () => Promise<boolean>; + "show": () => Promise<void>; /** * The summary to show in the details header. If you need to display HTML, use the `summary` slot instead. */ @@ -294,7 +294,7 @@ export namespace Components { /** * Hides the dialog */ - "hide": () => Promise<boolean>; + "hide": () => Promise<void>; /** * The dialog's label as displayed in the header. You should always include a relevant label even when using `no-header`, as it is required for proper accessibility. */ @@ -310,7 +310,7 @@ export namespace Components { /** * Shows the dialog */ - "show": () => Promise<boolean>; + "show": () => Promise<void>; } interface SlDrawer { /** @@ -320,7 +320,7 @@ export namespace Components { /** * Hides the drawer */ - "hide": () => Promise<boolean>; + "hide": () => Promise<void>; /** * The drawer's label as displayed in the header. You should always include a relevant label even when using `no-header`, as it is required for proper accessibility. */ @@ -340,7 +340,7 @@ export namespace Components { /** * Shows the drawer */ - "show": () => Promise<boolean>; + "show": () => Promise<void>; } interface SlDropdown { /** @@ -358,7 +358,7 @@ export namespace Components { /** * Hides the dropdown panel */ - "hide": () => Promise<boolean>; + "hide": () => Promise<void>; /** * Indicates whether or not the dropdown is open. You can use this in lieu of the show/hide methods. */ @@ -381,7 +381,7 @@ export namespace Components { /** * Shows the dropdown panel */ - "show": () => Promise<boolean>; + "show": () => Promise<void>; /** * The distance in pixels from which to offset the panel along its trigger. */ @@ -963,7 +963,7 @@ export namespace Components { /** * Shows the tooltip. */ - "hide": () => Promise<boolean>; + "hide": () => Promise<void>; /** * Indicates whether or not the tooltip is open. You can use this in lieu of the show/hide methods. */ @@ -986,7 +986,7 @@ export namespace Components { /** * Shows the tooltip. */ - "show": () => Promise<boolean>; + "show": () => Promise<void>; /** * The distance in pixels from which to offset the tooltip along its target. */ diff --git a/src/components/alert/alert.tsx b/src/components/alert/alert.tsx index 78fa7c46..4880c1e4 100644 --- a/src/components/alert/alert.tsx +++ b/src/components/alert/alert.tsx @@ -20,6 +20,7 @@ import { Component, Element, Event, EventEmitter, Host, Method, Prop, Watch, h } }) export class Tab { alert: HTMLElement; + isShowing = false; @Element() host: HTMLSlAlertElement; @@ -64,28 +65,38 @@ export class Tab { /** Shows the alert. */ @Method() async show() { - if (this.open) return; + // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher + if (this.isShowing) { + return; + } const slShow = this.slShow.emit(); if (slShow.defaultPrevented) { - return false; + this.open = false; + return; } this.host.hidden = false; this.host.clientWidth; // force a reflow + this.isShowing = true; this.open = true; } /** Hides the alert */ @Method() async hide() { - if (!this.open) return; + // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher + if (!this.isShowing) { + return; + } const slHide = this.slHide.emit(); if (slHide.defaultPrevented) { - return false; + this.open = true; + return; } + this.isShowing = false; this.open = false; } diff --git a/src/components/details/details.tsx b/src/components/details/details.tsx index 204a0d81..ce6070a4 100644 --- a/src/components/details/details.tsx +++ b/src/components/details/details.tsx @@ -23,10 +23,11 @@ let id = 0; shadow: true }) export class Details { + body: HTMLElement; + componentId = `details-${++id}`; details: HTMLElement; header: HTMLElement; - componentId = `details-${++id}`; - body: HTMLElement; + isShowing = false; /** Indicates whether or not the details is open. You can use this in lieu of the show/hide methods. */ @Prop({ mutable: true, reflect: true }) open = false; @@ -76,11 +77,15 @@ export class Details { /** Shows the alert. */ @Method() async show() { - if (this.open) return; + // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher + if (this.isShowing) { + return; + } const slShow = this.slShow.emit(); if (slShow.defaultPrevented) { - return false; + this.open = false; + return; } if (this.body.scrollHeight === 0) { @@ -93,17 +98,22 @@ export class Details { this.body.style.overflow = 'hidden'; } + this.isShowing = true; this.open = true; } /** Hides the alert */ @Method() async hide() { - if (!this.open) return; + // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher + if (!this.isShowing) { + return; + } const slHide = this.slHide.emit(); if (slHide.defaultPrevented) { - return false; + this.open = true; + return; } // We can't transition out of `height: auto`, so let's set it to the current height first @@ -115,6 +125,7 @@ export class Details { this.body.style.height = '0'; }); + this.isShowing = false; this.open = false; } diff --git a/src/components/dialog/dialog.tsx b/src/components/dialog/dialog.tsx index 007a9b41..5b67e5a8 100644 --- a/src/components/dialog/dialog.tsx +++ b/src/components/dialog/dialog.tsx @@ -27,9 +27,10 @@ let id = 0; shadow: true }) export class Dialog { - panel: HTMLElement; - dialog: HTMLElement; componentId = `dialog-${++id}`; + dialog: HTMLElement; + isShowing = false; + panel: HTMLElement; @Element() host: HTMLSlDialogElement; @@ -99,16 +100,21 @@ export class Dialog { /** Shows the dialog */ @Method() async show() { - if (this.open) return; + // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher + if (this.isShowing) { + return; + } const slShow = this.slShow.emit(); if (slShow.defaultPrevented) { - return false; + this.open = false; + return; } this.dialog.hidden = false; this.host.clientWidth; // force a reflow - requestAnimationFrame(() => (this.open = true)); + this.isShowing = true; + this.open = true; lockBodyScrolling(this.host); document.addEventListener('focusin', this.handleDocumentFocusIn); @@ -117,13 +123,18 @@ export class Dialog { /** Hides the dialog */ @Method() async hide() { - if (!this.open) return; + // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher + if (!this.isShowing) { + return; + } const slHide = this.slHide.emit(); if (slHide.defaultPrevented) { - return false; + this.open = true; + return; } + this.isShowing = false; this.open = false; unlockBodyScrolling(this.host); diff --git a/src/components/drawer/drawer.tsx b/src/components/drawer/drawer.tsx index 7066956a..6f83d645 100644 --- a/src/components/drawer/drawer.tsx +++ b/src/components/drawer/drawer.tsx @@ -26,9 +26,10 @@ let id = 0; shadow: true }) export class Drawer { - panel: HTMLElement; - drawer: HTMLElement; componentId = `drawer-${++id}`; + drawer: HTMLElement; + isShowing = false; + panel: HTMLElement; @Element() host: HTMLSlDrawerElement; @@ -107,16 +108,21 @@ export class Drawer { /** Shows the drawer */ @Method() async show() { - if (this.open) return; + // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher + if (this.isShowing) { + return; + } const slShow = this.slShow.emit(); if (slShow.defaultPrevented) { - return false; + this.open = false; + return; } this.drawer.hidden = false; this.host.clientWidth; // force a reflow - requestAnimationFrame(() => (this.open = true)); + this.isShowing = true; + this.open = true; // Lock body scrolling only if the drawer isn't contained if (!this.contained) { @@ -129,17 +135,21 @@ export class Drawer { /** Hides the drawer */ @Method() async hide() { - if (!this.open) return; + // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher + if (!this.isShowing) { + return; + } const slHide = this.slHide.emit(); if (slHide.defaultPrevented) { - return false; + this.open = true; + return; } + this.isShowing = false; this.open = false; unlockBodyScrolling(this.host); - document.removeEventListener('focusin', this.handleDocumentFocusIn); } diff --git a/src/components/dropdown/dropdown.tsx b/src/components/dropdown/dropdown.tsx index 9a6d0cb3..c0097574 100644 --- a/src/components/dropdown/dropdown.tsx +++ b/src/components/dropdown/dropdown.tsx @@ -23,6 +23,7 @@ let id = 0; }) export class Dropdown { componentId = `dropdown-${++id}`; + isShowing = false; panel: HTMLElement; popover: Popover; trigger: HTMLElement; @@ -123,11 +124,15 @@ export class Dropdown { /** Shows the dropdown panel */ @Method() async show() { - if (this.open) return; + // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher + if (this.isShowing) { + return; + } const slShow = this.slShow.emit(); if (slShow.defaultPrevented) { - return false; + this.open = false; + return; } this.panel.addEventListener('slActivate', this.handleMenuItemActivate); @@ -135,18 +140,23 @@ export class Dropdown { document.addEventListener('mousedown', this.handleDocumentMouseDown); document.addEventListener('keydown', this.handleDocumentKeyDown); - this.popover.show(); + this.isShowing = true; this.open = true; + this.popover.show(); } /** Hides the dropdown panel */ @Method() async hide() { - if (!this.open) return; + // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher + if (!this.isShowing) { + return; + } const slHide = this.slHide.emit(); if (slHide.defaultPrevented) { - return false; + this.open = true; + return; } this.panel.removeEventListener('slActivate', this.handleMenuItemActivate); @@ -154,8 +164,9 @@ export class Dropdown { document.removeEventListener('mousedown', this.handleDocumentMouseDown); document.removeEventListener('keydown', this.handleDocumentKeyDown); - this.popover.hide(); + this.isShowing = false; this.open = false; + this.popover.hide(); } focusOnTrigger() { diff --git a/src/components/tooltip/tooltip.tsx b/src/components/tooltip/tooltip.tsx index 7fb11247..1056b092 100644 --- a/src/components/tooltip/tooltip.tsx +++ b/src/components/tooltip/tooltip.tsx @@ -19,6 +19,7 @@ let id = 0; }) export class Tooltip { componentId = `tooltip-${++id}`; + isShowing = false; popover: Popover; target: HTMLElement; tooltip: any; @@ -126,29 +127,39 @@ export class Tooltip { /** Shows the tooltip. */ @Method() async show() { - if (this.open) return; + // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher + if (this.isShowing) { + return; + } const slShow = this.slShow.emit(); if (slShow.defaultPrevented) { - return false; + this.open = false; + return; } - this.popover.show(); + this.isShowing = true; this.open = true; + this.popover.show(); } /** Shows the tooltip. */ @Method() async hide() { - if (!this.open) return; + // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher + if (!this.isShowing) { + return; + } const slHide = this.slHide.emit(); if (slHide.defaultPrevented) { - return false; + this.open = true; + return; } - this.popover.hide(); + this.isShowing = false; this.open = false; + this.popover.hide(); } getTarget() {