Allow local storage decorator to register as property (#6776)

This commit is contained in:
Bram Kragten 2020-09-04 22:22:55 +02:00 committed by GitHub
parent 349a5f52b1
commit b065f002a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 114 additions and 21 deletions

View File

@ -1,7 +1,33 @@
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { PropertyDeclaration, UpdatingElement } from "lit-element";
import type { ClassElement } from "../../types";
type Callback = (oldValue: any, newValue: any) => void;
class Storage {
private _storage: any = {};
constructor() {
window.addEventListener("storage", (ev: StorageEvent) => {
if (ev.key && this.hasKey(ev.key)) {
this._storage[ev.key] = ev.newValue
? JSON.parse(ev.newValue)
: ev.newValue;
if (this._listeners[ev.key]) {
this._listeners[ev.key].forEach((listener) =>
listener(
ev.oldValue ? JSON.parse(ev.oldValue) : ev.oldValue,
this._storage[ev.key!]
)
);
}
}
});
}
private _storage: { [storageKey: string]: any } = {};
private _listeners: {
[storageKey: string]: Callback[];
} = {};
public addFromStorage(storageKey: any): void {
if (!this._storage[storageKey]) {
@ -12,6 +38,30 @@ class Storage {
}
}
public subscribeChanges(
storageKey: string,
callback: Callback
): UnsubscribeFunc {
if (this._listeners[storageKey]) {
this._listeners[storageKey].push(callback);
} else {
this._listeners[storageKey] = [callback];
}
return () => {
this.unsubscribeChanges(storageKey, callback);
};
}
public unsubscribeChanges(storageKey: string, callback: Callback) {
if (!(storageKey in this._listeners)) {
return;
}
const index = this._listeners[storageKey].indexOf(callback);
if (index !== -1) {
this._listeners[storageKey].splice(index, 1);
}
}
public hasKey(storageKey: string): any {
return storageKey in this._storage;
}
@ -32,30 +82,49 @@ class Storage {
const storage = new Storage();
export const LocalStorage = (key?: string) => {
return (element: ClassElement, propName: string) => {
const storageKey = key || propName;
const initVal = element.initializer ? element.initializer() : undefined;
export const LocalStorage = (
storageKey?: string,
property?: boolean,
propertyOptions?: PropertyDeclaration
): any => {
return (clsElement: ClassElement) => {
const key = String(clsElement.key);
storageKey = storageKey || String(clsElement.key);
const initVal = clsElement.initializer
? clsElement.initializer()
: undefined;
storage.addFromStorage(storageKey);
const subscribe = (el: UpdatingElement): UnsubscribeFunc =>
storage.subscribeChanges(storageKey!, (oldValue) => {
el.requestUpdate(clsElement.key, oldValue);
});
const getValue = (): any => {
return storage.hasKey(storageKey)
? storage.getValue(storageKey)
return storage.hasKey(storageKey!)
? storage.getValue(storageKey!)
: initVal;
};
const setValue = (val: any) => {
storage.setValue(storageKey, val);
const setValue = (el: UpdatingElement, value: any) => {
let oldValue: unknown | undefined;
if (property) {
oldValue = getValue();
}
storage.setValue(storageKey!, value);
if (property) {
el.requestUpdate(clsElement.key, oldValue);
}
};
return {
kind: "method",
placement: "own",
key: element.key,
placement: "prototype",
key: clsElement.key,
descriptor: {
set(value) {
setValue(value);
set(this: UpdatingElement, value: unknown) {
setValue(this, value);
},
get() {
return getValue();
@ -63,6 +132,24 @@ export const LocalStorage = (key?: string) => {
enumerable: true,
configurable: true,
},
finisher(cls: typeof UpdatingElement) {
if (property) {
const connectedCallback = cls.prototype.connectedCallback;
const disconnectedCallback = cls.prototype.disconnectedCallback;
cls.prototype.connectedCallback = function () {
connectedCallback.call(this);
this[`__unbsubLocalStorage${key}`] = subscribe(this);
};
cls.prototype.disconnectedCallback = function () {
disconnectedCallback.call(this);
this[`__unbsubLocalStorage${key}`]();
};
cls.createProperty(clsElement.key, {
noAccessor: true,
...propertyOptions,
});
}
},
};
};
};

View File

@ -1,4 +1,3 @@
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox";
import {
@ -12,6 +11,7 @@ import {
TemplateResult,
} from "lit-element";
import { fireEvent } from "../../common/dom/fire_event";
import "../ha-paper-dropdown-menu";
import { HaFormElement, HaFormSelectData, HaFormSelectSchema } from "./ha-form";
@customElement("ha-form-select")
@ -24,7 +24,7 @@ export class HaFormSelect extends LitElement implements HaFormElement {
@property() public suffix!: string;
@query("paper-dropdown-menu") private _input?: HTMLElement;
@query("ha-paper-dropdown-menu") private _input?: HTMLElement;
public focus() {
if (this._input) {
@ -34,7 +34,7 @@ export class HaFormSelect extends LitElement implements HaFormElement {
protected render(): TemplateResult {
return html`
<paper-dropdown-menu .label=${this.label}>
<ha-paper-dropdown-menu .label=${this.label}>
<paper-listbox
slot="dropdown-content"
attr-for-selected="item-value"
@ -51,7 +51,7 @@ export class HaFormSelect extends LitElement implements HaFormElement {
`
)}
</paper-listbox>
</paper-dropdown-menu>
</ha-paper-dropdown-menu>
`;
}
@ -74,7 +74,7 @@ export class HaFormSelect extends LitElement implements HaFormElement {
static get styles(): CSSResult {
return css`
paper-dropdown-menu {
ha-paper-dropdown-menu {
display: block;
}
`;

View File

@ -191,11 +191,15 @@ class HaSidebar extends LitElement {
private _recentKeydownActiveUntil = 0;
// @ts-ignore
@LocalStorage("sidebarPanelOrder")
@LocalStorage("sidebarPanelOrder", true, {
attribute: false,
})
private _panelOrder: string[] = [];
// @ts-ignore
@LocalStorage("sidebarHiddenPanels")
@LocalStorage("sidebarHiddenPanels", true, {
attribute: false,
})
private _hiddenPanels: string[] = [];
private _sortable?;
@ -400,7 +404,9 @@ class HaSidebar extends LitElement {
changedProps.has("_externalConfig") ||
changedProps.has("_notifications") ||
changedProps.has("_editMode") ||
changedProps.has("_renderEmptySortable")
changedProps.has("_renderEmptySortable") ||
changedProps.has("_hiddenPanels") ||
(changedProps.has("_panelOrder") && !this._editMode)
) {
return true;
}