@vaadin/app-layout
Advanced tools
Comparing version 24.7.0-alpha1 to 24.7.0-alpha2
{ | ||
"name": "@vaadin/app-layout", | ||
"version": "24.7.0-alpha1", | ||
"version": "24.7.0-alpha2", | ||
"publishConfig": { | ||
@@ -38,13 +38,14 @@ "access": "public" | ||
"dependencies": { | ||
"@open-wc/dedupe-mixin": "^1.3.0", | ||
"@polymer/polymer": "^3.0.0", | ||
"@vaadin/a11y-base": "24.7.0-alpha1", | ||
"@vaadin/button": "24.7.0-alpha1", | ||
"@vaadin/component-base": "24.7.0-alpha1", | ||
"@vaadin/vaadin-lumo-styles": "24.7.0-alpha1", | ||
"@vaadin/vaadin-material-styles": "24.7.0-alpha1", | ||
"@vaadin/vaadin-themable-mixin": "24.7.0-alpha1", | ||
"@vaadin/a11y-base": "24.7.0-alpha2", | ||
"@vaadin/button": "24.7.0-alpha2", | ||
"@vaadin/component-base": "24.7.0-alpha2", | ||
"@vaadin/vaadin-lumo-styles": "24.7.0-alpha2", | ||
"@vaadin/vaadin-material-styles": "24.7.0-alpha2", | ||
"@vaadin/vaadin-themable-mixin": "24.7.0-alpha2", | ||
"lit": "^3.0.0" | ||
}, | ||
"devDependencies": { | ||
"@vaadin/chai-plugins": "24.7.0-alpha1", | ||
"@vaadin/chai-plugins": "24.7.0-alpha2", | ||
"@vaadin/testing-helpers": "^1.0.0", | ||
@@ -57,3 +58,3 @@ "sinon": "^18.0.0" | ||
], | ||
"gitHead": "04be941c9a7b659871c97f31b9cc3ffd7528087b" | ||
"gitHead": "e2523f9b4abc5a9586fb758166f823dc40c399dd" | ||
} |
@@ -9,6 +9,5 @@ /** | ||
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js'; | ||
import { AppLayoutMixin } from './vaadin-app-layout-mixin.js'; | ||
export interface AppLayoutI18n { | ||
drawer: string; | ||
} | ||
export type { AppLayoutI18n } from './vaadin-app-layout-mixin.js'; | ||
@@ -127,3 +126,3 @@ /** | ||
*/ | ||
declare class AppLayout extends ElementMixin(ThemableMixin(ControllerMixin(HTMLElement))) { | ||
declare class AppLayout extends AppLayoutMixin(ElementMixin(ThemableMixin(ControllerMixin(HTMLElement)))) { | ||
/** | ||
@@ -134,55 +133,2 @@ * Helper static method that dispatches a `close-overlay-drawer` event | ||
/** | ||
* The object used to localize this component. | ||
* To change the default localization, replace the entire | ||
* `i18n` object with a custom one. | ||
* | ||
* To update individual properties, extend the existing i18n object as follows: | ||
* ```js | ||
* appLayout.i18n = { | ||
* ...appLayout.i18n, | ||
* drawer: 'Drawer' | ||
* } | ||
* ``` | ||
* | ||
* The object has the following structure and default values: | ||
* ``` | ||
* { | ||
* drawer: 'Drawer' | ||
* } | ||
* ``` | ||
*/ | ||
i18n: AppLayoutI18n; | ||
/** | ||
* Defines whether navbar or drawer will come first visually. | ||
* - By default (`primary-section="navbar"`), the navbar takes the full available width and moves the drawer down. | ||
* - If `primary-section="drawer"` is set, then the drawer will move the navbar, taking the full available height. | ||
* @attr {navbar|drawer} primary-section | ||
*/ | ||
primarySection: 'drawer' | 'navbar'; | ||
/** | ||
* Controls whether the drawer is opened (visible) or not. | ||
* Its default value depends on the viewport: | ||
* - `true`, for desktop size views | ||
* - `false`, for mobile size views | ||
* @attr {boolean} drawer-opened | ||
*/ | ||
drawerOpened: boolean; | ||
/** | ||
* Drawer is an overlay on top of the content | ||
* Controlled via CSS using `--vaadin-app-layout-drawer-overlay: true|false`; | ||
*/ | ||
readonly overlay: boolean; | ||
/** | ||
* A global event that causes the drawer to close (be hidden) when it is in overlay mode. | ||
* - The default is `vaadin-router-location-changed` dispatched by Vaadin Router | ||
* | ||
* @attr {string} close-drawer-on | ||
*/ | ||
closeDrawerOn: string; | ||
addEventListener<K extends keyof AppLayoutEventMap>( | ||
@@ -189,0 +135,0 @@ type: K, |
@@ -8,14 +8,11 @@ /** | ||
import './safe-area-inset.js'; | ||
import { afterNextRender, beforeNextRender } from '@polymer/polymer/lib/utils/render-status.js'; | ||
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js'; | ||
import { AriaModalController } from '@vaadin/a11y-base/src/aria-modal-controller.js'; | ||
import { FocusTrapController } from '@vaadin/a11y-base/src/focus-trap-controller.js'; | ||
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js'; | ||
import { defineCustomElement } from '@vaadin/component-base/src/define.js'; | ||
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js'; | ||
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js'; | ||
import { registerStyles, ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js'; | ||
import { AppLayoutMixin } from './vaadin-app-layout-mixin.js'; | ||
import { appLayoutStyles } from './vaadin-app-layout-styles.js'; | ||
/** | ||
* @typedef {import('./vaadin-app-layout.js').AppLayoutI18n} AppLayoutI18n | ||
*/ | ||
registerStyles('vaadin-app-layout', appLayoutStyles, { moduleId: 'vaadin-app-layout-styles' }); | ||
@@ -111,2 +108,3 @@ /** | ||
* @extends HTMLElement | ||
* @mixes AppLayoutMixin | ||
* @mixes ElementMixin | ||
@@ -116,164 +114,5 @@ * @mixes ThemableMixin | ||
*/ | ||
class AppLayout extends ElementMixin(ThemableMixin(ControllerMixin(PolymerElement))) { | ||
class AppLayout extends AppLayoutMixin(ElementMixin(ThemableMixin(ControllerMixin(PolymerElement)))) { | ||
static get template() { | ||
return html` | ||
<style> | ||
:host { | ||
display: block; | ||
box-sizing: border-box; | ||
height: 100%; | ||
--vaadin-app-layout-transition: 200ms; | ||
transition: padding var(--vaadin-app-layout-transition); | ||
--_vaadin-app-layout-drawer-width: var(--vaadin-app-layout-drawer-width, 16em); | ||
--vaadin-app-layout-touch-optimized: false; | ||
--vaadin-app-layout-navbar-offset-top: var(--_vaadin-app-layout-navbar-offset-size); | ||
--vaadin-app-layout-navbar-offset-bottom: var(--_vaadin-app-layout-navbar-offset-size-bottom); | ||
padding-block: var(--vaadin-app-layout-navbar-offset-top) var(--vaadin-app-layout-navbar-offset-bottom); | ||
padding-inline-start: var(--vaadin-app-layout-navbar-offset-left); | ||
} | ||
:host([hidden]), | ||
[hidden] { | ||
display: none !important; | ||
} | ||
:host([no-anim]) { | ||
--vaadin-app-layout-transition: none !important; | ||
} | ||
:host([drawer-opened]) { | ||
--vaadin-app-layout-drawer-offset-left: var(--_vaadin-app-layout-drawer-offset-size); | ||
} | ||
:host([overlay]) { | ||
--vaadin-app-layout-drawer-offset-left: 0; | ||
--vaadin-app-layout-navbar-offset-left: 0; | ||
} | ||
:host(:not([no-scroll])) [content] { | ||
overflow: auto; | ||
} | ||
[content] { | ||
height: 100%; | ||
} | ||
@media (pointer: coarse) and (max-width: 800px) and (min-height: 500px) { | ||
:host { | ||
--vaadin-app-layout-touch-optimized: true; | ||
} | ||
} | ||
[part='navbar'] { | ||
position: fixed; | ||
display: flex; | ||
align-items: center; | ||
top: 0; | ||
inset-inline: 0; | ||
transition: inset-inline-start var(--vaadin-app-layout-transition); | ||
padding-top: var(--safe-area-inset-top); | ||
padding-left: var(--safe-area-inset-left); | ||
padding-right: var(--safe-area-inset-right); | ||
z-index: 1; | ||
} | ||
:host([primary-section='drawer'][drawer-opened]:not([overlay])) [part='navbar'] { | ||
inset-inline-start: var(--vaadin-app-layout-drawer-offset-left, 0); | ||
} | ||
:host([primary-section='drawer']) [part='drawer'] { | ||
top: 0; | ||
} | ||
[part='navbar'][bottom] { | ||
top: auto; | ||
bottom: 0; | ||
padding-bottom: var(--safe-area-inset-bottom); | ||
} | ||
[part='drawer'] { | ||
overflow: auto; | ||
position: fixed; | ||
top: var(--vaadin-app-layout-navbar-offset-top, 0); | ||
bottom: var(--vaadin-app-layout-navbar-offset-bottom, var(--vaadin-viewport-offset-bottom, 0)); | ||
inset-inline: var(--vaadin-app-layout-navbar-offset-left, 0) auto; | ||
transition: | ||
transform var(--vaadin-app-layout-transition), | ||
visibility var(--vaadin-app-layout-transition); | ||
transform: translateX(-100%); | ||
max-width: 90%; | ||
width: var(--_vaadin-app-layout-drawer-width); | ||
box-sizing: border-box; | ||
padding: var(--safe-area-inset-top) 0 var(--safe-area-inset-bottom) var(--safe-area-inset-left); | ||
outline: none; | ||
/* The drawer should be inaccessible by the tabbing navigation when it is closed. */ | ||
visibility: hidden; | ||
display: flex; | ||
flex-direction: column; | ||
} | ||
:host([drawer-opened]) [part='drawer'] { | ||
/* The drawer should be accessible by the tabbing navigation when it is opened. */ | ||
visibility: visible; | ||
transform: translateX(0%); | ||
touch-action: manipulation; | ||
} | ||
[part='backdrop'] { | ||
background-color: #000; | ||
opacity: 0.3; | ||
} | ||
:host(:not([drawer-opened])) [part='backdrop'] { | ||
opacity: 0; | ||
} | ||
:host([overlay]) [part='backdrop'] { | ||
position: fixed; | ||
inset: 0; | ||
pointer-events: none; | ||
transition: opacity var(--vaadin-app-layout-transition); | ||
-webkit-tap-highlight-color: transparent; | ||
} | ||
:host([overlay]) [part='drawer'] { | ||
top: 0; | ||
bottom: 0; | ||
} | ||
:host([overlay]) [part='drawer'], | ||
:host([overlay]) [part='backdrop'] { | ||
z-index: 2; | ||
} | ||
:host([drawer-opened][overlay]) [part='backdrop'] { | ||
pointer-events: auto; | ||
touch-action: manipulation; | ||
} | ||
:host([dir='rtl']) [part='drawer'] { | ||
transform: translateX(100%); | ||
} | ||
:host([dir='rtl'][drawer-opened]) [part='drawer'] { | ||
transform: translateX(0%); | ||
} | ||
:host([drawer-opened]:not([overlay])) { | ||
padding-inline-start: var(--vaadin-app-layout-drawer-offset-left); | ||
} | ||
@media (max-width: 800px), (max-height: 600px) { | ||
:host { | ||
--vaadin-app-layout-drawer-overlay: true; | ||
--_vaadin-app-layout-drawer-width: var(--vaadin-app-layout-drawer-width, 20em); | ||
} | ||
} | ||
/* If a vaadin-scroller is used in the drawer, allow it to take all remaining space and contain scrolling */ | ||
[part='drawer'] ::slotted(vaadin-scroller) { | ||
flex: 1; | ||
overscroll-behavior: contain; | ||
} | ||
</style> | ||
<div part="navbar" id="navbarTop"> | ||
@@ -301,509 +140,2 @@ <slot name="navbar" on-slotchange="_updateTouchOptimizedMode"></slot> | ||
} | ||
static get properties() { | ||
return { | ||
/** | ||
* The object used to localize this component. | ||
* To change the default localization, replace the entire | ||
* `i18n` object with a custom one. | ||
* | ||
* To update individual properties, extend the existing i18n object as follows: | ||
* ```js | ||
* appLayout.i18n = { | ||
* ...appLayout.i18n, | ||
* drawer: 'Drawer' | ||
* } | ||
* ``` | ||
* | ||
* The object has the following structure and default values: | ||
* ``` | ||
* { | ||
* drawer: 'Drawer' | ||
* } | ||
* ``` | ||
* | ||
* @type {AppLayoutI18n} | ||
* @default {English/US} | ||
*/ | ||
i18n: { | ||
type: Object, | ||
observer: '__i18nChanged', | ||
value: () => { | ||
return { | ||
drawer: 'Drawer', | ||
}; | ||
}, | ||
}, | ||
/** | ||
* Defines whether navbar or drawer will come first visually. | ||
* - By default (`primary-section="navbar"`), the navbar takes the full available width and moves the drawer down. | ||
* - If `primary-section="drawer"` is set, then the drawer will move the navbar, taking the full available height. | ||
* @attr {navbar|drawer} primary-section | ||
* @type {!PrimarySection} | ||
*/ | ||
primarySection: { | ||
type: String, | ||
value: 'navbar', | ||
notify: true, | ||
reflectToAttribute: true, | ||
observer: '__primarySectionChanged', | ||
}, | ||
/** | ||
* Controls whether the drawer is opened (visible) or not. | ||
* Its default value depends on the viewport: | ||
* - `true`, for desktop size views | ||
* - `false`, for mobile size views | ||
* @attr {boolean} drawer-opened | ||
* @type {boolean} | ||
*/ | ||
drawerOpened: { | ||
type: Boolean, | ||
notify: true, | ||
value: true, | ||
reflectToAttribute: true, | ||
observer: '__drawerOpenedChanged', | ||
}, | ||
/** | ||
* Drawer is an overlay on top of the content | ||
* Controlled via CSS using `--vaadin-app-layout-drawer-overlay: true|false`; | ||
* @type {boolean} | ||
*/ | ||
overlay: { | ||
type: Boolean, | ||
notify: true, | ||
readOnly: true, | ||
value: false, | ||
reflectToAttribute: true, | ||
}, | ||
/** | ||
* A global event that causes the drawer to close (be hidden) when it is in overlay mode. | ||
* - The default is `vaadin-router-location-changed` dispatched by Vaadin Router | ||
* | ||
* @attr {string} close-drawer-on | ||
* @type {string} | ||
*/ | ||
closeDrawerOn: { | ||
type: String, | ||
value: 'vaadin-router-location-changed', | ||
observer: '_closeDrawerOnChanged', | ||
}, | ||
}; | ||
} | ||
/** | ||
* Helper static method that dispatches a `close-overlay-drawer` event | ||
*/ | ||
static dispatchCloseOverlayDrawerEvent() { | ||
window.dispatchEvent(new CustomEvent('close-overlay-drawer')); | ||
} | ||
constructor() { | ||
super(); | ||
// TODO(jouni): might want to debounce | ||
this.__boundResizeListener = this._resize.bind(this); | ||
this.__drawerToggleClickListener = this._drawerToggleClick.bind(this); | ||
this.__onDrawerKeyDown = this.__onDrawerKeyDown.bind(this); | ||
this.__closeOverlayDrawerListener = this.__closeOverlayDrawer.bind(this); | ||
this.__trapFocusInDrawer = this.__trapFocusInDrawer.bind(this); | ||
this.__releaseFocusFromDrawer = this.__releaseFocusFromDrawer.bind(this); | ||
// Hide all the elements except the drawer toggle and drawer content | ||
this.__ariaModalController = new AriaModalController(this, () => [ | ||
...this.querySelectorAll('vaadin-drawer-toggle, [slot="drawer"]'), | ||
]); | ||
this.__focusTrapController = new FocusTrapController(this); | ||
} | ||
/** @protected */ | ||
connectedCallback() { | ||
super.connectedCallback(); | ||
this._blockAnimationUntilAfterNextRender(); | ||
window.addEventListener('resize', this.__boundResizeListener); | ||
this.addEventListener('drawer-toggle-click', this.__drawerToggleClickListener); | ||
beforeNextRender(this, this._afterFirstRender); | ||
this._updateTouchOptimizedMode(); | ||
this._updateDrawerSize(); | ||
this._updateOverlayMode(); | ||
this._navbarSizeObserver = new ResizeObserver(() => { | ||
requestAnimationFrame(() => { | ||
// Prevent updating offset size multiple times | ||
// during the drawer open / close transition. | ||
if (this.__isDrawerAnimating) { | ||
this.__updateOffsetSizePending = true; | ||
} else { | ||
this._updateOffsetSize(); | ||
} | ||
}); | ||
}); | ||
this._navbarSizeObserver.observe(this.$.navbarTop); | ||
this._navbarSizeObserver.observe(this.$.navbarBottom); | ||
window.addEventListener('close-overlay-drawer', this.__closeOverlayDrawerListener); | ||
window.addEventListener('keydown', this.__onDrawerKeyDown); | ||
} | ||
/** @protected */ | ||
ready() { | ||
super.ready(); | ||
this.addController(this.__focusTrapController); | ||
this.__setAriaExpanded(); | ||
this.$.drawer.addEventListener('transitionstart', () => { | ||
this.__isDrawerAnimating = true; | ||
}); | ||
this.$.drawer.addEventListener('transitionend', () => { | ||
// Update offset size after drawer animation. | ||
if (this.__updateOffsetSizePending) { | ||
this.__updateOffsetSizePending = false; | ||
this._updateOffsetSize(); | ||
} | ||
// Delay resetting the flag until animation frame | ||
// to avoid updating offset size again on resize. | ||
requestAnimationFrame(() => { | ||
this.__isDrawerAnimating = false; | ||
}); | ||
}); | ||
} | ||
/** @protected */ | ||
disconnectedCallback() { | ||
super.disconnectedCallback(); | ||
window.removeEventListener('resize', this.__boundResizeListener); | ||
this.removeEventListener('drawer-toggle-click', this.__drawerToggleClickListener); | ||
window.removeEventListener('close-overlay-drawer', this.__drawerToggleClickListener); | ||
window.removeEventListener('keydown', this.__onDrawerKeyDown); | ||
} | ||
/** | ||
* A callback for the `primarySection` property observer. | ||
* | ||
* Ensures the property is set to its default value `navbar` | ||
* whenever the new value is not one of the valid values: `navbar`, `drawer`. | ||
* | ||
* @param {string} value | ||
* @private | ||
*/ | ||
__primarySectionChanged(value) { | ||
const isValid = ['navbar', 'drawer'].includes(value); | ||
if (!isValid) { | ||
this.set('primarySection', 'navbar'); | ||
} | ||
} | ||
/** | ||
* A callback for the `drawerOpened` property observer. | ||
* | ||
* When the drawer opens, the method ensures the drawer has a proper height and sets focus on it. | ||
* As long as the drawer is open, the focus is trapped within the drawer. | ||
* | ||
* When the drawer closes, the method releases focus from the drawer, setting focus on the drawer toggle. | ||
* | ||
* @param {boolean} drawerOpened | ||
* @param {boolean} oldDrawerOpened | ||
* @private | ||
*/ | ||
__drawerOpenedChanged(drawerOpened, oldDrawerOpened) { | ||
if (this.overlay) { | ||
if (drawerOpened) { | ||
this.__trapFocusInDrawer(); | ||
} else if (oldDrawerOpened) { | ||
this.__releaseFocusFromDrawer(); | ||
} | ||
} | ||
this.__setAriaExpanded(); | ||
} | ||
/** | ||
* A callback for the `i18n` property observer. | ||
* | ||
* The method ensures the drawer has ARIA attributes updated | ||
* once the `i18n` property changes. | ||
* | ||
* @private | ||
*/ | ||
__i18nChanged() { | ||
this.__updateDrawerAriaAttributes(); | ||
} | ||
/** @protected */ | ||
_afterFirstRender() { | ||
this._blockAnimationUntilAfterNextRender(); | ||
this._updateOffsetSize(); | ||
} | ||
/** @private */ | ||
_drawerToggleClick(e) { | ||
e.stopPropagation(); | ||
this.drawerOpened = !this.drawerOpened; | ||
} | ||
/** @private */ | ||
__closeOverlayDrawer() { | ||
if (this.overlay) { | ||
this.drawerOpened = false; | ||
} | ||
} | ||
/** @private */ | ||
__setAriaExpanded() { | ||
const toggle = this.querySelector('vaadin-drawer-toggle'); | ||
if (toggle) { | ||
toggle.setAttribute('aria-expanded', this.drawerOpened); | ||
} | ||
} | ||
/** @protected */ | ||
_updateDrawerSize() { | ||
const childCount = this.querySelectorAll('[slot=drawer]').length; | ||
const drawer = this.$.drawer; | ||
if (childCount === 0) { | ||
drawer.setAttribute('hidden', ''); | ||
this.style.setProperty('--_vaadin-app-layout-drawer-width', 0); | ||
} else { | ||
drawer.removeAttribute('hidden'); | ||
this.style.removeProperty('--_vaadin-app-layout-drawer-width'); | ||
} | ||
this._updateOffsetSize(); | ||
} | ||
/** @private */ | ||
_resize() { | ||
this._blockAnimationUntilAfterNextRender(); | ||
this._updateTouchOptimizedMode(); | ||
this._updateOverlayMode(); | ||
} | ||
/** @protected */ | ||
_updateOffsetSize() { | ||
const navbar = this.$.navbarTop; | ||
const navbarRect = navbar.getBoundingClientRect(); | ||
const navbarBottom = this.$.navbarBottom; | ||
const navbarBottomRect = navbarBottom.getBoundingClientRect(); | ||
const drawer = this.$.drawer; | ||
const drawerRect = drawer.getBoundingClientRect(); | ||
this.style.setProperty('--_vaadin-app-layout-navbar-offset-size', `${navbarRect.height}px`); | ||
this.style.setProperty('--_vaadin-app-layout-navbar-offset-size-bottom', `${navbarBottomRect.height}px`); | ||
this.style.setProperty('--_vaadin-app-layout-drawer-offset-size', `${drawerRect.width}px`); | ||
} | ||
/** @protected */ | ||
_updateOverlayMode() { | ||
const overlay = this._getCustomPropertyValue('--vaadin-app-layout-drawer-overlay') === 'true'; | ||
if (!this.overlay && overlay) { | ||
// Changed from not overlay to overlay | ||
this._drawerStateSaved = this.drawerOpened; | ||
this.drawerOpened = false; | ||
} | ||
this._setOverlay(overlay); | ||
if (!this.overlay && this._drawerStateSaved) { | ||
this.drawerOpened = this._drawerStateSaved; | ||
this._drawerStateSaved = null; | ||
} | ||
this.__updateDrawerAriaAttributes(); | ||
} | ||
/** | ||
* Updates ARIA attributes on the drawer depending on the drawer mode. | ||
* | ||
* - In the overlay mode, the method marks the drawer with ARIA attributes as a dialog | ||
* labelled with the `i18n.drawer` property. | ||
* - In the normal mode, the method removes the ARIA attributes that has been set for the overlay mode. | ||
* | ||
* @private | ||
*/ | ||
__updateDrawerAriaAttributes() { | ||
const drawer = this.$.drawer; | ||
if (this.overlay) { | ||
drawer.setAttribute('role', 'dialog'); | ||
drawer.setAttribute('aria-modal', 'true'); | ||
drawer.setAttribute('aria-label', this.i18n.drawer); | ||
} else { | ||
drawer.removeAttribute('role'); | ||
drawer.removeAttribute('aria-modal'); | ||
drawer.removeAttribute('aria-label'); | ||
} | ||
} | ||
/** | ||
* Returns a promise that resolves when the drawer opening/closing CSS transition ends. | ||
* | ||
* The method relies on the `--vaadin-app-layout-transition` CSS variable to detect whether | ||
* the drawer has a CSS transition that needs to be awaited. If the CSS variable equals `none`, | ||
* the promise resolves immediately. | ||
* | ||
* @return {Promise} | ||
* @private | ||
*/ | ||
__drawerTransitionComplete() { | ||
return new Promise((resolve) => { | ||
if (this._getCustomPropertyValue('--vaadin-app-layout-transition') === 'none') { | ||
resolve(); | ||
return; | ||
} | ||
this.$.drawer.addEventListener('transitionend', resolve, { once: true }); | ||
}); | ||
} | ||
/** @private */ | ||
async __trapFocusInDrawer() { | ||
// Wait for the drawer CSS transition before focusing the drawer | ||
// in order for VoiceOver to have a proper outline. | ||
await this.__drawerTransitionComplete(); | ||
if (!this.drawerOpened) { | ||
// The drawer has been closed during the CSS transition. | ||
return; | ||
} | ||
this.$.drawer.setAttribute('tabindex', '0'); | ||
this.__ariaModalController.showModal(); | ||
this.__focusTrapController.trapFocus(this.$.drawer); | ||
} | ||
/** @private */ | ||
async __releaseFocusFromDrawer() { | ||
// Wait for the drawer CSS transition in order to restore focus to the toggle | ||
// only after `visibility` becomes `hidden`, that is, the drawer becomes inaccessible by the tabbing navigation. | ||
await this.__drawerTransitionComplete(); | ||
if (this.drawerOpened) { | ||
// The drawer has been opened during the CSS transition. | ||
return; | ||
} | ||
this.__ariaModalController.close(); | ||
this.__focusTrapController.releaseFocus(); | ||
this.$.drawer.removeAttribute('tabindex'); | ||
// Move focus to the drawer toggle when closing the drawer. | ||
const toggle = this.querySelector('vaadin-drawer-toggle'); | ||
if (toggle) { | ||
toggle.focus(); | ||
toggle.setAttribute('focus-ring', 'focus'); | ||
} | ||
} | ||
/** | ||
* Closes the drawer on Escape press if it has been opened in the overlay mode. | ||
* | ||
* @param {KeyboardEvent} event | ||
* @private | ||
*/ | ||
__onDrawerKeyDown(event) { | ||
if (event.key === 'Escape' && this.overlay) { | ||
this.drawerOpened = false; | ||
} | ||
} | ||
/** @private */ | ||
_closeDrawerOnChanged(closeDrawerOn, oldCloseDrawerOn) { | ||
if (oldCloseDrawerOn) { | ||
window.removeEventListener(oldCloseDrawerOn, this.__closeOverlayDrawerListener); | ||
} | ||
if (closeDrawerOn) { | ||
window.addEventListener(closeDrawerOn, this.__closeOverlayDrawerListener); | ||
} | ||
} | ||
/** @private */ | ||
_onBackdropClick() { | ||
this._close(); | ||
} | ||
/** @private */ | ||
_onBackdropTouchend(event) { | ||
// Prevent the click event from being fired | ||
// on clickable element behind the backdrop | ||
event.preventDefault(); | ||
this._close(); | ||
} | ||
/** @protected */ | ||
_close() { | ||
this.drawerOpened = false; | ||
} | ||
/** @private */ | ||
_getCustomPropertyValue(customProperty) { | ||
const customPropertyValue = getComputedStyle(this).getPropertyValue(customProperty); | ||
return (customPropertyValue || '').trim().toLowerCase(); | ||
} | ||
/** @protected */ | ||
_updateTouchOptimizedMode() { | ||
const touchOptimized = this._getCustomPropertyValue('--vaadin-app-layout-touch-optimized') === 'true'; | ||
const navbarItems = this.querySelectorAll('[slot*="navbar"]'); | ||
if (navbarItems.length > 0) { | ||
Array.from(navbarItems).forEach((navbar) => { | ||
if (navbar.getAttribute('slot').indexOf('touch-optimized') > -1) { | ||
navbar.__touchOptimized = true; | ||
} | ||
if (touchOptimized && navbar.__touchOptimized) { | ||
navbar.setAttribute('slot', 'navbar-bottom'); | ||
} else { | ||
navbar.setAttribute('slot', 'navbar'); | ||
} | ||
}); | ||
} | ||
if (this.$.navbarTop.querySelector('[name=navbar]').assignedNodes().length === 0) { | ||
this.$.navbarTop.setAttribute('hidden', ''); | ||
} else { | ||
this.$.navbarTop.removeAttribute('hidden'); | ||
} | ||
if (this.$.navbarBottom.querySelector('[name=navbar-bottom]').assignedNodes().length === 0) { | ||
this.$.navbarBottom.setAttribute('hidden', ''); | ||
} else { | ||
this.$.navbarBottom.removeAttribute('hidden'); | ||
} | ||
this._updateOffsetSize(); | ||
} | ||
/** @protected */ | ||
_blockAnimationUntilAfterNextRender() { | ||
this.setAttribute('no-anim', ''); | ||
afterNextRender(this, () => { | ||
this.removeAttribute('no-anim'); | ||
}); | ||
} | ||
/** | ||
* App Layout listens to `close-overlay-drawer` on the window level. | ||
* A custom event can be dispatched and the App Layout will close the drawer in overlay. | ||
* | ||
* That can be used, for instance, when a navigation occurs when user clicks in a menu item inside the drawer. | ||
* | ||
* See `dispatchCloseOverlayDrawerEvent()` helper method. | ||
* | ||
* @event close-overlay-drawer | ||
*/ | ||
} | ||
@@ -810,0 +142,0 @@ |
{ | ||
"$schema": "https://json.schemastore.org/web-types", | ||
"name": "@vaadin/app-layout", | ||
"version": "24.7.0-alpha1", | ||
"version": "24.7.0-alpha2", | ||
"description-markup": "markdown", | ||
@@ -6,0 +6,0 @@ "contributions": { |
{ | ||
"$schema": "https://json.schemastore.org/web-types", | ||
"name": "@vaadin/app-layout", | ||
"version": "24.7.0-alpha1", | ||
"version": "24.7.0-alpha2", | ||
"description-markup": "markdown", | ||
@@ -6,0 +6,0 @@ "framework": "lit", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
86266
53
1778
9
+ Added@open-wc/dedupe-mixin@^1.3.0
+ Added@vaadin/a11y-base@24.7.0-alpha2(transitive)
+ Added@vaadin/button@24.7.0-alpha2(transitive)
+ Added@vaadin/component-base@24.7.0-alpha2(transitive)
+ Added@vaadin/icon@24.7.0-alpha2(transitive)
+ Added@vaadin/vaadin-lumo-styles@24.7.0-alpha2(transitive)
+ Added@vaadin/vaadin-material-styles@24.7.0-alpha2(transitive)
+ Added@vaadin/vaadin-themable-mixin@24.7.0-alpha2(transitive)
- Removed@vaadin/a11y-base@24.7.0-alpha1(transitive)
- Removed@vaadin/button@24.7.0-alpha1(transitive)
- Removed@vaadin/component-base@24.7.0-alpha1(transitive)
- Removed@vaadin/icon@24.7.0-alpha1(transitive)
- Removed@vaadin/vaadin-lumo-styles@24.7.0-alpha1(transitive)
- Removed@vaadin/vaadin-material-styles@24.7.0-alpha1(transitive)
- Removed@vaadin/vaadin-themable-mixin@24.7.0-alpha1(transitive)
Updated@vaadin/button@24.7.0-alpha2