@vaadin/vaadin-app-layout
Advanced tools
Comparing version 1.0.2 to 2.0.0-alpha1
@@ -13,3 +13,3 @@ { | ||
"name": "@vaadin/vaadin-app-layout", | ||
"version": "1.0.2", | ||
"version": "2.0.0-alpha1", | ||
"main": "vaadin-app-layout.js", | ||
@@ -37,3 +37,4 @@ "author": "Vaadin Ltd", | ||
"@vaadin/vaadin-lumo-styles": "^1.1.0", | ||
"@vaadin/vaadin-material-styles": "^1.1.0" | ||
"@vaadin/vaadin-material-styles": "^1.1.0", | ||
"@vaadin/vaadin-button": "^2.1.0" | ||
}, | ||
@@ -47,4 +48,5 @@ "devDependencies": { | ||
"@vaadin/vaadin-tabs": "^2.1.0", | ||
"@vaadin/vaadin-icons": "^4.2.0" | ||
"@vaadin/vaadin-icons": "^4.2.0", | ||
"@vaadin/vaadin-text-field": "^2.3.0" | ||
} | ||
} |
/** | ||
@license | ||
Vaadin App Layout | ||
Copyright (C) 2018 Vaadin Ltd | ||
Vaadin Navbar Layout | ||
Copyright (C) 2019 Vaadin Ltd | ||
This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ | ||
*/ | ||
import './safe-area-inset.js'; | ||
import './detect-ios-navbar.js'; | ||
import { PolymerElement } from '@polymer/polymer/polymer-element.js'; | ||
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js'; | ||
import { ElementMixin } from '@vaadin/vaadin-element-mixin/vaadin-element-mixin.js'; | ||
import { AppLayoutMixin } from './vaadin-app-layout-mixin.js'; | ||
import { FlattenedNodesObserver } from '@polymer/polymer/lib/utils/flattened-nodes-observer.js'; | ||
import { html } from '@polymer/polymer/lib/utils/html-tag.js'; | ||
/** | ||
* `<vaadin-app-layout>` is a Web Component providing a quick and easy way to get a common application layout structure done. | ||
* | ||
* ``` | ||
* <vaadin-app-layout> | ||
* <h3 slot="branding">Company Name</h3> | ||
* <vaadin-tabs slot="menu"> | ||
* <vaadin-tab>Menu item</vaadin-tab> | ||
* </vaadin-tabs> | ||
* <!-- Everything else will be the page content --> | ||
* <div> | ||
* <h3>Page title</h3> | ||
* <p>Page content</p> | ||
* </div> | ||
* </vaadin-app-layout> | ||
* ``` | ||
* | ||
* For best results, the component should be added to the root level of your application (i.e., as a direct child of `<body>`) | ||
* | ||
* ### Styling | ||
* | ||
* The following Shadow DOM parts of the `<vaadin-app-layout>` are available for styling: | ||
* | ||
* Part name | Description | ||
* --------------|---------------------------------------------------------| | ||
* `navbar` | Container for the navigation bar | ||
* `branding` | Placeholder for the logo or for the app name. By default is invisible on small screens | ||
* `content` | Container for page content. | ||
* | ||
* The following custom CSS properties are available for styling: | ||
* | ||
* Custom CSS property | Description | Default value | ||
* ---|---|--- | ||
* `--vaadin-app-layout-viewport-bottom` | Bottom offset of the visible viewport area | `0` or detected offset | ||
* `--vaadin-app-layout-navbar-height` | Height of the navigation bar and branding inside | `auto`, depends on the navbar content | ||
* `--vaadin-app-layout-navbar-background` | Background of the navigation bar | slightly gray, depends on the theme | ||
* | ||
* See [ThemableMixin – how to apply styles for shadow parts](https://github.com/vaadin/vaadin-themable-mixin/wiki) | ||
* | ||
* ### Component's slots | ||
* | ||
* The following slots are available to be set | ||
* | ||
* Slot name | Description | ||
* --------------|---------------------------------------------------| | ||
* no name | Default placeholder for the page content | ||
* `branding` | Placeholder for the logo or application name | ||
* `menu` | Placeholder for an application menu | ||
* | ||
* See examples of setting the content into slots in the live demos. | ||
* | ||
* @memberof Vaadin | ||
* @mixes Vaadin.ElementMixin | ||
* @mixes Vaadin.ThemableMixin | ||
* @mixes Vaadin.AppLayoutMixin | ||
* @extends Polymer.Element | ||
* @demo demo/index.html | ||
*/ | ||
class AppLayoutElement extends ElementMixin(ThemableMixin(PolymerElement)) { | ||
class AppLayoutElement extends AppLayoutMixin(PolymerElement) { | ||
static get template() { | ||
return html` | ||
<style> | ||
<style include="vaadin-app-layout-mixin"> | ||
:host { | ||
display: flex !important; | ||
flex-direction: column-reverse; | ||
--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-top: var(--vaadin-app-layout-navbar-offset-top); | ||
padding-bottom: var(--vaadin-app-layout-navbar-offset-bottom); | ||
padding-left: var(--vaadin-app-layout-navbar-offset-left); | ||
} | ||
: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-top: 0; | ||
--vaadin-app-layout-navbar-offset-bottom: 0; | ||
--vaadin-app-layout-navbar-offset-left: 0; | ||
} | ||
@media (pointer: coarse) and (max-width: 800px) and (min-height: 500px) { | ||
:host { | ||
--vaadin-app-layout-touch-optimized: true; | ||
} | ||
} | ||
[part="navbar"] { | ||
position: fixed; | ||
display: flex; | ||
top: 0; | ||
right: 0; | ||
left: 0; | ||
right: 0; | ||
bottom: var(--vaadin-app-layout-viewport-bottom); | ||
/* CSS API for host */ | ||
--vaadin-app-layout-viewport-bottom: 0; | ||
transition: left 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); | ||
} | ||
:host([hidden]) { | ||
display: none !important; | ||
:host([drawer-first][drawer-opened]:not([overlay])) [part="navbar"] { | ||
left: var(--vaadin-app-layout-drawer-offset-left, 0); | ||
} | ||
[part="branding"], | ||
[part="navbar"]::after { | ||
display: none; | ||
:host([drawer-first]) [part="drawer"] { | ||
top: 0; | ||
} | ||
[part="navbar"] { | ||
flex: none; | ||
display: flex; | ||
align-items: center; | ||
height: var(--vaadin-app-layout-navbar-height, auto); | ||
background: var(--vaadin-app-layout-navbar-background, #eee); | ||
:host([orientation="vertical"]) [part="navbar"][bottom] { | ||
top: auto; | ||
bottom: 0; | ||
padding-bottom: var(--safe-area-inset-bottom); | ||
} | ||
[part="navbar"]::after { | ||
content: ''; | ||
[part="drawer"] { | ||
position: fixed; | ||
top: var(--vaadin-app-layout-navbar-offset-top, 0); | ||
right: auto; | ||
bottom: var(--vaadin-app-layout-navbar-offset-bottom, var(--vaadin-viewport-offset-bottom, 0)); | ||
left: var(--vaadin-app-layout-navbar-offset-left, 0); | ||
transition: transform var(--vaadin-app-layout-transition); | ||
transform: translateX(-100%); | ||
max-width: 90%; | ||
width: 16em; | ||
box-sizing: border-box; | ||
padding: var(--safe-area-inset-top) 0 var(--safe-area-inset-bottom) var(--safe-area-inset-left); | ||
} | ||
[part="branding"], | ||
[part="navbar"]::after { | ||
/* | ||
Makes the menu part horizontally centered on wide viewports, | ||
regardless of the size of contents inside the branding | ||
and the secondary parts. Prevents unnecessary menu shrinking. | ||
:host([drawer-opened]) [part="drawer"] { | ||
transform: translateX(0%); | ||
touch-action: manipulation; | ||
} | ||
NOTE: IE requires a unit for the flex-basis value. | ||
[part="backdrop"] { | ||
background-color: #000; | ||
opacity: 0.3; | ||
} | ||
NOTE: \`0px\` might confuse linters and minifiers. | ||
*/ | ||
flex: 1 0 0.001px; | ||
:host(:not([drawer-opened])) [part="backdrop"] { | ||
opacity: 0; | ||
} | ||
[part=navbar] > ::slotted([slot="menu"]) { | ||
display: flex; | ||
align-items: center; | ||
overflow: hidden; | ||
max-width: 100%; | ||
margin: auto; | ||
:host([overlay]) [part="backdrop"] { | ||
position: fixed; | ||
top: 0; | ||
right: 0; | ||
bottom: 0; | ||
left: 0; | ||
pointer-events: none; | ||
transition: opacity var(--vaadin-app-layout-transition); | ||
-webkit-tap-highlight-color: transparent; | ||
} | ||
[part="content"] { | ||
flex: auto; | ||
height: 100%; | ||
overflow: auto; | ||
-webkit-overflow-scrolling: touch; | ||
:host([overlay]) [part="drawer"], | ||
:host([overlay]) [part="backdrop"] { | ||
z-index: 1; | ||
} | ||
@media (min-width: 700px) { | ||
:host([drawer-opened][overlay]) [part="backdrop"] { | ||
pointer-events: auto; | ||
touch-action: manipulation; | ||
} | ||
:host([drawer-opened]:not([overlay])) { | ||
padding-left: var(--vaadin-app-layout-drawer-offset-left); | ||
} | ||
@media (max-width: 800px), | ||
(max-height: 600px) { | ||
:host { | ||
flex-direction: column; | ||
--vaadin-app-layout-drawer-overlay: true; | ||
} | ||
[part=navbar] > ::slotted([slot="menu"]) { | ||
flex: initial; | ||
[part="drawer"] { | ||
width: 20em; | ||
} | ||
} | ||
[part="branding"], | ||
[part="navbar"]::after { | ||
display: flex; | ||
} | ||
:host([orientation="horizontal"]) { | ||
--vaadin-app-layout-navbar-offset-top: 0; | ||
--vaadin-app-layout-navbar-offset-left: var(--_vaadin-app-layout-navbar-offset-size); | ||
} | ||
:host([orientation="horizontal"]) [part="navbar"] { | ||
right: auto; | ||
bottom: var(--vaadin-viewport-offset-bottom, 0); | ||
} | ||
</style> | ||
<nav part="navbar"> | ||
<div part="branding" role="banner"> | ||
<slot name="branding"></slot> | ||
</div> | ||
<slot name="menu"></slot> | ||
</nav> | ||
<div part="content" role="main" aria-live="polite"> | ||
<div part="backdrop" on-click="_close" on-touchstart="_close"></div> | ||
<div part="drawer"> | ||
<slot name="drawer" id="drawer"></slot> | ||
</div> | ||
<div content=""> | ||
<slot></slot> | ||
</div> | ||
<div part="navbar"> | ||
<slot name="navbar"></slot> | ||
</div> | ||
<div part="navbar" bottom="" hidden=""> | ||
<slot name="navbar-bottom"></slot> | ||
</div> | ||
`; | ||
@@ -166,34 +170,156 @@ } | ||
} | ||
static get version() { | ||
return '1.0.2'; | ||
return '2.0.0-alpha1'; | ||
} | ||
constructor() { | ||
super(); | ||
static get properties() { | ||
return { | ||
// vertical (default) or horizontal | ||
orientation: { | ||
type: String, | ||
value: 'vertical', | ||
reflectToAttribute: true | ||
}, | ||
if (/iPad|iPhone|iPod/.test(navigator.userAgent)) { | ||
this._boundIosResizeListener = () => this._detectIosNavbar(); | ||
} | ||
drawerFirst: { | ||
type: Boolean, | ||
reflectToAttribute: true | ||
}, | ||
drawerOpened: { | ||
type: Boolean, | ||
value: true, | ||
reflectToAttribute: true | ||
}, | ||
// Drawer is an overlay on top of the content | ||
// Controlled via CSS using --vaadin-app-layout-drawer-overlay: true|false; | ||
overlay: { | ||
type: Boolean, | ||
value: false, | ||
reflectToAttribute: true | ||
} | ||
}; | ||
} | ||
/** @private */ | ||
connectedCallback() { | ||
super.connectedCallback(); | ||
if (this._boundIosResizeListener) { | ||
this._detectIosNavbar(); | ||
window.addEventListener('resize', this._boundIosResizeListener); | ||
this._updateTouchOptimizedMode(); | ||
this._setupDrawerToggleClickListener(); | ||
this._navbarChildObserver = new FlattenedNodesObserver(this.$.drawer, (info) => { | ||
this._updateDrawerSize(); | ||
}); | ||
this._updateDrawerSize(); | ||
} | ||
_setupDrawerToggleClickListener() { | ||
this.toggleClickListener = this.addEventListener('drawer-toggle-click', this._drawerToggleClickListener); | ||
} | ||
_drawerToggleClickListener(e) { | ||
e.stopPropagation(); | ||
this.drawerOpened = !this.drawerOpened; | ||
} | ||
_updateDrawerSize() { | ||
const childCount = this.querySelectorAll('[slot=drawer]').length; | ||
const drawer = this.shadowRoot.querySelector('[part=drawer]'); | ||
if (childCount === 0) { | ||
drawer.setAttribute('hidden', ''); | ||
} else { | ||
drawer.removeAttribute('hidden'); | ||
} | ||
this._updateOffsetSize(); | ||
} | ||
_detectIosNavbar() { | ||
const innerHeight = window.innerHeight; | ||
const innerWidth = window.innerWidth; | ||
const landscape = innerWidth > innerHeight; | ||
const clientHeight = document.documentElement.clientHeight; | ||
if (landscape && clientHeight > innerHeight) { | ||
this.style.setProperty('--vaadin-app-layout-viewport-bottom', clientHeight - innerHeight + 'px'); | ||
disconnectedCallback() { | ||
super.disconnectedCallback(); | ||
this._navbarChildObserver && this._navbarChildObserver.disconnect(); | ||
this.removeEventListener('drawer-toggle-click', this._drawerToggleClickListener); | ||
} | ||
_resize() { | ||
super._resize(); | ||
this._updateTouchOptimizedMode(); | ||
this._updateOverlayMode(); | ||
} | ||
_updateOffsetSize() { | ||
super._updateOffsetSize(); | ||
const navbar = this.shadowRoot.querySelector('[part="navbar"]'); | ||
const navbarRect = navbar.getBoundingClientRect(); | ||
// TODO(jouni): use shady css if we want to support IE11 | ||
if (this.orientation == 'vertical') { | ||
this.style.setProperty('--_vaadin-app-layout-navbar-offset-size', navbarRect.height + 'px'); | ||
const navbarBottom = this.shadowRoot.querySelector('[part="navbar"][bottom]'); | ||
const navbarBottomRect = navbarBottom.getBoundingClientRect(); | ||
this.style.setProperty('--_vaadin-app-layout-navbar-offset-size-bottom', navbarBottomRect.height + 'px'); | ||
} else { | ||
this.style.setProperty('--vaadin-app-layout-viewport-bottom', '0'); | ||
this.style.setProperty('--_vaadin-app-layout-navbar-offset-size', navbarRect.width + 'px'); | ||
} | ||
const drawer = this.shadowRoot.querySelector('[part="drawer"]'); | ||
const drawerRect = drawer.getBoundingClientRect(); | ||
// TODO(jouni): use shady css if we want to support IE11 | ||
this.style.setProperty('--_vaadin-app-layout-drawer-offset-size', drawerRect.width + 'px'); | ||
} | ||
_updateOverlayMode() { | ||
// TODO(jouni): use shady css if we want to support IE11 | ||
let overlay = getComputedStyle(this).getPropertyValue('--vaadin-app-layout-drawer-overlay'); | ||
overlay = overlay.trim().toLowerCase() == 'true'; | ||
this.overlay = overlay; | ||
if (this.overlay) { | ||
this.drawerOpened = false; | ||
} | ||
// TODO(jouni): ARIA attributes. The drawer should act similar to a modal dialog when in ”overlay” mode | ||
} | ||
// TODO(jouni): what mechanism should we use to close the overlay drawer a navigation is triggered? | ||
_close() { | ||
this.drawerOpened = false; | ||
} | ||
_updateTouchOptimizedMode() { | ||
// TODO(jouni): use shady css if we want to support IE11 | ||
let touchOptimized = getComputedStyle(this).getPropertyValue('--vaadin-app-layout-touch-optimized'); | ||
touchOptimized = touchOptimized.trim().toLowerCase() == 'true'; | ||
const navbarBottom = this.shadowRoot.querySelector('[part="navbar"][bottom]'); | ||
const navbars = this.querySelectorAll('[slot*="navbar"]'); | ||
if (navbars.length > 0) { | ||
Array.from(navbars).forEach(navbar => { | ||
if (navbar.getAttribute('slot').indexOf('touch-optimized') > -1) { | ||
navbar.__touchOptimized = true; | ||
} | ||
if (this.orientation == 'vertical' && touchOptimized && navbar.__touchOptimized) { | ||
navbar.setAttribute('slot', 'navbar-bottom'); | ||
} else { | ||
navbar.setAttribute('slot', 'navbar'); | ||
} | ||
}); | ||
} | ||
if (this.orientation == 'vertical' && touchOptimized) { | ||
navbarBottom.removeAttribute('hidden'); | ||
} else { | ||
navbarBottom.setAttribute('hidden', ''); | ||
} | ||
this._updateOffsetSize(); | ||
} | ||
} | ||
@@ -200,0 +326,0 @@ |
@@ -9,24 +9,37 @@ import '@vaadin/vaadin-lumo-styles/color.js'; | ||
<style> | ||
:host { | ||
background-color: var(--lumo-base-color); | ||
[part="navbar"] { | ||
background: var(--lumo-base-color) linear-gradient(var(--lumo-contrast-5pct), var(--lumo-contrast-5pct)); | ||
} | ||
[part="navbar"] { | ||
background: var(--vaadin-app-layout-navbar-background, linear-gradient(var(--lumo-shade-5pct), var(--lumo-shade-5pct)) var(--lumo-base-color)); | ||
:host(:not([overlay])) [part="drawer"] { | ||
border-right: 1px solid var(--lumo-contrast-10pct); | ||
} | ||
[part="navbar"] > ::slotted(vaadin-tabs[slot="menu"]) { | ||
box-shadow: none; | ||
--lumo-font-size-m: var(--lumo-font-size-s); | ||
--lumo-icon-size-m: 1.5rem; | ||
:host([overlay]) [part="drawer"] { | ||
background: var(--lumo-base-color); | ||
} | ||
@media (min-width: 700px) { | ||
[part="backdrop"] { | ||
background-color: var(--lumo-shade-20pct); | ||
opacity: 1; | ||
} | ||
[part] ::slotted(h2), | ||
[part] ::slotted(h3), | ||
[part] ::slotted(h4) { | ||
margin-top: var(--lumo-space-xs) !important; | ||
margin-bottom: var(--lumo-space-xs) !important; | ||
} | ||
@supports (-webkit-backdrop-filter: blur(1px)) { | ||
[part="navbar"] { | ||
padding: 0 var(--lumo-space-m); | ||
/* TODO(jouni): should use a Lumo color, but we don’t have a suitable one */ | ||
background: rgba(255, 255, 255, 0.8) linear-gradient(var(--lumo-contrast-5pct), var(--lumo-contrast-5pct)); | ||
-webkit-backdrop-filter: blur(24px); | ||
} | ||
[part="navbar"] > ::slotted(vaadin-tabs[slot="menu"]) { | ||
--lumo-font-size-m: unset; | ||
--lumo-icon-size-m: unset; | ||
:host([overlay]) [part="drawer"] { | ||
/* TODO(jouni): should use a Lumo color, but we don’t have a suitable one */ | ||
background: rgba(255, 255, 255, 0.9); | ||
-webkit-backdrop-filter: blur(24px); | ||
} | ||
@@ -33,0 +46,0 @@ } |
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
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
36890
16
549
6
8
1
1
+ Added@vaadin/vaadin-button@^2.1.0
+ Added@vaadin/vaadin-button@2.4.0(transitive)
+ Added@vaadin/vaadin-control-state-mixin@2.2.6(transitive)