Clean up mixins (#4076)

* Clean up mixins

* Lint
This commit is contained in:
Paulus Schoutsen 2019-10-20 05:31:58 -07:00 committed by Bram Kragten
parent 4dbf5327bd
commit 785453aa79
29 changed files with 165 additions and 291 deletions

View File

@ -1,8 +1,9 @@
import { Constructor, customElement, CSSResult, css } from "lit-element";
import { customElement, CSSResult, css } from "lit-element";
import "@material/mwc-checkbox";
// tslint:disable-next-line
import { Checkbox } from "@material/mwc-checkbox";
import { style } from "@material/mwc-checkbox/mwc-checkbox-css";
import { Constructor } from "../types";
// tslint:disable-next-line
const MwcCheckbox = customElements.get("mwc-checkbox") as Constructor<Checkbox>;

View File

@ -1,12 +1,8 @@
import {
classMap,
html,
customElement,
Constructor,
} from "@material/mwc-base/base-element";
import { classMap, html, customElement } from "@material/mwc-base/base-element";
import { ripple } from "@material/mwc-ripple/ripple-directive.js";
import "@material/mwc-fab";
import { Constructor } from "../types";
// tslint:disable-next-line
import { Fab } from "@material/mwc-fab";
// tslint:disable-next-line

View File

@ -1,4 +1,5 @@
import { Constructor } from "lit-element";
import { Constructor } from "../types";
import "@polymer/iron-icon/iron-icon";
// Not duplicate, this is for typing.
// tslint:disable-next-line

View File

@ -1,6 +1,6 @@
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
import { Constructor } from "lit-element";
import { PolymerElement } from "@polymer/polymer";
import { Constructor } from "../types";
const paperDropdownClass = customElements.get(
"paper-dropdown-menu"

View File

@ -1,5 +1,5 @@
import { Constructor } from "lit-element";
import "@polymer/paper-icon-button/paper-icon-button";
import { Constructor } from "../types";
// Not duplicate, this is for typing.
// tslint:disable-next-line
import { PaperIconButtonElement } from "@polymer/paper-icon-button/paper-icon-button";

View File

@ -1,5 +1,5 @@
import { Constructor } from "lit-element";
import "@polymer/paper-icon-button/paper-icon-button";
import { Constructor } from "../types";
// Not duplicate, this is for typing.
// tslint:disable-next-line
import { PaperIconButtonElement } from "@polymer/paper-icon-button/paper-icon-button";

View File

@ -1,5 +1,5 @@
import { Constructor } from "lit-element";
import "@polymer/paper-icon-button/paper-icon-button";
import { Constructor } from "../types";
// Not duplicate, this is for typing.
// tslint:disable-next-line
import { PaperIconButtonElement } from "@polymer/paper-icon-button/paper-icon-button";

View File

@ -1,5 +1,5 @@
import { Constructor } from "lit-element";
import "@polymer/paper-icon-button/paper-icon-button";
import { Constructor } from "../types";
// Not duplicate, this is for typing.
// tslint:disable-next-line
import { PaperIconButtonElement } from "@polymer/paper-icon-button/paper-icon-button";

View File

@ -1,8 +1,9 @@
import { Constructor, customElement, CSSResult, css, query } from "lit-element";
import { customElement, CSSResult, css, query } from "lit-element";
import "@material/mwc-switch";
import { style } from "@material/mwc-switch/mwc-switch-css";
// tslint:disable-next-line
import { Switch } from "@material/mwc-switch";
import { Constructor } from "../types";
// tslint:disable-next-line
const MwcSwitch = customElements.get("mwc-switch") as Constructor<Switch>;

View File

@ -1,45 +1,20 @@
import {
Constructor,
LitElement,
PropertyDeclarations,
PropertyValues,
} from "lit-element";
import { getLocalLanguage } from "../util/hass-translation";
import { localizeLiteBaseMixin } from "./localize-lite-base-mixin";
import { LitElement, PropertyValues, property } from "lit-element";
import { getLocalLanguage, getTranslation } from "../util/hass-translation";
import { computeLocalize, LocalizeFunc } from "../common/translations/localize";
import { Constructor, Resources } from "../types";
const empty = () => "";
interface LitLocalizeLiteMixin {
language: string;
resources: {};
translationFragment: string;
localize: LocalizeFunc;
}
export const litLocalizeLiteMixin = <T extends LitElement>(
superClass: Constructor<T>
): Constructor<T & LitLocalizeLiteMixin> =>
// @ts-ignore
class extends localizeLiteBaseMixin(superClass) {
// Decorators not possible in anonymous classes
// And also, we cannot declare the variable without overriding the Lit setter.
static get properties(): PropertyDeclarations {
return {
localize: {},
language: {},
resources: {},
translationFragment: {},
};
}
constructor() {
super();
// This will prevent undefined errors if called before connected to DOM.
this.localize = empty;
export const litLocalizeLiteMixin = <T extends Constructor<LitElement>>(
superClass: T
) => {
class LitLocalizeLiteClass extends superClass {
// Initialized to empty will prevent undefined errors if called before connected to DOM.
@property() public localize: LocalizeFunc = empty;
@property() public resources?: Resources;
// Use browser language setup before login.
this.language = getLocalLanguage();
}
@property() public language?: string = getLocalLanguage();
@property() public translationFragment?: string;
public connectedCallback(): void {
super.connectedCallback();
@ -51,7 +26,7 @@ export const litLocalizeLiteMixin = <T extends LitElement>(
);
}
public updated(changedProperties: PropertyValues) {
protected updated(changedProperties: PropertyValues) {
super.updated(changedProperties);
if (
changedProperties.has("language") ||
@ -64,4 +39,38 @@ export const litLocalizeLiteMixin = <T extends LitElement>(
);
}
}
protected async _initializeLocalizeLite() {
if (this.resources) {
return;
}
if (!this.translationFragment) {
// In dev mode, we will issue a warning if after a second we are still
// not configured correctly.
if (__DEV__) {
setTimeout(
() =>
!this.resources &&
// tslint:disable-next-line
console.error(
"Forgot to pass in resources or set translationFragment for",
this.nodeName
),
1000
);
}
return;
}
const { language, data } = await getTranslation(
this.translationFragment!,
this.language!
);
this.resources = {
[language]: data,
};
}
}
return LitLocalizeLiteClass;
};

View File

@ -1,51 +0,0 @@
/**
* Lite base mixin to add localization without depending on the Hass object.
*/
import { getTranslation } from "../util/hass-translation";
import { Resources } from "../types";
/**
* @polymerMixin
*/
export const localizeLiteBaseMixin = (superClass) =>
class extends superClass {
public resources?: Resources;
public language?: string;
public translationFragment?: string;
protected _initializeLocalizeLite() {
if (this.resources) {
return;
}
if (!this.translationFragment) {
// In dev mode, we will issue a warning if after a second we are still
// not configured correctly.
if (__DEV__) {
setTimeout(
() =>
!this.resources &&
// tslint:disable-next-line
console.error(
"Forgot to pass in resources or set translationFragment for",
this.nodeName
),
1000
);
}
return;
}
this._downloadResources();
}
private async _downloadResources() {
const { language, data } = await getTranslation(
this.translationFragment!,
this.language!
);
this.resources = {
[language]: data,
};
}
};

View File

@ -1,51 +0,0 @@
/**
* Lite mixin to add localization without depending on the Hass object.
*/
import { dedupingMixin } from "@polymer/polymer/lib/utils/mixin";
import { getLocalLanguage } from "../util/hass-translation";
import { localizeLiteBaseMixin } from "./localize-lite-base-mixin";
import { computeLocalize } from "../common/translations/localize";
/**
* @polymerMixin
*/
export const localizeLiteMixin = dedupingMixin(
(superClass) =>
class extends localizeLiteBaseMixin(superClass) {
static get properties() {
return {
language: {
type: String,
// Use browser language setup before login.
value: getLocalLanguage(),
},
resources: Object,
// The fragment to load.
translationFragment: String,
/**
* Translates a string to the current `language`. Any parameters to the
* string should be passed in order, as follows:
* `localize(stringKey, param1Name, param1Value, param2Name, param2Value)`
*/
localize: {
type: Function,
computed: "__computeLocalize(language, resources, formats)",
},
};
}
public ready() {
super.ready();
this._initializeLocalizeLite();
}
protected __computeLocalize(language, resources, formats?) {
return computeLocalize(
this.constructor.prototype,
language,
resources,
formats
);
}
}
);

View File

@ -1,5 +1,5 @@
import { UpdatingElement, Constructor, PropertyValues } from "lit-element";
import { HomeAssistant } from "../types";
import { UpdatingElement, PropertyValues } from "lit-element";
import { HomeAssistant, Constructor } from "../types";
export interface ProvideHassElement {
provideHass(element: HTMLElement);
@ -7,9 +7,9 @@ export interface ProvideHassElement {
/* tslint:disable */
export const ProvideHassLitMixin = <T extends UpdatingElement>(
superClass: Constructor<T>
): Constructor<T & ProvideHassElement> =>
export const ProvideHassLitMixin = <T extends Constructor<UpdatingElement>>(
superClass: T
) =>
// @ts-ignore
class extends superClass {
protected hass!: HomeAssistant;

View File

@ -1,32 +1,21 @@
import {
LitElement,
Constructor,
PropertyValues,
PropertyDeclarations,
} from "lit-element";
import { LitElement, PropertyValues, property } from "lit-element";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { HomeAssistant, Constructor } from "../types";
export interface HassSubscribeElement {
hassSubscribe(): UnsubscribeFunc[];
}
/* tslint:disable-next-line */
export const SubscribeMixin = <T extends LitElement>(
superClass: Constructor<T>
): Constructor<T & HassSubscribeElement> =>
// @ts-ignore
class extends superClass {
export const SubscribeMixin = <T extends Constructor<LitElement>>(
superClass: T
) => {
class SubscribeClass extends superClass {
@property() public hass?: HomeAssistant;
/* tslint:disable-next-line */
private __unsubs?: UnsubscribeFunc[];
// Decorators not possible in anonymous classes
// And also, we cannot declare the variable without overriding the Lit setter.
static get properties(): PropertyDeclarations {
return {
hass: {},
};
}
public connectedCallback() {
super.connectedCallback();
this.__checkSubscribed();
@ -57,11 +46,12 @@ export const SubscribeMixin = <T extends LitElement>(
if (
this.__unsubs !== undefined ||
!((this as unknown) as Element).isConnected ||
// @ts-ignore
this.hass === undefined
) {
return;
}
this.__unsubs = this.hassSubscribe();
}
}
return SubscribeClass;
};

View File

@ -1,8 +1,8 @@
import { clearState } from "../util/ha-pref-storage";
import { askWrite } from "../common/auth/token_storage";
import { subscribeUser, userCollection } from "../data/ws-user";
import { Constructor, LitElement } from "lit-element";
import { HassBaseEl } from "./hass-base-mixin";
import { Constructor } from "../types";
declare global {
// for fire event
@ -11,7 +11,7 @@ declare global {
}
}
export default (superClass: Constructor<LitElement & HassBaseEl>) =>
export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
class extends superClass {
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);

View File

@ -17,12 +17,12 @@ import hassCallApi from "../util/hass-call-api";
import { subscribePanels } from "../data/ws-panels";
import { forwardHaptic } from "../data/haptics";
import { fireEvent } from "../common/dom/fire_event";
import { Constructor, LitElement } from "lit-element";
import { Constructor } from "../types";
import { HassBaseEl } from "./hass-base-mixin";
import { broadcastConnectionStatus } from "../data/connection-status";
export const connectionMixin = (
superClass: Constructor<LitElement & HassBaseEl>
export const connectionMixin = <T extends Constructor<HassBaseEl>>(
superClass: T
) =>
class extends superClass {
protected initializeHass(auth: Auth, conn: Connection) {
@ -90,13 +90,13 @@ export const connectionMixin = (
conn.sendMessage(msg);
},
// For messages that expect a response
callWS: <T>(msg) => {
callWS: <R>(msg) => {
if (__DEV__) {
// tslint:disable-next-line: no-console
console.log("Sending", msg);
}
const resp = conn.sendMessagePromise<T>(msg);
const resp = conn.sendMessagePromise<R>(msg);
if (__DEV__) {
resp.then(

View File

@ -1,7 +1,7 @@
import { Constructor, LitElement } from "lit-element";
import { HASSDomEvent } from "../common/dom/fire_event";
import { HassBaseEl } from "./hass-base-mixin";
import { makeDialogManager, showDialog } from "../dialogs/make-dialog-manager";
import { Constructor } from "../types";
interface RegisterDialogParams {
dialogShowEvent: keyof HASSDomEvents;
@ -20,8 +20,8 @@ declare global {
}
}
export const dialogManagerMixin = (
superClass: Constructor<LitElement & HassBaseEl>
export const dialogManagerMixin = <T extends Constructor<HassBaseEl>>(
superClass: T
) =>
class extends superClass {
protected firstUpdated(changedProps) {

View File

@ -1,8 +1,8 @@
import { Constructor, LitElement } from "lit-element";
import { HassBaseEl } from "./hass-base-mixin";
import { showToast } from "../util/toast";
import { Constructor } from "../types";
export default (superClass: Constructor<LitElement & HassBaseEl>) =>
export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
class extends superClass {
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);

View File

@ -1,8 +1,8 @@
import { Constructor, LitElement, PropertyValues } from "lit-element";
import { PropertyValues } from "lit-element";
import { HassBaseEl } from "./hass-base-mixin";
import { HapticType } from "../data/haptics";
import { HomeAssistant } from "../types";
import { HomeAssistant, Constructor } from "../types";
import { HASSDomEvent } from "../common/dom/fire_event";
import { storeState } from "../util/ha-pref-storage";
@ -35,7 +35,7 @@ const handleHaptic = (hapticTypeEvent: HASSDomEvent<HapticType>) => {
navigator.vibrate(hapticPatterns[hapticTypeEvent.detail]);
};
export const hapticMixin = (superClass: Constructor<LitElement & HassBaseEl>) =>
export const hapticMixin = <T extends Constructor<HassBaseEl>>(superClass: T) =>
class extends superClass {
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);

View File

@ -1,39 +1,21 @@
import {
Constructor,
// @ts-ignore
property,
PropertyDeclarations,
} from "lit-element";
import { Auth, Connection } from "home-assistant-js-websocket";
import { LitElement, property } from "lit-element";
import { HomeAssistant } from "../types";
import { Auth, Connection } from "home-assistant-js-websocket";
/* tslint:disable */
export class HassBaseEl {
protected hass?: HomeAssistant;
protected _pendingHass: Partial<HomeAssistant> = {};
protected initializeHass(_auth: Auth, _conn: Connection) {}
protected hassConnected() {}
protected hassReconnected() {}
protected hassDisconnected() {}
protected hassChanged(_hass: HomeAssistant, _oldHass?: HomeAssistant) {}
protected panelUrlChanged(_newPanelUrl: string) {}
public provideHass(_el: HTMLElement) {}
protected _updateHass(_obj: Partial<HomeAssistant>) {}
}
export default <T>(superClass: Constructor<T>): Constructor<T & HassBaseEl> =>
// @ts-ignore
class extends superClass {
export class HassBaseEl extends LitElement {
@property() public hass?: HomeAssistant;
protected _pendingHass: Partial<HomeAssistant> = {};
// tslint:disable-next-line: variable-name
private __provideHass: HTMLElement[] = [];
// Decorators not possible in anonymous classes
// And also, we cannot declare the variable without overriding the Lit setter.
static get properties(): PropertyDeclarations {
return {
hass: {},
};
public provideHass(el) {
this.__provideHass.push(el);
el.hass = this.hass;
}
protected initializeHass(_auth: Auth, _conn: Connection) {
// implemented in connection-mixin
// tslint:disable-next-line
}
// Exists so all methods can safely call super method
@ -59,19 +41,11 @@ export default <T>(superClass: Constructor<T>): Constructor<T & HassBaseEl> =>
});
}
public provideHass(el) {
this.__provideHass.push(el);
// @ts-ignore
el.hass = this.hass;
}
protected _updateHass(obj: Partial<HomeAssistant>) {
// @ts-ignore
if (!this.hass) {
this._pendingHass = { ...this._pendingHass, ...obj };
return;
}
// @ts-ignore
this.hass = { ...this.hass, ...obj };
}
};
}

View File

@ -1,4 +1,3 @@
import HassBaseMixin from "./hass-base-mixin";
import AuthMixin from "./auth-mixin";
import TranslationsMixin from "./translations-mixin";
import ThemesMixin from "./themes-mixin";
@ -11,12 +10,13 @@ import NotificationMixin from "./notification-mixin";
import DisconnectToastMixin from "./disconnect-toast-mixin";
import { hapticMixin } from "./haptic-mixin";
import { urlSyncMixin } from "./url-sync-mixin";
import { LitElement } from "lit-element";
import { Constructor } from "../types";
import { HassBaseEl } from "./hass-base-mixin";
const ext = <T>(baseClass: T, mixins): T =>
const ext = <T extends Constructor>(baseClass: T, mixins): T =>
mixins.reduceRight((base, mixin) => mixin(base), baseClass);
export class HassElement extends ext(HassBaseMixin(LitElement), [
export class HassElement extends ext(HassBaseEl, [
AuthMixin,
ThemesMixin,
TranslationsMixin,

View File

@ -1,6 +1,5 @@
import { Constructor, LitElement } from "lit-element";
import { HassBaseEl } from "./hass-base-mixin";
import { Constructor } from "../types";
declare global {
// for fire event
@ -11,7 +10,7 @@ declare global {
}
}
export default (superClass: Constructor<LitElement & HassBaseEl>) =>
export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
class extends superClass {
private _moreInfoEl?: any;

View File

@ -1,7 +1,11 @@
export default (superClass) =>
import { HassBaseEl } from "./hass-base-mixin";
import { Constructor } from "../types";
export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
class extends superClass {
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
// @ts-ignore
this.registerDialog({
dialogShowEvent: "hass-notification",
dialogTag: "notification-manager",

View File

@ -1,8 +1,7 @@
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";
import { HomeAssistant } from "../types";
import { HomeAssistant, Constructor } from "../types";
interface DockSidebarParams {
dock: HomeAssistant["dockedSidebar"];
@ -19,7 +18,7 @@ declare global {
}
}
export default (superClass: Constructor<LitElement & HassBaseEl>) =>
export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
class extends superClass {
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);

View File

@ -1,9 +1,9 @@
import applyThemesOnElement from "../common/dom/apply_themes_on_element";
import { storeState } from "../util/ha-pref-storage";
import { subscribeThemes } from "../data/ws-themes";
import { Constructor, LitElement } from "lit-element";
import { HassBaseEl } from "./hass-base-mixin";
import { HASSDomEvent } from "../common/dom/fire_event";
import { Constructor } from "../types";
declare global {
// for add event listener
@ -12,7 +12,7 @@ declare global {
}
}
export default (superClass: Constructor<LitElement & HassBaseEl>) =>
export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
class extends superClass {
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);

View File

@ -4,11 +4,10 @@ import {
getLocalLanguage,
getUserLanguage,
} from "../util/hass-translation";
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";
import { HomeAssistant, Constructor } from "../types";
import { storeState } from "../util/ha-pref-storage";
import {
getHassTranslations,
@ -19,7 +18,7 @@ import {
* superClass needs to contain `this.hass` and `this._updateHass`.
*/
export default (superClass: Constructor<LitElement & HassBaseEl>) =>
export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
class extends superClass {
// tslint:disable-next-line: variable-name
private __coreProgress?: string;

View File

@ -1,12 +1,12 @@
import { Constructor, LitElement } from "lit-element";
import { HassBaseEl } from "./hass-base-mixin";
import { fireEvent } from "../common/dom/fire_event";
import { Constructor } from "../types";
/* tslint:disable:no-console */
const DEBUG = false;
export const urlSyncMixin = (
superClass: Constructor<LitElement & HassBaseEl>
export const urlSyncMixin = <T extends Constructor<HassBaseEl>>(
superClass: T
) =>
// Disable this functionality in the demo.
__DEMO__

View File

@ -1,10 +1,11 @@
import { Constructor, LitElement } from "lit-element";
import { UpdatingElement } from "lit-element";
import { HassBaseEl } from "./hass-base-mixin";
import {
showZHADeviceInfoDialog,
ZHADeviceInfoDialogParams,
} from "../dialogs/zha-device-info-dialog/show-dialog-zha-device-info";
import { HASSDomEvent } from "../common/dom/fire_event";
import { Constructor } from "../types";
declare global {
// for fire event
@ -15,7 +16,7 @@ declare global {
}
}
export default (superClass: Constructor<LitElement & HassBaseEl>) =>
export default (superClass: Constructor<UpdatingElement & HassBaseEl>) =>
class extends superClass {
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);

View File

@ -40,6 +40,8 @@ declare global {
}
}
export type Constructor<T = {}> = new (...args: any[]) => T;
export interface WebhookError {
code: number;
message: string;