perf: use a separate icons.svg file (#1067)

* perf: use a separate icons.svg file

This splits icons into inline and non-inline. The inline ones are high
priority; the rest go in an icons.svg file.

* create SvgIcon.html

* determine inlined svgs at build time
This commit is contained in:
Nolan Lawson 2019-03-02 19:02:06 -08:00 committed by GitHub
parent 2fc6897ee3
commit 880bc7a38a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 255 additions and 176 deletions

View File

@ -11,6 +11,7 @@ tests
/mastodon.log
/src/template.html
/static/*.css
/static/icons.svg
/static/robots.txt
/static/inline-script.js.map
/static/emoji-mart-all.json

1
.gitignore vendored
View File

@ -5,6 +5,7 @@
/mastodon.log
/src/template.html
/static/*.css
/static/icons.svg
/static/robots.txt
/static/inline-script.js.map
/static/emoji-mart-all.json

View File

@ -5,6 +5,7 @@
/mastodon.log
/src/template.html
/static/*.css
/static/icons.svg
/static/inline-script.js.map
/static/emoji-mart-all.json
/src/inline-script/checksum.js

View File

@ -7,20 +7,32 @@ import $ from 'cheerio'
const svgo = new SVGO()
const readFile = promisify(fs.readFile)
const writeFile = promisify(fs.writeFile)
async function readSvg (svg) {
let filepath = path.join(__dirname, '../', svg.src)
let content = await readFile(filepath, 'utf8')
let optimized = (await svgo.optimize(content))
let $optimized = $(optimized.data)
let $path = $optimized.find('path').removeAttr('fill')
let $symbol = $('<symbol></symbol>')
.attr('id', svg.id)
.attr('viewBox', `0 0 ${optimized.info.width} ${optimized.info.height}`)
.append($path)
return $.xml($symbol)
}
export async function buildSvg () {
let result = (await Promise.all(svgs.map(async svg => {
let filepath = path.join(__dirname, '../', svg.src)
let content = await readFile(filepath, 'utf8')
let optimized = (await svgo.optimize(content))
let $optimized = $(optimized.data)
let $path = $optimized.find('path').removeAttr('fill')
let $symbol = $('<symbol></symbol>')
.attr('id', svg.id)
.attr('viewBox', `0 0 ${optimized.info.width} ${optimized.info.height}`)
.append($path)
return $.xml($symbol)
}))).join('\n')
let inlineSvgs = svgs.filter(_ => _.inline)
let regularSvgs = svgs.filter(_ => !_.inline)
return `<svg xmlns="http://www.w3.org/2000/svg" style="display:none;">\n${result}\n</svg>`
let inlineSvgStrings = (await Promise.all(inlineSvgs.map(readSvg))).join('')
let regularSvgStrings = (await Promise.all(regularSvgs.map(readSvg))).join('')
let inlineOutput = `<svg xmlns="http://www.w3.org/2000/svg" style="display:none">${inlineSvgStrings}</svg>`
let regularOutput = `<svg xmlns="http://www.w3.org/2000/svg">${regularSvgStrings}</svg>`
await writeFile(path.resolve(__dirname, '../static/icons.svg'), regularOutput, 'utf8')
return inlineOutput
}

View File

@ -1,9 +1,9 @@
module.exports = [
{ id: 'pinafore-logo', src: 'src/static/sailboat.svg' },
{ id: 'fa-bell', src: 'src/thirdparty/font-awesome-svg-png/white/svg/bell.svg' },
{ id: 'fa-users', src: 'src/thirdparty/font-awesome-svg-png/white/svg/users.svg' },
{ id: 'pinafore-logo', src: 'src/static/sailboat.svg', inline: true },
{ id: 'fa-bell', src: 'src/thirdparty/font-awesome-svg-png/white/svg/bell.svg', inline: true },
{ id: 'fa-users', src: 'src/thirdparty/font-awesome-svg-png/white/svg/users.svg', inline: true },
{ id: 'fa-globe', src: 'src/thirdparty/font-awesome-svg-png/white/svg/globe.svg' },
{ id: 'fa-gear', src: 'src/thirdparty/font-awesome-svg-png/white/svg/gear.svg' },
{ id: 'fa-gear', src: 'src/thirdparty/font-awesome-svg-png/white/svg/gear.svg', inline: true },
{ id: 'fa-reply', src: 'src/thirdparty/font-awesome-svg-png/white/svg/reply.svg' },
{ id: 'fa-reply-all', src: 'src/thirdparty/font-awesome-svg-png/white/svg/reply-all.svg' },
{ id: 'fa-retweet', src: 'src/thirdparty/font-awesome-svg-png/white/svg/retweet.svg' },
@ -21,8 +21,8 @@ module.exports = [
{ id: 'fa-user-times', src: 'src/thirdparty/font-awesome-svg-png/white/svg/user-times.svg' },
{ id: 'fa-user-plus', src: 'src/thirdparty/font-awesome-svg-png/white/svg/user-plus.svg' },
{ id: 'fa-external-link', src: 'src/thirdparty/font-awesome-svg-png/white/svg/external-link.svg' },
{ id: 'fa-search', src: 'src/thirdparty/font-awesome-svg-png/white/svg/search.svg' },
{ id: 'fa-comments', src: 'src/thirdparty/font-awesome-svg-png/white/svg/comments.svg' },
{ id: 'fa-search', src: 'src/thirdparty/font-awesome-svg-png/white/svg/search.svg', inline: true },
{ id: 'fa-comments', src: 'src/thirdparty/font-awesome-svg-png/white/svg/comments.svg', inline: true },
{ id: 'fa-paperclip', src: 'src/thirdparty/font-awesome-svg-png/white/svg/paperclip.svg' },
{ id: 'fa-thumb-tack', src: 'src/thirdparty/font-awesome-svg-png/white/svg/thumb-tack.svg' },
{ id: 'fa-bars', src: 'src/thirdparty/font-awesome-svg-png/white/svg/bars.svg' },

View File

@ -5,10 +5,10 @@
"scripts": {
"lint": "standard && standard --plugin html 'src/routes/**/*.html'",
"lint-fix": "standard --fix && standard --fix --plugin html 'src/routes/**/*.html'",
"dev": "run-s build-template-html build-third-party-assets serve-dev",
"dev": "run-s build-template-html build-assets serve-dev",
"serve-dev": "run-p --race build-template-html-watch sapper-dev",
"sapper-dev": "cross-env NODE_ENV=development PORT=4002 sapper dev",
"before-build": "run-s build-template-html build-third-party-assets",
"before-build": "run-s build-template-html build-assets",
"build": "cross-env NODE_ENV=production run-s build-steps",
"build-steps": "run-s before-build sapper-export build-now-json",
"sapper-build": "sapper build",
@ -16,7 +16,7 @@
"build-and-start": "run-s build start",
"build-template-html": "node -r esm ./bin/build-template-html.js",
"build-template-html-watch": "node -r esm ./bin/build-template-html.js --watch",
"build-third-party-assets": "node -r esm ./bin/build-third-party-assets.js",
"build-assets": "node -r esm ./bin/build-assets.js",
"run-mastodon": "node -r esm ./bin/run-mastodon.js",
"test": "cross-env BROWSER=chrome:headless run-s test-browser",
"test-browser": "run-p --race run-mastodon build-and-start test-mastodon",

View File

@ -1,7 +1,5 @@
{#if error}
<svg class={computedClass} style={svgStyle} aria-hidden="true">
<use xlink:href="#fa-user" />
</svg>
<SvgIcon className={computedClass} style={svgStyle} href="#fa-user" />
{:elseif $autoplayGifs}
<LazyImage
className={computedClass}
@ -37,7 +35,7 @@
background: none;
}
svg.avatar {
:global(svg.avatar) {
fill: var(--deemphasized-text-color);
}
</style>
@ -46,6 +44,7 @@
import NonAutoplayImg from './NonAutoplayImg.html'
import { classname } from '../_utils/classname'
import LazyImage from './LazyImage.html'
import SvgIcon from './SvgIcon.html'
export default {
data: () => ({
@ -80,7 +79,8 @@
},
components: {
NonAutoplayImg,
LazyImage
LazyImage,
SvgIcon
}
}
</script>

View File

@ -2,9 +2,7 @@
role="navigation" aria-label="Page header"
>
{#if icon}
<svg class="dynamic-page-banner-svg">
<use xlink:href={icon} />
</svg>
<SvgIcon className="dynamic-page-banner-svg" href={icon} />
{/if}
<h1 class="dynamic-page-title" aria-label={ariaTitle}>{title}</h1>
<button type="button"
@ -24,7 +22,7 @@
.dynamic-page-banner.dynamic-page-with-icon {
grid-template-columns: min-content 1fr min-content;
}
.dynamic-page-banner-svg {
:global(.dynamic-page-banner-svg) {
width: 24px;
height: 24px;
fill: var(--body-text-color);
@ -64,13 +62,17 @@
</style>
<script>
import Shortcut from './shortcut/Shortcut.html'
import SvgIcon from './SvgIcon.html'
export default {
data: () => ({
icon: void 0,
ariaTitle: ''
}),
components: { Shortcut },
components: {
Shortcut,
SvgIcon
},
methods: {
onGoBack () {
window.history.back()

View File

@ -4,27 +4,26 @@
aria-label={ariaLabel}
class={computedClass}>
<slot></slot>{#if showIcon}
<svg class="external-link-svg">
<use xlink:href="#fa-external-link" />
</svg>
<SvgIcon className="external-link-svg" href="#fa-external-link" />
{/if}</a>
<style>
.external-link-with-icon {
display: inline-flex;
align-items: center;
}
.external-link-with-icon .external-link-svg {
:global(.external-link-with-icon .external-link-svg) {
margin-left: 4px;
width: 14px;
height: 14px;
fill: var(--deemphasized-text-color);
}
.external-link-with-icon.normal-icon-color .external-link-svg {
:global(.external-link-with-icon.normal-icon-color .external-link-svg) {
fill: var(--body-text-color);
}
</style>
<script>
import { classname } from '../_utils/classname'
import SvgIcon from './SvgIcon.html'
export default {
data: () => ({
@ -40,6 +39,9 @@
showIcon && 'external-link-with-icon',
normalIconColor && 'normal-icon-color'
))
},
components: {
SvgIcon
}
}
</script>

View File

@ -7,9 +7,7 @@
{disabled}
ref:node
>
<svg class="icon-button-svg {svgClassName || ''}" ref:svg>
<use xlink:href={href} />
</svg>
<SvgIcon className="icon-button-svg {svgClassName || ''}" ref:svg {href} />
</button>
<style>
.icon-button {
@ -21,14 +19,14 @@
justify-content: center;
}
.icon-button-svg {
:global(.icon-button-svg) {
width: 24px;
height: 24px;
fill: var(--action-button-fill-color);
pointer-events: none; /* hack for Edge */
}
.icon-button.big-icon .icon-button-svg {
:global(.icon-button.big-icon .icon-button-svg) {
width: 32px;
height: 32px;
}
@ -37,24 +35,24 @@
* regular styles
*/
.icon-button:hover .icon-button-svg {
:global(.icon-button:hover .icon-button-svg) {
fill: var(--action-button-fill-color-hover);
}
.icon-button.not-pressable:active .icon-button-svg,
.icon-button.same-pressed:active .icon-button-svg {
:global(.icon-button.not-pressable:active .icon-button-svg,
.icon-button.same-pressed:active .icon-button-svg) {
fill: var(--action-button-fill-color-active);
}
.icon-button.pressed.not-same-pressed .icon-button-svg {
:global(.icon-button.pressed.not-same-pressed .icon-button-svg) {
fill: var(--action-button-fill-color-pressed);
}
.icon-button.pressed.not-same-pressed:hover .icon-button-svg {
:global(.icon-button.pressed.not-same-pressed:hover .icon-button-svg) {
fill: var(--action-button-fill-color-pressed-hover);
}
.icon-button.pressed.not-same-pressed:active .icon-button-svg {
:global(.icon-button.pressed.not-same-pressed:active .icon-button-svg) {
fill: var(--action-button-fill-color-pressed-active);
}
@ -62,28 +60,28 @@
* muted
*/
.icon-button.muted-style .icon-button-svg {
:global(.icon-button.muted-style .icon-button-svg) {
fill: var(--action-button-deemphasized-fill-color);
}
.icon-button.muted-style:hover .icon-button-svg {
:global(.icon-button.muted-style:hover .icon-button-svg) {
fill: var(--action-button-deemphasized-fill-color-hover);
}
.icon-button.muted-style.not-pressable:active .icon-button-svg,
.icon-button.muted-style.same-pressed:active .icon-button-svg {
:global(.icon-button.muted-style.not-pressable:active .icon-button-svg,
.icon-button.muted-style.same-pressed:active .icon-button-svg) {
fill: var(--action-button-deemphasized-fill-color-active);
}
.icon-button.muted-style.pressed.not-same-pressed .icon-button-svg {
:global(.icon-button.muted-style.pressed.not-same-pressed .icon-button-svg) {
fill: var(--action-button-deemphasized-fill-color-pressed);
}
.icon-button.muted-style.pressed.not-same-pressed:hover .icon-button-svg {
:global(.icon-button.muted-style.pressed.not-same-pressed:hover .icon-button-svg) {
fill: var(--action-button-deemphasized-fill-color-pressed-hover);
}
.icon-button.muted-style.pressed.not-same-pressed:active .icon-button-svg {
:global(.icon-button.muted-style.pressed.not-same-pressed:active .icon-button-svg) {
fill: var(--action-button-deemphasized-fill-color-pressed-active);
}
@ -91,7 +89,7 @@
<script>
import { classname } from '../_utils/classname'
import { store } from '../_store/store'
import { animate } from '../_utils/animate'
import SvgIcon from './SvgIcon.html'
export default {
oncreate () {
@ -139,16 +137,14 @@
},
methods: {
animate (animation) {
let { reduceMotion } = this.store.get()
if (!animation || reduceMotion) {
return
}
let svg = this.refs.svg
animate(svg, animation)
this.refs.svg.animate(animation)
},
onClick (e) {
this.fire('click', e)
}
},
components: {
SvgIcon
}
}
</script>

View File

@ -1,23 +1,27 @@
<svg class="loading-spinner-icon spin {maskStyle ? 'mask-style' : ''}"
style="width: {size}px; height: {size}px;"
aria-label="Loading"
>
<use xlink:href="#fa-spinner" />
</svg>
<SvgIcon className="loading-spinner-icon spin {maskStyle ? 'mask-style' : ''}"
style="width: {size}px; height: {size}px;"
href="#fa-spinner"
ariaLabel="Loading"
/>
<style>
.loading-spinner-icon {
:global(.loading-spinner-icon) {
fill: var(--svg-fill);
}
.loading-spinner-icon.mask-style {
:global(.loading-spinner-icon.mask-style) {
fill: var(--mask-svg-fill);
}
</style>
<script>
import SvgIcon from './SvgIcon.html'
export default {
data: () => ({
maskStyle: false,
size: 64
})
}),
components: {
SvgIcon
}
}
</script>

Before

Width:  |  Height:  |  Size: 443 B

After

Width:  |  Height:  |  Size: 543 B

View File

@ -1,16 +1,12 @@
{#if showBadge}
<div class="nav-link-svg-wrapper">
<svg class="nav-link-svg">
<use xlink:href={svg} />
</svg>
<SvgIcon className="nav-link-svg" href={svg} />
<span class="nav-link-badge nav-link-badge-digits-{badgeDigits}" aria-hidden="true">
{badgeNumberToShow}
</span>
</div>
{:else}
<svg class="nav-link-svg">
<use xlink:href={svg} />
</svg>
<SvgIcon className="nav-link-svg" href={svg} />
{/if}
<style>
.nav-link-svg-wrapper {
@ -18,7 +14,7 @@
display: inline-block;
}
.nav-link-svg-wrapper, .nav-link-svg {
:global(.nav-link-svg-wrapper, .nav-link-svg) {
width: 20px;
height: 20px;
}
@ -51,7 +47,7 @@
font-size: 0.6em;
}
.nav-link-svg {
:global(.nav-link-svg) {
display: inline-block;
fill: var(--nav-svg-fill);
}
@ -61,7 +57,7 @@
}
@media (max-width: 991px) {
.nav-link-svg-wrapper, .nav-link-svg {
:global(.nav-link-svg-wrapper, .nav-link-svg) {
width: 25px;
height: 25px;
}
@ -72,6 +68,8 @@
}
</style>
<script>
import SvgIcon from './SvgIcon.html'
export default {
data: () => ({
showBadge: false,
@ -80,6 +78,9 @@
computed: {
badgeDigits: ({ badgeNumber }) => Math.min(3, badgeNumber.toString().length),
badgeNumberToShow: ({ badgeNumber }) => (badgeNumber < 100 ? badgeNumber.toString() : '99+')
},
components: {
SvgIcon
}
}
</script>

View File

@ -2,9 +2,7 @@
<FreeTextLayout>
<div class="not-logged-in-home">
<div class="banner">
<svg aria-hidden="true" class="not-logged-in-home-svg">
<use xlink:href="#pinafore-logo" />
</svg>
<SvgIcon className="not-logged-in-home-svg" href="#pinafore-logo" />
<h1>Pinafore</h1>
</div>
<p>Pinafore is a web client for <ExternalLink href="https://joinmastodon.org">Mastodon</ExternalLink>, designed for speed and simplicity.</p>
@ -24,7 +22,7 @@
align-items: center;
margin: 0 0 30px;
}
.not-logged-in-home-svg {
:global(.not-logged-in-home-svg) {
width: 70px;
height: 70px;
fill: var(--banner-fill);
@ -47,12 +45,14 @@
import FreeTextLayout from './FreeTextLayout.html'
import HiddenFromSSR from './HiddenFromSSR.html'
import ExternalLink from './ExternalLink.html'
import SvgIcon from './SvgIcon.html'
export default {
components: {
FreeTextLayout,
HiddenFromSSR,
ExternalLink
ExternalLink,
SvgIcon
}
}
</script>

View File

@ -1,7 +1,5 @@
<div class="play-video-icon {className || ''}">
<svg class="play-video-icon-svg">
<use xlink:href="#fa-play-circle" />
</svg>
<SvgIcon className="play-video-icon-svg" href="#fa-play-circle" />
</div>
<style>
.play-video-icon {
@ -16,7 +14,7 @@
z-index: 40;
pointer-events: none;
}
.play-video-icon-svg {
:global(.play-video-icon-svg) {
width: 72px;
height: 72px;
fill: var(--mask-svg-fill);
@ -25,9 +23,14 @@
}
</style>
<script>
import SvgIcon from './SvgIcon.html'
export default {
data: () => ({
className: void 0
})
}),
components: {
SvgIcon
}
}
</script>

View File

@ -0,0 +1,37 @@
<svg
class={className}
{style}
aria-hidden={!ariaLabel}
aria-label={ariaLabel}
ref:svg>
<use xlink:href="{inline ? '' : '/icons.svg'}{href}" />
</svg>
<script>
import { animate } from '../_utils/animate'
import { store } from '../_store/store'
export default {
data: () => ({
className: '',
style: '',
ariaLabel: ''
}),
store: () => store,
computed: {
inline: ({ href }) => {
// filled in during build
return process.env.INLINE_SVGS.includes(href)
}
},
methods: {
animate (animation) {
let { reduceMotion } = this.store.get()
if (!animation || reduceMotion) {
return
}
let svg = this.refs.svg
animate(svg, animation)
}
}
}
</script>

After

Width:  |  Height:  |  Size: 790 B

View File

@ -1,8 +1,6 @@
<li class="page-list-item">
<a {href} rel="prefetch">
<svg class="page-list-item-svg">
<use xlink:href={icon} />
</svg>
<SvgIcon className="page-list-item-svg" href={icon} />
<span aria-label={ariaLabel}>
{label}
</span>
@ -40,7 +38,7 @@
.page-list-item a:active {
background: var(--settings-list-item-bg-active);
}
.page-list-item-svg {
:global(.page-list-item-svg) {
width: 24px;
height: 24px;
display: inline-block;
@ -57,7 +55,7 @@
.page-list-item a {
padding: 20px 10px;
}
.page-list-item-svg {
:global(.page-list-item-svg) {
margin-right: 10px;
}
}
@ -66,6 +64,7 @@
<script>
import { store } from '../../_store/store'
import IconButton from '../IconButton'
import SvgIcon from '../SvgIcon.html'
export default {
store: () => store,
@ -82,7 +81,8 @@
}
},
components: {
IconButton
IconButton,
SvgIcon
},
methods: {
onPinClick (e) {

View File

@ -8,16 +8,14 @@
</span>
<div class="compose-box-button-spinner"
aria-hidden="true">
<svg class="compose-box-button-svg {$postingStatus ? 'spin' : 'hidden'}">
<use xlink:href="#fa-spinner" />
</svg>
<SvgIcon className="compose-box-button-svg {$postingStatus ? 'spin' : 'hidden'}"
href="#fa-spinner" />
</div>
<div class="compose-box-button-compose {sticky ? '' : 'hidden'}"
aria-hidden="true">
<svg class="compose-box-button-svg">
<use xlink:href="#fa-pencil" />
</svg>
<SvgIcon className="compose-box-button-svg"
href="#fa-pencil" />
</div>
</button>
</div>
@ -49,7 +47,7 @@
left: 0;
right: 0;
}
.compose-box-button-svg {
:global(.compose-box-button-svg) {
width: 24px;
height: 24px;
fill: var(--button-primary-text);
@ -65,12 +63,16 @@
}
</style>
<script>
import SvgIcon from '../SvgIcon.html'
import { store } from '../../_store/store'
export default {
store: () => store,
computed: {
disabled: ({ $postingStatus, overLimit }) => $postingStatus || overLimit
},
components: {
SvgIcon
}
}
</script>

View File

@ -4,9 +4,7 @@
<button class="compose-media-delete-button"
aria-label="Delete {shortName}"
on:click="onDeleteMedia()" >
<svg class="compose-media-delete-button-svg">
<use xlink:href="#fa-times" />
</svg>
<SvgIcon className="compose-media-delete-button-svg" href="#fa-times" />
</button>
</div>
<div class="compose-media-alt">
@ -73,7 +71,7 @@
.compose-media-delete-button:hover {
background: var(--toast-border);
}
.compose-media-delete-button-svg {
:global(.compose-media-delete-button-svg) {
fill: var(--button-text);
width: 18px;
height: 18px;
@ -95,6 +93,7 @@
import debounce from 'lodash-es/debounce'
import { scheduleIdleTask } from '../../_utils/scheduleIdleTask'
import { observe } from 'svelte-extras'
import SvgIcon from '../SvgIcon.html'
export default {
oncreate () {
@ -147,6 +146,9 @@
} = this.get()
deleteMedia(realm, index)
}
},
components: {
SvgIcon
}
}
</script>

View File

@ -2,16 +2,13 @@
{#each items as item (item.key)}
<li class="generic-dialog-list-item">
<button class="generic-dialog-list-button" on:click="fire('click', item)">
<svg class="generic-dialog-list-item-svg">
<use xlink:href={item.icon} />
</svg>
<SvgIcon className="generic-dialog-list-item-svg" href={item.icon} />
<span class="generic-dialog-list-button-span">
{item.label}
</span>
{#if selectable}
<svg class="generic-dialog-list-item-svg {item.selected ? '' : 'hidden'}" aria-hidden={!item.selected}>
<use xlink:href="#fa-check" />
</svg>
<SvgIcon className="generic-dialog-list-item-svg {item.selected ? '' : 'hidden'}"
href="#fa-check" />
{/if}
</button>
</li>
@ -31,7 +28,7 @@
font-size: 1.2em;
display: flex;
}
.generic-dialog-list-item-svg {
:global(.generic-dialog-list-item-svg) {
width: 24px;
height: 24px;
fill: var(--svg-fill);
@ -88,3 +85,12 @@
}
}
</style>
<script>
import SvgIcon from '../../SvgIcon.html'
export default {
components: {
SvgIcon
}
}
</script>

View File

@ -15,9 +15,7 @@
<div class="close-dialog-button-wrapper">
<button class="close-dialog-button"
data-a11y-dialog-hide aria-label="Close dialog">
<svg class="close-dialog-button-svg">
<use xlink:href="#fa-times" />
</svg>
<SvgIcon className="close-dialog-button-svg" href="#fa-times" />
</button>
</div>
</div>
@ -102,7 +100,7 @@
justify-content: center;
align-items: center;
}
.close-dialog-button-svg {
:global(.close-dialog-button-svg) {
padding: 10px;
fill: var(--button-primary-text);
width: 24px;
@ -129,7 +127,7 @@
.modal-dialog-title {
font-size: 1.3em;
}
.close-dialog-button-svg {
:global(.close-dialog-button-svg) {
padding: 7px;
width: 18px;
height: 18px;
@ -138,6 +136,7 @@
</style>
<script>
import Shortcut from '../../shortcut/Shortcut.html'
import SvgIcon from '../../SvgIcon.html'
import { A11yDialog } from '../../../_thirdparty/a11y-dialog/a11y-dialog'
import { classname } from '../../../_utils/classname'
import { on, emit } from '../../../_utils/eventBus'
@ -164,7 +163,7 @@
ondestroy () {
popShortcutScope('modal')
},
components: { Shortcut },
components: { Shortcut, SvgIcon },
data: () => ({
// don't animate if we're showing a modal dialog on top of another modal dialog. it looks ugly
shouldAnimate: !process.browser || document.getElementsByClassName('modal-dialog').length < 2,

View File

@ -19,9 +19,7 @@
</div>
<div class="account-profile-meta-cell account-profile-meta-verified">
{#if field.verified}
<svg class="account-profile-meta-verified-svg">
<use xlink:href='#fa-check' />
</svg>
<SvgIcon className="account-profile-meta-verified-svg" href="#fa-check" />
{/if}
</div>
{/each}
@ -54,7 +52,7 @@
font-size: 1.1em;
}
.account-profile-meta-verified-svg {
:global(.account-profile-meta-verified-svg) {
width: 24px;
height: 24px;
fill: var(--svg-fill);
@ -102,6 +100,7 @@
}
</style>
<script>
import SvgIcon from '../SvgIcon.html'
import { emojifyText } from '../../_utils/emojifyText'
import { store } from '../../_store/store'
@ -115,6 +114,9 @@
value: emojifyText(field.value, emojis, $autoplayGifs),
verified: !!field.verified_at
})))
},
components: {
SvgIcon
}
}
</script>

View File

@ -1,9 +1,7 @@
<div class="account-profile-moved-banner">
<Avatar className="from-avatar" size="extra-small" {account} />
<div class="moved-label">
<svg class="moved-svg">
<use xlink:href="#fa-suitcase"/>
</svg>
<SvgIcon className="moved-svg" href="#fa-suitcase" />
{accessibleName} has moved:
</div>
<a class="moved-avatar" href="/accounts/{moved.id}">
@ -27,7 +25,7 @@
grid-area: from-avatar;
justify-self: flex-end;
}
.moved-svg {
:global(.moved-svg) {
width: 18px;
height: 18px;
fill: var(--deemphasized-text-color);
@ -64,6 +62,7 @@
<script>
import { removeEmoji } from '../../_utils/removeEmoji'
import Avatar from '../Avatar.html'
import SvgIcon from '../SvgIcon.html'
export default {
computed: {
@ -84,7 +83,8 @@
}
},
components: {
Avatar
Avatar,
SvgIcon
}
}
</script>

View File

@ -8,9 +8,7 @@
bind:value="$queryInSearch">
</div>
<button type="submit" class="primary search-button" aria-label="Search" disabled={$searchLoading}>
<svg class="search-button-svg">
<use xlink:href="#fa-search" />
</svg>
<SvgIcon className="search-button-svg" href="#fa-search" />
</button>
</form>
{#if $searchLoading}
@ -39,7 +37,7 @@
border-radius: 10px;
flex: 1;
}
.search-button-svg {
:global(.search-button-svg) {
fill: var(--button-primary-text);
width: 18px;
height: 18px;
@ -60,12 +58,14 @@
import LoadingPage from '../LoadingPage.html'
import { doSearch } from '../../_actions/search'
import SearchResults from './SearchResults.html'
import SvgIcon from '../SvgIcon.html'
export default {
store: () => store,
components: {
LoadingPage,
SearchResults
SearchResults,
SvgIcon
},
methods: {
async onSubmit (e) {

View File

@ -28,18 +28,14 @@
rel="prefetch"
href="/statuses/{originalStatusId}/reblogs"
aria-label={reblogsLabel}>
<svg class="status-favs-reblogs-svg">
<use xlink:href="#fa-retweet"/>
</svg>
<SvgIcon className="status-favs-reblogs-svg" href="#fa-retweet" />
<span>{numReblogs}</span>
</a>
<a class="status-favs-reblogs status-favs"
rel="prefetch"
href="/statuses/{originalStatusId}/favorites"
aria-label={favoritesLabel}>
<svg class="status-favs-reblogs-svg">
<use xlink:href="#fa-star" />
</svg>
<SvgIcon className="status-favs-reblogs-svg" href="#fa-star" />
<span>{numFavs}</span>
</a>
</div>
@ -105,7 +101,7 @@
color: var(--deemphasized-text-color);
}
.status-favs-reblogs-svg {
:global(.status-favs-reblogs-svg) {
fill: var(--deemphasized-text-color);
width: 18px;
height: 18px;
@ -137,6 +133,7 @@
import ExternalLink from '../ExternalLink.html'
import { store } from '../../_store/store'
import { absoluteDateFormatter, shortAbsoluteDateFormatter } from '../../_utils/formatters'
import SvgIcon from '../SvgIcon.html'
import { on } from '../../_utils/eventBus'
export default {
@ -191,7 +188,8 @@
}
},
components: {
ExternalLink
ExternalLink,
SvgIcon
}
}
</script>

View File

@ -2,9 +2,7 @@
<div class="status-header-avatar {timelineType === 'pinned' ? 'hidden' : ''}">
<Avatar {account} size="extra-small"/>
</div>
<svg class="status-header-svg">
<use xlink:href={icon}/>
</svg>
<SvgIcon className="status-header-svg" href={icon} />
<div class="status-header-content">
{#if timelineType === 'pinned'}
@ -50,7 +48,7 @@
margin-left: 19px; /* offset for avatar, 48px - 24px - 5px */
}
.status-header-svg {
:global(.status-header-svg) {
min-width: 18px;
margin-left: 20px;
width: 18px;
@ -58,7 +56,7 @@
fill: var(--deemphasized-text-color);
}
.status-header.status-in-notification .status-header-svg {
:global(.status-header.status-in-notification .status-header-svg) {
fill: var(--body-text-color);
}
@ -98,7 +96,7 @@
}
@media (max-width: 767px) {
.status-header-svg {
:global(.status-header-svg) {
margin-left: 10px;
}
}
@ -106,11 +104,13 @@
<script>
import Avatar from '../Avatar.html'
import AccountDisplayName from '../profile/AccountDisplayName.html'
import SvgIcon from '../SvgIcon.html'
export default {
components: {
Avatar,
AccountDisplayName
AccountDisplayName,
SvgIcon
},
computed: {
elementId: ({ uuid }) => `status-header-${uuid}`,

View File

@ -7,9 +7,7 @@
class="status-sensitive-media-button"
aria-label="Hide sensitive media" >
<div class="svg-wrapper">
<svg class="status-sensitive-media-svg">
<use xlink:href="#fa-eye-slash" />
</svg>
<SvgIcon className="status-sensitive-media-svg" href="#fa-eye-slash" />
</div>
</button>
<MediaAttachments {mediaAttachments} {sensitive} {uuid} />
@ -23,9 +21,7 @@
Sensitive content. Click to show.
</div>
<div class="svg-wrapper">
<svg class="status-sensitive-media-svg">
<use xlink:href="#fa-eye" />
</svg>
<SvgIcon className="status-sensitive-media-svg" href="#fa-eye" />
</div>
</button>
{/if}
@ -135,7 +131,7 @@
.status-sensitive-media-container.status-sensitive-media-shown .svg-wrapper {
background: none;
}
.status-sensitive-media-svg {
:global(.status-sensitive-media-svg) {
width: 24px;
height: 24px;
fill: var(--mask-svg-fill);
@ -144,7 +140,7 @@
margin: 5px;
padding: 6px 10px;
}
.status-sensitive-media-container.status-sensitive-media-hidden .status-sensitive-media-svg {
:global(.status-sensitive-media-container.status-sensitive-media-hidden .status-sensitive-media-svg) {
fill: var(--deemphasized-text-color);
background: var(--mask-opaque-bg);
}
@ -152,6 +148,7 @@
<script>
import MediaAttachments from './MediaAttachments.html'
import Shortcut from '../shortcut/Shortcut.html'
import SvgIcon from '../SvgIcon.html'
import { store } from '../../_store/store'
import { registerClickDelegate } from '../../_utils/delegate'
import { classname } from '../../_utils/classname'
@ -163,7 +160,8 @@
},
components: {
MediaAttachments,
Shortcut
Shortcut,
SvgIcon
},
store: () => store,
computed: {

View File

@ -16,9 +16,8 @@
title={instance.switchLabel}
aria-pressed={instance.current}
on:click="onSwitchToThisInstance(event, instance.name)">
<svg class="instance-switcher-button-svg">
<use xlink:href="{instance.current ? '#fa-star' : '#fa-star-o'}" />
</svg>
<SvgIcon className="instance-switcher-button-svg"
href={instance.current ? '#fa-star' : '#fa-star-o'} />
</button>
</div>
</SettingsListRow>
@ -55,7 +54,7 @@
justify-content: center;
margin: 1px;
}
.instance-switcher-button-svg {
:global(.instance-switcher-button-svg) {
width: 24px;
height: 24px;
display: inline-block;
@ -69,13 +68,15 @@
import SettingsList from '../../../_components/settings/SettingsList.html'
import SettingsListRow from '../../../_components/settings/SettingsListRow.html'
import SettingsListButton from '../../../_components/settings/SettingsListButton.html'
import SvgIcon from '../../../_components/SvgIcon.html'
export default {
components: {
SettingsLayout,
SettingsList,
SettingsListRow,
SettingsListButton
SettingsListButton,
SvgIcon
},
methods: {
onSwitchToThisInstance (e, instanceName) {

View File

@ -4,7 +4,7 @@ const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPl
const LodashModuleReplacementPlugin = require('lodash-webpack-plugin')
const terser = require('./terser.config')
const CircularDependencyPlugin = require('circular-dependency-plugin')
const { mode, dev, resolve } = require('./shared.config')
const { mode, dev, resolve, inlineSvgs } = require('./shared.config')
const output = Object.assign(config.client.output(), {
// enables HMR in workers
@ -51,6 +51,11 @@ module.exports = {
}
},
plugins: [
new webpack.DefinePlugin({
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode),
'process.env.INLINE_SVGS': JSON.stringify(inlineSvgs)
}),
new webpack.NormalModuleReplacementPlugin(
/\/_database\/database\.js$/, // this version plays nicer with IDEs
'./database.prod.js'
@ -66,10 +71,6 @@ module.exports = {
requestTimeout: 120000
})
] : [
new webpack.DefinePlugin({
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode)
}),
new BundleAnalyzerPlugin({ // generates report.html and stats.json
analyzerMode: 'static',
generateStatsFile: true,

View File

@ -1,6 +1,7 @@
const webpack = require('webpack')
const config = require('sapper/config/webpack.js')
const pkg = require('../package.json')
const { mode, dev, resolve } = require('./shared.config')
const { mode, dev, resolve, inlineSvgs } = require('./shared.config')
module.exports = {
entry: config.server.entry(),
@ -28,5 +29,10 @@ module.exports = {
mode,
performance: {
hints: false // it doesn't matter if server.js is large
}
},
plugins: [
new webpack.DefinePlugin({
'process.env.INLINE_SVGS': JSON.stringify(inlineSvgs)
})
]
}

View File

@ -1,4 +1,7 @@
const mode = process.env.NODE_ENV
const svgs = require('../bin/svgs')
const inlineSvgs = svgs.filter(_ => _.inline).map(_ => `#${_.id}`)
const mode = process.env.NODE_ENV || 'production'
const dev = mode === 'development'
const resolve = {
@ -14,5 +17,6 @@ const resolve = {
module.exports = {
mode,
dev,
resolve
resolve,
inlineSvgs
}