From a073a5678f0f62d2dc84a00aab0ad0b221e2885a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Lov=C3=A9n?= Date: Mon, 10 May 2021 21:46:47 +0000 Subject: [PATCH] Better icon processing --- README.md | 6 ++-- custom_components/fontawesome/__init__.py | 28 ++++++++++++--- custom_components/fontawesome/main.js | 2 +- custom_components/fontawesome/manifest.json | 1 + js/main.js | 38 +++++++++++++++++++++ 5 files changed, 65 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 6271492..ecacebd 100644 --- a/README.md +++ b/README.md @@ -42,13 +42,11 @@ The icons are useable anywhere in Home Assistant - not only in lovelace. Yes. You need the `.svg` files from the Pro icon set. -Place the icon files in `/custom_components/fontawesome/data/pro/` and access them with the `fapro:` prefix, e.g. `fapro:lightbulb-on`. - -This does not work with the duotone icon set. +Place the icon files in `/custom_components/fontawesome/data/pro/` (the filenames must end with `.svg`) and access them with the `fapro:` prefix, e.g. `fapro:lightbulb-on`. ### Can I add non-fontawesome icons using this? -Yes, provided you have svg files of the icons which consist of a single `` element. +Yes, provided you have svg files of the icons which consist one or more `` elements and no transforms or other weird stuff. Just do the same as for the Pro icon set above. Put svg files in the same directory, and use the same prefix. diff --git a/custom_components/fontawesome/__init__.py b/custom_components/fontawesome/__init__.py index 81eda23..c1bb395 100644 --- a/custom_components/fontawesome/__init__.py +++ b/custom_components/fontawesome/__init__.py @@ -1,6 +1,8 @@ import os -import re import logging + +from bs4 import BeautifulSoup + from homeassistant.components.frontend import add_extra_js_url from homeassistant.components.http import HomeAssistantView from homeassistant.core import callback @@ -17,6 +19,13 @@ ICONS_URL = f'/{DOMAIN}/icons' ICONS_PATH = f'custom_components/{DOMAIN}/data' +FONTAWESOME_CLASSES = { + "fa-primary": "primary", + "fa-secondary": "secondary", + "primary": "primary", + "secondary": "secondary", +} + async def async_setup(hass, config): hass.http.register_static_path( @@ -61,10 +70,19 @@ async def get(self, request, filename): try: with open(path, mode="r", encoding="utf-8", errors="ignore") as fp: data = fp.read() - svgPath = re.search('d="([^"]+)"', data) - viewBox = re.search('viewBox="([^"]+)"', data) - response["viewBox"] = viewBox.group(1) if viewBox else None - response["path"] = svgPath.group(1) if svgPath else None + + soup = BeautifulSoup(data, 'html.parser') + paths = soup.find_all("path") + svgPath = " ".join([p.get("d", "") for p in paths]) + viewBox = soup.svg.get("viewbox", None) if soup.svg else None + + response["viewBox"] = viewBox + response["path"] = svgPath + response["paths"] = {} + for p in paths: + key = FONTAWESOME_CLASSES.get(p.get("class", [])[0], None) + if key: + response["paths"][key] = p.get("d", "") except Exception: pass diff --git a/custom_components/fontawesome/main.js b/custom_components/fontawesome/main.js index 32fa4fa..184ef25 100644 --- a/custom_components/fontawesome/main.js +++ b/custom_components/fontawesome/main.js @@ -1 +1 @@ -(()=>{const o={},s=(s,n)=>new Promise((async(t,c)=>{const w=`${s}:${n}`;o[w]&&t(o[w]);const a=(await fetch(`/fontawesome/icons/${s}/${n}`)).json();a&&(await a).path&&(o[w]=a),t(o[w])}));"customIconsets"in window||(window.customIconsets={}),window.customIconsets.fab=o=>s("brands",o),window.customIconsets.far=o=>s("regular",o),window.customIconsets.fas=o=>s("solid",o),window.customIconsets.fapro=o=>s("pro",o)})(); \ No newline at end of file +(()=>{const t={},o=(o,s)=>new Promise((async(e,n)=>{const a=`${o}:${s}`;t[a]&&e(t[a]);const c=(await fetch(`/fontawesome/icons/${o}/${s}`)).json();c&&(await c).path&&(t[a]=c),e(t[a])}));"customIconsets"in window||(window.customIconsets={}),window.customIconsets.fab=t=>o("brands",t),window.customIconsets.far=t=>o("regular",t),window.customIconsets.fas=t=>o("solid",t),window.customIconsets.fapro=t=>o("pro",t),customElements.whenDefined("ha-icon").then((()=>{customElements.get("ha-icon").prototype._setCustomPath=async function(t){const o=await t;this._path=o.path,this._viewBox=o.viewBox,await this.UpdateComplete;const s=this.shadowRoot.querySelector("ha-svg-icon");s&&s.setPaths&&s.setPaths(o.paths)}})),customElements.whenDefined("ha-svg-icon").then((()=>{customElements.get("ha-svg-icon").prototype.setPaths=async function(t){await this.updateComplete;const o=this.shadowRoot.querySelector("style")||document.createElement("style");o.innerHTML="svg path.secondary { fill: var(--paper-item-icon-color); }",this.shadowRoot.appendChild(o);const s=this.shadowRoot.querySelector("g");for(const o in t){const e=document.createElementNS("http://www.w3.org/2000/svg","path");e.setAttribute("d",t[o]),e.classList.add(o),s.appendChild(e)}}}))})(); \ No newline at end of file diff --git a/custom_components/fontawesome/manifest.json b/custom_components/fontawesome/manifest.json index f13f746..d0f67e8 100644 --- a/custom_components/fontawesome/manifest.json +++ b/custom_components/fontawesome/manifest.json @@ -3,6 +3,7 @@ "name": "Fontawesome icons", "documentation": "https://github.com/thomasloven/hass-fontawesome", "dependencies": ["frontend"], + "after_dependencies": ["scrape"], "codeowners": [], "requirements": [], "config_flow": true, diff --git a/js/main.js b/js/main.js index 0d9ab10..477bdb1 100644 --- a/js/main.js +++ b/js/main.js @@ -32,3 +32,41 @@ window.customIconsets["fab"] = (iconName) => getIcon("brands", iconName); window.customIconsets["far"] = (iconName) => getIcon("regular", iconName); window.customIconsets["fas"] = (iconName) => getIcon("solid", iconName); window.customIconsets["fapro"] = (iconName) => getIcon("pro", iconName); + +// Duotone patches +customElements.whenDefined("ha-icon").then(() => { + const HaIcon = customElements.get("ha-icon"); + HaIcon.prototype._setCustomPath = async function (promise) { + const icon = await promise; + this._path = icon.path; + this._viewBox = icon.viewBox; + + await this.UpdateComplete; + + const el = this.shadowRoot.querySelector("ha-svg-icon"); + if (!el || !el.setPaths) { + return; + } + el.setPaths(icon.paths); + }; +}); + +customElements.whenDefined("ha-svg-icon").then(() => { + const HaSvgIcon = customElements.get("ha-svg-icon"); + + HaSvgIcon.prototype.setPaths = async function (paths) { + await this.updateComplete; + const styleEl = + this.shadowRoot.querySelector("style") || document.createElement("style"); + styleEl.innerHTML = + "svg path.secondary { fill: var(--paper-item-icon-color); }"; + this.shadowRoot.appendChild(styleEl); + const root = this.shadowRoot.querySelector("g"); + for (const k in paths) { + const el = document.createElementNS("http://www.w3.org/2000/svg", "path"); + el.setAttribute("d", paths[k]); + el.classList.add(k); + root.appendChild(el); + } + }; +});