@vaadin/app-layout 24.7.0-alpha1 → 24.7.0-alpha3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +11 -10
- package/src/vaadin-app-layout-mixin.d.ts +69 -0
- package/src/vaadin-app-layout-mixin.js +529 -0
- package/src/vaadin-app-layout-styles.d.ts +8 -0
- package/src/vaadin-app-layout-styles.js +166 -0
- package/src/vaadin-app-layout.d.ts +3 -57
- package/src/vaadin-app-layout.js +6 -674
- package/src/vaadin-lit-app-layout.d.ts +1 -0
- package/src/vaadin-lit-app-layout.js +59 -0
- package/src/vaadin-lit-drawer-toggle.d.ts +1 -0
- package/src/vaadin-lit-drawer-toggle.js +87 -0
- package/theme/lumo/vaadin-lit-app-layout.d.ts +2 -0
- package/theme/lumo/vaadin-lit-app-layout.js +2 -0
- package/theme/lumo/vaadin-lit-drawer-toggle.d.ts +2 -0
- package/theme/lumo/vaadin-lit-drawer-toggle.js +2 -0
- package/theme/material/vaadin-lit-app-layout.d.ts +2 -0
- package/theme/material/vaadin-lit-app-layout.js +2 -0
- package/theme/material/vaadin-lit-drawer-toggle.d.ts +2 -0
- package/theme/material/vaadin-lit-drawer-toggle.js +2 -0
- package/vaadin-lit-app-layout.d.ts +1 -0
- package/vaadin-lit-app-layout.js +2 -0
- package/vaadin-lit-drawer-toggle.d.ts +1 -0
- package/vaadin-lit-drawer-toggle.js +2 -0
- package/web-types.json +1 -1
- package/web-types.lit.json +1 -1
package/src/vaadin-app-layout.js
CHANGED
|
@@ -5,18 +5,15 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import './detect-ios-navbar.js';
|
|
7
7
|
import './safe-area-inset.js';
|
|
8
|
-
import { afterNextRender, beforeNextRender } from '@polymer/polymer/lib/utils/render-status.js';
|
|
9
8
|
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
10
|
-
import { AriaModalController } from '@vaadin/a11y-base/src/aria-modal-controller.js';
|
|
11
|
-
import { FocusTrapController } from '@vaadin/a11y-base/src/focus-trap-controller.js';
|
|
12
9
|
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
|
|
13
10
|
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
|
|
14
11
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
15
|
-
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
12
|
+
import { registerStyles, ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
13
|
+
import { AppLayoutMixin } from './vaadin-app-layout-mixin.js';
|
|
14
|
+
import { appLayoutStyles } from './vaadin-app-layout-styles.js';
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
* @typedef {import('./vaadin-app-layout.js').AppLayoutI18n} AppLayoutI18n
|
|
19
|
-
*/
|
|
16
|
+
registerStyles('vaadin-app-layout', appLayoutStyles, { moduleId: 'vaadin-app-layout-styles' });
|
|
20
17
|
|
|
21
18
|
/**
|
|
22
19
|
* `<vaadin-app-layout>` is a Web Component providing a quick and easy way to get a common application layout structure done.
|
|
@@ -107,172 +104,14 @@ import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mix
|
|
|
107
104
|
*
|
|
108
105
|
* @customElement
|
|
109
106
|
* @extends HTMLElement
|
|
107
|
+
* @mixes AppLayoutMixin
|
|
110
108
|
* @mixes ElementMixin
|
|
111
109
|
* @mixes ThemableMixin
|
|
112
110
|
* @mixes ControllerMixin
|
|
113
111
|
*/
|
|
114
|
-
class AppLayout extends ElementMixin(ThemableMixin(ControllerMixin(PolymerElement))) {
|
|
112
|
+
class AppLayout extends AppLayoutMixin(ElementMixin(ThemableMixin(ControllerMixin(PolymerElement)))) {
|
|
115
113
|
static get template() {
|
|
116
114
|
return html`
|
|
117
|
-
<style>
|
|
118
|
-
:host {
|
|
119
|
-
display: block;
|
|
120
|
-
box-sizing: border-box;
|
|
121
|
-
height: 100%;
|
|
122
|
-
--vaadin-app-layout-transition: 200ms;
|
|
123
|
-
transition: padding var(--vaadin-app-layout-transition);
|
|
124
|
-
--_vaadin-app-layout-drawer-width: var(--vaadin-app-layout-drawer-width, 16em);
|
|
125
|
-
--vaadin-app-layout-touch-optimized: false;
|
|
126
|
-
--vaadin-app-layout-navbar-offset-top: var(--_vaadin-app-layout-navbar-offset-size);
|
|
127
|
-
--vaadin-app-layout-navbar-offset-bottom: var(--_vaadin-app-layout-navbar-offset-size-bottom);
|
|
128
|
-
padding-block: var(--vaadin-app-layout-navbar-offset-top) var(--vaadin-app-layout-navbar-offset-bottom);
|
|
129
|
-
padding-inline-start: var(--vaadin-app-layout-navbar-offset-left);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
:host([hidden]),
|
|
133
|
-
[hidden] {
|
|
134
|
-
display: none !important;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
:host([no-anim]) {
|
|
138
|
-
--vaadin-app-layout-transition: none !important;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
:host([drawer-opened]) {
|
|
142
|
-
--vaadin-app-layout-drawer-offset-left: var(--_vaadin-app-layout-drawer-offset-size);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
:host([overlay]) {
|
|
146
|
-
--vaadin-app-layout-drawer-offset-left: 0;
|
|
147
|
-
--vaadin-app-layout-navbar-offset-left: 0;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
:host(:not([no-scroll])) [content] {
|
|
151
|
-
overflow: auto;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
[content] {
|
|
155
|
-
height: 100%;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
@media (pointer: coarse) and (max-width: 800px) and (min-height: 500px) {
|
|
159
|
-
:host {
|
|
160
|
-
--vaadin-app-layout-touch-optimized: true;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
[part='navbar'] {
|
|
165
|
-
position: fixed;
|
|
166
|
-
display: flex;
|
|
167
|
-
align-items: center;
|
|
168
|
-
top: 0;
|
|
169
|
-
inset-inline: 0;
|
|
170
|
-
transition: inset-inline-start var(--vaadin-app-layout-transition);
|
|
171
|
-
padding-top: var(--safe-area-inset-top);
|
|
172
|
-
padding-left: var(--safe-area-inset-left);
|
|
173
|
-
padding-right: var(--safe-area-inset-right);
|
|
174
|
-
z-index: 1;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
:host([primary-section='drawer'][drawer-opened]:not([overlay])) [part='navbar'] {
|
|
178
|
-
inset-inline-start: var(--vaadin-app-layout-drawer-offset-left, 0);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
:host([primary-section='drawer']) [part='drawer'] {
|
|
182
|
-
top: 0;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
[part='navbar'][bottom] {
|
|
186
|
-
top: auto;
|
|
187
|
-
bottom: 0;
|
|
188
|
-
padding-bottom: var(--safe-area-inset-bottom);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
[part='drawer'] {
|
|
192
|
-
overflow: auto;
|
|
193
|
-
position: fixed;
|
|
194
|
-
top: var(--vaadin-app-layout-navbar-offset-top, 0);
|
|
195
|
-
bottom: var(--vaadin-app-layout-navbar-offset-bottom, var(--vaadin-viewport-offset-bottom, 0));
|
|
196
|
-
inset-inline: var(--vaadin-app-layout-navbar-offset-left, 0) auto;
|
|
197
|
-
transition:
|
|
198
|
-
transform var(--vaadin-app-layout-transition),
|
|
199
|
-
visibility var(--vaadin-app-layout-transition);
|
|
200
|
-
transform: translateX(-100%);
|
|
201
|
-
max-width: 90%;
|
|
202
|
-
width: var(--_vaadin-app-layout-drawer-width);
|
|
203
|
-
box-sizing: border-box;
|
|
204
|
-
padding: var(--safe-area-inset-top) 0 var(--safe-area-inset-bottom) var(--safe-area-inset-left);
|
|
205
|
-
outline: none;
|
|
206
|
-
/* The drawer should be inaccessible by the tabbing navigation when it is closed. */
|
|
207
|
-
visibility: hidden;
|
|
208
|
-
display: flex;
|
|
209
|
-
flex-direction: column;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
:host([drawer-opened]) [part='drawer'] {
|
|
213
|
-
/* The drawer should be accessible by the tabbing navigation when it is opened. */
|
|
214
|
-
visibility: visible;
|
|
215
|
-
transform: translateX(0%);
|
|
216
|
-
touch-action: manipulation;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
[part='backdrop'] {
|
|
220
|
-
background-color: #000;
|
|
221
|
-
opacity: 0.3;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
:host(:not([drawer-opened])) [part='backdrop'] {
|
|
225
|
-
opacity: 0;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
:host([overlay]) [part='backdrop'] {
|
|
229
|
-
position: fixed;
|
|
230
|
-
inset: 0;
|
|
231
|
-
pointer-events: none;
|
|
232
|
-
transition: opacity var(--vaadin-app-layout-transition);
|
|
233
|
-
-webkit-tap-highlight-color: transparent;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
:host([overlay]) [part='drawer'] {
|
|
237
|
-
top: 0;
|
|
238
|
-
bottom: 0;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
:host([overlay]) [part='drawer'],
|
|
242
|
-
:host([overlay]) [part='backdrop'] {
|
|
243
|
-
z-index: 2;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
:host([drawer-opened][overlay]) [part='backdrop'] {
|
|
247
|
-
pointer-events: auto;
|
|
248
|
-
touch-action: manipulation;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
:host([dir='rtl']) [part='drawer'] {
|
|
252
|
-
transform: translateX(100%);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
:host([dir='rtl'][drawer-opened]) [part='drawer'] {
|
|
256
|
-
transform: translateX(0%);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
:host([drawer-opened]:not([overlay])) {
|
|
260
|
-
padding-inline-start: var(--vaadin-app-layout-drawer-offset-left);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
@media (max-width: 800px), (max-height: 600px) {
|
|
264
|
-
:host {
|
|
265
|
-
--vaadin-app-layout-drawer-overlay: true;
|
|
266
|
-
--_vaadin-app-layout-drawer-width: var(--vaadin-app-layout-drawer-width, 20em);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/* If a vaadin-scroller is used in the drawer, allow it to take all remaining space and contain scrolling */
|
|
271
|
-
[part='drawer'] ::slotted(vaadin-scroller) {
|
|
272
|
-
flex: 1;
|
|
273
|
-
overscroll-behavior: contain;
|
|
274
|
-
}
|
|
275
|
-
</style>
|
|
276
115
|
<div part="navbar" id="navbarTop">
|
|
277
116
|
<slot name="navbar" on-slotchange="_updateTouchOptimizedMode"></slot>
|
|
278
117
|
</div>
|
|
@@ -295,513 +134,6 @@ class AppLayout extends ElementMixin(ThemableMixin(ControllerMixin(PolymerElemen
|
|
|
295
134
|
static get is() {
|
|
296
135
|
return 'vaadin-app-layout';
|
|
297
136
|
}
|
|
298
|
-
|
|
299
|
-
static get properties() {
|
|
300
|
-
return {
|
|
301
|
-
/**
|
|
302
|
-
* The object used to localize this component.
|
|
303
|
-
* To change the default localization, replace the entire
|
|
304
|
-
* `i18n` object with a custom one.
|
|
305
|
-
*
|
|
306
|
-
* To update individual properties, extend the existing i18n object as follows:
|
|
307
|
-
* ```js
|
|
308
|
-
* appLayout.i18n = {
|
|
309
|
-
* ...appLayout.i18n,
|
|
310
|
-
* drawer: 'Drawer'
|
|
311
|
-
* }
|
|
312
|
-
* ```
|
|
313
|
-
*
|
|
314
|
-
* The object has the following structure and default values:
|
|
315
|
-
* ```
|
|
316
|
-
* {
|
|
317
|
-
* drawer: 'Drawer'
|
|
318
|
-
* }
|
|
319
|
-
* ```
|
|
320
|
-
*
|
|
321
|
-
* @type {AppLayoutI18n}
|
|
322
|
-
* @default {English/US}
|
|
323
|
-
*/
|
|
324
|
-
i18n: {
|
|
325
|
-
type: Object,
|
|
326
|
-
observer: '__i18nChanged',
|
|
327
|
-
value: () => {
|
|
328
|
-
return {
|
|
329
|
-
drawer: 'Drawer',
|
|
330
|
-
};
|
|
331
|
-
},
|
|
332
|
-
},
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Defines whether navbar or drawer will come first visually.
|
|
336
|
-
* - By default (`primary-section="navbar"`), the navbar takes the full available width and moves the drawer down.
|
|
337
|
-
* - If `primary-section="drawer"` is set, then the drawer will move the navbar, taking the full available height.
|
|
338
|
-
* @attr {navbar|drawer} primary-section
|
|
339
|
-
* @type {!PrimarySection}
|
|
340
|
-
*/
|
|
341
|
-
primarySection: {
|
|
342
|
-
type: String,
|
|
343
|
-
value: 'navbar',
|
|
344
|
-
notify: true,
|
|
345
|
-
reflectToAttribute: true,
|
|
346
|
-
observer: '__primarySectionChanged',
|
|
347
|
-
},
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Controls whether the drawer is opened (visible) or not.
|
|
351
|
-
* Its default value depends on the viewport:
|
|
352
|
-
* - `true`, for desktop size views
|
|
353
|
-
* - `false`, for mobile size views
|
|
354
|
-
* @attr {boolean} drawer-opened
|
|
355
|
-
* @type {boolean}
|
|
356
|
-
*/
|
|
357
|
-
drawerOpened: {
|
|
358
|
-
type: Boolean,
|
|
359
|
-
notify: true,
|
|
360
|
-
value: true,
|
|
361
|
-
reflectToAttribute: true,
|
|
362
|
-
observer: '__drawerOpenedChanged',
|
|
363
|
-
},
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Drawer is an overlay on top of the content
|
|
367
|
-
* Controlled via CSS using `--vaadin-app-layout-drawer-overlay: true|false`;
|
|
368
|
-
* @type {boolean}
|
|
369
|
-
*/
|
|
370
|
-
overlay: {
|
|
371
|
-
type: Boolean,
|
|
372
|
-
notify: true,
|
|
373
|
-
readOnly: true,
|
|
374
|
-
value: false,
|
|
375
|
-
reflectToAttribute: true,
|
|
376
|
-
},
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* A global event that causes the drawer to close (be hidden) when it is in overlay mode.
|
|
380
|
-
* - The default is `vaadin-router-location-changed` dispatched by Vaadin Router
|
|
381
|
-
*
|
|
382
|
-
* @attr {string} close-drawer-on
|
|
383
|
-
* @type {string}
|
|
384
|
-
*/
|
|
385
|
-
closeDrawerOn: {
|
|
386
|
-
type: String,
|
|
387
|
-
value: 'vaadin-router-location-changed',
|
|
388
|
-
observer: '_closeDrawerOnChanged',
|
|
389
|
-
},
|
|
390
|
-
};
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Helper static method that dispatches a `close-overlay-drawer` event
|
|
395
|
-
*/
|
|
396
|
-
static dispatchCloseOverlayDrawerEvent() {
|
|
397
|
-
window.dispatchEvent(new CustomEvent('close-overlay-drawer'));
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
constructor() {
|
|
401
|
-
super();
|
|
402
|
-
// TODO(jouni): might want to debounce
|
|
403
|
-
this.__boundResizeListener = this._resize.bind(this);
|
|
404
|
-
this.__drawerToggleClickListener = this._drawerToggleClick.bind(this);
|
|
405
|
-
this.__onDrawerKeyDown = this.__onDrawerKeyDown.bind(this);
|
|
406
|
-
this.__closeOverlayDrawerListener = this.__closeOverlayDrawer.bind(this);
|
|
407
|
-
this.__trapFocusInDrawer = this.__trapFocusInDrawer.bind(this);
|
|
408
|
-
this.__releaseFocusFromDrawer = this.__releaseFocusFromDrawer.bind(this);
|
|
409
|
-
|
|
410
|
-
// Hide all the elements except the drawer toggle and drawer content
|
|
411
|
-
this.__ariaModalController = new AriaModalController(this, () => [
|
|
412
|
-
...this.querySelectorAll('vaadin-drawer-toggle, [slot="drawer"]'),
|
|
413
|
-
]);
|
|
414
|
-
this.__focusTrapController = new FocusTrapController(this);
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
/** @protected */
|
|
418
|
-
connectedCallback() {
|
|
419
|
-
super.connectedCallback();
|
|
420
|
-
|
|
421
|
-
this._blockAnimationUntilAfterNextRender();
|
|
422
|
-
|
|
423
|
-
window.addEventListener('resize', this.__boundResizeListener);
|
|
424
|
-
this.addEventListener('drawer-toggle-click', this.__drawerToggleClickListener);
|
|
425
|
-
|
|
426
|
-
beforeNextRender(this, this._afterFirstRender);
|
|
427
|
-
|
|
428
|
-
this._updateTouchOptimizedMode();
|
|
429
|
-
this._updateDrawerSize();
|
|
430
|
-
this._updateOverlayMode();
|
|
431
|
-
|
|
432
|
-
this._navbarSizeObserver = new ResizeObserver(() => {
|
|
433
|
-
requestAnimationFrame(() => {
|
|
434
|
-
// Prevent updating offset size multiple times
|
|
435
|
-
// during the drawer open / close transition.
|
|
436
|
-
if (this.__isDrawerAnimating) {
|
|
437
|
-
this.__updateOffsetSizePending = true;
|
|
438
|
-
} else {
|
|
439
|
-
this._updateOffsetSize();
|
|
440
|
-
}
|
|
441
|
-
});
|
|
442
|
-
});
|
|
443
|
-
this._navbarSizeObserver.observe(this.$.navbarTop);
|
|
444
|
-
this._navbarSizeObserver.observe(this.$.navbarBottom);
|
|
445
|
-
|
|
446
|
-
window.addEventListener('close-overlay-drawer', this.__closeOverlayDrawerListener);
|
|
447
|
-
window.addEventListener('keydown', this.__onDrawerKeyDown);
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
/** @protected */
|
|
451
|
-
ready() {
|
|
452
|
-
super.ready();
|
|
453
|
-
this.addController(this.__focusTrapController);
|
|
454
|
-
this.__setAriaExpanded();
|
|
455
|
-
|
|
456
|
-
this.$.drawer.addEventListener('transitionstart', () => {
|
|
457
|
-
this.__isDrawerAnimating = true;
|
|
458
|
-
});
|
|
459
|
-
|
|
460
|
-
this.$.drawer.addEventListener('transitionend', () => {
|
|
461
|
-
// Update offset size after drawer animation.
|
|
462
|
-
if (this.__updateOffsetSizePending) {
|
|
463
|
-
this.__updateOffsetSizePending = false;
|
|
464
|
-
this._updateOffsetSize();
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
// Delay resetting the flag until animation frame
|
|
468
|
-
// to avoid updating offset size again on resize.
|
|
469
|
-
requestAnimationFrame(() => {
|
|
470
|
-
this.__isDrawerAnimating = false;
|
|
471
|
-
});
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
/** @protected */
|
|
476
|
-
disconnectedCallback() {
|
|
477
|
-
super.disconnectedCallback();
|
|
478
|
-
|
|
479
|
-
window.removeEventListener('resize', this.__boundResizeListener);
|
|
480
|
-
this.removeEventListener('drawer-toggle-click', this.__drawerToggleClickListener);
|
|
481
|
-
window.removeEventListener('close-overlay-drawer', this.__drawerToggleClickListener);
|
|
482
|
-
window.removeEventListener('keydown', this.__onDrawerKeyDown);
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/**
|
|
486
|
-
* A callback for the `primarySection` property observer.
|
|
487
|
-
*
|
|
488
|
-
* Ensures the property is set to its default value `navbar`
|
|
489
|
-
* whenever the new value is not one of the valid values: `navbar`, `drawer`.
|
|
490
|
-
*
|
|
491
|
-
* @param {string} value
|
|
492
|
-
* @private
|
|
493
|
-
*/
|
|
494
|
-
__primarySectionChanged(value) {
|
|
495
|
-
const isValid = ['navbar', 'drawer'].includes(value);
|
|
496
|
-
if (!isValid) {
|
|
497
|
-
this.set('primarySection', 'navbar');
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
/**
|
|
502
|
-
* A callback for the `drawerOpened` property observer.
|
|
503
|
-
*
|
|
504
|
-
* When the drawer opens, the method ensures the drawer has a proper height and sets focus on it.
|
|
505
|
-
* As long as the drawer is open, the focus is trapped within the drawer.
|
|
506
|
-
*
|
|
507
|
-
* When the drawer closes, the method releases focus from the drawer, setting focus on the drawer toggle.
|
|
508
|
-
*
|
|
509
|
-
* @param {boolean} drawerOpened
|
|
510
|
-
* @param {boolean} oldDrawerOpened
|
|
511
|
-
* @private
|
|
512
|
-
*/
|
|
513
|
-
__drawerOpenedChanged(drawerOpened, oldDrawerOpened) {
|
|
514
|
-
if (this.overlay) {
|
|
515
|
-
if (drawerOpened) {
|
|
516
|
-
this.__trapFocusInDrawer();
|
|
517
|
-
} else if (oldDrawerOpened) {
|
|
518
|
-
this.__releaseFocusFromDrawer();
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
this.__setAriaExpanded();
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
/**
|
|
526
|
-
* A callback for the `i18n` property observer.
|
|
527
|
-
*
|
|
528
|
-
* The method ensures the drawer has ARIA attributes updated
|
|
529
|
-
* once the `i18n` property changes.
|
|
530
|
-
*
|
|
531
|
-
* @private
|
|
532
|
-
*/
|
|
533
|
-
__i18nChanged() {
|
|
534
|
-
this.__updateDrawerAriaAttributes();
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
/** @protected */
|
|
538
|
-
_afterFirstRender() {
|
|
539
|
-
this._blockAnimationUntilAfterNextRender();
|
|
540
|
-
this._updateOffsetSize();
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
/** @private */
|
|
544
|
-
_drawerToggleClick(e) {
|
|
545
|
-
e.stopPropagation();
|
|
546
|
-
this.drawerOpened = !this.drawerOpened;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
/** @private */
|
|
550
|
-
__closeOverlayDrawer() {
|
|
551
|
-
if (this.overlay) {
|
|
552
|
-
this.drawerOpened = false;
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
/** @private */
|
|
557
|
-
__setAriaExpanded() {
|
|
558
|
-
const toggle = this.querySelector('vaadin-drawer-toggle');
|
|
559
|
-
if (toggle) {
|
|
560
|
-
toggle.setAttribute('aria-expanded', this.drawerOpened);
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
/** @protected */
|
|
565
|
-
_updateDrawerSize() {
|
|
566
|
-
const childCount = this.querySelectorAll('[slot=drawer]').length;
|
|
567
|
-
const drawer = this.$.drawer;
|
|
568
|
-
|
|
569
|
-
if (childCount === 0) {
|
|
570
|
-
drawer.setAttribute('hidden', '');
|
|
571
|
-
this.style.setProperty('--_vaadin-app-layout-drawer-width', 0);
|
|
572
|
-
} else {
|
|
573
|
-
drawer.removeAttribute('hidden');
|
|
574
|
-
this.style.removeProperty('--_vaadin-app-layout-drawer-width');
|
|
575
|
-
}
|
|
576
|
-
this._updateOffsetSize();
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
/** @private */
|
|
580
|
-
_resize() {
|
|
581
|
-
this._blockAnimationUntilAfterNextRender();
|
|
582
|
-
this._updateTouchOptimizedMode();
|
|
583
|
-
this._updateOverlayMode();
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
/** @protected */
|
|
587
|
-
_updateOffsetSize() {
|
|
588
|
-
const navbar = this.$.navbarTop;
|
|
589
|
-
const navbarRect = navbar.getBoundingClientRect();
|
|
590
|
-
|
|
591
|
-
const navbarBottom = this.$.navbarBottom;
|
|
592
|
-
const navbarBottomRect = navbarBottom.getBoundingClientRect();
|
|
593
|
-
|
|
594
|
-
const drawer = this.$.drawer;
|
|
595
|
-
const drawerRect = drawer.getBoundingClientRect();
|
|
596
|
-
|
|
597
|
-
this.style.setProperty('--_vaadin-app-layout-navbar-offset-size', `${navbarRect.height}px`);
|
|
598
|
-
this.style.setProperty('--_vaadin-app-layout-navbar-offset-size-bottom', `${navbarBottomRect.height}px`);
|
|
599
|
-
this.style.setProperty('--_vaadin-app-layout-drawer-offset-size', `${drawerRect.width}px`);
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
/** @protected */
|
|
603
|
-
_updateOverlayMode() {
|
|
604
|
-
const overlay = this._getCustomPropertyValue('--vaadin-app-layout-drawer-overlay') === 'true';
|
|
605
|
-
|
|
606
|
-
if (!this.overlay && overlay) {
|
|
607
|
-
// Changed from not overlay to overlay
|
|
608
|
-
this._drawerStateSaved = this.drawerOpened;
|
|
609
|
-
this.drawerOpened = false;
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
this._setOverlay(overlay);
|
|
613
|
-
|
|
614
|
-
if (!this.overlay && this._drawerStateSaved) {
|
|
615
|
-
this.drawerOpened = this._drawerStateSaved;
|
|
616
|
-
this._drawerStateSaved = null;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
this.__updateDrawerAriaAttributes();
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
/**
|
|
623
|
-
* Updates ARIA attributes on the drawer depending on the drawer mode.
|
|
624
|
-
*
|
|
625
|
-
* - In the overlay mode, the method marks the drawer with ARIA attributes as a dialog
|
|
626
|
-
* labelled with the `i18n.drawer` property.
|
|
627
|
-
* - In the normal mode, the method removes the ARIA attributes that has been set for the overlay mode.
|
|
628
|
-
*
|
|
629
|
-
* @private
|
|
630
|
-
*/
|
|
631
|
-
__updateDrawerAriaAttributes() {
|
|
632
|
-
const drawer = this.$.drawer;
|
|
633
|
-
if (this.overlay) {
|
|
634
|
-
drawer.setAttribute('role', 'dialog');
|
|
635
|
-
drawer.setAttribute('aria-modal', 'true');
|
|
636
|
-
drawer.setAttribute('aria-label', this.i18n.drawer);
|
|
637
|
-
} else {
|
|
638
|
-
drawer.removeAttribute('role');
|
|
639
|
-
drawer.removeAttribute('aria-modal');
|
|
640
|
-
drawer.removeAttribute('aria-label');
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
/**
|
|
645
|
-
* Returns a promise that resolves when the drawer opening/closing CSS transition ends.
|
|
646
|
-
*
|
|
647
|
-
* The method relies on the `--vaadin-app-layout-transition` CSS variable to detect whether
|
|
648
|
-
* the drawer has a CSS transition that needs to be awaited. If the CSS variable equals `none`,
|
|
649
|
-
* the promise resolves immediately.
|
|
650
|
-
*
|
|
651
|
-
* @return {Promise}
|
|
652
|
-
* @private
|
|
653
|
-
*/
|
|
654
|
-
__drawerTransitionComplete() {
|
|
655
|
-
return new Promise((resolve) => {
|
|
656
|
-
if (this._getCustomPropertyValue('--vaadin-app-layout-transition') === 'none') {
|
|
657
|
-
resolve();
|
|
658
|
-
return;
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
this.$.drawer.addEventListener('transitionend', resolve, { once: true });
|
|
662
|
-
});
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
/** @private */
|
|
666
|
-
async __trapFocusInDrawer() {
|
|
667
|
-
// Wait for the drawer CSS transition before focusing the drawer
|
|
668
|
-
// in order for VoiceOver to have a proper outline.
|
|
669
|
-
await this.__drawerTransitionComplete();
|
|
670
|
-
|
|
671
|
-
if (!this.drawerOpened) {
|
|
672
|
-
// The drawer has been closed during the CSS transition.
|
|
673
|
-
return;
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
this.$.drawer.setAttribute('tabindex', '0');
|
|
677
|
-
|
|
678
|
-
this.__ariaModalController.showModal();
|
|
679
|
-
this.__focusTrapController.trapFocus(this.$.drawer);
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
/** @private */
|
|
683
|
-
async __releaseFocusFromDrawer() {
|
|
684
|
-
// Wait for the drawer CSS transition in order to restore focus to the toggle
|
|
685
|
-
// only after `visibility` becomes `hidden`, that is, the drawer becomes inaccessible by the tabbing navigation.
|
|
686
|
-
await this.__drawerTransitionComplete();
|
|
687
|
-
|
|
688
|
-
if (this.drawerOpened) {
|
|
689
|
-
// The drawer has been opened during the CSS transition.
|
|
690
|
-
return;
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
this.__ariaModalController.close();
|
|
694
|
-
this.__focusTrapController.releaseFocus();
|
|
695
|
-
this.$.drawer.removeAttribute('tabindex');
|
|
696
|
-
|
|
697
|
-
// Move focus to the drawer toggle when closing the drawer.
|
|
698
|
-
const toggle = this.querySelector('vaadin-drawer-toggle');
|
|
699
|
-
if (toggle) {
|
|
700
|
-
toggle.focus();
|
|
701
|
-
toggle.setAttribute('focus-ring', 'focus');
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
/**
|
|
706
|
-
* Closes the drawer on Escape press if it has been opened in the overlay mode.
|
|
707
|
-
*
|
|
708
|
-
* @param {KeyboardEvent} event
|
|
709
|
-
* @private
|
|
710
|
-
*/
|
|
711
|
-
__onDrawerKeyDown(event) {
|
|
712
|
-
if (event.key === 'Escape' && this.overlay) {
|
|
713
|
-
this.drawerOpened = false;
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
/** @private */
|
|
718
|
-
_closeDrawerOnChanged(closeDrawerOn, oldCloseDrawerOn) {
|
|
719
|
-
if (oldCloseDrawerOn) {
|
|
720
|
-
window.removeEventListener(oldCloseDrawerOn, this.__closeOverlayDrawerListener);
|
|
721
|
-
}
|
|
722
|
-
if (closeDrawerOn) {
|
|
723
|
-
window.addEventListener(closeDrawerOn, this.__closeOverlayDrawerListener);
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
/** @private */
|
|
728
|
-
_onBackdropClick() {
|
|
729
|
-
this._close();
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
/** @private */
|
|
733
|
-
_onBackdropTouchend(event) {
|
|
734
|
-
// Prevent the click event from being fired
|
|
735
|
-
// on clickable element behind the backdrop
|
|
736
|
-
event.preventDefault();
|
|
737
|
-
|
|
738
|
-
this._close();
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
/** @protected */
|
|
742
|
-
_close() {
|
|
743
|
-
this.drawerOpened = false;
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
/** @private */
|
|
747
|
-
_getCustomPropertyValue(customProperty) {
|
|
748
|
-
const customPropertyValue = getComputedStyle(this).getPropertyValue(customProperty);
|
|
749
|
-
return (customPropertyValue || '').trim().toLowerCase();
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
/** @protected */
|
|
753
|
-
_updateTouchOptimizedMode() {
|
|
754
|
-
const touchOptimized = this._getCustomPropertyValue('--vaadin-app-layout-touch-optimized') === 'true';
|
|
755
|
-
|
|
756
|
-
const navbarItems = this.querySelectorAll('[slot*="navbar"]');
|
|
757
|
-
|
|
758
|
-
if (navbarItems.length > 0) {
|
|
759
|
-
Array.from(navbarItems).forEach((navbar) => {
|
|
760
|
-
if (navbar.getAttribute('slot').indexOf('touch-optimized') > -1) {
|
|
761
|
-
navbar.__touchOptimized = true;
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
if (touchOptimized && navbar.__touchOptimized) {
|
|
765
|
-
navbar.setAttribute('slot', 'navbar-bottom');
|
|
766
|
-
} else {
|
|
767
|
-
navbar.setAttribute('slot', 'navbar');
|
|
768
|
-
}
|
|
769
|
-
});
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
if (this.$.navbarTop.querySelector('[name=navbar]').assignedNodes().length === 0) {
|
|
773
|
-
this.$.navbarTop.setAttribute('hidden', '');
|
|
774
|
-
} else {
|
|
775
|
-
this.$.navbarTop.removeAttribute('hidden');
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
if (this.$.navbarBottom.querySelector('[name=navbar-bottom]').assignedNodes().length === 0) {
|
|
779
|
-
this.$.navbarBottom.setAttribute('hidden', '');
|
|
780
|
-
} else {
|
|
781
|
-
this.$.navbarBottom.removeAttribute('hidden');
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
this._updateOffsetSize();
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
/** @protected */
|
|
788
|
-
_blockAnimationUntilAfterNextRender() {
|
|
789
|
-
this.setAttribute('no-anim', '');
|
|
790
|
-
afterNextRender(this, () => {
|
|
791
|
-
this.removeAttribute('no-anim');
|
|
792
|
-
});
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
/**
|
|
796
|
-
* App Layout listens to `close-overlay-drawer` on the window level.
|
|
797
|
-
* A custom event can be dispatched and the App Layout will close the drawer in overlay.
|
|
798
|
-
*
|
|
799
|
-
* That can be used, for instance, when a navigation occurs when user clicks in a menu item inside the drawer.
|
|
800
|
-
*
|
|
801
|
-
* See `dispatchCloseOverlayDrawerEvent()` helper method.
|
|
802
|
-
*
|
|
803
|
-
* @event close-overlay-drawer
|
|
804
|
-
*/
|
|
805
137
|
}
|
|
806
138
|
|
|
807
139
|
defineCustomElement(AppLayout);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './vaadin-app-layout.js';
|