Custom iconsets in Icon Picker (#10399)

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
Paul Bottein 2021-10-28 15:28:14 +02:00 committed by GitHub
parent 5ef7a37c20
commit df572d59c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 98 additions and 18 deletions

View File

@ -7,6 +7,7 @@ import { css, html, LitElement, TemplateResult } from "lit";
import { ComboBoxLitRenderer, comboBoxRenderer } from "lit-vaadin-helpers"; import { ComboBoxLitRenderer, comboBoxRenderer } from "lit-vaadin-helpers";
import { customElement, property, query, state } from "lit/decorators"; import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import { customIcons } from "../data/custom_icons";
import { PolymerChangedEvent } from "../polymer-types"; import { PolymerChangedEvent } from "../polymer-types";
import "./ha-icon"; import "./ha-icon";
import "./ha-icon-button"; import "./ha-icon-button";
@ -111,11 +112,36 @@ export class HaIconPicker extends LitElement {
this._opened = ev.detail.value; this._opened = ev.detail.value;
if (this._opened && !iconItems.length) { if (this._opened && !iconItems.length) {
const iconList = await import("../../build/mdi/iconList.json"); const iconList = await import("../../build/mdi/iconList.json");
iconItems = iconList.default.map((icon) => ({ iconItems = iconList.default.map((icon) => ({
icon: `mdi:${icon.name}`, icon: `mdi:${icon.name}`,
keywords: icon.keywords, keywords: icon.keywords,
})); }));
(this.comboBox as any).filteredItems = iconItems; (this.comboBox as any).filteredItems = iconItems;
Object.keys(customIcons).forEach((iconSet) => {
this._loadCustomIconItems(iconSet);
});
}
}
private async _loadCustomIconItems(iconsetPrefix: string) {
try {
const getIconList = customIcons[iconsetPrefix].getIconList;
if (typeof getIconList !== "function") {
return;
}
const iconList = await getIconList();
const customIconItems = iconList.map((icon) => ({
icon: `${iconsetPrefix}:${icon.name}`,
keywords: icon.keywords ?? [],
}));
iconItems.push(...customIconItems);
(this.comboBox as any).filteredItems = iconItems;
} catch (e) {
// eslint-disable-next-line
console.warn(`Unable to load icon list for ${iconsetPrefix} iconset`);
} }
} }
@ -158,7 +184,9 @@ export class HaIconPicker extends LitElement {
if (filteredItems.length > 0) { if (filteredItems.length > 0) {
(this.comboBox as any).filteredItems = filteredItems; (this.comboBox as any).filteredItems = filteredItems;
} else { } else {
(this.comboBox as any).filteredItems = [filterString]; (this.comboBox as any).filteredItems = [
{ icon: filterString, keywords: [] },
];
} }
} else { } else {
(this.comboBox as any).filteredItems = iconItems; (this.comboBox as any).filteredItems = iconItems;

View File

@ -10,7 +10,7 @@ import {
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import { debounce } from "../common/util/debounce"; import { debounce } from "../common/util/debounce";
import { CustomIcon, customIconsets } from "../data/custom_iconsets"; import { CustomIcon, customIcons } from "../data/custom_icons";
import { import {
checkCacheVersion, checkCacheVersion,
Chunks, Chunks,
@ -356,7 +356,7 @@ export class HaIcon extends LitElement {
@state() private _path?: string; @state() private _path?: string;
@state() private _viewBox?; @state() private _viewBox?: string;
@state() private _legacy = false; @state() private _legacy = false;
@ -386,6 +386,7 @@ export class HaIcon extends LitElement {
if (!this.icon) { if (!this.icon) {
return; return;
} }
const requestedIcon = this.icon;
const [iconPrefix, origIconName] = this.icon.split(":", 2); const [iconPrefix, origIconName] = this.icon.split(":", 2);
let iconName = origIconName; let iconName = origIconName;
@ -395,10 +396,10 @@ export class HaIcon extends LitElement {
} }
if (!MDI_PREFIXES.includes(iconPrefix)) { if (!MDI_PREFIXES.includes(iconPrefix)) {
if (iconPrefix in customIconsets) { if (iconPrefix in customIcons) {
const customIconset = customIconsets[iconPrefix]; const customIcon = customIcons[iconPrefix];
if (customIconset) { if (customIcon && typeof customIcon.getIcon === "function") {
this._setCustomPath(customIconset(iconName)); this._setCustomPath(customIcon.getIcon(iconName), requestedIcon);
} }
return; return;
} }
@ -441,14 +442,16 @@ export class HaIcon extends LitElement {
} }
if (databaseIcon) { if (databaseIcon) {
if (this.icon === requestedIcon) {
this._path = databaseIcon; this._path = databaseIcon;
}
cachedIcons[iconName] = databaseIcon; cachedIcons[iconName] = databaseIcon;
return; return;
} }
const chunk = findIconChunk(iconName); const chunk = findIconChunk(iconName);
if (chunk in chunks) { if (chunk in chunks) {
this._setPath(chunks[chunk], iconName); this._setPath(chunks[chunk], iconName, requestedIcon);
return; return;
} }
@ -456,19 +459,31 @@ export class HaIcon extends LitElement {
response.json() response.json()
); );
chunks[chunk] = iconPromise; chunks[chunk] = iconPromise;
this._setPath(iconPromise, iconName); this._setPath(iconPromise, iconName, requestedIcon);
debouncedWriteCache(); debouncedWriteCache();
} }
private async _setCustomPath(promise: Promise<CustomIcon>) { private async _setCustomPath(
promise: Promise<CustomIcon>,
requestedIcon: string
) {
const icon = await promise; const icon = await promise;
if (this.icon !== requestedIcon) {
return;
}
this._path = icon.path; this._path = icon.path;
this._viewBox = icon.viewBox; this._viewBox = icon.viewBox;
} }
private async _setPath(promise: Promise<Icons>, iconName: string) { private async _setPath(
promise: Promise<Icons>,
iconName: string,
requestedIcon: string
) {
const iconPack = await promise; const iconPack = await promise;
if (this.icon === requestedIcon) {
this._path = iconPack[iconName]; this._path = iconPack[iconName];
}
cachedIcons[iconName] = iconPack[iconName]; cachedIcons[iconName] = iconPack[iconName];
} }

40
src/data/custom_icons.ts Normal file
View File

@ -0,0 +1,40 @@
import { customIconsets } from "./custom_iconsets";
export interface CustomIcon {
path: string;
viewBox?: string;
}
export interface CustomIconListItem {
name: string;
keywords?: string[];
}
export interface CustomIconHelpers {
getIcon: (name: string) => Promise<CustomIcon>;
getIconList?: () => Promise<CustomIconListItem[]>;
}
export interface CustomIconsWindow {
customIcons?: {
[key: string]: CustomIconHelpers;
};
}
const customIconsWindow = window as CustomIconsWindow;
if (!("customIcons" in customIconsWindow)) {
customIconsWindow.customIcons = {};
}
// Proxy for backward compatibility with icon sets
export const customIcons = new Proxy(customIconsWindow.customIcons!, {
get: (obj, prop: string) =>
obj[prop] ??
(customIconsets[prop]
? {
getIcon: customIconsets[prop],
}
: undefined),
has: (obj, prop: string) => prop in obj || prop in customIconsets,
});

View File

@ -1,9 +1,6 @@
export interface CustomIcon { import { CustomIcon } from "./custom_icons";
path: string;
viewBox?: string;
}
export interface CustomIconsetsWindow { interface CustomIconsetsWindow {
customIconsets?: { [key: string]: (name: string) => Promise<CustomIcon> }; customIconsets?: { [key: string]: (name: string) => Promise<CustomIcon> };
} }