mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-23 17:26:42 +00:00
Fix RTL issues (#2648)
* Convert home-assistant-main to Lit/TS * different approach * LRT RTL * Lint * RTL fix for generic entity row * Remove fetching from selectedLanguage * RTL the RTL languages in the picker * Fix drawer adjust to RTL
This commit is contained in:
parent
e2ff51f425
commit
2afc8607c6
@ -365,18 +365,12 @@ class HaWeatherCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
|
||||
computeDate(data) {
|
||||
const date = new Date(data);
|
||||
return date.toLocaleDateString(
|
||||
this.hass.selectedLanguage || this.hass.language,
|
||||
{ weekday: "short" }
|
||||
);
|
||||
return date.toLocaleDateString(this.hass.language, { weekday: "short" });
|
||||
}
|
||||
|
||||
computeTime(data) {
|
||||
const date = new Date(data);
|
||||
return date.toLocaleTimeString(
|
||||
this.hass.selectedLanguage || this.hass.language,
|
||||
{ hour: "numeric" }
|
||||
);
|
||||
return date.toLocaleTimeString(this.hass.language, { hour: "numeric" });
|
||||
}
|
||||
|
||||
_computeRTL(hass) {
|
||||
|
@ -17,6 +17,7 @@ import "./ha-icon";
|
||||
import isComponentLoaded from "../common/config/is_component_loaded";
|
||||
import { HomeAssistant, Panel } from "../types";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { DEFAULT_PANEL } from "../common/const";
|
||||
|
||||
const computeInitials = (name: string) => {
|
||||
if (!name) {
|
||||
@ -82,7 +83,12 @@ const computePanels = (hass: HomeAssistant) => {
|
||||
*/
|
||||
class HaSidebar extends LitElement {
|
||||
public hass?: HomeAssistant;
|
||||
public defaultPage?: string;
|
||||
public _defaultPage?: string;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._defaultPage = localStorage.defaultPage || DEFAULT_PANEL;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const hass = this.hass;
|
||||
@ -114,8 +120,8 @@ class HaSidebar extends LitElement {
|
||||
|
||||
<paper-listbox attr-for-selected="data-panel" .selected=${hass.panelUrl}>
|
||||
<a
|
||||
href="${computeUrl(this.defaultPage)}"
|
||||
data-panel=${this.defaultPage}
|
||||
href="${computeUrl(this._defaultPage)}"
|
||||
data-panel=${this._defaultPage}
|
||||
tabindex="-1"
|
||||
>
|
||||
<paper-icon-item>
|
||||
@ -214,7 +220,7 @@ class HaSidebar extends LitElement {
|
||||
static get properties(): PropertyDeclarations {
|
||||
return {
|
||||
hass: {},
|
||||
defaultPage: {},
|
||||
_defaultPage: {},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -190,7 +190,7 @@ class StateHistoryChartTimeline extends LocalizeMixin(PolymerElement) {
|
||||
yaxe.maxWidth = yaxe.chart.width * 0.18;
|
||||
},
|
||||
position: this.hass.translationMetadata.translations[
|
||||
this.hass.selectedLanguage || this.hass.language
|
||||
this.hass.language
|
||||
].isRTL
|
||||
? "right"
|
||||
: "left",
|
||||
|
@ -178,20 +178,18 @@ class MoreInfoWeather extends LocalizeMixin(PolymerElement) {
|
||||
provider === "Data provided by OpenWeatherMap"
|
||||
) {
|
||||
if (new Date().getDay() === date.getDay()) {
|
||||
return date.toLocaleTimeString(
|
||||
this.hass.selectedLanguage || this.hass.language,
|
||||
{ hour: "numeric" }
|
||||
);
|
||||
return date.toLocaleTimeString(this.hass.language, { hour: "numeric" });
|
||||
}
|
||||
return date.toLocaleDateString(
|
||||
this.hass.selectedLanguage || this.hass.language,
|
||||
{ weekday: "long", hour: "numeric" }
|
||||
);
|
||||
return date.toLocaleDateString(this.hass.language, {
|
||||
weekday: "long",
|
||||
hour: "numeric",
|
||||
});
|
||||
}
|
||||
return date.toLocaleDateString(
|
||||
this.hass.selectedLanguage || this.hass.language,
|
||||
{ weekday: "long", month: "short", day: "numeric" }
|
||||
);
|
||||
return date.toLocaleDateString(this.hass.language, {
|
||||
weekday: "long",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
});
|
||||
}
|
||||
|
||||
getUnit(measure) {
|
||||
|
@ -1,16 +0,0 @@
|
||||
import { storeState } from "../../util/ha-pref-storage";
|
||||
|
||||
export default (superClass) =>
|
||||
class extends superClass {
|
||||
firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
this.addEventListener("hass-dock-sidebar", (e) =>
|
||||
this._handleDockSidebar(e)
|
||||
);
|
||||
}
|
||||
|
||||
_handleDockSidebar(ev) {
|
||||
this._updateHass({ dockedSidebar: ev.detail.dock });
|
||||
storeState(this.hass);
|
||||
}
|
||||
};
|
30
src/layouts/app/sidebar-mixin.ts
Normal file
30
src/layouts/app/sidebar-mixin.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { storeState } from "../../util/ha-pref-storage";
|
||||
import { Constructor, LitElement } from "lit-element";
|
||||
import { HassBaseEl } from "./hass-base-mixin";
|
||||
import { HASSDomEvent } from "../../common/dom/fire_event";
|
||||
|
||||
interface DockSidebarParams {
|
||||
dock: boolean;
|
||||
}
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"hass-dock-sidebar": DockSidebarParams;
|
||||
}
|
||||
// for add event listener
|
||||
interface HTMLElementEventMap {
|
||||
"hass-dock-sidebar": HASSDomEvent<DockSidebarParams>;
|
||||
}
|
||||
}
|
||||
|
||||
export default (superClass: Constructor<LitElement & HassBaseEl>) =>
|
||||
class extends superClass {
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
this.addEventListener("hass-dock-sidebar", (ev) => {
|
||||
this._updateHass({ dockedSidebar: ev.detail.dock });
|
||||
storeState(this.hass);
|
||||
});
|
||||
}
|
||||
};
|
@ -4,6 +4,8 @@ import { storeState } from "../../util/ha-pref-storage";
|
||||
import { Constructor, LitElement } from "lit-element";
|
||||
import { HassBaseEl } from "./hass-base-mixin";
|
||||
import { computeLocalize } from "../../common/translations/localize";
|
||||
import { computeRTL } from "../../common/util/compute_rtl";
|
||||
import { HomeAssistant } from "../../types";
|
||||
|
||||
/*
|
||||
* superClass needs to contain `this.hass` and `this._updateHass`.
|
||||
@ -22,6 +24,7 @@ export default (superClass: Constructor<LitElement & HassBaseEl>) =>
|
||||
protected hassConnected() {
|
||||
super.hassConnected();
|
||||
this._loadBackendTranslations();
|
||||
this.style.direction = computeRTL(this.hass!) ? "rtl" : "ltr";
|
||||
}
|
||||
|
||||
protected hassReconnected() {
|
||||
@ -79,15 +82,17 @@ export default (superClass: Constructor<LitElement & HassBaseEl>) =>
|
||||
...data,
|
||||
},
|
||||
};
|
||||
this._updateHass({
|
||||
language,
|
||||
resources,
|
||||
localize: computeLocalize(this, language, resources),
|
||||
});
|
||||
const changes: Partial<HomeAssistant> = { resources };
|
||||
if (language === this.hass!.language) {
|
||||
changes.localize = computeLocalize(this, language, resources);
|
||||
}
|
||||
this._updateHass(changes);
|
||||
}
|
||||
|
||||
private _selectLanguage(event) {
|
||||
this._updateHass({ selectedLanguage: event.detail.language });
|
||||
const language: string = event.detail.language;
|
||||
this._updateHass({ language, selectedLanguage: language });
|
||||
this.style.direction = computeRTL(this.hass!) ? "rtl" : "ltr";
|
||||
storeState(this.hass);
|
||||
this._loadResources();
|
||||
this._loadBackendTranslations();
|
||||
|
@ -1,152 +0,0 @@
|
||||
import "@polymer/app-layout/app-drawer-layout/app-drawer-layout";
|
||||
import "@polymer/app-layout/app-drawer/app-drawer";
|
||||
import "@polymer/app-route/app-route";
|
||||
import "@polymer/iron-media-query/iron-media-query";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
import "../util/ha-url-sync";
|
||||
|
||||
import "./partial-panel-resolver";
|
||||
import EventsMixin from "../mixins/events-mixin";
|
||||
import NavigateMixin from "../mixins/navigate-mixin";
|
||||
import { computeRTL } from "../common/util/compute_rtl";
|
||||
import { DEFAULT_PANEL } from "../common/const";
|
||||
|
||||
import(/* webpackChunkName: "ha-sidebar" */ "../components/ha-sidebar");
|
||||
import(/* webpackChunkName: "voice-command-dialog" */ "../dialogs/ha-voice-command-dialog");
|
||||
|
||||
const NON_SWIPABLE_PANELS = ["kiosk", "map"];
|
||||
|
||||
class HomeAssistantMain extends NavigateMixin(EventsMixin(PolymerElement)) {
|
||||
static get template() {
|
||||
return html`
|
||||
<style>
|
||||
:host {
|
||||
color: var(--primary-text-color);
|
||||
/* remove the grey tap highlights in iOS on the fullscreen touch targets */
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
:host([rtl]) {
|
||||
direction: rtl;
|
||||
}
|
||||
partial-panel-resolver,
|
||||
ha-sidebar {
|
||||
/* allow a light tap highlight on the actual interface elements */
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
partial-panel-resolver {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
<ha-url-sync hass="[[hass]]"></ha-url-sync>
|
||||
<ha-voice-command-dialog
|
||||
hass="[[hass]]"
|
||||
id="voiceDialog"
|
||||
></ha-voice-command-dialog>
|
||||
<iron-media-query query="(max-width: 870px)" query-matches="{{narrow}}">
|
||||
</iron-media-query>
|
||||
|
||||
<app-drawer-layout
|
||||
fullbleed=""
|
||||
force-narrow="[[computeForceNarrow(narrow, dockedSidebar)]]"
|
||||
responsive-width="0"
|
||||
>
|
||||
<app-drawer
|
||||
id="drawer"
|
||||
align="start"
|
||||
slot="drawer"
|
||||
disable-swipe="[[_computeDisableSwipe(hass)]]"
|
||||
swipe-open="[[!_computeDisableSwipe(hass)]]"
|
||||
persistent="[[dockedSidebar]]"
|
||||
>
|
||||
<ha-sidebar
|
||||
hass="[[hass]]"
|
||||
default-page="[[_defaultPage]]"
|
||||
></ha-sidebar>
|
||||
</app-drawer>
|
||||
|
||||
<partial-panel-resolver
|
||||
narrow="[[narrow]]"
|
||||
hass="[[hass]]"
|
||||
route="[[route]]"
|
||||
show-menu="[[dockedSidebar]]"
|
||||
></partial-panel-resolver>
|
||||
</app-drawer-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
hass: Object,
|
||||
narrow: Boolean,
|
||||
route: {
|
||||
type: Object,
|
||||
observer: "_routeChanged",
|
||||
},
|
||||
dockedSidebar: {
|
||||
type: Boolean,
|
||||
computed: "computeDockedSidebar(hass)",
|
||||
},
|
||||
rtl: {
|
||||
type: Boolean,
|
||||
reflectToAttribute: true,
|
||||
computed: "_computeRTL(hass)",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
ready() {
|
||||
super.ready();
|
||||
this._defaultPage = localStorage.defaultPage || DEFAULT_PANEL;
|
||||
this.addEventListener("hass-open-menu", () => this.handleOpenMenu());
|
||||
this.addEventListener("hass-close-menu", () => this.handleCloseMenu());
|
||||
this.addEventListener("hass-start-voice", (ev) =>
|
||||
this.handleStartVoice(ev)
|
||||
);
|
||||
}
|
||||
|
||||
_routeChanged() {
|
||||
if (this.narrow) {
|
||||
this.$.drawer.close();
|
||||
}
|
||||
}
|
||||
|
||||
handleStartVoice(ev) {
|
||||
ev.stopPropagation();
|
||||
this.$.voiceDialog.opened = true;
|
||||
}
|
||||
|
||||
handleOpenMenu() {
|
||||
if (this.narrow) {
|
||||
this.$.drawer.open();
|
||||
} else {
|
||||
this.fire("hass-dock-sidebar", { dock: true });
|
||||
}
|
||||
}
|
||||
|
||||
handleCloseMenu() {
|
||||
this.$.drawer.close();
|
||||
if (this.dockedSidebar) {
|
||||
this.fire("hass-dock-sidebar", { dock: false });
|
||||
}
|
||||
}
|
||||
|
||||
computeForceNarrow(narrow, dockedSidebar) {
|
||||
return narrow || !dockedSidebar;
|
||||
}
|
||||
|
||||
computeDockedSidebar(hass) {
|
||||
return hass.dockedSidebar;
|
||||
}
|
||||
|
||||
_computeDisableSwipe(hass) {
|
||||
return NON_SWIPABLE_PANELS.indexOf(hass.panelUrl) !== -1;
|
||||
}
|
||||
|
||||
_computeRTL(hass) {
|
||||
return computeRTL(hass);
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("home-assistant-main", HomeAssistantMain);
|
151
src/layouts/home-assistant-main.ts
Normal file
151
src/layouts/home-assistant-main.ts
Normal file
@ -0,0 +1,151 @@
|
||||
import {
|
||||
LitElement,
|
||||
html,
|
||||
TemplateResult,
|
||||
PropertyDeclarations,
|
||||
CSSResult,
|
||||
css,
|
||||
PropertyValues,
|
||||
} from "lit-element";
|
||||
import "@polymer/app-layout/app-drawer-layout/app-drawer-layout";
|
||||
import "@polymer/app-layout/app-drawer/app-drawer";
|
||||
// Not a duplicate, it's for typing
|
||||
// tslint:disable-next-line
|
||||
import { AppDrawerElement } from "@polymer/app-layout/app-drawer/app-drawer";
|
||||
import "@polymer/app-route/app-route";
|
||||
import "@polymer/iron-media-query/iron-media-query";
|
||||
|
||||
import "../util/ha-url-sync";
|
||||
|
||||
import "./partial-panel-resolver";
|
||||
import { HomeAssistant, Route } from "../types";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { PolymerChangedEvent } from "../polymer-types";
|
||||
|
||||
import(/* webpackChunkName: "ha-sidebar" */ "../components/ha-sidebar");
|
||||
import(/* webpackChunkName: "voice-command-dialog" */ "../dialogs/ha-voice-command-dialog");
|
||||
|
||||
const NON_SWIPABLE_PANELS = ["kiosk", "map"];
|
||||
|
||||
class HomeAssistantMain extends LitElement {
|
||||
public hass?: HomeAssistant;
|
||||
public route?: Route;
|
||||
private _narrow?: boolean;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return {
|
||||
hass: {},
|
||||
narrow: {},
|
||||
route: {},
|
||||
};
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
const hass = this.hass;
|
||||
|
||||
if (!hass) {
|
||||
return;
|
||||
}
|
||||
|
||||
const disableSwipe = NON_SWIPABLE_PANELS.indexOf(hass.panelUrl) !== -1;
|
||||
|
||||
return html`
|
||||
<ha-url-sync .hass=${hass}></ha-url-sync>
|
||||
<ha-voice-command-dialog .hass=${hass}></ha-voice-command-dialog>
|
||||
<iron-media-query
|
||||
query="(max-width: 870px)"
|
||||
query-matches-changed=${this._narrowChanged}
|
||||
></iron-media-query>
|
||||
|
||||
<app-drawer-layout
|
||||
fullbleed
|
||||
.forceNarrow=${this._narrow || !hass.dockedSidebar}
|
||||
responsive-width="0"
|
||||
>
|
||||
<app-drawer
|
||||
id="drawer"
|
||||
align="start"
|
||||
slot="drawer"
|
||||
.disableSwipe=${disableSwipe}
|
||||
.swipeOpen=${!disableSwipe}
|
||||
.persistent=${hass.dockedSidebar}
|
||||
>
|
||||
<ha-sidebar .hass=${hass}></ha-sidebar>
|
||||
</app-drawer>
|
||||
|
||||
<partial-panel-resolver
|
||||
.narrow=${this._narrow}
|
||||
.hass=${hass}
|
||||
.route=${this.route}
|
||||
.showMenu=${hass.dockedSidebar}
|
||||
></partial-panel-resolver>
|
||||
</app-drawer-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated() {
|
||||
this.addEventListener("hass-open-menu", () => {
|
||||
if (this._narrow) {
|
||||
this.drawer.open();
|
||||
} else {
|
||||
fireEvent(this, "hass-dock-sidebar", { dock: true });
|
||||
}
|
||||
});
|
||||
this.addEventListener("hass-close-menu", () => {
|
||||
this.drawer.close();
|
||||
if (this.hass!.dockedSidebar) {
|
||||
fireEvent(this, "hass-dock-sidebar", { dock: false });
|
||||
}
|
||||
});
|
||||
this.addEventListener("hass-start-voice", () => {
|
||||
(this.voiceDialog as any).opened = true;
|
||||
});
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
super.updated(changedProps);
|
||||
|
||||
if (changedProps.has("route") && this._narrow) {
|
||||
this.drawer.close();
|
||||
}
|
||||
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
|
||||
// Make app-drawer adjust to a potential LTR/RTL change
|
||||
if (oldHass && oldHass.language !== this.hass!.language) {
|
||||
this.drawer._resetPosition();
|
||||
}
|
||||
}
|
||||
|
||||
private _narrowChanged(ev: PolymerChangedEvent<boolean>) {
|
||||
this._narrow = ev.detail.value;
|
||||
}
|
||||
|
||||
private get drawer(): AppDrawerElement {
|
||||
return this.shadowRoot!.querySelector("app-drawer")!;
|
||||
}
|
||||
|
||||
private get voiceDialog() {
|
||||
return this.shadowRoot!.querySelector("ha-voice-command-dialog")!;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
:host {
|
||||
color: var(--primary-text-color);
|
||||
/* remove the grey tap highlights in iOS on the fullscreen touch targets */
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
partial-panel-resolver,
|
||||
ha-sidebar {
|
||||
/* allow a light tap highlight on the actual interface elements */
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
partial-panel-resolver {
|
||||
height: 100%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("home-assistant-main", HomeAssistantMain);
|
@ -9,9 +9,11 @@ import {
|
||||
html,
|
||||
css,
|
||||
CSSResult,
|
||||
PropertyValues,
|
||||
} from "lit-element";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { EntitiesCardEntityConfig } from "../cards/hui-entities-card";
|
||||
import { computeRTL } from "../../../common/util/compute_rtl";
|
||||
|
||||
class HuiGenericEntityRow extends LitElement {
|
||||
public hass?: HomeAssistant;
|
||||
@ -76,6 +78,13 @@ class HuiGenericEntityRow extends LitElement {
|
||||
};
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
super.updated(changedProps);
|
||||
if (changedProps.has("hass")) {
|
||||
this.toggleAttribute("rtl", computeRTL(this.hass!));
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
:host {
|
||||
@ -119,6 +128,14 @@ class HuiGenericEntityRow extends LitElement {
|
||||
state-badge {
|
||||
flex: 0 0 40px;
|
||||
}
|
||||
:host([rtl]) .flex {
|
||||
margin-left: 0;
|
||||
margin-right: 16px;
|
||||
}
|
||||
:host([rtl]) .flex ::slotted(*) {
|
||||
margin-left: 0;
|
||||
margin-right: 8px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,12 @@ class HaPickLanguageRow extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
paper-item {
|
||||
direction: ltr;
|
||||
}
|
||||
paper-item[is-rtl] {
|
||||
direction: rtl;
|
||||
}
|
||||
</style>
|
||||
<ha-settings-row narrow="[[narrow]]">
|
||||
<span slot="heading"
|
||||
@ -43,9 +49,9 @@ class HaPickLanguageRow extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
selected="{{languageSelection}}"
|
||||
>
|
||||
<template is="dom-repeat" items="[[languages]]">
|
||||
<paper-item language-tag$="[[item.tag]]"
|
||||
>[[item.nativeName]]</paper-item
|
||||
>
|
||||
<paper-item language-tag$="[[item.key]]" is-rtl$="[[item.isRTL]]">
|
||||
[[item.nativeName]]
|
||||
</paper-item>
|
||||
</template>
|
||||
</paper-listbox>
|
||||
</paper-dropdown-menu>
|
||||
@ -76,9 +82,10 @@ class HaPickLanguageRow extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
if (!hass || !hass.translationMetadata) {
|
||||
return [];
|
||||
}
|
||||
return Object.keys(hass.translationMetadata.translations).map((key) => ({
|
||||
tag: key,
|
||||
nativeName: hass.translationMetadata.translations[key].nativeName,
|
||||
const translations = hass.translationMetadata.translations;
|
||||
return Object.keys(translations).map((key) => ({
|
||||
key,
|
||||
...translations[key],
|
||||
}));
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user