Use app-route for routing (#328)

* Use app-route for routing

* Fix bower

* Fix default route

* Add back button support back
This commit is contained in:
Paulus Schoutsen 2017-07-06 21:06:07 -07:00 committed by GitHub
parent 33ee6a9075
commit 10d5a05d1f
7 changed files with 142 additions and 137 deletions

View File

@ -9,6 +9,7 @@
"private": true,
"dependencies": {
"app-layout": "^2.0.0",
"app-route": "PolymerElements/app-route#^2.0.0",
"app-storage": "^2.0.2",
"fecha": "~2.3.0",
"font-roboto-local": "~1.0.1",
@ -70,6 +71,7 @@
"iron-input": "^2.0.0",
"iron-jsonp-library": "^2.0.0",
"iron-list": "^2.0.0",
"iron-location": "^2.0.1",
"iron-media-query": "^2.0.0",
"iron-menu-behavior": "^2.0.0",
"iron-meta": "^2.0.0",
@ -103,7 +105,6 @@
"paper-scroll-header-panel": "^2.0.0",
"paper-slider": "^2.0.0",
"paper-spinner": "^2.0.0",
"paper-styles": "2.0.0",
"paper-styles": "^2.0.0",
"paper-tabs": "^2.0.0",
"paper-toast": "^2.0.0",

View File

@ -99,7 +99,7 @@
<paper-icon-button icon='mdi:chevron-left' hidden$='[[narrow]]' on-tap='toggleMenu'></paper-icon-button>
</app-toolbar>
<paper-listbox attr-for-selected='data-panel' selected='[[hass.currentPanel]]' on-iron-select='menuSelect'>
<paper-listbox attr-for-selected='data-panel' selected='[[route.panel]]'>
<paper-icon-item on-tap='menuClicked' data-panel='states'>
<iron-icon slot="item-icon" icon='mdi:apps'></iron-icon>
<span class='item-text'>States</span>
@ -178,9 +178,9 @@ Polymer({
type: String,
},
narrow: {
type: Boolean,
},
narrow: Boolean,
route: Object,
panels: {
type: Array,
@ -193,10 +193,6 @@ Polymer({
},
},
created: function () {
this._boundUpdateStyles = this.updateStyles.bind(this);
},
computePanels: function (hass) {
var panels = hass.config.panels;
var sortValue = {
@ -236,10 +232,6 @@ Polymer({
return result;
},
menuSelect: function () {
this.debounce('updateStyles', this._boundUpdateStyles, 1);
},
menuClicked: function (ev) {
var target = ev.target;
var checks = 5;
@ -262,14 +254,16 @@ Polymer({
},
selectPanel: function (newChoice) {
if (newChoice === this.hass.currentPanel) {
return;
} else if (newChoice === 'logout') {
if (newChoice === 'logout') {
this.handleLogOut();
return;
}
this.fire('hass-navigate', { panel: newChoice });
this.debounce('updateStyles', this._boundUpdateStyles, 1);
var path = '/' + newChoice;
if (path === document.location.pathname) {
return;
}
history.pushState(null, null, path);
this.fire('location-changed');
},
handleLogOut: function () {

View File

@ -25,7 +25,6 @@
<template is='dom-if' if='[[showMain]]' restamp>
<home-assistant-main
on-hass-more-info='handleMoreInfo'
on-hass-navigate='handleNavigate'
on-hass-dock-sidebar='handleDockSidebar'
on-hass-notification='handleNotification'
on-hass-logout='handleLogout'
@ -127,8 +126,6 @@ Polymer({
states: null,
config: null,
dockedSidebar: false,
currentPanel: 'states',
currentView: null,
moreInfoEntityId: null,
callService: function (domain, service, serviceData) {
return conn.callService(domain, service, serviceData || {})
@ -217,22 +214,6 @@ Polymer({
{ moreInfoEntityId: ev.detail.entityId });
},
handleNavigate: function (ev) {
ev.stopPropagation();
var hass = Object.assign({}, this.hass);
if ('panel' in ev.detail) {
hass.currentPanel = ev.detail.panel;
}
if ('view' in ev.detail) {
hass.currentView = ev.detail.view;
}
this.hass = hass;
},
handleDockSidebar: function (ev) {
ev.stopPropagation();
this.hass = Object.assign(

View File

@ -3,6 +3,10 @@
<link rel='import' href='../../bower_components/iron-media-query/iron-media-query.html'>
<link rel='import' href='../../bower_components/iron-pages/iron-pages.html'>
<link rel='import' href='../../bower_components/app-route/app-route.html'>
<link rel='import' href='../../bower_components/app-route/app-location.html'>
<link rel='import' href='../layouts/partial-cards.html'>
<link rel='import' href='../layouts/partial-panel-resolver.html'>
<link rel="import" href="../dialogs/more-info-dialog.html">
@ -15,6 +19,13 @@
<template>
<more-info-dialog hass='[[hass]]'></more-info-dialog>
<ha-url-sync hass='[[hass]]'></ha-url-sync>
<app-location route="{{route}}"></app-location>
<app-route
route="{{route}}"
pattern="/:panel"
data="{{routeData}}"
tail="{{routeTail}}"
></app-route>
<ha-voice-command-dialog
hass='[[hass]]'
id='voiceDialog'
@ -26,13 +37,18 @@
force-narrow='[[computeForceNarrow(narrow, dockedSidebar)]]'
responsive-width='0' disable-swipe='[[isSelectedMap]]'
disable-edge-swipe='[[isSelectedMap]]'>
<ha-sidebar slot="drawer" narrow='[[narrow]]' hass='[[hass]]'></ha-sidebar>
<ha-sidebar
slot="drawer"
narrow='[[narrow]]'
hass='[[hass]]'
route='[[routeData]]'
></ha-sidebar>
<iron-pages
slot="main"
attr-for-selected='id'
fallback-selection='panel-resolver'
selected='[[currentPanel]]'
selected='[[_computeSelected(routeData)]]'
selected-attribute='panel-visible'
>
<partial-cards
@ -40,12 +56,14 @@
narrow='[[narrow]]'
hass='[[hass]]'
show-menu='[[dockedSidebar]]'
route='[[routeTail]]'
></partial-cards>
<partial-panel-resolver
id='panel-resolver'
narrow='[[narrow]]'
hass='[[hass]]'
route='[[route]]'
show-menu='[[dockedSidebar]]'
></partial-panel-resolver>
@ -60,19 +78,16 @@ Polymer({
is: 'home-assistant-main',
properties: {
hass: {
hass: Object,
narrow: Boolean,
route: {
type: Object,
value: null,
observer: 'hassChanged',
},
narrow: {
type: Boolean,
},
currentPanel: {
type: String,
observer: '_routeChanged',
},
routeData: Object,
routeTail: Object,
dockedSidebar: {
type: Boolean,
@ -86,14 +101,10 @@ Polymer({
'hass-start-voice': 'handleStartVoice',
},
hassChanged: function (hass) {
if (this.currentPanel !== hass.currentPanel) {
this.currentPanel = hass.currentPanel;
_routeChanged: function () {
if (this.narrow) {
this.$.drawer.closeDrawer();
}
}
},
handleStartVoice: function (ev) {
@ -127,5 +138,9 @@ Polymer({
computeDockedSidebar: function (hass) {
return hass.dockedSidebar;
},
_computeSelected: function (routeData) {
return routeData.panel || 'states';
},
});
</script>

View File

@ -11,6 +11,7 @@
<link rel="import" href="../../bower_components/app-layout/app-scroll-effects/effects/waterfall.html">
<link rel="import" href="../../bower_components/app-layout/app-header/app-header.html">
<link rel="import" href="../../bower_components/app-layout/app-toolbar/app-toolbar.html">
<link rel='import' href='../../bower_components/app-route/app-route.html'>
<link rel="import" href="../components/ha-menu-button.html">
<link rel="import" href="../components/ha-start-voice-button.html">
@ -35,7 +36,12 @@
text-transform: uppercase;
}
</style>
<app-route
route="{{route}}"
pattern="/:view"
data="{{routeData}}"
active="{{routeMatch}}"
></app-route>
<app-header-layout has-scrolling-region id='layout'>
<app-header effects="waterfall" condenses fixed slot="header">
<app-toolbar>
@ -49,7 +55,7 @@
scrollable
selected='[[currentView]]'
attr-for-selected='data-entity'
on-iron-select='handleViewSelected'
on-iron-activate='handleViewSelected'
>
<paper-tab
data-entity=''
@ -59,10 +65,19 @@
[[locationName]]
</template>
<template is='dom-if' if='[[defaultView]]'>
<template is='dom-if' if='[[defaultView.attributes.icon]]'>
<iron-icon title$='[[computeStateName(defaultView)]]' icon='[[defaultView.attributes.icon]]'></iron-icon>
<template
is='dom-if'
if='[[defaultView.attributes.icon]]'
>
<iron-icon
title$='[[computeStateName(defaultView)]]'
icon='[[defaultView.attributes.icon]]'
></iron-icon>
</template>
<template is='dom-if' if='[[!defaultView.attributes.icon]]'>
<template
is='dom-if'
if='[[!defaultView.attributes.icon]]'
>
[[computeStateName(defaultView)]]
</template>
</template>
@ -101,7 +116,6 @@
<template is='dom-repeat' items='[[views]]'>
<ha-cards
data-view$='[[item.entity_id]]'
show-introduction='[[computeShowIntroduction(currentView, introductionLoaded, viewStates)]]'
states='[[viewStates]]'
columns='[[_columns]]'
hass='[[hass]]'
@ -144,6 +158,10 @@ Polymer({
value: false,
},
route: Object,
routeData: Object,
routeMatch: Boolean,
_columns: {
type: Number,
value: 1,
@ -162,6 +180,7 @@ Polymer({
currentView: {
type: String,
computed: '_computeCurrentView(routeMatch, routeData)',
},
views: {
@ -242,10 +261,20 @@ Polymer({
handleViewSelected: function (ev) {
var view = ev.detail.item.getAttribute('data-entity') || null;
var current = this.currentView || null;
var current = this.currentView;
if (view !== current) {
this.fire('hass-navigate', { view: view });
var path = this.route.prefix;
if (view) {
path += '/' + view;
}
history.pushState(null, null, path);
this.fire('location-changed');
}
},
_computeCurrentView: function (routeMatch, routeData) {
return routeMatch ? routeData.view : '';
},
computeTitle: function (views, locationName) {
@ -276,12 +305,6 @@ Polymer({
},
hassChanged: function (hass) {
var newView = hass.currentView || '';
if (newView !== this.currentView) {
this.currentView = newView;
}
var views = window.HAWS.extractViews(hass.states);
// If default view present, it's in first index.
if (views.length > 0 && views[0].entity_id === this.DEFAULT_VIEW_ENTITY_ID) {

View File

@ -1,4 +1,5 @@
<link rel='import' href='../../bower_components/polymer/polymer.html'>
<link rel='import' href='../../bower_components/app-route/app-route.html'>
<link rel="import" href="./hass-loading-screen.html">
@ -9,6 +10,12 @@
display: none !important;
}
</style>
<app-route
route="{{route}}"
pattern="/:panel"
data="{{routeData}}"
tail="{{routeTail}}"
></app-route>
<template is='dom-if' if='[[!resolved]]'>
<hass-loading-screen
@ -43,6 +50,15 @@ Polymer({
observer: 'updateAttributes',
},
route: Object,
routeData: Object,
routeTail: {
type: Object,
observer: 'updateAttributes',
},
resolved: {
type: Boolean,
value: false,
@ -55,13 +71,13 @@ Polymer({
panel: {
type: Object,
computed: 'computeCurrentPanel(hass)',
computed: 'computeCurrentPanel(hass, routeData)',
observer: 'panelChanged',
},
},
computeCurrentPanel: function (hass) {
return hass.config.panels[hass.currentPanel];
computeCurrentPanel: function (hass, routeData) {
return hass.config.panels[routeData.panel];
},
panelChanged: function (panel) {
@ -102,6 +118,7 @@ Polymer({
customEl.hass = this.hass;
customEl.narrow = this.narrow;
customEl.showMenu = this.showMenu;
customEl.route = this.routeTail;
},
});
</script>

View File

@ -1,19 +1,7 @@
<script>
(function () {
var PAGE_TITLE = 'Home Assistant';
function pageState(panel, view) {
var state = { panel: panel };
if (panel === 'states') {
state.view = view || null;
}
return state;
}
function pageUrl(pane, view) {
return pane === 'states' && view ?
'/' + pane + '/' + view : '/' + pane;
}
/* eslint-disable no-console */
var DEBUG = false;
Polymer({
is: 'ha-url-sync',
@ -26,63 +14,49 @@
},
hassChanged: function (newHass, oldHass) {
if (!oldHass) {
if (this.ignoreNextHassChange) {
if (DEBUG) console.log('ignore hasschange');
this.ignoreNextHassChange = false;
return;
} else if (newHass.currentPanel === oldHass.currentPanel &&
newHass.currentView === oldHass.currentView) {
// did the more info entity change?
if (oldHass.moreInfoEntityId !== newHass.moreInfoEntityId) {
} else if (!oldHass || oldHass.moreInfoEntityId === newHass.moreInfoEntityId) {
return;
}
if (newHass.moreInfoEntityId) {
// push same state so that back button works.
history.pushState(history.state, PAGE_TITLE, window.location.pathname);
} else if (this.ignoreNextDeselectEntity) {
this.ignoreNextDeselectEntity = false;
if (DEBUG) console.log('pushing state');
history.pushState(null, null, window.location.pathname);
} else {
if (DEBUG) console.log('history back');
this.ignoreNextPopstate = true;
history.back();
}
}
return;
} else if (this.ignoreNextNav) {
this.ignoreNextNav = false;
return;
}
history.pushState(
pageState(newHass.currentPanel, newHass.currentView), PAGE_TITLE,
pageUrl(newHass.currentPanel, newHass.currentView));
},
popstateChangeListener: function (ev) {
if (this.ignoreNextPopstate) {
if (DEBUG) console.log('ignore popstate');
this.ignoreNextPopstate = false;
return;
}
if (DEBUG) console.log('popstate', ev);
if (this.hass.moreInfoEntityId) {
this.ignoreNextDeselectEntity = true;
if (DEBUG) console.log('deselect entity');
this.ignoreNextHassChange = true;
this.fire('hass-more-info', { entityId: null });
} else if (this.hass.currentPanel !== ev.state.panel ||
this.hass.currentView !== ev.state.view) {
this.ignoreNextNav = true;
this.fire('hass-navigate', ev.state);
}
},
// initial url sync
attached: function () {
this.ignoreNextPopstate = false;
this.ignoreNextHassChange = false;
this.popstateChangeListener = this.popstateChangeListener.bind(this);
// keep state in sync when url changes via forward/back buttons
window.addEventListener('popstate', this.popstateChangeListener);
},
// store current view / panel
if (window.location.pathname === '/') {
var currentPanel = this.hass.currentPanel;
var currentView = this.hass.currentView;
history.replaceState(
pageState(currentPanel, currentView), PAGE_TITLE,
pageUrl(currentPanel, currentView));
} else {
var parts = window.location.pathname.substr(1).split('/');
this.fire('hass-navigate', pageState(parts[0], parts[1]));
}
detached: function () {
window.removeEventListener('popstate', this.popstateChangeListener);
}
});
}());