From 3fd2ab199151ccce982f5517d3fe9b1fb49c0ed0 Mon Sep 17 00:00:00 2001 From: Cory LaViska Date: Fri, 8 Jan 2021 10:25:29 -0500 Subject: [PATCH] Fix transitions in Safari --- docs/components/dialog.md | 2 +- docs/components/drawer.md | 4 +--- src/components/dialog/dialog.tsx | 33 +++++++++++++++++++++++++------- src/components/drawer/drawer.tsx | 33 +++++++++++++++++++++++++------- 4 files changed, 54 insertions(+), 18 deletions(-) diff --git a/docs/components/dialog.md b/docs/components/dialog.md index e57588ed..8ad0a123 100644 --- a/docs/components/dialog.md +++ b/docs/components/dialog.md @@ -132,7 +132,7 @@ By default, the dialog's panel will gain focus when opened. To set focus on a di dialog.addEventListener('sl-initial-focus', event => { event.preventDefault(); - input.setFocus() + input.setFocus({ preventScroll: true }); }); })(); diff --git a/docs/components/drawer.md b/docs/components/drawer.md index 83a3f49f..153838c5 100644 --- a/docs/components/drawer.md +++ b/docs/components/drawer.md @@ -227,10 +227,8 @@ By default, the drawer's panel will gain focus when opened. To set focus on a di closeButton.addEventListener('click', () => drawer.hide()); drawer.addEventListener('sl-initial-focus', event => { - // preventScroll is necessary for the transition to work properly, - // otherwise the drawer will appear immediately event.preventDefault(); - input.setFocus({ preventScroll: true }) + input.setFocus({ preventScroll: true }); }); })(); diff --git a/src/components/dialog/dialog.tsx b/src/components/dialog/dialog.tsx index d0a735cc..06bce13e 100644 --- a/src/components/dialog/dialog.tsx +++ b/src/components/dialog/dialog.tsx @@ -1,8 +1,11 @@ import { Component, Element, Event, EventEmitter, Method, Prop, State, Watch, h } from '@stencil/core'; import { lockBodyScrolling, unlockBodyScrolling } from '../../utilities/scroll'; import { hasSlot } from '../../utilities/slot'; +import { isPreventScrollSupported } from '../../utilities/support'; import Modal from '../../utilities/modal'; +const hasPreventScroll = isPreventScrollSupported(); + let id = 0; /** @@ -128,13 +131,29 @@ export class Dialog { lockBodyScrolling(this.host); if (this.open) { - // Wait for the next frame before setting initial focus so the dialog is technically visible - requestAnimationFrame(() => { - const slInitialFocus = this.slInitialFocus.emit(); - if (!slInitialFocus.defaultPrevented) { - this.panel.focus({ preventScroll: true }); - } - }); + if (hasPreventScroll) { + // Wait for the next frame before setting initial focus so the dialog is technically visible + requestAnimationFrame(() => { + const slInitialFocus = this.slInitialFocus.emit(); + if (!slInitialFocus.defaultPrevented) { + this.panel.focus({ preventScroll: true }); + } + }); + } else { + // Once Safari supports { preventScroll: true } we can remove this nasty little hack, but until then we need to + // wait for the transition to complete before setting focus, otherwise the panel may render in a buggy way its + // out of view initially. + // + // Fiddle: https://jsfiddle.net/g6buoafq/1/ + // Safari: https://bugs.webkit.org/show_bug.cgi?id=178583 + // + setTimeout(() => { + const slInitialFocus = this.slInitialFocus.emit(); + if (!slInitialFocus.defaultPrevented) { + this.panel.focus(); + } + }, 250); + } } } diff --git a/src/components/drawer/drawer.tsx b/src/components/drawer/drawer.tsx index acb912a1..9d460b29 100644 --- a/src/components/drawer/drawer.tsx +++ b/src/components/drawer/drawer.tsx @@ -1,8 +1,11 @@ import { Component, Element, Event, EventEmitter, Method, Prop, State, Watch, h } from '@stencil/core'; import { lockBodyScrolling, unlockBodyScrolling } from '../../utilities/scroll'; import { hasSlot } from '../../utilities/slot'; +import { isPreventScrollSupported } from '../../utilities/support'; import Modal from '../../utilities/modal'; +const hasPreventScroll = isPreventScrollSupported(); + let id = 0; /** @@ -139,13 +142,29 @@ export class Drawer { } if (this.open) { - // Wait for the next frame before setting initial focus so the drawer is technically visible - requestAnimationFrame(() => { - const slInitialFocus = this.slInitialFocus.emit(); - if (!slInitialFocus.defaultPrevented) { - this.panel.focus({ preventScroll: true }); - } - }); + if (hasPreventScroll) { + // Wait for the next frame before setting initial focus so the dialog is technically visible + requestAnimationFrame(() => { + const slInitialFocus = this.slInitialFocus.emit(); + if (!slInitialFocus.defaultPrevented) { + this.panel.focus({ preventScroll: true }); + } + }); + } else { + // Once Safari supports { preventScroll: true } we can remove this nasty little hack, but until then we need to + // wait for the transition to complete before setting focus, otherwise the panel may render in a buggy way its + // out of view initially. + // + // Fiddle: https://jsfiddle.net/g6buoafq/1/ + // Safari: https://bugs.webkit.org/show_bug.cgi?id=178583 + // + setTimeout(() => { + const slInitialFocus = this.slInitialFocus.emit(); + if (!slInitialFocus.defaultPrevented) { + this.panel.focus(); + } + }, 250); + } } }