diff --git a/src/js/Icons.js b/src/js/Icons.js index c9c923c..b5630b8 100644 --- a/src/js/Icons.js +++ b/src/js/Icons.js @@ -1,7 +1,7 @@ import { parseSync as parseSVG, stringify as stringifySVG } from "svgson"; export default class Icons { - /** @typedef {"baseline" | "outline" | "round" | "sharp" | "twotone"} IconStyle */ + /** @typedef {"custom" | "material:baseline" | "material:outline" | "material:round" | "material:sharp" | "material:twotone"} IconStyle */ /** * @typedef {Object} IconOptions @@ -19,20 +19,41 @@ export default class Icons { this.#icons = process.env.DRIBBBLISH_ICONS; } - getRawSVG(name, style = "round") { + /** + * @param {String} name + * @param {IconStyle} style + * @returns {String} + */ + getRawSVG(name, style) { + if (style == null) style = this.#getDefaultStyle(name); if (!this.#icons.hasOwnProperty(name)) throw new Error(`Icon "${name}" does not exist`); - if (typeof this.#icons[name] == "string") { - return this.#icons[name]; - } else { - if (!this.#icons[name].hasOwnProperty(style)) { - const styles = Object.keys(this.#icons[name]) - .map((s) => `"${s}"`) - .join(", "); - throw new Error(`Icon "${name}" does not have style "${style}". It is available in styles [${styles}].`); - } - return this.#icons[name][style]; + if (!this.#icons[name].hasOwnProperty(style)) { + const styles = Object.keys(this.#icons[name]) + .map((s) => `"${s}"`) + .join(", "); + throw new Error(`Icon "${name}" does not have style "${style}". It is available in styles [${styles}].`); } + + return this.#icons[name][style]; + } + + getAvailableStyles(name) { + if (!this.#icons.hasOwnProperty(name)) throw new Error(`Icon "${name}" does not exist`); + return Object.keys(this.#icons[name]); + } + + /** + * @param {String} name + * @returns + */ + #getDefaultStyle(name) { + const styles = this.getAvailableStyles(name); + for (const s of ["custom", "material:round"]) { + if (styles.includes(s)) return s; + } + + return styles[0]; } /** @@ -44,7 +65,7 @@ export default class Icons { get(name, options) { /** @type {IconOptions} */ const defaultOptions = { - style: "round", + style: this.#getDefaultStyle(name), size: 16, scale: 1, fill: "currentColor", @@ -54,7 +75,8 @@ export default class Icons { const svg = parseSVG(this.getRawSVG(name, options.style)); - svg.attributes.type = "dribbblish-icon"; + svg.attributes["icon-type"] = "dribbblish"; + svg.attributes["icon-style"] = options.style; svg.attributes.fill = options.fill; svg.attributes.width = options.size; svg.attributes.height = options.size; @@ -66,7 +88,7 @@ export default class Icons { styles.transformOrigin = "center"; } svg.children = svg.children.map((child) => { - child.attributes.style = styles.cssText; + if (styles.cssText != "") child.attributes.style = styles.cssText; return child; }); diff --git a/src/styles/Icons.scss b/src/styles/Icons.scss new file mode 100644 index 0000000..05b3a26 --- /dev/null +++ b/src/styles/Icons.scss @@ -0,0 +1,7 @@ +svg[icon-type="dribbblish"] { + overflow: visible; + + // &[icon-style="custom"] {} + + // &[icon-style^="material:"] {} +} diff --git a/src/styles/main.scss b/src/styles/main.scss index 36a2090..647d5dd 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -10,6 +10,7 @@ @import "Loader"; @import "Modals"; @import "ConnectDeviceList"; +@import "Icons"; :root { --bar-height: 70px; @@ -591,6 +592,7 @@ html.sidebar-hide-text .GlueDropTarget span { transform: translateX(calc(-50%)); padding: 0 5px; text-align: center; + white-space: nowrap; pointer-events: none; border: 1px solid spiceColor("subtext", 0.1, 0.1); border-radius: var(--main-corner-radius); @@ -1280,10 +1282,6 @@ html.right-expanded-cover.buddyfeed-visible .main-coverSlotExpanded-container { display: none; } -svg[type="dribbblish-icon"] { - overflow: visible; -} - // ! WORKAROUNDS / TEMP FIXES // Spotify UI breaks after advertisements #63 canvas[width="250"][height="250"] { diff --git a/webpack.config.js b/webpack.config.js index d5a9a23..ec89d78 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -5,18 +5,22 @@ const path = require("path"); const fs = require("fs"); const icons = {}; +function addIcon(name, style, path) { + name = name.replace(/_/g, "-"); + if (!icons.hasOwnProperty(name)) icons[name] = {}; + icons[name][style] = fs.readFileSync(path, { encoding: "utf8" }); +} // Add Material Icons let iconDir = path.resolve(__dirname, "node_modules/@material-icons/svg/svg"); for (const dir of fs.readdirSync(iconDir)) { - icons[dir.replace("_", "-")] = {}; for (const file of fs.readdirSync(path.resolve(iconDir, dir))) { - icons[dir.replace("_", "-")][file.replace(/\..*?$/, "")] = fs.readFileSync(path.resolve(iconDir, dir, file), { encoding: "utf8" }); + addIcon(dir, `material:${file.replace(/\..*?$/, "")}`, path.resolve(iconDir, dir, file)); } } // Add Custom Icons iconDir = path.resolve(__dirname, "src/icons"); for (const icon of fs.readdirSync(iconDir)) { - icons[icon.replace(/\..*?$/, "")] = fs.readFileSync(path.resolve(iconDir, icon), { encoding: "utf8" }); + addIcon(icon.replace(/\..*?$/, ""), "custom", path.resolve(iconDir, icon)); } /** @type {import('webpack').Configuration} */