Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@vaadin/vaadin-app-layout

Package Overview
Dependencies
Maintainers
16
Versions
253
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@vaadin/vaadin-app-layout - npm Package Compare versions

Comparing version 1.0.2 to 2.0.0-alpha1

src/detect-ios-navbar.js

8

package.json

@@ -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 @@ }

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc