Use web components for SVG icons

This commit is contained in:
Eduard Urbach 2019-08-30 16:04:28 +09:00
parent b32005cdee
commit d974156d68
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
13 changed files with 113 additions and 61 deletions

View File

@ -92,8 +92,9 @@ animation rotate-x-once
&.fade-out
pointer-events none
.icon-play
transform translateX(3px)
svg-icon
[name="play"]
transform translateX(3px)
#audio-player-play,
#audio-player-pause,

View File

@ -1,5 +1,5 @@
component Icon(name string)
go:utils.Icon(name)
svg-icon.padded-icon(name=name, class="icon-" + name)
component RawIcon(name string)
go:utils.RawIcon(name)
svg-icon(name=name, class="icon-" + name)

View File

@ -57,9 +57,10 @@ const item-color-anime-support-ticket = hsl(217, 64%, 50%)
width 100%
justify-content center
.icon-diamond
margin-left 0.3rem
margin-right 0
svg-icon
[name="diamond"]
margin-left 0.3rem
margin-right 0
#pro-benefits-footer
margin-top 3rem

View File

@ -47,8 +47,9 @@ const soundtrack-margin = 1rem
pointer-events none
box-shadow outline-shadow-medium
.icon-play
transform translateX(27%)
svg-icon
[name="play"]
transform translateX(27%)
.media-visualizer
horizontal

View File

@ -13,8 +13,9 @@ import ServiceWorkerManager from "./ServiceWorkerManager"
import ServerEvents from "./ServerEvents"
import { displayAiringDate, displayDate, displayTime } from "./DateView"
import { findAll, supportsWebP, requestIdleCallback, swapElements, delay, findAllInside } from "./Utils"
import * as actions from "./Actions"
import ToolTip from "./Elements/tool-tip/tool-tip"
import * as actions from "./Actions"
import * as WebComponents from "./WebComponents"
export default class AnimeNotifier {
app: Application
@ -102,7 +103,7 @@ export default class AnimeNotifier {
}
// Web components
this.registerWebComponents()
WebComponents.register()
// Tooltip
this.tip = new ToolTip()
@ -210,23 +211,6 @@ export default class AnimeNotifier {
}
}
registerWebComponents() {
if(!("customElements" in window)) {
console.warn("Web components not supported in your current browser")
return
}
// Custom element names must have a dash in their name
const elements = new Map<string, Function>([
["tool-tip", ToolTip]
])
// Register all custom elements
for(const [tag, definition] of elements.entries()) {
window.customElements.define(tag, definition)
}
}
applyPageTitle() {
let headers = document.getElementsByTagName("h1")

View File

@ -147,6 +147,12 @@ export default class Diff {
}
}
// Never diff the contents of web components
if(a.nodeName.includes("-")) {
continue
}
// Child nodes
Diff.childNodes(a, b)
}
}

View File

@ -0,0 +1,10 @@
svg-icon
display inline-block
width 1em
height 1em
svg
display inherit
width 1em
height 1em
fill currentColor

View File

@ -0,0 +1,48 @@
import Diff from "scripts/Diff"
export default class SVGIcon extends HTMLElement {
static cache = new Map<string, Promise<string>>()
static get observedAttributes() {
return ["name"]
}
attributeChangedCallback(attrName) {
if(attrName === "name") {
this.render()
}
}
async render() {
let cache = SVGIcon.cache[this.name]
if(cache) {
let text = await cache
Diff.mutations.queue(() => this.innerHTML = text)
return
}
SVGIcon.cache[this.name] = new Promise(async (resolve, reject) => {
let url = `//media.notify.moe/images/icons/${this.name}.svg`
let response = await fetch(url)
if(!response.ok) {
console.warn(`Failed loading SVG icon: ${url}`)
reject(response.statusText)
return
}
let text = await response.text()
Diff.mutations.queue(() => this.innerHTML = text)
resolve(text)
})
}
get name() {
return this.getAttribute("name") || ""
}
set name(value: string) {
this.setAttribute("name", value)
}
}

20
scripts/WebComponents.ts Normal file
View File

@ -0,0 +1,20 @@
import ToolTip from "./Elements/tool-tip/tool-tip"
import SVGIcon from "./Elements/svg-icon/svg-icon"
export function register() {
if(!("customElements" in window)) {
console.warn("Web components not supported in your current browser")
return
}
// Custom element names must have a dash in their name
const elements = new Map<string, Function>([
["tool-tip", ToolTip],
["svg-icon", SVGIcon]
])
// Register all custom elements
for(const [tag, definition] of elements.entries()) {
window.customElements.define(tag, definition)
}
}

View File

@ -1,16 +1,7 @@
.icon
width 1em
height 1em
min-width 1em
.padded-icon
margin-right 0.5em
vertical-align text-bottom
fill currentColor
.raw-icon
width 1em
height 1em
min-width 1em
fill currentColor
.icon-headphones
transform translateY(2px)
svg-icon
[name="headphones"]
transform translateY(2px)

View File

@ -13,8 +13,9 @@
transform scale(1.1)
opacity 1
.icon-discord
transform scale(1.1)
svg-icon
[name="discord"]
transform scale(1.1)
// .sidebar-social-media
// position relative

View File

@ -1,4 +1,6 @@
.tab
horizontal
align-items center
color text-color
padding 0.5rem 1rem
background-color tab-background

View File

@ -1,28 +1,15 @@
package utils
import (
"io/ioutil"
"strings"
"fmt"
)
var svgIcons = make(map[string]string)
func init() {
files, _ := ioutil.ReadDir("images/icons/")
for _, file := range files {
name := strings.TrimSuffix(file.Name(), ".svg")
data, _ := ioutil.ReadFile("images/icons/" + name + ".svg")
svgIcons[name] = strings.Replace(string(data), "<svg ", "<svg class='icon icon-"+name+"' ", 1)
}
}
// Icon ...
// Icon shows an icon that has a margin-right attribute.
func Icon(name string) string {
return svgIcons[name]
return fmt.Sprintf("<svg-icon name='%s' class='padded-icon'></svg-icon>", name)
}
// RawIcon ...
// RawIcon shows the raw icon without any additional margin.
func RawIcon(name string) string {
return strings.Replace(svgIcons[name], "class='icon", "class='raw-icon", 1)
return fmt.Sprintf("<svg-icon name='%s'></svg-icon>", name)
}