Compare commits

...

21 Commits

Author SHA1 Message Date
Nolan Lawson 1cd4b9da68
chore: remove focus-visible polyfill from local dev server (#420) 2024-04-13 09:04:55 -07:00
Nolan Lawson 028f4dc8ed
test: add test to ensure that node is defined (#419) 2024-04-12 19:05:34 -07:00
Nolan Lawson 3d84cf384e 1.21.3 2024-04-09 07:12:08 -07:00
Éric Le Maître e52867681a
fix: improved French translations (#417) 2024-04-09 07:10:03 -07:00
dependabot[bot] 9211adedfa
chore(deps-dev): bump vite from 5.2.4 to 5.2.8 (#416)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.2.4 to 5.2.8.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.2.8/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-05 06:34:10 -07:00
dependabot[bot] 320414bcb7
chore(deps-dev): bump express from 4.19.1 to 4.19.2 (#415)
Bumps [express](https://github.com/expressjs/express) from 4.19.1 to 4.19.2.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.19.1...4.19.2)

---
updated-dependencies:
- dependency-name: express
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-29 17:13:08 -07:00
Nolan Lawson 6a0f4a25ad
chore: remove references to yarn in docs (#414) 2024-03-23 10:52:06 -07:00
Nolan Lawson 6456dbf8a4
chore: switch from yarn to pnpm (#413) 2024-03-23 10:45:43 -07:00
Nolan Lawson c067cbab29 1.21.2 2024-03-21 19:02:47 -07:00
Nolan Lawson 1ae4e30e91
chore: update dependencies (#412) 2024-03-21 19:00:57 -07:00
Nolan Lawson ce950ff740
fix: avoid HTML comments, simplify replacement logic (#409) 2024-03-21 18:43:12 -07:00
Nolan Lawson ff88212004
chore: update node version in CI (#411) 2024-03-17 19:00:14 -07:00
Nolan Lawson 2a57ba18e8
test: centralize fetch mocks in one place (#408) 2024-03-10 20:13:46 -07:00
Nolan Lawson 55872ba996
fix: minor refactor to reduce code size (#406) 2024-03-09 15:40:36 -08:00
Nolan Lawson 15bca2197f
chore: update dependencies (#405) 2024-03-09 13:42:46 -08:00
Nolan Lawson cc1f64d23e
chore: switch from jest to vitest (#404) 2024-03-09 13:21:00 -08:00
Nolan Lawson 7365322a5f
chore: update devDeps (#402) 2024-03-02 10:41:38 -08:00
Nolan Lawson ddb6aa3a9d 1.21.1 2024-02-17 09:12:25 -08:00
Nolan Lawson 19331c6be6
fix: avoid calling getRootNode (#399) 2024-02-17 09:11:14 -08:00
Nolan Lawson d0e94c7194
chore: update dependencies (#400) 2024-02-17 09:11:02 -08:00
Nolan Lawson 82b1f7fabc
docs: add docs on usage in meta-frameworks (#394) 2023-12-20 08:18:24 -08:00
95 changed files with 8374 additions and 9013 deletions

View File

@ -14,33 +14,20 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- uses: actions/checkout@v4
- run: corepack enable
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- run: pnpm i --frozen-lockfile
# via https://github.com/actions/cache/blob/0638051/examples.md#node---yarn
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v2
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: yarn install
if: steps.cache.outputs.cache-hit != 'true'
- name: install chromedriver
run: |
yarn --immutable --ignore-scripts
# install the chromedriver corresponding to whatever version of chrome is installed
yarn add --ignore-scripts chromedriver@^$(google-chrome --version | awk '{print $3}' | cut -d. -f1)
PERF=1 yarn build:rollup
yarn benchmark:runtime:setup
pnpm i chromedriver@^$(google-chrome --version | awk '{print $3}' | cut -d. -f1)
PERF=1 pnpm build:rollup
pnpm benchmark:runtime:setup
# first-load
- name: Benchmark first-load

View File

@ -8,24 +8,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- uses: actions/checkout@v4
- run: corepack enable
- uses: actions/setup-node@v4
with:
node-version: '20'
# via https://github.com/actions/cache/blob/0638051/examples.md#node---yarn
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v2
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
cache: 'pnpm'
- run: pnpm i --frozen-lockfile
- uses: preactjs/compressed-size-action@v2
with:
build-script: benchmark:bundle

View File

@ -8,14 +8,16 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@v4
- run: corepack enable
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'yarn'
cache: 'pnpm'
- run: pnpm i --frozen-lockfile
- run: sudo apt-get install fonts-noto-color-emoji
- run: yarn --frozen-lockfile
- run: yarn lint
- run: yarn benchmark:bundlesize
- run: yarn cover
- run: yarn test:leak
- run: pnpm lint
- run: pnpm benchmark:bundlesize
- run: pnpm cover
- run: npx puppeteer browsers install chrome
- run: pnpm test:leak

7
.gitignore vendored
View File

@ -108,13 +108,6 @@ dist
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.pnp.*
/database.js
/database.js.map
/picker.js

1
.yarnrc.yml Normal file
View File

@ -0,0 +1 @@
nodeLinker: node-modules

View File

@ -1,3 +1,31 @@
## [1.21.3](https://github.com/nolanlawson/emoji-picker-element/compare/v1.21.2...v1.21.3) (2024-04-09)
### Bug Fixes
* improved French translations ([#417](https://github.com/nolanlawson/emoji-picker-element/issues/417)) ([e528676](https://github.com/nolanlawson/emoji-picker-element/commit/e52867681ab07f9eca575e7899f453b9fcd2070a))
## [1.21.2](https://github.com/nolanlawson/emoji-picker-element/compare/v1.21.1...v1.21.2) (2024-03-22)
### Bug Fixes
* avoid HTML comments, simplify replacement logic ([#409](https://github.com/nolanlawson/emoji-picker-element/issues/409)) ([ce950ff](https://github.com/nolanlawson/emoji-picker-element/commit/ce950ff740292e6914ed0744b5587db2f3dcc1f7))
* minor refactor to reduce code size ([#406](https://github.com/nolanlawson/emoji-picker-element/issues/406)) ([55872ba](https://github.com/nolanlawson/emoji-picker-element/commit/55872ba99647425008b5b047960893cca9f88713))
## [1.21.1](https://github.com/nolanlawson/emoji-picker-element/compare/v1.21.0...v1.21.1) (2024-02-17)
### Bug Fixes
* avoid calling getRootNode ([#399](https://github.com/nolanlawson/emoji-picker-element/issues/399)) ([19331c6](https://github.com/nolanlawson/emoji-picker-element/commit/19331c6be6de9da9199a43c6aa13e48fd310a952))
# [1.21.0](https://github.com/nolanlawson/emoji-picker-element/compare/v1.20.1...v1.21.0) (2023-12-17)

View File

@ -2,58 +2,57 @@
## Basic dev workflow
Install
yarn
pnpm i
Run a local dev server on `localhost:3000`:
yarn dev
pnpm dev
## Testing
Lint:
yarn lint
pnpm lint
Fix most lint issues:
yarn lint:fix
pnpm lint:fix
Run the tests:
yarn test
pnpm test
Check code coverage:
yarn cover
pnpm cover
## Other
Benchmark runtime performance:
yarn benchmark:runtime
pnpm benchmark:runtime
Benchmark memory usage:
yarn benchmark:memory
pnpm benchmark:memory
Benchmark bundle size:
yarn benchmark:bundlesize
pnpm benchmark:bundlesize
Benchmark storage size:
yarn benchmark:storage
pnpm benchmark:storage
Run memory leak test:
yarn test:leak
pnpm test:leak
Build the GitHub Pages docs site:
yarn docs
pnpm docs
## FAQs

View File

@ -62,6 +62,7 @@ A lightweight emoji picker, distributed as a web component.
* [setPreferredSkinTone](#setpreferredskintone)
+ [Custom emoji](#custom-emoji)
+ [Tree-shaking](#tree-shaking)
+ [Within a meta-framework (Next.js, SvelteKit, etc.)](#within-a-meta-framework-nextjs-sveltekit-etc)
+ [Within a Svelte project](#within-a-svelte-project)
* [Data and offline](#data-and-offline)
+ [Data source and JSON format](#data-source-and-json-format)
@ -272,7 +273,7 @@ Here is a full list of options:
### Focus outline
For accessibility reasons, `emoji-picker-element` displays a prominent focus ring. If you want to hide the focus ring for non-keyboard users (e.g. mouse and touch only), then use the [focus-visible](https://github.com/WICG/focus-visible) polyfill, e.g.:
For accessibility reasons, `emoji-picker-element` displays a prominent focus ring for keyboard users. This uses [`:focus-visible`](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible) under the hood. To properly support [browsers that do not support `:focus-visible`](https://caniuse.com/css-focus-visible), you can use the [focus-visible](https://github.com/WICG/focus-visible) polyfill, e.g.:
```js
import 'focus-visible';
@ -825,6 +826,14 @@ import Database from 'emoji-picker-element/database';
The reason for this is that `Picker` automatically registers itself as a custom element, following [web component best practices](https://justinfagnani.com/2019/11/01/how-to-publish-web-components-to-npm/). But this adds side effects, so bundlers like Webpack and Rollup do not tree-shake as well, unless the modules are imported from completely separate files.
### Within a meta-framework (Next.js, SvelteKit, etc.)
Some meta-frameworks will attempt to server-side render (SSR) any dependencies you `import`. However, `emoji-picker-element` only supports client-side rendering it does not work on the server side. If you attempt to import it on the server side, you will see an error like `requestAnimationFrame is not defined`.
To load `emoji-picker-element` only on the client side, use your meta-framework's technique for client-side-only imports. For example, you can use [dynamic `import()`s](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import) with [`next/dynamic` in Next.js](https://stackoverflow.com/a/61881528/680742) or [`onMount()` in SvelteKit](https://www.banjocode.com/post/svelte/client-side-library).
`emoji-picker-element` is not designed for SSR. In most apps, an emoji picker should be lazy-loaded upon user interaction (for example, when the user clicks a button).
### Within a Svelte project
> [!WARNING]

View File

@ -1,8 +0,0 @@
import * as sass from 'sass'
import { minify } from 'csso'
export function buildStyles () {
const file = './src/picker/styles/picker.scss'
const css = sass.compile(file, { style: 'compressed' }).css
return minify(css).css
}

View File

@ -1,22 +0,0 @@
import { buildStyles } from './buildStyles.js'
import { writeFile, mkdirp } from './fs.js'
import path from 'node:path'
const __dirname = path.dirname(new URL(import.meta.url).pathname)
// Build a file containing the CSS just for Jest, because I can't figure out any better way to do this
async function main () {
const styles = buildStyles()
const targetDir = path.join(__dirname, '../node_modules/.cache/emoji-picker-element')
await mkdirp(targetDir)
await writeFile(
path.join(targetDir, 'styles.js'),
`export default ${JSON.stringify(styles)};`,
'utf8'
)
}
main().catch(err => {
console.error(err)
process.exit(1)
})

View File

@ -38,8 +38,9 @@ for (const benchmark of benchmarks) {
repo: 'https://github.com/nolanlawson/emoji-picker-element.git',
ref: 'master',
setupCommands: [
'yarn --immutable --ignore-scripts',
'PERF=1 yarn build:rollup'
// we're comparing against historical branches, so support yarn as well as pnpm since we switched
'if [ -f yarn.lock ]; then yarn --frozen-lockfile; else pnpm i --frozen-lockfile; fi',
'PERF=1 npm run build:rollup'
]
}
}

View File

@ -0,0 +1,18 @@
import * as sass from 'sass'
import { minify } from 'csso'
export function buildStylesRollupPlugin () {
return {
name: 'build-styles-from-scss',
transform (content, id) {
if (id.includes('picker.scss')) {
const css = sass.compile(id, { style: 'compressed' }).css
const code = `export default ${JSON.stringify(minify(css).css)}`
return {
code,
map: null
}
}
}
}
}

View File

@ -1,52 +0,0 @@
import '@testing-library/jest-dom/jest-globals'
import { jest } from '@jest/globals'
import * as FakeIndexedDB from 'fake-indexeddb'
import { Crypto } from '@peculiar/webcrypto'
import { ResizeObserver } from 'd2l-resize-aware/resize-observer-module.js'
import { deleteDatabase } from '../src/database/databaseLifecycle'
import styles from '../node_modules/.cache/emoji-picker-element/styles.js'
import * as fetchMockJest from 'fetch-mock-jest'
const { IDBFactory, IDBKeyRange } = FakeIndexedDB
// See https://github.com/jsdom/jsdom/issues/3455#issuecomment-1333567714
globalThis.crypto.subtle = new Crypto().subtle
if (!globalThis.performance) {
globalThis.performance = {}
}
if (!globalThis.performance.mark) {
globalThis.performance.mark = () => {}
}
if (!globalThis.performance.measure) {
globalThis.performance.measure = () => {}
}
jest.setTimeout(60000)
globalThis.ResizeObserver = ResizeObserver
process.env.NODE_ENV = 'test'
process.env.STYLES = styles
globalThis.IDBKeyRange = IDBKeyRange
globalThis.indexedDB = new IDBFactory()
// Hack to work around an issue with jest-environment-jsdom https://github.com/jsdom/jsdom/issues/3363
globalThis.structuredClone = globalThis.structuredClone ?? (_ => JSON.parse(JSON.stringify(_)))
beforeAll(() => {
jest.spyOn(globalThis.console, 'log').mockImplementation()
jest.spyOn(globalThis.console, 'warn').mockImplementation()
const fetch = fetchMockJest.default.sandbox()
globalThis.fetch = fetch
globalThis.Response = fetch.Response
})
afterEach(async () => {
// fresh indexedDB for every test
const dbs = await globalThis.indexedDB.databases()
await Promise.all(dbs.map(({ name }) => deleteDatabase(name)))
})

View File

@ -1,9 +0,0 @@
import { minifyHTMLLiterals } from 'minify-html-literals'
export default {
processAsync (source, fileName) {
return minifyHTMLLiterals(source, {
fileName
})
}
}

View File

@ -0,0 +1,14 @@
import { minifyHTMLLiterals } from 'minify-html-literals'
export function minifyHtmlLiteralsRollupPlugin () {
return {
name: 'minify-html-in-tag-template-literals',
transform (content, id) {
if (content.includes('html`')) {
return minifyHTMLLiterals(content, {
fileName: id
})
}
}
}
}

26
config/vitest.setup.js Normal file
View File

@ -0,0 +1,26 @@
import { vi } from 'vitest'
import '@testing-library/jest-dom/vitest'
import { IDBFactory, IDBKeyRange } from 'fake-indexeddb'
import { ResizeObserver } from 'd2l-resize-aware/resize-observer-module.js'
import { deleteDatabase } from '../src/database/databaseLifecycle'
import fetchMock from 'fetch-mock'
beforeAll(() => {
globalThis.ResizeObserver = ResizeObserver
globalThis.IDBKeyRange = IDBKeyRange
globalThis.indexedDB = new IDBFactory()
vi.spyOn(globalThis.console, 'log').mockImplementation(() => undefined)
vi.spyOn(globalThis.console, 'warn').mockImplementation(() => undefined)
})
beforeEach(() => {
globalThis.fetch = fetchMock.sandbox()
globalThis.Response = fetch.Response
})
afterEach(async () => {
// fresh indexedDB for every test
const dbs = await globalThis.indexedDB.databases()
await Promise.all(dbs.map(({ name }) => deleteDatabase(name)))
})

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#673AB7" d="M38 44H12V4h26c2.2 0 4 1.8 4 4v32c0 2.2-1.8 4-4 4"/><path fill="#311B92" d="M10 4h2v40h-2c-2.2 0-4-1.8-4-4V8c0-2.2 1.8-4 4-4"/><path fill="#fff" d="M36 24.2c-.1 4.8-3.1 6.9-5.3 6.7-.6-.1-2.1-.1-2.9-1.6-.8 1-1.8 1.6-3.1 1.6-2.6 0-3.3-2.5-3.4-3.1-.1-.7-.2-1.4-.1-2.2.1-1 1.1-6.5 5.7-6.5 2.2 0 3.5 1.1 3.7 1.3l-.6 6.8c0 .3-.2 1.6 1.1 1.6 2.1 0 2.4-3.9 2.4-4.6.1-1.2.3-8.2-7-8.2-6.9 0-7.9 7.4-8 9.2-.5 8.5 6 8.5 7.2 8.5 1.7 0 3.7-.7 3.9-.8l.4 2c-.3.2-2 1.1-4.4 1.1-2.2 0-10.1-.4-9.8-10.8.3-2.1 1.6-11.2 10.8-11.2 9.2 0 9.4 8.1 9.4 10.2m-11.9 1.3c-.1 1 0 1.8.2 2.3.2.5.6.8 1.2.8.1 0 .3 0 .4-.1.2-.1.3-.1.5-.3.2-.1.3-.3.5-.6.2-.2.3-.6.4-1l.5-5.4c-.2-.1-.5-.1-.7-.1-.5 0-.9.1-1.2.3-.3.2-.6.5-.9.8-.2.4-.4.8-.6 1.3s-.2 1.3-.3 2"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#673AB7" d="M38 44H12V4h26c2.2 0 4 1.8 4 4v32c0 2.2-1.8 4-4 4"/><path fill="#311B92" d="M10 4h2v40h-2c-2.2 0-4-1.8-4-4V8c0-2.2 1.8-4 4-4"/><path fill="#fff" d="M36 24.2c-.1 4.8-3.1 6.9-5.3 6.7-.6-.1-2.1-.1-2.9-1.6-.8 1-1.8 1.6-3.1 1.6-2.6 0-3.3-2.5-3.4-3.1-.1-.7-.2-1.4-.1-2.2.1-1 1.1-6.5 5.7-6.5 2.2 0 3.5 1.1 3.7 1.3l-.6 6.8c0 .3-.2 1.6 1.1 1.6 2.1 0 2.4-3.9 2.4-4.6.1-1.2.3-8.2-7-8.2-6.9 0-7.9 7.4-8 9.2-.5 8.5 6 8.5 7.2 8.5 1.7 0 3.7-.7 3.9-.8l.4 2c-.3.2-2 1.1-4.4 1.1-2.2 0-10.1-.4-9.8-10.8.3-2.1 1.6-11.2 10.8-11.2S36 22.1 36 24.2m-11.9 1.3c-.1 1 0 1.8.2 2.3s.6.8 1.2.8c.1 0 .3 0 .4-.1.2-.1.3-.1.5-.3.2-.1.3-.3.5-.6.2-.2.3-.6.4-1l.5-5.4c-.2-.1-.5-.1-.7-.1q-.75 0-1.2.3c-.3.2-.6.5-.9.8-.2.4-.4.8-.6 1.3s-.2 1.3-.3 2"/></svg>

Before

Width:  |  Height:  |  Size: 824 B

After

Width:  |  Height:  |  Size: 813 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#37474F" d="m38.5 44.6-4-4 2.1-2.1 4 4c.6.6.6 1.5 0 2.1-.5.5-1.5.5-2.1 0m-29 0 4-4-2.1-2.1-4 4c-.6.6-.6 1.5 0 2.1.5.5 1.5.5 2.1 0"/><circle cx="24" cy="24" r="20" fill="#C62828"/><circle cx="24" cy="24" r="16" fill="#eee"/><path fill="#E53935" d="m15.096 33.48-.566-.566 9.191-9.191.566.565z"/><path d="M23 11h2v13h-2z"/><path d="M31.285 29.654 29.66 31.28l-6.504-6.504 1.626-1.627z"/><circle cx="24" cy="24" r="2"/><circle cx="24" cy="24" r="1" fill="#C62828"/><path fill="#37474F" d="M22 1h4v3h-4zm22.4 15.2c2.5-3.5 2.1-8.4-1-11.5-3.1-3.1-8-3.5-11.5-1zm-40.8 0c-2.5-3.5-2.1-8.4 1-11.5 3.1-3.1 8-3.5 11.5-1z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#37474F" d="m38.5 44.6-4-4 2.1-2.1 4 4c.6.6.6 1.5 0 2.1-.5.5-1.5.5-2.1 0m-29 0 4-4-2.1-2.1-4 4c-.6.6-.6 1.5 0 2.1.5.5 1.5.5 2.1 0"/><circle cx="24" cy="24" r="20" fill="#C62828"/><circle cx="24" cy="24" r="16" fill="#eee"/><path fill="#E53935" d="m15.096 33.48-.566-.566 9.191-9.191.566.565z"/><path d="M23 11h2v13h-2z"/><path d="M31.285 29.654 29.66 31.28l-6.504-6.504 1.626-1.627z"/><circle cx="24" cy="24" r="2"/><circle cx="24" cy="24" r="1" fill="#C62828"/><path fill="#37474F" d="M22 1h4v3h-4zm22.4 15.2c2.5-3.5 2.1-8.4-1-11.5s-8-3.5-11.5-1zm-40.8 0c-2.5-3.5-2.1-8.4 1-11.5s8-3.5 11.5-1z"/></svg>

Before

Width:  |  Height:  |  Size: 701 B

After

Width:  |  Height:  |  Size: 686 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><g fill="#37474F"><circle cx="33" cy="16" r="6"/><circle cx="15" cy="16" r="6"/><path d="m46.7 25-15.3 3H16.7L1.4 25l4.3-7.9C6.8 15.2 8.8 14 11 14h26.2c2.2 0 4.2 1.2 5.3 3.1z"/><circle cx="38" cy="30" r="10"/><circle cx="10" cy="30" r="10"/><circle cx="24" cy="28" r="5"/></g><circle cx="24" cy="28" r="2" fill="#546E7A"/><g fill="#a0f"><circle cx="38" cy="30" r="7"/><circle cx="10" cy="30" r="7"/></g><path fill="#CE93D8" d="M41.7 27.7c-1-1.1-2.3-1.7-3.7-1.7s-2.8.6-3.7 1.7c-.4.4-.3 1 .1 1.4.4.4 1 .3 1.4-.1 1.2-1.3 3.3-1.3 4.5 0 .2.2.5.3.7.3.2 0 .5-.1.7-.3.4-.3.4-.9 0-1.3M10 26c-1.4 0-2.8.6-3.7 1.7-.4.4-.3 1 .1 1.4.4.4 1 .3 1.4-.1 1.2-1.3 3.3-1.3 4.5 0 .2.2.5.3.7.3.2 0 .5-.1.7-.3.4-.4.4-1 .1-1.4-1-1-2.4-1.6-3.8-1.6"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><g fill="#37474F"><circle cx="33" cy="16" r="6"/><circle cx="15" cy="16" r="6"/><path d="m46.7 25-15.3 3H16.7L1.4 25l4.3-7.9C6.8 15.2 8.8 14 11 14h26.2c2.2 0 4.2 1.2 5.3 3.1z"/><circle cx="38" cy="30" r="10"/><circle cx="10" cy="30" r="10"/><circle cx="24" cy="28" r="5"/></g><circle cx="24" cy="28" r="2" fill="#546E7A"/><g fill="#a0f"><circle cx="38" cy="30" r="7"/><circle cx="10" cy="30" r="7"/></g><path fill="#CE93D8" d="M41.7 27.7c-1-1.1-2.3-1.7-3.7-1.7s-2.8.6-3.7 1.7c-.4.4-.3 1 .1 1.4s1 .3 1.4-.1c1.2-1.3 3.3-1.3 4.5 0 .2.2.5.3.7.3s.5-.1.7-.3c.4-.3.4-.9 0-1.3M10 26c-1.4 0-2.8.6-3.7 1.7-.4.4-.3 1 .1 1.4s1 .3 1.4-.1c1.2-1.3 3.3-1.3 4.5 0 .2.2.5.3.7.3s.5-.1.7-.3c.4-.4.4-1 .1-1.4-1-1-2.4-1.6-3.8-1.6"/></svg>

Before

Width:  |  Height:  |  Size: 802 B

After

Width:  |  Height:  |  Size: 788 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#00A344" d="M24 13c-7.2 0-13 5.8-13 13s5.8 13 13 13 13-5.8 13-13-5.8-13-13-13m0 22c-5 0-9-4-9-9s4-9 9-9 9 4 9 9-4 9-9 9"/><path fill="#00C853" d="M8.5 25.4c4-2.2 9-1.1 11.5 2.5.1.1.2.1.3.1l1.2-.7c.1-.1.2-.2.1-.3 0-.2-.1-.4-.1-.6v-.6c0-.1 0-.1.1-.2 0-.1 0-.1.1-.2 0 0 0-.1.1-.1 0 0 0-.1.1-.1 0 0 0-.1.1-.1l.1-.1.1-.1.1-.1.1-.1.1-.1h.1c.2-.1.4-.2.5-.2.1 0 .2-.1.2-.3v-1.3c0-.1-.1-.2-.2-.2-4.5-.4-8-4.1-8-8.6 0-4.1 3-7.6 6.9-8.4.1 0 .2-.1.2-.3v-.5c0-.1-.1-.2-.2-.2-5.6.9-10 5.8-10 11.7 0 1.3.2 2.6.6 3.8-1.2.2-2.5.7-3.6 1.3-5.2 3-7.3 9.2-5.2 14.5.1.1.2.1.3.1l.3-.2c.1-.1.2-.2.1-.3-1.2-3.8.3-8.1 4-10.1m30.5-4c-1.2-.7-2.4-1.1-3.6-1.3.4-1.2.6-2.4.6-3.8 0-5.9-4.4-10.8-10.2-11.7-.1 0-.2.1-.2.2v.4c0 .1.1.2.2.3 4 .8 6.9 4.3 6.9 8.4 0 4.5-3.5 8.2-8 8.6-.1 0-.2.1-.2.2V24c0 .1.1.2.2.3.2.1.4.1.6.2l.1.1c.1 0 .1.1.1.1l.3.3.1.1.1.1v.2s0 .1.1.1c0 .1 0 .1.1.2v.7c0 .2 0 .4-.1.6 0 .1 0 .2.1.3l1.2.7c.1.1.2 0 .3-.1 2.6-3.6 7.6-4.8 11.5-2.5 3.6 2.1 5.2 6.3 3.9 10.1 0 .1 0 .2.1.3l.3.2c.1.1.2 0 .3-.1 2.5-5.4.4-11.6-4.8-14.5m-8.2 18.9c-4-2.2-5.5-7.1-3.5-11.1.1-.1 0-.2-.1-.3l-1.2-.7c-.1-.1-.2 0-.3 0-.2.1-.3.3-.5.3-.1 0-.1.1-.2.1s-.1 0-.2.1c-.1 0-.3.1-.4.1h-1.2c-.1 0-.1 0-.2-.1 0 0-.1 0-.1-.1h-.1c-.2-.1-.3-.2-.5-.3-.1-.1-.2-.1-.3 0l-1.2.7c-.1.1-.1.2-.1.3 1.9 4 .4 8.8-3.5 11.1-3.6 2.1-8.2 1.3-10.9-1.7-.1-.1-.2-.1-.3-.1l-.3.2c-.1.1-.1.2-.1.3 3.6 4.5 10.2 5.8 15.4 2.8 1.2-.7 2.2-1.5 3-2.4.8.9 1.8 1.8 3 2.4 5.2 3 11.7 1.6 15.4-2.8.1-.1 0-.2-.1-.3l-.3-.3c-.1-.1-.2 0-.3.1-2.7 2.9-7.3 3.7-10.9 1.7"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#00A344" d="M24 13c-7.2 0-13 5.8-13 13s5.8 13 13 13 13-5.8 13-13-5.8-13-13-13m0 22c-5 0-9-4-9-9s4-9 9-9 9 4 9 9-4 9-9 9"/><path fill="#00C853" d="M8.5 25.4c4-2.2 9-1.1 11.5 2.5.1.1.2.1.3.1l1.2-.7c.1-.1.2-.2.1-.3 0-.2-.1-.4-.1-.6v-.6c0-.1 0-.1.1-.2 0-.1 0-.1.1-.2 0 0 0-.1.1-.1 0 0 0-.1.1-.1 0 0 0-.1.1-.1l.1-.1.1-.1.1-.1.1-.1.1-.1h.1c.2-.1.4-.2.5-.2s.2-.1.2-.3v-1.3c0-.1-.1-.2-.2-.2-4.5-.4-8-4.1-8-8.6 0-4.1 3-7.6 6.9-8.4.1 0 .2-.1.2-.3v-.5c0-.1-.1-.2-.2-.2-5.6.9-10 5.8-10 11.7 0 1.3.2 2.6.6 3.8-1.2.2-2.5.7-3.6 1.3-5.2 3-7.3 9.2-5.2 14.5.1.1.2.1.3.1l.3-.2c.1-.1.2-.2.1-.3-1.2-3.8.3-8.1 4-10.1m30.5-4c-1.2-.7-2.4-1.1-3.6-1.3.4-1.2.6-2.4.6-3.8 0-5.9-4.4-10.8-10.2-11.7-.1 0-.2.1-.2.2v.4c0 .1.1.2.2.3 4 .8 6.9 4.3 6.9 8.4 0 4.5-3.5 8.2-8 8.6-.1 0-.2.1-.2.2V24c0 .1.1.2.2.3.2.1.4.1.6.2l.1.1c.1 0 .1.1.1.1l.3.3.1.1.1.1v.2s0 .1.1.1c0 .1 0 .1.1.2v.7c0 .2 0 .4-.1.6 0 .1 0 .2.1.3l1.2.7c.1.1.2 0 .3-.1 2.6-3.6 7.6-4.8 11.5-2.5 3.6 2.1 5.2 6.3 3.9 10.1 0 .1 0 .2.1.3l.3.2c.1.1.2 0 .3-.1 2.5-5.4.4-11.6-4.8-14.5m-8.2 18.9c-4-2.2-5.5-7.1-3.5-11.1.1-.1 0-.2-.1-.3l-1.2-.7c-.1-.1-.2 0-.3 0-.2.1-.3.3-.5.3-.1 0-.1.1-.2.1s-.1 0-.2.1c-.1 0-.3.1-.4.1h-1.2c-.1 0-.1 0-.2-.1 0 0-.1 0-.1-.1h-.1c-.2-.1-.3-.2-.5-.3q-.15-.15-.3 0l-1.2.7c-.1.1-.1.2-.1.3 1.9 4 .4 8.8-3.5 11.1-3.6 2.1-8.2 1.3-10.9-1.7-.1-.1-.2-.1-.3-.1l-.3.2c-.1.1-.1.2-.1.3 3.6 4.5 10.2 5.8 15.4 2.8q1.8-1.05 3-2.4c.8.9 1.8 1.8 3 2.4 5.2 3 11.7 1.6 15.4-2.8.1-.1 0-.2-.1-.3l-.3-.3c-.1-.1-.2 0-.3.1-2.7 2.9-7.3 3.7-10.9 1.7"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#2196F3" d="M26.4 33.9s4-2.6 4.8-3c.8-.4 1.7-.6 2.2-.2.8.5 7.5 4.9 8.1 5.3.6.4.8 1.5.1 2.6-.8 1.1-4.3 5.5-5.8 5.4-1.5 0-8.4.4-20.3-11.4C3.6 20.7 4 13.8 4 12.3s4.3-5.1 5.4-5.8c1.1-.8 2.2-.5 2.6.1.4.6 4.8 7.3 5.3 8.1.3.5.2 1.4-.2 2.2-.4.8-3 4.8-3 4.8s.7 2.8 5 7.2c4.4 4.3 7.3 5 7.3 5"/><g fill="#3F51B5"><path d="M35 9H25v4h10c1.1 0 2 .9 2 2v10h4V15c0-3.3-2.7-6-6-6"/><path d="m28 16-6.7-5L28 6z"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#2196F3" d="M26.4 33.9s4-2.6 4.8-3 1.7-.6 2.2-.2c.8.5 7.5 4.9 8.1 5.3s.8 1.5.1 2.6c-.8 1.1-4.3 5.5-5.8 5.4-1.5 0-8.4.4-20.3-11.4C3.6 20.7 4 13.8 4 12.3s4.3-5.1 5.4-5.8c1.1-.8 2.2-.5 2.6.1s4.8 7.3 5.3 8.1c.3.5.2 1.4-.2 2.2s-3 4.8-3 4.8.7 2.8 5 7.2c4.4 4.3 7.3 5 7.3 5"/><g fill="#3F51B5"><path d="M35 9H25v4h10c1.1 0 2 .9 2 2v10h4V15c0-3.3-2.7-6-6-6"/><path d="m28 16-6.7-5L28 6z"/></g></svg>

Before

Width:  |  Height:  |  Size: 490 B

After

Width:  |  Height:  |  Size: 475 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#607D8B" d="M20 42H10c-2.2 0-4-1.8-4-4V15c0-5 4-9 9-9s9 4 9 9v23c0 2.2-1.8 4-4 4"/><circle cx="15" cy="15" r="7" fill="#455A64"/><circle cx="15" cy="15" r="5.2" fill="#42A5F5"/><path fill="#90CAF9" d="M18.3 13c-.8-.9-2-1.5-3.3-1.5s-2.4.5-3.3 1.5c-.3.4-.3.9.1 1.2.4.3.9.3 1.2-.1 1-1.2 2.9-1.2 3.9 0 .2.2.4.3.7.3.2 0 .4-.1.6-.2.4-.3.4-.9.1-1.2"/><path fill="#607D8B" d="M40 31H28c-1.1 0-2-.9-2-2V19c0-1.1.9-2 2-2h12c1.1 0 2 .9 2 2v10c0 1.1-.9 2-2 2"/><path fill="#455A64" d="M24 19h2v10h-2z"/><path fill="#03A9F4" d="M28 19h12v10H28z"/><path fill="#4FC3F7" d="M33 22.2 29 28h8z"/><g fill="#B3E5FC"><circle cx="37.5" cy="21.5" r="1"/><path d="M36 24.2 33 28h6z"/></g><circle cx="15" cy="35" r="3" fill="#455A64"/><circle cx="15" cy="35" r="2" fill="#F44336"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#607D8B" d="M20 42H10c-2.2 0-4-1.8-4-4V15c0-5 4-9 9-9s9 4 9 9v23c0 2.2-1.8 4-4 4"/><circle cx="15" cy="15" r="7" fill="#455A64"/><circle cx="15" cy="15" r="5.2" fill="#42A5F5"/><path fill="#90CAF9" d="M18.3 13c-.8-.9-2-1.5-3.3-1.5s-2.4.5-3.3 1.5c-.3.4-.3.9.1 1.2s.9.3 1.2-.1c1-1.2 2.9-1.2 3.9 0 .2.2.4.3.7.3.2 0 .4-.1.6-.2.4-.3.4-.9.1-1.2"/><path fill="#607D8B" d="M40 31H28c-1.1 0-2-.9-2-2V19c0-1.1.9-2 2-2h12c1.1 0 2 .9 2 2v10c0 1.1-.9 2-2 2"/><path fill="#455A64" d="M24 19h2v10h-2z"/><path fill="#03A9F4" d="M28 19h12v10H28z"/><path fill="#4FC3F7" d="M33 22.2 29 28h8z"/><g fill="#B3E5FC"><circle cx="37.5" cy="21.5" r="1"/><path d="M36 24.2 33 28h6z"/></g><circle cx="15" cy="35" r="3" fill="#455A64"/><circle cx="15" cy="35" r="2" fill="#F44336"/></svg>

Before

Width:  |  Height:  |  Size: 846 B

After

Width:  |  Height:  |  Size: 843 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#512DA8" d="M33.9 12.1H14.2L17.6 7c.4-.6 1-.9 1.7-.9h9.6c.7 0 1.3.3 1.7.9z"/><path fill="#8667C4" d="M14 11H8V9.2C8 8.5 8.5 8 9.2 8h3.6c.7 0 1.2.5 1.2 1.2z"/><path fill="#5E35B1" d="M40 42H8c-2.2 0-4-1.8-4-4V14c0-2.2 1.8-4 4-4h32c2.2 0 4 1.8 4 4v24c0 2.2-1.8 4-4 4"/><circle cx="24" cy="26" r="12" fill="#512DA8"/><circle cx="24" cy="26" r="9" fill="#B388FF"/><path fill="#C7A7FF" d="M29 23c-1.2-1.4-3-2.2-4.8-2.2-1.8 0-3.6.8-4.8 2.2-.5.5-.4 1.3.1 1.8s1.3.4 1.8-.1c1.5-1.7 4.3-1.7 5.8 0 .3.3.6.4 1 .4.3 0 .6-.1.9-.3.4-.4.5-1.3 0-1.8"/><ellipse cx="11" cy="13.5" fill="#8667C4" rx="2" ry="1.5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#512DA8" d="M33.9 12.1H14.2L17.6 7c.4-.6 1-.9 1.7-.9h9.6c.7 0 1.3.3 1.7.9z"/><path fill="#8667C4" d="M14 11H8V9.2C8 8.5 8.5 8 9.2 8h3.6c.7 0 1.2.5 1.2 1.2z"/><path fill="#5E35B1" d="M40 42H8c-2.2 0-4-1.8-4-4V14c0-2.2 1.8-4 4-4h32c2.2 0 4 1.8 4 4v24c0 2.2-1.8 4-4 4"/><circle cx="24" cy="26" r="12" fill="#512DA8"/><circle cx="24" cy="26" r="9" fill="#B388FF"/><path fill="#C7A7FF" d="M29 23c-1.2-1.4-3-2.2-4.8-2.2s-3.6.8-4.8 2.2c-.5.5-.4 1.3.1 1.8s1.3.4 1.8-.1c1.5-1.7 4.3-1.7 5.8 0 .3.3.6.4 1 .4q.45 0 .9-.3c.4-.4.5-1.3 0-1.8"/><ellipse cx="11" cy="13.5" fill="#8667C4" rx="2" ry="1.5"/></svg>

Before

Width:  |  Height:  |  Size: 684 B

After

Width:  |  Height:  |  Size: 678 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#512DA8" d="M33.9 12.1H14.2L17.6 7c.4-.6 1-.9 1.7-.9h9.6c.7 0 1.3.3 1.7.9z"/><path fill="#8667C4" d="M14 11H8V9.2C8 8.5 8.5 8 9.2 8h3.6c.7 0 1.2.5 1.2 1.2z"/><path fill="#5E35B1" d="M40 42H8c-2.2 0-4-1.8-4-4V14c0-2.2 1.8-4 4-4h32c2.2 0 4 1.8 4 4v24c0 2.2-1.8 4-4 4"/><circle cx="24" cy="26" r="12" fill="#512DA8"/><circle cx="24" cy="26" r="9" fill="#B388FF"/><path fill="#C7A7FF" d="M28.8 23c-1.2-1.4-3-2.2-4.8-2.2s-3.6.8-4.8 2.2c-.5.5-.4 1.3.1 1.8s1.3.4 1.8-.1c1.5-1.7 4.3-1.7 5.8 0 .3.3.6.4 1 .4.3 0 .6-.1.9-.3.4-.4.5-1.3 0-1.8"/><ellipse cx="11" cy="13.5" fill="#8667C4" rx="2" ry="1.5"/><path fill="#8BC34A" d="M48 33.8c0-1.3-1.1-2.4-2.4-2.4H42c-.4 0-.7-.5-.4-.8.4-.6.5-1.3.4-2.1-.2-1.2-1.1-2.1-2.3-2.4-2-.4-3.7 1-3.7 2.9 0 .6.2 1.1.4 1.6.2.4 0 .8-.5.8h-3.6c-1.3 0-2.4 1.1-2.4 2.4V37c0 .4.5.7.8.4.6-.4 1.3-.5 2.1-.4 1.2.2 2.1 1.1 2.4 2.3.4 1.9-1.1 3.6-2.9 3.6-.6 0-1.1-.2-1.6-.4-.4-.2-.8 0-.8.5v2.6c0 1.3 1.1 2.4 2.4 2.4h13.2c1.3 0 2.4-1.1 2.4-2.4V33.8z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#512DA8" d="M33.9 12.1H14.2L17.6 7c.4-.6 1-.9 1.7-.9h9.6c.7 0 1.3.3 1.7.9z"/><path fill="#8667C4" d="M14 11H8V9.2C8 8.5 8.5 8 9.2 8h3.6c.7 0 1.2.5 1.2 1.2z"/><path fill="#5E35B1" d="M40 42H8c-2.2 0-4-1.8-4-4V14c0-2.2 1.8-4 4-4h32c2.2 0 4 1.8 4 4v24c0 2.2-1.8 4-4 4"/><circle cx="24" cy="26" r="12" fill="#512DA8"/><circle cx="24" cy="26" r="9" fill="#B388FF"/><path fill="#C7A7FF" d="M28.8 23c-1.2-1.4-3-2.2-4.8-2.2s-3.6.8-4.8 2.2c-.5.5-.4 1.3.1 1.8s1.3.4 1.8-.1c1.5-1.7 4.3-1.7 5.8 0 .3.3.6.4 1 .4q.45 0 .9-.3c.4-.4.5-1.3 0-1.8"/><ellipse cx="11" cy="13.5" fill="#8667C4" rx="2" ry="1.5"/><path fill="#8BC34A" d="M48 33.8c0-1.3-1.1-2.4-2.4-2.4H42c-.4 0-.7-.5-.4-.8.4-.6.5-1.3.4-2.1-.2-1.2-1.1-2.1-2.3-2.4-2-.4-3.7 1-3.7 2.9 0 .6.2 1.1.4 1.6.2.4 0 .8-.5.8h-3.6c-1.3 0-2.4 1.1-2.4 2.4V37c0 .4.5.7.8.4.6-.4 1.3-.5 2.1-.4 1.2.2 2.1 1.1 2.4 2.3.4 1.9-1.1 3.6-2.9 3.6-.6 0-1.1-.2-1.6-.4-.4-.2-.8 0-.8.5v2.6c0 1.3 1.1 2.4 2.4 2.4h13.2c1.3 0 2.4-1.1 2.4-2.4V33.8z"/></svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#512DA8" d="M33.9 12.1H14.2L17.6 7c.4-.6 1-.9 1.7-.9h9.6c.7 0 1.3.3 1.7.9z"/><path fill="#8667C4" d="M14 11H8V9.2C8 8.5 8.5 8 9.2 8h3.6c.7 0 1.2.5 1.2 1.2z"/><path fill="#5E35B1" d="M40 42H8c-2.2 0-4-1.8-4-4V14c0-2.2 1.8-4 4-4h32c2.2 0 4 1.8 4 4v24c0 2.2-1.8 4-4 4"/><circle cx="24" cy="26" r="12" fill="#512DA8"/><circle cx="24" cy="26" r="9" fill="#B388FF"/><g fill="#616161"><path d="m38.912 40.703 1.696-1.697 7.353 7.353-1.697 1.696z"/><circle cx="35" cy="35" r="10"/></g><path fill="#37474F" d="m42.305 44.107 1.697-1.697 3.96 3.959-1.698 1.697z"/><circle cx="35" cy="35" r="8" fill="#64B5F6"/><path fill="#BBDEFB" d="M39.3 31.4c-1.1-1.3-2.6-2-4.2-2s-3.2.7-4.2 2c-.2.3-.2.6.1.9.3.2.6.2.9-.1.8-1 2-1.5 3.3-1.5s2.5.6 3.3 1.5c.1.1.3.2.5.2.1 0 .3 0 .4-.1.1-.2.1-.6-.1-.9"/><path fill="#C7A7FF" d="M29 23c-1.2-1.4-3-2.2-4.8-2.2-1.8 0-3.6.8-4.8 2.2-.5.5-.4 1.3.1 1.8s1.3.4 1.8-.1c1.5-1.7 4.3-1.7 5.8 0 .3.3.6.4 1 .4.3 0 .6-.1.9-.3.4-.4.5-1.3 0-1.8"/><ellipse cx="11" cy="13.5" fill="#8667C4" rx="2" ry="1.5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#512DA8" d="M33.9 12.1H14.2L17.6 7c.4-.6 1-.9 1.7-.9h9.6c.7 0 1.3.3 1.7.9z"/><path fill="#8667C4" d="M14 11H8V9.2C8 8.5 8.5 8 9.2 8h3.6c.7 0 1.2.5 1.2 1.2z"/><path fill="#5E35B1" d="M40 42H8c-2.2 0-4-1.8-4-4V14c0-2.2 1.8-4 4-4h32c2.2 0 4 1.8 4 4v24c0 2.2-1.8 4-4 4"/><circle cx="24" cy="26" r="12" fill="#512DA8"/><circle cx="24" cy="26" r="9" fill="#B388FF"/><g fill="#616161"><path d="m38.912 40.703 1.696-1.697 7.353 7.353-1.697 1.696z"/><circle cx="35" cy="35" r="10"/></g><path fill="#37474F" d="m42.305 44.107 1.697-1.697 3.96 3.959-1.698 1.697z"/><circle cx="35" cy="35" r="8" fill="#64B5F6"/><path fill="#BBDEFB" d="M39.3 31.4c-1.1-1.3-2.6-2-4.2-2s-3.2.7-4.2 2c-.2.3-.2.6.1.9.3.2.6.2.9-.1.8-1 2-1.5 3.3-1.5s2.5.6 3.3 1.5c.1.1.3.2.5.2.1 0 .3 0 .4-.1.1-.2.1-.6-.1-.9"/><path fill="#C7A7FF" d="M29 23c-1.2-1.4-3-2.2-4.8-2.2s-3.6.8-4.8 2.2c-.5.5-.4 1.3.1 1.8s1.3.4 1.8-.1c1.5-1.7 4.3-1.7 5.8 0 .3.3.6.4 1 .4q.45 0 .9-.3c.4-.4.5-1.3 0-1.8"/><ellipse cx="11" cy="13.5" fill="#8667C4" rx="2" ry="1.5"/></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 48 48"><path fill="#CFD8DC" d="M41 6H7c-.6 0-1 .4-1 1v35h36V7c0-.6-.4-1-1-1"/><path fill="#263238" d="M8 13h32v27H8z"/><path fill="#76FF03" d="M22 27.6c-.1 1.1-.4 1.9-1 2.5-.6.6-1.4.9-2.5.9s-2-.4-2.6-1.1c-.6-.7-.9-1.8-.9-3.1v-1.6c0-1.3.3-2.4.9-3.1.6-.7 1.5-1.1 2.6-1.1s1.9.3 2.5.9c.6.6.9 1.4 1 2.6h-2c0-.7-.1-1.2-.3-1.4-.2-.3-.6-.4-1.1-.4-.5 0-.9.2-1.2.6-.2.4-.3 1-.4 1.8v1.8c0 1 .1 1.6.3 2 .2.4.6.5 1.1.5.5 0 .9-.1 1.1-.4.2-.3.3-.7.3-1.4zm2-3.6c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3.3 0 .5.1.7.3.2.2.3.4.3.7 0 .3-.1.5-.3.7s-.4.3-.7.3c-.3 0-.5-.1-.7-.3s-.3-.4-.3-.7m0 6c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3.3 0 .5.1.7.3.2.2.3.4.3.7 0 .3-.1.5-.3.7s-.4.3-.7.3c-.3 0-.5-.1-.7-.3s-.3-.4-.3-.7m4-9h2l3 10h-2z"/><g fill="#90A4AE"><circle cx="13.5" cy="9.5" r="1.5"/><circle cx="9.5" cy="9.5" r="1.5"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 48 48"><path fill="#CFD8DC" d="M41 6H7c-.6 0-1 .4-1 1v35h36V7c0-.6-.4-1-1-1"/><path fill="#263238" d="M8 13h32v27H8z"/><path fill="#76FF03" d="M22 27.6c-.1 1.1-.4 1.9-1 2.5s-1.4.9-2.5.9-2-.4-2.6-1.1-.9-1.8-.9-3.1v-1.6c0-1.3.3-2.4.9-3.1s1.5-1.1 2.6-1.1 1.9.3 2.5.9.9 1.4 1 2.6h-2c0-.7-.1-1.2-.3-1.4-.2-.3-.6-.4-1.1-.4q-.75 0-1.2.6c-.2.4-.3 1-.4 1.8v1.8c0 1 .1 1.6.3 2s.6.5 1.1.5.9-.1 1.1-.4.3-.7.3-1.4zm2-3.6c0-.3.1-.5.3-.7s.4-.3.7-.3.5.1.7.3.3.4.3.7-.1.5-.3.7-.4.3-.7.3-.5-.1-.7-.3-.3-.4-.3-.7m0 6c0-.3.1-.5.3-.7s.4-.3.7-.3.5.1.7.3.3.4.3.7-.1.5-.3.7-.4.3-.7.3-.5-.1-.7-.3-.3-.4-.3-.7m4-9h2l3 10h-2z"/><g fill="#90A4AE"><circle cx="13.5" cy="9.5" r="1.5"/><circle cx="9.5" cy="9.5" r="1.5"/></g></svg>

Before

Width:  |  Height:  |  Size: 864 B

After

Width:  |  Height:  |  Size: 774 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#607D8B" d="M40 39H8c-2.2 0-4-1.8-4-4V13c0-2.2 1.8-4 4-4h32c2.2 0 4 1.8 4 4v22c0 2.2-1.8 4-4 4"/><circle cx="29" cy="24" r="12" fill="#455A64"/><circle cx="29" cy="24" r="9" fill="#42A5F5"/><path fill="#90CAF9" d="M33.8 21c-1.2-1.4-3-2.2-4.8-2.2s-3.6.8-4.8 2.2c-.5.5-.4 1.3.1 1.8s1.3.4 1.8-.1c1.5-1.7 4.3-1.7 5.8 0 .3.3.6.4 1 .4.3 0 .6-.1.9-.3.4-.4.5-1.3 0-1.8"/><path fill="#ADD8FB" d="M8 13h6v3H8z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#607D8B" d="M40 39H8c-2.2 0-4-1.8-4-4V13c0-2.2 1.8-4 4-4h32c2.2 0 4 1.8 4 4v22c0 2.2-1.8 4-4 4"/><circle cx="29" cy="24" r="12" fill="#455A64"/><circle cx="29" cy="24" r="9" fill="#42A5F5"/><path fill="#90CAF9" d="M33.8 21c-1.2-1.4-3-2.2-4.8-2.2s-3.6.8-4.8 2.2c-.5.5-.4 1.3.1 1.8s1.3.4 1.8-.1c1.5-1.7 4.3-1.7 5.8 0 .3.3.6.4 1 .4q.45 0 .9-.3c.4-.4.5-1.3 0-1.8"/><path fill="#ADD8FB" d="M8 13h6v3H8z"/></svg>

Before

Width:  |  Height:  |  Size: 492 B

After

Width:  |  Height:  |  Size: 490 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><circle cx="18" cy="18" r="15" fill="#3F51B5"/><path fill="#FFF59D" d="M20.3 16v1.7h-3.8v1.4h3.8v1.7h-3.8c0 .6.1 1.2.3 1.6.2.4.4.8.7 1 .3.3.7.4 1.1.6.4.1.9.2 1.4.2.4 0 .7 0 1.1-.1.4-.1.7-.1 1-.3l.4 2.7c-.4.1-.9.2-1.4.2-.5.1-1 .1-1.5.1-.9 0-1.8-.1-2.6-.4-.8-.2-1.5-.6-2-1.1-.6-.5-1-1.1-1.4-1.9-.3-.7-.5-1.6-.5-2.6h-1.9v-1.7h1.9v-1.4h-1.9V16h1.9c.1-1 .3-1.8.6-2.6.4-.7.8-1.4 1.4-1.9.6-.5 1.3-.9 2.1-1.1.8-.3 1.7-.4 2.6-.4.4 0 .9 0 1.3.1s.9.1 1.3.3l-.4 2.7c-.3-.1-.6-.2-1-.3-.4-.1-.7-.1-1.1-.1-.5 0-1 .1-1.4.2-.4.1-.8.3-1 .6-.3.3-.5.6-.7 1s-.3.9-.3 1.5z"/><circle cx="30" cy="30" r="15" fill="#4CAF50"/><path fill="#fff" d="M28.4 27c.1.2.2.4.4.6.2.2.4.4.7.5.3.2.7.3 1.1.5.7.3 1.4.6 2 .9.6.3 1.1.7 1.5 1.1.4.4.8.9 1 1.4.2.5.4 1.2.4 1.9s-.1 1.3-.3 1.8c-.2.5-.5 1-.9 1.4s-.9.7-1.4.9c-.6.2-1.2.4-1.8.5v2.2h-1.8v-2.2c-.6-.1-1.2-.2-1.8-.4s-1.1-.5-1.5-1c-.5-.4-.8-1-1.1-1.6-.3-.6-.4-1.4-.4-2.3h3.3c0 .5.1 1 .2 1.3.1.4.3.6.6.9.2.2.5.4.8.5.3.1.6.1.9.1.4 0 .7 0 .9-.1.3-.1.5-.2.7-.4.2-.2.3-.4.4-.6.1-.2.1-.5.1-.8 0-.3 0-.6-.1-.8-.1-.2-.2-.5-.4-.7s-.4-.4-.7-.5c-.3-.2-.7-.3-1.1-.5-.7-.3-1.4-.6-2-.9-.6-.3-1.1-.7-1.5-1.1-.4-.4-.8-.9-1-1.4-.2-.5-.4-1.2-.4-1.9 0-.6.1-1.2.3-1.7.2-.5.5-1 .9-1.4.4-.4.9-.7 1.4-1 .5-.2 1.2-.4 1.8-.5v-2.4h1.8v2.4c.6.1 1.2.3 1.8.6.5.3 1 .6 1.3 1.1.4.4.7 1 .9 1.6.2.6.3 1.3.3 2h-3.3c0-.9-.2-1.6-.6-2-.4-.4-.9-.7-1.5-.7-.3 0-.6.1-.9.2-.2.1-.4.2-.6.4-.2.2-.3.4-.3.6-.1.2-.1.5-.1.8-.1.2 0 .5 0 .7"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><circle cx="18" cy="18" r="15" fill="#3F51B5"/><path fill="#FFF59D" d="M20.3 16v1.7h-3.8v1.4h3.8v1.7h-3.8c0 .6.1 1.2.3 1.6s.4.8.7 1c.3.3.7.4 1.1.6.4.1.9.2 1.4.2.4 0 .7 0 1.1-.1s.7-.1 1-.3l.4 2.7c-.4.1-.9.2-1.4.2-.5.1-1 .1-1.5.1-.9 0-1.8-.1-2.6-.4-.8-.2-1.5-.6-2-1.1-.6-.5-1-1.1-1.4-1.9-.3-.7-.5-1.6-.5-2.6h-1.9v-1.7h1.9v-1.4h-1.9V16h1.9c.1-1 .3-1.8.6-2.6.4-.7.8-1.4 1.4-1.9s1.3-.9 2.1-1.1c.8-.3 1.7-.4 2.6-.4.4 0 .9 0 1.3.1s.9.1 1.3.3l-.4 2.7c-.3-.1-.6-.2-1-.3s-.7-.1-1.1-.1c-.5 0-1 .1-1.4.2s-.8.3-1 .6c-.3.3-.5.6-.7 1s-.3.9-.3 1.5z"/><circle cx="30" cy="30" r="15" fill="#4CAF50"/><path fill="#fff" d="M28.4 27c.1.2.2.4.4.6s.4.4.7.5c.3.2.7.3 1.1.5.7.3 1.4.6 2 .9s1.1.7 1.5 1.1.8.9 1 1.4.4 1.2.4 1.9q0 1.05-.3 1.8c-.2.5-.5 1-.9 1.4s-.9.7-1.4.9c-.6.2-1.2.4-1.8.5v2.2h-1.8v-2.2c-.6-.1-1.2-.2-1.8-.4s-1.1-.5-1.5-1c-.5-.4-.8-1-1.1-1.6s-.4-1.4-.4-2.3h3.3c0 .5.1 1 .2 1.3.1.4.3.6.6.9.2.2.5.4.8.5s.6.1.9.1c.4 0 .7 0 .9-.1.3-.1.5-.2.7-.4s.3-.4.4-.6.1-.5.1-.8 0-.6-.1-.8-.2-.5-.4-.7-.4-.4-.7-.5c-.3-.2-.7-.3-1.1-.5-.7-.3-1.4-.6-2-.9s-1.1-.7-1.5-1.1-.8-.9-1-1.4-.4-1.2-.4-1.9c0-.6.1-1.2.3-1.7s.5-1 .9-1.4.9-.7 1.4-1c.5-.2 1.2-.4 1.8-.5v-2.4h1.8v2.4q.9.15 1.8.6c.5.3 1 .6 1.3 1.1.4.4.7 1 .9 1.6s.3 1.3.3 2h-3.3c0-.9-.2-1.6-.6-2s-.9-.7-1.5-.7c-.3 0-.6.1-.9.2-.2.1-.4.2-.6.4q-.3.3-.3.6c-.1.2-.1.5-.1.8-.1.2 0 .5 0 .7"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#E0E0E0" d="M27.8 39.7c-.1 0-.2 0-.4-.1s-.4-.3-.6-.5l-3.7-8.6-4.5 4.2c-.1.2-.3.3-.6.3-.1 0-.3 0-.4-.1-.3-.1-.6-.5-.6-.9V12c0-.4.2-.8.6-.9.1-.1.3-.1.4-.1.2 0 .5.1.7.3l16 15c.3.3.4.7.3 1.1-.1.4-.5.6-.9.7l-6.3.6 3.9 8.5c.1.2.1.5 0 .8-.1.2-.3.5-.5.6l-2.9 1.3c-.2-.2-.4-.2-.5-.2"/><path fill="#212121" d="m18 12 16 15-7.7.7 4.5 9.8-2.9 1.3-4.3-9.9L18 34zm0-2c-.3 0-.5.1-.8.2-.7.3-1.2 1-1.2 1.8v22c0 .8.5 1.5 1.2 1.8.3.2.6.2.8.2.5 0 1-.2 1.4-.5l3.4-3.2 3.1 7.3c.2.5.6.9 1.1 1.1.2.1.5.1.7.1.3 0 .5-.1.8-.2l2.9-1.3c.5-.2.9-.6 1.1-1.1.2-.5.2-1.1 0-1.5l-3.3-7.2 4.9-.4c.8-.1 1.5-.6 1.7-1.3.3-.7.1-1.6-.5-2.1l-16-15c-.3-.5-.8-.7-1.3-.7"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#E0E0E0" d="M27.8 39.7c-.1 0-.2 0-.4-.1s-.4-.3-.6-.5l-3.7-8.6-4.5 4.2q-.15.3-.6.3c-.1 0-.3 0-.4-.1-.3-.1-.6-.5-.6-.9V12c0-.4.2-.8.6-.9.1-.1.3-.1.4-.1.2 0 .5.1.7.3l16 15c.3.3.4.7.3 1.1s-.5.6-.9.7l-6.3.6 3.9 8.5c.1.2.1.5 0 .8-.1.2-.3.5-.5.6l-2.9 1.3c-.2-.2-.4-.2-.5-.2"/><path fill="#212121" d="m18 12 16 15-7.7.7 4.5 9.8-2.9 1.3-4.3-9.9L18 34zm0-2c-.3 0-.5.1-.8.2-.7.3-1.2 1-1.2 1.8v22c0 .8.5 1.5 1.2 1.8.3.2.6.2.8.2.5 0 1-.2 1.4-.5l3.4-3.2 3.1 7.3c.2.5.6.9 1.1 1.1.2.1.5.1.7.1.3 0 .5-.1.8-.2l2.9-1.3c.5-.2.9-.6 1.1-1.1s.2-1.1 0-1.5l-3.3-7.2 4.9-.4c.8-.1 1.5-.6 1.7-1.3.3-.7.1-1.6-.5-2.1l-16-15c-.3-.5-.8-.7-1.3-.7"/></svg>

Before

Width:  |  Height:  |  Size: 717 B

After

Width:  |  Height:  |  Size: 706 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#FFB74D" d="M29 43v-4.6l2.6.5c2.9.6 5.6-1.5 5.8-4.4L38 28l2.9-1.2c1-.4 1.4-1.6.8-2.6L38 18c-.6-7.6-4.9-15-16-15C10.6 3 5 11.4 5 20c0 3.7 1.3 6.9 3.3 9.6 1.8 2.5 2.7 5.5 2.7 8.5v4.8h18z"/><path fill="#FF9800" d="M29 43v-4.6L22 37v6z"/><circle cx="33.5" cy="21.5" r="1.5" fill="#784719"/><path fill="#FF5722" d="M21.4 3C12.3 3 5 10.3 5 19.4c0 11.1 6 11.4 6 18.6l2.6-.9c2.1-.7 3.9-2.3 4.7-4.4l2.8-6.8L27 23v-6s7-3.8 7-10.3C31 4.2 25.7 3 21.4 3"/><path fill="#546E7A" d="M21 2.1c-.6 0-1 .4-1 1V17c0 .6.4 1 1 1s1-.4 1-1V3.1c0-.6-.4-1-1-1m15.9 29.8c-7.9 0-10.3-4.9-10.4-5.1-.2-.5-.8-.7-1.3-.5-.5.2-.7.8-.5 1.3.1.3 3 6.3 12.2 6.3.6 0 1-.4 1-1s-.5-1-1-1"/><circle cx="37" cy="33" r="2" fill="#37474F"/><circle cx="21" cy="23" r="7" fill="#37474F"/><circle cx="21" cy="23" r="4" fill="#546E7A"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#FFB74D" d="M29 43v-4.6l2.6.5c2.9.6 5.6-1.5 5.8-4.4L38 28l2.9-1.2c1-.4 1.4-1.6.8-2.6L38 18c-.6-7.6-4.9-15-16-15C10.6 3 5 11.4 5 20c0 3.7 1.3 6.9 3.3 9.6 1.8 2.5 2.7 5.5 2.7 8.5v4.8h18z"/><path fill="#FF9800" d="M29 43v-4.6L22 37v6z"/><circle cx="33.5" cy="21.5" r="1.5" fill="#784719"/><path fill="#FF5722" d="M21.4 3C12.3 3 5 10.3 5 19.4c0 11.1 6 11.4 6 18.6l2.6-.9c2.1-.7 3.9-2.3 4.7-4.4l2.8-6.8L27 23v-6s7-3.8 7-10.3C31 4.2 25.7 3 21.4 3"/><path fill="#546E7A" d="M21 2.1c-.6 0-1 .4-1 1V17c0 .6.4 1 1 1s1-.4 1-1V3.1c0-.6-.4-1-1-1m15.9 29.8c-7.9 0-10.3-4.9-10.4-5.1-.2-.5-.8-.7-1.3-.5s-.7.8-.5 1.3c.1.3 3 6.3 12.2 6.3.6 0 1-.4 1-1s-.5-1-1-1"/><circle cx="37" cy="33" r="2" fill="#37474F"/><circle cx="21" cy="23" r="7" fill="#37474F"/><circle cx="21" cy="23" r="4" fill="#546E7A"/></svg>

Before

Width:  |  Height:  |  Size: 876 B

After

Width:  |  Height:  |  Size: 873 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#D1C4E9" d="M38 7H10c-1.1 0-2 .9-2 2v6c0 1.1.9 2 2 2h28c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2m0 12H10c-1.1 0-2 .9-2 2v6c0 1.1.9 2 2 2h28c1.1 0 2-.9 2-2v-6c0-1.1-.9-2-2-2m0 12H10c-1.1 0-2 .9-2 2v6c0 1.1.9 2 2 2h28c1.1 0 2-.9 2-2v-6c0-1.1-.9-2-2-2"/><path fill="#607D8B" d="M45.2 38.1c.1-.4.1-.8.1-1.1s0-.8-.1-1.1l2.3-1.7c.2-.2.3-.5.2-.7l-2.3-3.9c-.1-.2-.4-.3-.7-.2l-2.6 1.2c-.6-.5-1.3-.9-2-1.2l-.3-2.9c0-.3-.3-.5-.5-.5h-4.5c-.3 0-.5.2-.5.5l-.3 2.9c-.7.3-1.4.7-2 1.2l-2.6-1.2c-.3-.1-.6 0-.7.2l-2.3 3.9c-.1.2-.1.6.2.7l2.3 1.7c-.1.4-.1.8-.1 1.1s0 .8.1 1.1l-2.3 1.7c-.2.2-.3.5-.2.7l2.3 3.9c.1.2.4.3.7.2l2.6-1.2c.6.5 1.3.9 2 1.2l.3 2.9c0 .3.3.5.5.5h4.5c.3 0 .5-.2.5-.5l.3-2.9c.7-.3 1.4-.7 2-1.2l2.6 1.2c.3.1.6 0 .7-.2l2.3-3.9c.1-.2.1-.6-.2-.7zM37 42.2c-2.9 0-5.2-2.3-5.2-5.2 0-2.9 2.3-5.2 5.2-5.2 2.9 0 5.2 2.3 5.2 5.2 0 2.9-2.3 5.2-5.2 5.2"/><path fill="#455A64" d="M37 31c-3.3 0-6 2.7-6 6s2.7 6 6 6 6-2.7 6-6-2.7-6-6-6m0 9c-1.7 0-3-1.3-3-3s1.3-3 3-3 3 1.3 3 3-1.3 3-3 3"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#D1C4E9" d="M38 7H10c-1.1 0-2 .9-2 2v6c0 1.1.9 2 2 2h28c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2m0 12H10c-1.1 0-2 .9-2 2v6c0 1.1.9 2 2 2h28c1.1 0 2-.9 2-2v-6c0-1.1-.9-2-2-2m0 12H10c-1.1 0-2 .9-2 2v6c0 1.1.9 2 2 2h28c1.1 0 2-.9 2-2v-6c0-1.1-.9-2-2-2"/><path fill="#607D8B" d="M45.2 38.1c.1-.4.1-.8.1-1.1s0-.8-.1-1.1l2.3-1.7c.2-.2.3-.5.2-.7l-2.3-3.9c-.1-.2-.4-.3-.7-.2l-2.6 1.2c-.6-.5-1.3-.9-2-1.2l-.3-2.9c0-.3-.3-.5-.5-.5h-4.5c-.3 0-.5.2-.5.5l-.3 2.9c-.7.3-1.4.7-2 1.2l-2.6-1.2c-.3-.1-.6 0-.7.2l-2.3 3.9c-.1.2-.1.6.2.7l2.3 1.7c-.1.4-.1.8-.1 1.1s0 .8.1 1.1l-2.3 1.7c-.2.2-.3.5-.2.7l2.3 3.9c.1.2.4.3.7.2l2.6-1.2c.6.5 1.3.9 2 1.2l.3 2.9c0 .3.3.5.5.5h4.5c.3 0 .5-.2.5-.5l.3-2.9c.7-.3 1.4-.7 2-1.2l2.6 1.2c.3.1.6 0 .7-.2l2.3-3.9c.1-.2.1-.6-.2-.7zM37 42.2c-2.9 0-5.2-2.3-5.2-5.2s2.3-5.2 5.2-5.2 5.2 2.3 5.2 5.2-2.3 5.2-5.2 5.2"/><path fill="#455A64" d="M37 31c-3.3 0-6 2.7-6 6s2.7 6 6 6 6-2.7 6-6-2.7-6-6-6m0 9c-1.7 0-3-1.3-3-3s1.3-3 3-3 3 1.3 3 3-1.3 3-3 3"/></svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#D1C4E9" d="M38 7H10c-1.1 0-2 .9-2 2v6c0 1.1.9 2 2 2h28c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2m0 12H10c-1.1 0-2 .9-2 2v6c0 1.1.9 2 2 2h25.1c1.3-1.3 4.9-.9 4.9-2v-6c0-1.1-.9-2-2-2m-3.6 12H10c-1.1 0-2 .9-2 2v6c0 1.1.9 2 2 2h28c1.1 0 2-.9 2-2v-2.4c0-3.1-2.5-5.6-5.6-5.6"/><g fill="#FFA000"><path d="m43 46-2 2h-2l-2-2V35.4h6V40l-1 1 1 1v1l-1 1 1 1z"/><path d="M47.5 28.5c-.3-.9-1-1.6-2-1.8-1.3-.3-3.3-.7-5.5-.7s-4.2.4-5.5.6c-1 .2-1.7.9-2 1.8-.2 1-.5 2.2-.5 3.6s.3 2.6.5 3.5c.3.9 1 1.6 2 1.8 1.3.3 3.2.6 5.5.6s4.2-.4 5.5-.6c1-.2 1.7-.9 2-1.8.3-.9.5-2.1.5-3.5s-.3-2.6-.5-3.5M42.9 31h-5.7c-.6 0-1.1-.5-1.1-1.1v-1.4c0-.3 1.8-.6 4-.6s4 .3 4 .6v1.4c-.1.6-.6 1.1-1.2 1.1"/></g><path fill="#D68600" d="M39 37.1h1V48h-1z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#D1C4E9" d="M38 7H10c-1.1 0-2 .9-2 2v6c0 1.1.9 2 2 2h28c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2m0 12H10c-1.1 0-2 .9-2 2v6c0 1.1.9 2 2 2h25.1c1.3-1.3 4.9-.9 4.9-2v-6c0-1.1-.9-2-2-2m-3.6 12H10c-1.1 0-2 .9-2 2v6c0 1.1.9 2 2 2h28c1.1 0 2-.9 2-2v-2.4c0-3.1-2.5-5.6-5.6-5.6"/><g fill="#FFA000"><path d="m43 46-2 2h-2l-2-2V35.4h6V40l-1 1 1 1v1l-1 1 1 1z"/><path d="M47.5 28.5c-.3-.9-1-1.6-2-1.8-1.3-.3-3.3-.7-5.5-.7s-4.2.4-5.5.6c-1 .2-1.7.9-2 1.8-.2 1-.5 2.2-.5 3.6s.3 2.6.5 3.5c.3.9 1 1.6 2 1.8 1.3.3 3.2.6 5.5.6s4.2-.4 5.5-.6c1-.2 1.7-.9 2-1.8s.5-2.1.5-3.5-.3-2.6-.5-3.5M42.9 31h-5.7c-.6 0-1.1-.5-1.1-1.1v-1.4c0-.3 1.8-.6 4-.6s4 .3 4 .6v1.4c-.1.6-.6 1.1-1.2 1.1"/></g><path fill="#D68600" d="M39 37.1h1V48h-1z"/></svg>

Before

Width:  |  Height:  |  Size: 794 B

After

Width:  |  Height:  |  Size: 789 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#FFB74D" d="M10 12c-2.8 0-5-2.2-5-5s2.2-5 5-5 5 2.2 5 5-2.2 5-5 5"/><path fill="#607D8B" d="M2 22v8l3 2 1 14h8l1-14 3-2v-8c0-4.4-3.6-8-8-8s-8 3.6-8 8"/><g fill="#263238"><path d="M22.4 40.4c-.6 2.5-1 3.6-2.4 3.6-.6 0-1.2-.5-1.9-1.1-1-.8-2.2-1.9-4.1-1.9v2c1.1 0 1.9.7 2.8 1.4.9.7 1.9 1.6 3.2 1.6 3.1 0 3.8-2.9 4.4-5.2.6-2.6 1-3.8 2.6-3.8v-2c-3.3 0-4.1 3.1-4.6 5.4"/><path d="M14.4 40H10v4h4.1z"/></g><circle cx="36" cy="36" r="10" fill="#4CAF50"/><path fill="#fff" d="M35 34c.1.2.1.3.3.4.1.1.3.2.5.4.2.1.5.2.8.3.5.2.9.4 1.3.6.4.2.7.4 1 .7.3.3.5.6.7.9.2.4.2.8.2 1.3 0 .4-.1.8-.2 1.2-.1.4-.3.7-.6.9-.3.3-.6.5-.9.6-.4.2-.8.3-1.2.3v1.5h-1.2v-1.5c-.4 0-.8-.1-1.2-.3-.4-.2-.7-.4-1-.6-.3-.3-.5-.6-.7-1.1-.2-.4-.3-.9-.3-1.5h2.2c0 .4 0 .7.1.9.1.2.2.4.4.6.2.1.3.2.5.3.2.1.4.1.6.1.2 0 .4 0 .6-.1.2-.1.3-.2.4-.3.1-.1.2-.3.3-.4.1-.2.1-.3.1-.5s0-.4-.1-.6c-.1-.2-.1-.3-.3-.4-.1-.1-.3-.3-.5-.4-.2-.1-.4-.2-.7-.3-.5-.2-.9-.4-1.3-.6-.4-.2-.7-.4-1-.7-.3-.3-.5-.6-.7-.9-.2-.4-.2-.8-.2-1.3 0-.4.1-.8.2-1.2.1-.3.3-.7.6-.9.3-.3.6-.5.9-.6.4-.2.8-.3 1.2-.3v-1.6H37v1.6c.4.1.8.2 1.2.4.4.2.6.4.9.7.2.3.4.6.6 1 .1.4.2.9.2 1.4h-2.2c0-.6-.1-1-.4-1.3-.2-.3-.6-.4-1-.4-.2 0-.4 0-.6.1-.2.1-.3.2-.4.3-.2 0-.3.1-.3.3s-.1.3-.1.5 0 .4.1.5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#FFB74D" d="M10 12c-2.8 0-5-2.2-5-5s2.2-5 5-5 5 2.2 5 5-2.2 5-5 5"/><path fill="#607D8B" d="M2 22v8l3 2 1 14h8l1-14 3-2v-8c0-4.4-3.6-8-8-8s-8 3.6-8 8"/><g fill="#263238"><path d="M22.4 40.4c-.6 2.5-1 3.6-2.4 3.6-.6 0-1.2-.5-1.9-1.1-1-.8-2.2-1.9-4.1-1.9v2c1.1 0 1.9.7 2.8 1.4S18.7 46 20 46c3.1 0 3.8-2.9 4.4-5.2.6-2.6 1-3.8 2.6-3.8v-2c-3.3 0-4.1 3.1-4.6 5.4"/><path d="M14.4 40H10v4h4.1z"/></g><circle cx="36" cy="36" r="10" fill="#4CAF50"/><path fill="#fff" d="M35 34c.1.2.1.3.3.4.1.1.3.2.5.4.2.1.5.2.8.3.5.2.9.4 1.3.6s.7.4 1 .7.5.6.7.9c.2.4.2.8.2 1.3 0 .4-.1.8-.2 1.2q-.15.6-.6.9-.45.45-.9.6-.6.3-1.2.3v1.5h-1.2v-1.5q-.6 0-1.2-.3c-.4-.2-.7-.4-1-.6-.3-.3-.5-.6-.7-1.1q-.3-.6-.3-1.5h2.2c0 .4 0 .7.1.9s.2.4.4.6c.2.1.3.2.5.3s.4.1.6.1.4 0 .6-.1.3-.2.4-.3.2-.3.3-.4c.1-.2.1-.3.1-.5s0-.4-.1-.6-.1-.3-.3-.4c-.1-.1-.3-.3-.5-.4s-.4-.2-.7-.3c-.5-.2-.9-.4-1.3-.6s-.7-.4-1-.7-.5-.6-.7-.9c-.2-.4-.2-.8-.2-1.3 0-.4.1-.8.2-1.2.1-.3.3-.7.6-.9q.45-.45.9-.6.6-.3 1.2-.3v-1.6H37v1.6c.4.1.8.2 1.2.4s.6.4.9.7c.2.3.4.6.6 1 .1.4.2.9.2 1.4h-2.2c0-.6-.1-1-.4-1.3-.2-.3-.6-.4-1-.4-.2 0-.4 0-.6.1s-.3.2-.4.3q-.3 0-.3.3c0 .3-.1.3-.1.5s0 .4.1.5"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#EF6C00" d="m37.4 24.6-11.6-2.2-3.9-11.2-3.8 1.3L22 23.6l-7.8 9 3 2.6 7.8-9 11.6 2.2z"/><g fill="#FF9800"><path d="M24 19c-2.8 0-5 2.2-5 5s2.2 5 5 5 5-2.2 5-5-2.2-5-5-5m0 7c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2"/><path d="M40.7 27c.2-1 .3-2 .3-3s-.1-2-.3-3l3.3-2.4c.4-.3.6-.9.3-1.4L40 9.8c-.3-.5-.8-.7-1.3-.4L35 11c-1.5-1.3-3.3-2.3-5.2-3l-.4-4.1c-.1-.5-.5-.9-1-.9h-8.6c-.5 0-1 .4-1 .9L18.2 8c-1.9.7-3.7 1.7-5.2 3L9.3 9.3c-.5-.2-1.1 0-1.3.5l-4.3 7.4c-.3.5-.1 1.1.3 1.4L7.3 21c-.2 1-.3 2-.3 3s.1 2 .3 3L4 29.4c-.4.3-.6.9-.3 1.4L8 38.2c.3.5.8.7 1.3.4L13 37c1.5 1.3 3.3 2.3 5.2 3l.4 4.1c.1.5.5.9 1 .9h8.6c.5 0 1-.4 1-.9l.4-4.1c1.9-.7 3.7-1.7 5.2-3l3.7 1.7c.5.2 1.1 0 1.3-.4l4.3-7.4c.3-.5.1-1.1-.3-1.4zM24 35c-6.1 0-11-4.9-11-11s4.9-11 11-11 11 4.9 11 11-4.9 11-11 11"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#EF6C00" d="m37.4 24.6-11.6-2.2-3.9-11.2-3.8 1.3L22 23.6l-7.8 9 3 2.6 7.8-9 11.6 2.2z"/><g fill="#FF9800"><path d="M24 19c-2.8 0-5 2.2-5 5s2.2 5 5 5 5-2.2 5-5-2.2-5-5-5m0 7c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2"/><path d="M40.7 27q.3-1.5.3-3t-.3-3l3.3-2.4c.4-.3.6-.9.3-1.4L40 9.8c-.3-.5-.8-.7-1.3-.4L35 11c-1.5-1.3-3.3-2.3-5.2-3l-.4-4.1c-.1-.5-.5-.9-1-.9h-8.6c-.5 0-1 .4-1 .9L18.2 8c-1.9.7-3.7 1.7-5.2 3L9.3 9.3c-.5-.2-1.1 0-1.3.5l-4.3 7.4c-.3.5-.1 1.1.3 1.4L7.3 21Q7 22.5 7 24t.3 3L4 29.4c-.4.3-.6.9-.3 1.4L8 38.2c.3.5.8.7 1.3.4L13 37c1.5 1.3 3.3 2.3 5.2 3l.4 4.1c.1.5.5.9 1 .9h8.6c.5 0 1-.4 1-.9l.4-4.1c1.9-.7 3.7-1.7 5.2-3l3.7 1.7c.5.2 1.1 0 1.3-.4l4.3-7.4c.3-.5.1-1.1-.3-1.4zM24 35c-6.1 0-11-4.9-11-11s4.9-11 11-11 11 4.9 11 11-4.9 11-11 11"/></g></svg>

Before

Width:  |  Height:  |  Size: 864 B

After

Width:  |  Height:  |  Size: 846 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#558B2F" d="M15 40h23l6 6V25c0-2.2-1.8-4-4-4H15c-2.2 0-4 1.8-4 4v11c0 2.2 1.8 4 4 4"/><path fill="#1B5E20" d="M28.8 32.8h-3.6l-.7 2.1h-2.2l3.7-10h1.9l3.7 10h-2.2zm-3.1-1.6h2.5L27 27.4z"/><path fill="#8BC34A" d="M33 25H10l-6 6V8c0-2.2 1.8-4 4-4h25c2.2 0 4 1.8 4 4v13c0 2.2-1.8 4-4 4"/><path fill="#fff" d="M25.4 14.2c0 1-.2 1.8-.5 2.5s-.7 1.3-1.3 1.7l1.7 1.3-1.3 1.2-2.2-1.7c-.2 0-.5.1-.8.1-.6 0-1.2-.1-1.8-.3-.5-.2-1-.6-1.4-1-.4-.4-.7-1-.9-1.6-.2-.6-.3-1.3-.3-2.1v-.4c0-.8.1-1.5.3-2.1.2-.6.5-1.2.9-1.6.4-.4.8-.8 1.4-1 .5-.2 1.1-.3 1.8-.3.6 0 1.2.1 1.8.3.5.2 1 .6 1.4 1 .4.4.7 1 .9 1.6.2.6.3 1.3.3 2.1zm-2.2-.5c0-1.1-.2-1.9-.6-2.4-.4-.6-.9-.8-1.6-.8-.7 0-1.3.3-1.6.8-.4.6-.6 1.4-.6 2.4v.5c0 .5.1 1 .2 1.4.1.4.2.8.4 1 .2.3.4.5.7.6.3.1.6.2.9.2.7 0 1.3-.3 1.6-.8.4-.6.6-1.4.6-2.5z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#558B2F" d="M15 40h23l6 6V25c0-2.2-1.8-4-4-4H15c-2.2 0-4 1.8-4 4v11c0 2.2 1.8 4 4 4"/><path fill="#1B5E20" d="M28.8 32.8h-3.6l-.7 2.1h-2.2l3.7-10h1.9l3.7 10h-2.2zm-3.1-1.6h2.5L27 27.4z"/><path fill="#8BC34A" d="M33 25H10l-6 6V8c0-2.2 1.8-4 4-4h25c2.2 0 4 1.8 4 4v13c0 2.2-1.8 4-4 4"/><path fill="#fff" d="M25.4 14.2c0 1-.2 1.8-.5 2.5s-.7 1.3-1.3 1.7l1.7 1.3-1.3 1.2-2.2-1.7c-.2 0-.5.1-.8.1q-.9 0-1.8-.3c-.5-.2-1-.6-1.4-1s-.7-1-.9-1.6q-.3-.9-.3-2.1v-.4q0-1.2.3-2.1c.2-.6.5-1.2.9-1.6s.8-.8 1.4-1q.75-.3 1.8-.3.9 0 1.8.3c.5.2 1 .6 1.4 1s.7 1 .9 1.6q.3.9.3 2.1zm-2.2-.5q0-1.65-.6-2.4c-.4-.6-.9-.8-1.6-.8s-1.3.3-1.6.8q-.6.9-.6 2.4v.5c0 .5.1 1 .2 1.4s.2.8.4 1c.2.3.4.5.7.6s.6.2.9.2c.7 0 1.3-.3 1.6-.8.4-.6.6-1.4.6-2.5z"/></svg>

Before

Width:  |  Height:  |  Size: 869 B

After

Width:  |  Height:  |  Size: 805 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#7CB342" d="M24 4C13 4 4 13 4 24s9 20 20 20 20-9 20-20S35 4 24 4"/><path fill="#0277BD" d="M45 24c0 11.7-9.5 21-21 21S3 35.7 3 24 12.3 3 24 3s21 9.3 21 21m-21.2 9.7c0-.4-.2-.6-.6-.8-1.3-.4-2.5-.4-3.6-1.5-.2-.4-.2-.8-.4-1.3-.4-.4-1.5-.6-2.1-.8h-4.2c-.6-.2-1.1-1.1-1.5-1.7 0-.2 0-.6-.4-.6-.4-.2-.8.2-1.3 0-.2-.2-.2-.4-.2-.6 0-.6.4-1.3.8-1.7.6-.4 1.3.2 1.9.2.2 0 .2 0 .4.2.6.2.8 1 .8 1.7v.4c0 .2.2.2.4.2.2-1.1.2-2.1.4-3.2 0-1.3 1.3-2.5 2.3-2.9.4-.2.6.2 1.1 0 1.3-.4 4.4-1.7 3.8-3.4-.4-1.5-1.7-2.9-3.4-2.7-.4.2-.6.4-1 .6-.6.4-1.9 1.7-2.5 1.7-1.1-.2-1.1-1.7-.8-2.3.2-.8 2.1-3.6 3.4-3.1l.8.8c.4.2 1.1.2 1.7.2.2 0 .4 0 .6-.2.2-.2.2-.2.2-.4 0-.6-.6-1.3-1-1.7-.4-.4-1.1-.8-1.7-1.1-2.1-.6-5.5.2-7.1 1.7s-2.9 4-3.8 6.1c-.4 1.3-.8 2.9-1 4.4-.2 1-.4 1.9.2 2.9.6 1.3 1.9 2.5 3.2 3.4.8.6 2.5.6 3.4 1.7.6.8.4 1.9.4 2.9 0 1.3.8 2.3 1.3 3.4.2.6.4 1.5.6 2.1 0 .2.2 1.5.2 1.7 1.3.6 2.3 1.3 3.8 1.7.2 0 1-1.3 1-1.5.6-.6 1.1-1.5 1.7-1.9.4-.2.8-.4 1.3-.8.4-.4.6-1.3.8-1.9.1-.5.3-1.3.1-1.9m.4-19.4c.2 0 .4-.2.8-.4.6-.4 1.3-1.1 1.9-1.5.6-.4 1.3-1.1 1.7-1.5.6-.4 1.1-1.3 1.3-1.9.2-.4.8-1.3.6-1.9-.2-.4-1.3-.6-1.7-.8-1.7-.4-3.1-.6-4.8-.6-.6 0-1.5.2-1.7.8-.2 1.1.6.8 1.5 1.1 0 0 .2 1.7.2 1.9.2 1-.4 1.7-.4 2.7 0 .6 0 1.7.4 2.1zM41.8 29c.2-.4.2-1.1.4-1.5.2-1 .2-2.1.2-3.1 0-2.1-.2-4.2-.8-6.1-.4-.6-.6-1.3-.8-1.9-.4-1.1-1-2.1-1.9-2.9-.8-1.1-1.9-4-3.8-3.1-.6.2-1 1-1.5 1.5-.4.6-.8 1.3-1.3 1.9-.2.2-.4.6-.2.8 0 .2.2.2.4.2.4.2.6.2 1 .4.2 0 .4.2.2.4 0 0 0 .2-.2.2-1 1.1-2.1 1.9-3.1 2.9-.2.2-.4.6-.4.8 0 .2.2.2.2.4s-.2.2-.4.4c-.4.2-.8.4-1.1.6-.2.4 0 1.1-.2 1.5-.2 1.1-.8 1.9-1.3 2.9-.4.6-.6 1.3-1 1.9 0 .8-.2 1.5.2 2.1 1 1.5 2.9.6 4.4 1.3.4.2.8.2 1.1.6.6.6.6 1.7.8 2.3.2.8.4 1.7.8 2.5.2 1 .6 2.1.8 2.9 1.9-1.5 3.6-3.1 4.8-5.2 1.5-1.3 2.1-3 2.7-4.7"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#7CB342" d="M24 4C13 4 4 13 4 24s9 20 20 20 20-9 20-20S35 4 24 4"/><path fill="#0277BD" d="M45 24c0 11.7-9.5 21-21 21S3 35.7 3 24 12.3 3 24 3s21 9.3 21 21m-21.2 9.7c0-.4-.2-.6-.6-.8-1.3-.4-2.5-.4-3.6-1.5-.2-.4-.2-.8-.4-1.3-.4-.4-1.5-.6-2.1-.8h-4.2c-.6-.2-1.1-1.1-1.5-1.7 0-.2 0-.6-.4-.6-.4-.2-.8.2-1.3 0-.2-.2-.2-.4-.2-.6 0-.6.4-1.3.8-1.7.6-.4 1.3.2 1.9.2.2 0 .2 0 .4.2.6.2.8 1 .8 1.7v.4c0 .2.2.2.4.2.2-1.1.2-2.1.4-3.2 0-1.3 1.3-2.5 2.3-2.9.4-.2.6.2 1.1 0 1.3-.4 4.4-1.7 3.8-3.4-.4-1.5-1.7-2.9-3.4-2.7-.4.2-.6.4-1 .6-.6.4-1.9 1.7-2.5 1.7-1.1-.2-1.1-1.7-.8-2.3.2-.8 2.1-3.6 3.4-3.1l.8.8c.4.2 1.1.2 1.7.2.2 0 .4 0 .6-.2s.2-.2.2-.4c0-.6-.6-1.3-1-1.7s-1.1-.8-1.7-1.1c-2.1-.6-5.5.2-7.1 1.7s-2.9 4-3.8 6.1c-.4 1.3-.8 2.9-1 4.4-.2 1-.4 1.9.2 2.9.6 1.3 1.9 2.5 3.2 3.4.8.6 2.5.6 3.4 1.7.6.8.4 1.9.4 2.9 0 1.3.8 2.3 1.3 3.4.2.6.4 1.5.6 2.1 0 .2.2 1.5.2 1.7 1.3.6 2.3 1.3 3.8 1.7.2 0 1-1.3 1-1.5.6-.6 1.1-1.5 1.7-1.9.4-.2.8-.4 1.3-.8.4-.4.6-1.3.8-1.9.1-.5.3-1.3.1-1.9m.4-19.4c.2 0 .4-.2.8-.4.6-.4 1.3-1.1 1.9-1.5s1.3-1.1 1.7-1.5c.6-.4 1.1-1.3 1.3-1.9.2-.4.8-1.3.6-1.9-.2-.4-1.3-.6-1.7-.8-1.7-.4-3.1-.6-4.8-.6-.6 0-1.5.2-1.7.8-.2 1.1.6.8 1.5 1.1 0 0 .2 1.7.2 1.9.2 1-.4 1.7-.4 2.7 0 .6 0 1.7.4 2.1zM41.8 29c.2-.4.2-1.1.4-1.5.2-1 .2-2.1.2-3.1 0-2.1-.2-4.2-.8-6.1-.4-.6-.6-1.3-.8-1.9-.4-1.1-1-2.1-1.9-2.9-.8-1.1-1.9-4-3.8-3.1-.6.2-1 1-1.5 1.5-.4.6-.8 1.3-1.3 1.9-.2.2-.4.6-.2.8 0 .2.2.2.4.2.4.2.6.2 1 .4.2 0 .4.2.2.4 0 0 0 .2-.2.2-1 1.1-2.1 1.9-3.1 2.9-.2.2-.4.6-.4.8s.2.2.2.4-.2.2-.4.4c-.4.2-.8.4-1.1.6-.2.4 0 1.1-.2 1.5-.2 1.1-.8 1.9-1.3 2.9-.4.6-.6 1.3-1 1.9 0 .8-.2 1.5.2 2.1 1 1.5 2.9.6 4.4 1.3.4.2.8.2 1.1.6.6.6.6 1.7.8 2.3.2.8.4 1.7.8 2.5.2 1 .6 2.1.8 2.9 1.9-1.5 3.6-3.1 4.8-5.2 1.5-1.3 2.1-3 2.7-4.7"/></svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><g fill="#37474F"><path d="M9 20h30v13H9z"/><ellipse cx="24" cy="33" rx="15" ry="6"/></g><path fill="#78909C" d="M23.1 8.2.6 18.1c-.8.4-.8 1.5 0 1.9l22.5 9.9c.6.2 1.2.2 1.8 0L47.4 20c.8-.4.8-1.5 0-1.9L24.9 8.2c-.6-.3-1.2-.3-1.8 0"/><g fill="#37474F"><path d="m43.2 20.4-20-3.4c-.5-.1-1.1.3-1.2.8-.1.5.3 1.1.8 1.2L42 22.2V37c0 .6.4 1 1 1s1-.4 1-1V21.4c0-.5-.4-.9-.8-1"/><circle cx="43" cy="37" r="2"/><path d="M46 40c0 1.7-3 6-3 6s-3-4.3-3-6 1.3-3 3-3 3 1.3 3 3"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><g fill="#37474F"><path d="M9 20h30v13H9z"/><ellipse cx="24" cy="33" rx="15" ry="6"/></g><path fill="#78909C" d="M23.1 8.2.6 18.1c-.8.4-.8 1.5 0 1.9l22.5 9.9q.9.3 1.8 0L47.4 20c.8-.4.8-1.5 0-1.9L24.9 8.2q-.9-.45-1.8 0"/><g fill="#37474F"><path d="m43.2 20.4-20-3.4c-.5-.1-1.1.3-1.2.8s.3 1.1.8 1.2L42 22.2V37c0 .6.4 1 1 1s1-.4 1-1V21.4c0-.5-.4-.9-.8-1"/><circle cx="43" cy="37" r="2"/><path d="M46 40c0 1.7-3 6-3 6s-3-4.3-3-6 1.3-3 3-3 3 1.3 3 3"/></g></svg>

Before

Width:  |  Height:  |  Size: 545 B

After

Width:  |  Height:  |  Size: 529 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#F44336" d="m21.2 44.8-18-18c-1.6-1.6-1.6-4.1 0-5.7l18-18c1.6-1.6 4.1-1.6 5.7 0l18 18c1.6 1.6 1.6 4.1 0 5.7l-18 18c-1.6 1.6-4.2 1.6-5.7 0"/><path fill="#fff" d="M21.6 32.7c0-.3.1-.6.2-.9.1-.3.3-.5.5-.7.2-.2.5-.4.8-.5s.6-.2 1-.2.7.1 1 .2c.3.1.6.3.8.5.2.2.4.4.5.7.1.3.2.6.2.9s-.1.6-.2.9-.3.5-.5.7c-.2.2-.5.4-.8.5-.3.1-.6.2-1 .2s-.7-.1-1-.2-.5-.3-.8-.5c-.2-.2-.4-.4-.5-.7s-.2-.5-.2-.9m4.2-4.6h-3.6L21.7 13h4.6z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#F44336" d="m21.2 44.8-18-18c-1.6-1.6-1.6-4.1 0-5.7l18-18c1.6-1.6 4.1-1.6 5.7 0l18 18c1.6 1.6 1.6 4.1 0 5.7l-18 18c-1.6 1.6-4.2 1.6-5.7 0"/><path fill="#fff" d="M21.6 32.7c0-.3.1-.6.2-.9s.3-.5.5-.7.5-.4.8-.5.6-.2 1-.2.7.1 1 .2.6.3.8.5.4.4.5.7.2.6.2.9-.1.6-.2.9-.3.5-.5.7-.5.4-.8.5-.6.2-1 .2-.7-.1-1-.2-.5-.3-.8-.5c-.2-.2-.4-.4-.5-.7s-.2-.5-.2-.9m4.2-4.6h-3.6L21.7 13h4.6z"/></svg>

Before

Width:  |  Height:  |  Size: 500 B

After

Width:  |  Height:  |  Size: 464 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><circle cx="24" cy="22" r="20" fill="#FFF59D"/><path fill="#FBC02D" d="M37 22c0-7.7-6.6-13.8-14.5-12.9-6 .7-10.8 5.5-11.4 11.5-.5 4.6 1.4 8.7 4.6 11.3 1.4 1.2 2.3 2.9 2.3 4.8v.3h12v-.1c0-1.8.8-3.6 2.2-4.8 2.9-2.4 4.8-6 4.8-10.1"/><path fill="#FFF59D" d="m30.6 20.2-3-2c-.3-.2-.8-.2-1.1 0L24 19.8l-2.4-1.6c-.3-.2-.8-.2-1.1 0l-3 2c-.2.2-.4.4-.4.7s0 .6.2.8l3.8 4.7V37h2V26c0-.2-.1-.4-.2-.6l-3.3-4.1 1.5-1 2.4 1.6c.3.2.8.2 1.1 0l2.4-1.6 1.5 1-3.3 4.1c-.1.2-.2.4-.2.6v11h2V26.4l3.8-4.7c.2-.2.3-.5.2-.8s-.2-.6-.4-.7"/><circle cx="24" cy="44" r="3" fill="#5C6BC0"/><path fill="#9FA8DA" d="M26 45h-4c-2.2 0-4-1.8-4-4v-5h12v5c0 2.2-1.8 4-4 4"/><path fill="#5C6BC0" d="m30 41-11.6 1.6c.3.7.9 1.4 1.6 1.8l9.4-1.3c.4-.6.6-1.3.6-2.1m-12-2.3v2L30 39v-2z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><circle cx="24" cy="22" r="20" fill="#FFF59D"/><path fill="#FBC02D" d="M37 22c0-7.7-6.6-13.8-14.5-12.9-6 .7-10.8 5.5-11.4 11.5-.5 4.6 1.4 8.7 4.6 11.3 1.4 1.2 2.3 2.9 2.3 4.8v.3h12v-.1c0-1.8.8-3.6 2.2-4.8 2.9-2.4 4.8-6 4.8-10.1"/><path fill="#FFF59D" d="m30.6 20.2-3-2c-.3-.2-.8-.2-1.1 0L24 19.8l-2.4-1.6c-.3-.2-.8-.2-1.1 0l-3 2c-.2.2-.4.4-.4.7s0 .6.2.8l3.8 4.7V37h2V26c0-.2-.1-.4-.2-.6l-3.3-4.1 1.5-1 2.4 1.6c.3.2.8.2 1.1 0l2.4-1.6 1.5 1-3.3 4.1c-.1.2-.2.4-.2.6v11h2V26.4l3.8-4.7c.2-.2.3-.5.2-.8s-.2-.6-.4-.7"/><circle cx="24" cy="44" r="3" fill="#5C6BC0"/><path fill="#9FA8DA" d="M26 45h-4c-2.2 0-4-1.8-4-4v-5h12v5c0 2.2-1.8 4-4 4"/><path fill="#5C6BC0" d="m30 41-11.6 1.6c.3.7.9 1.4 1.6 1.8l9.4-1.3q.6-.9.6-2.1m-12-2.3v2L30 39v-2z"/></svg>

Before

Width:  |  Height:  |  Size: 820 B

After

Width:  |  Height:  |  Size: 814 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#607D8B" d="M38 42H10c-2.2 0-4-1.8-4-4V10c0-2.2 1.8-4 4-4h28c2.2 0 4 1.8 4 4v28c0 2.2-1.8 4-4 4"/><circle cx="24" cy="24" r="12" fill="#455A64"/><circle cx="24" cy="24" r="9" fill="#42A5F5"/><path fill="#90CAF9" d="M28.8 21c-1.2-1.4-3-2.2-4.8-2.2s-3.6.8-4.8 2.2c-.5.5-.4 1.3.1 1.8s1.3.4 1.8-.1c1.5-1.7 4.3-1.7 5.8 0 .3.3.6.4 1 .4.3 0 .6-.1.9-.3.4-.4.5-1.3 0-1.8"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#607D8B" d="M38 42H10c-2.2 0-4-1.8-4-4V10c0-2.2 1.8-4 4-4h28c2.2 0 4 1.8 4 4v28c0 2.2-1.8 4-4 4"/><circle cx="24" cy="24" r="12" fill="#455A64"/><circle cx="24" cy="24" r="9" fill="#42A5F5"/><path fill="#90CAF9" d="M28.8 21c-1.2-1.4-3-2.2-4.8-2.2s-3.6.8-4.8 2.2c-.5.5-.4 1.3.1 1.8s1.3.4 1.8-.1c1.5-1.7 4.3-1.7 5.8 0 .3.3.6.4 1 .4q.45 0 .9-.3c.4-.4.5-1.3 0-1.8"/></svg>

Before

Width:  |  Height:  |  Size: 454 B

After

Width:  |  Height:  |  Size: 452 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><g fill="#FFA000"><path d="m30 41-4 4h-4l-4-4V21h12v8l-2 2 2 2v2l-2 2 2 2z"/><path d="M38 7.8c-.5-1.8-2-3.1-3.7-3.6C31.9 3.7 28.2 3 24 3s-7.9.7-10.3 1.2C12 4.7 10.5 6 10 7.8c-.5 1.7-1 4.1-1 6.7 0 2.6.5 5 1 6.7.5 1.8 1.9 3.1 3.7 3.5 2.4.6 6.1 1.3 10.3 1.3s7.9-.7 10.3-1.2c1.8-.4 3.2-1.8 3.7-3.5s1-4.1 1-6.7c0-2.7-.5-5.1-1-6.8M29 13H19c-1.1 0-2-.9-2-2V9c0-.6 3.1-1 7-1s7 .4 7 1v2c0 1.1-.9 2-2 2"/></g><path fill="#D68600" d="M23 26h2v19h-2z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><g fill="#FFA000"><path d="m30 41-4 4h-4l-4-4V21h12v8l-2 2 2 2v2l-2 2 2 2z"/><path d="M38 7.8c-.5-1.8-2-3.1-3.7-3.6C31.9 3.7 28.2 3 24 3s-7.9.7-10.3 1.2C12 4.7 10.5 6 10 7.8c-.5 1.7-1 4.1-1 6.7s.5 5 1 6.7c.5 1.8 1.9 3.1 3.7 3.5 2.4.6 6.1 1.3 10.3 1.3s7.9-.7 10.3-1.2c1.8-.4 3.2-1.8 3.7-3.5s1-4.1 1-6.7c0-2.7-.5-5.1-1-6.8M29 13H19c-1.1 0-2-.9-2-2V9c0-.6 3.1-1 7-1s7 .4 7 1v2c0 1.1-.9 2-2 2"/></g><path fill="#D68600" d="M23 26h2v19h-2z"/></svg>

Before

Width:  |  Height:  |  Size: 519 B

After

Width:  |  Height:  |  Size: 515 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#FFCDD2" d="M5 38V14h38v24c0 2.2-1.8 4-4 4H9c-2.2 0-4-1.8-4-4"/><path fill="#F44336" d="M43 10v6H5v-6c0-2.2 1.8-4 4-4h30c2.2 0 4 1.8 4 4"/><g fill="#B71C1C"><circle cx="33" cy="10" r="3"/><circle cx="15" cy="10" r="3"/></g><path fill="#BDBDBD" d="M33 3c-1.1 0-2 .9-2 2v5c0 1.1.9 2 2 2s2-.9 2-2V5c0-1.1-.9-2-2-2M15 3c-1.1 0-2 .9-2 2v5c0 1.1.9 2 2 2s2-.9 2-2V5c0-1.1-.9-2-2-2"/><path fill="#F44336" d="M22.2 35.3c0-.2 0-.5.1-.7.1-.2.2-.4.4-.5s.3-.3.5-.3c.2-.1.5-.1.7-.1s.5 0 .7.1l.6.3c.2.1.3.3.4.5.1.2.1.4.1.7 0 .2 0 .5-.1.7-.1.2-.2.4-.4.5-.2.1-.3.3-.6.3s-.3.2-.6.2-.5 0-.7-.1c-.2-.1-.4-.2-.5-.3-.2-.1-.3-.3-.4-.5-.1-.3-.2-.5-.2-.8m3.1-4.3h-2.6l-.4-11h3.3z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#FFCDD2" d="M5 38V14h38v24c0 2.2-1.8 4-4 4H9c-2.2 0-4-1.8-4-4"/><path fill="#F44336" d="M43 10v6H5v-6c0-2.2 1.8-4 4-4h30c2.2 0 4 1.8 4 4"/><g fill="#B71C1C"><circle cx="33" cy="10" r="3"/><circle cx="15" cy="10" r="3"/></g><path fill="#BDBDBD" d="M33 3c-1.1 0-2 .9-2 2v5c0 1.1.9 2 2 2s2-.9 2-2V5c0-1.1-.9-2-2-2M15 3c-1.1 0-2 .9-2 2v5c0 1.1.9 2 2 2s2-.9 2-2V5c0-1.1-.9-2-2-2"/><path fill="#F44336" d="M22.2 35.3c0-.2 0-.5.1-.7s.2-.4.4-.5.3-.3.5-.3c.2-.1.5-.1.7-.1s.5 0 .7.1l.6.3c.2.1.3.3.4.5s.1.4.1.7c0 .2 0 .5-.1.7s-.2.4-.4.5-.3.3-.6.3-.3.2-.6.2-.5 0-.7-.1-.4-.2-.5-.3c-.2-.1-.3-.3-.4-.5-.1-.3-.2-.5-.2-.8m3.1-4.3h-2.6l-.4-11h3.3z"/></svg>

Before

Width:  |  Height:  |  Size: 747 B

After

Width:  |  Height:  |  Size: 723 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#455A64" d="M42 41H12c-2.2 0-4-1.8-4-4V17c0-2.2 1.8-4 4-4h30c2.2 0 4 1.8 4 4v20c0 2.2-1.8 4-4 4"/><path fill="#78909C" d="M36 36H6c-2.2 0-4-1.8-4-4V12c0-2.2 1.8-4 4-4h30c2.2 0 4 1.8 4 4v20c0 2.2-1.8 4-4 4"/><circle cx="26" cy="22" r="10" fill="#455A64"/><circle cx="26" cy="22" r="7" fill="#42A5F5"/><path fill="#90CAF9" d="M29.7 19.7c-1-1.1-2.3-1.7-3.7-1.7s-2.8.6-3.7 1.7c-.4.4-.3 1 .1 1.4.4.4 1 .3 1.4-.1 1.2-1.3 3.3-1.3 4.5 0 .2.2.5.3.7.3.2 0 .5-.1.7-.3.4-.3.4-.9 0-1.3"/><path fill="#ADD8FB" d="M6 12h6v3H6z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#455A64" d="M42 41H12c-2.2 0-4-1.8-4-4V17c0-2.2 1.8-4 4-4h30c2.2 0 4 1.8 4 4v20c0 2.2-1.8 4-4 4"/><path fill="#78909C" d="M36 36H6c-2.2 0-4-1.8-4-4V12c0-2.2 1.8-4 4-4h30c2.2 0 4 1.8 4 4v20c0 2.2-1.8 4-4 4"/><circle cx="26" cy="22" r="10" fill="#455A64"/><circle cx="26" cy="22" r="7" fill="#42A5F5"/><path fill="#90CAF9" d="M29.7 19.7c-1-1.1-2.3-1.7-3.7-1.7s-2.8.6-3.7 1.7c-.4.4-.3 1 .1 1.4s1 .3 1.4-.1c1.2-1.3 3.3-1.3 4.5 0 .2.2.5.3.7.3s.5-.1.7-.3c.4-.3.4-.9 0-1.3"/><path fill="#ADD8FB" d="M6 12h6v3H6z"/></svg>

Before

Width:  |  Height:  |  Size: 604 B

After

Width:  |  Height:  |  Size: 597 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#90A4AE" d="M40 35v5H8v-5H4v5c0 2.2 1.8 4 4 4h32c2.2 0 4-1.8 4-4v-5z"/><g fill="#1565C0"><path d="M24 23.4 17 15h14z"/><path d="M22 4h4v14h-4zm9.5 22.9-.7 1.1 3.5 1.9.6-1.2c1.6-3 2.6-4.7 3.5-5.2.9-.5 2.6-.5 5.6-.5v-4c-7.7 0-8.4.4-12.5 7.9"/><path d="m38.4 31-9 4L28 25zm-21.9-4.1.6 1.2-3.5 1.9-.6-1.2c-1.6-3-2.6-4.7-3.5-5.2C8.7 23 7 23 4 23v-4c7.7 0 8.4.4 12.5 7.9"/><path d="m20 25-1.4 10-9-4z"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#90A4AE" d="M40 35v5H8v-5H4v5c0 2.2 1.8 4 4 4h32c2.2 0 4-1.8 4-4v-5z"/><g fill="#1565C0"><path d="M24 23.4 17 15h14z"/><path d="M22 4h4v14h-4zm9.5 22.9-.7 1.1 3.5 1.9.6-1.2c1.6-3 2.6-4.7 3.5-5.2S41 23 44 23v-4c-7.7 0-8.4.4-12.5 7.9"/><path d="m38.4 31-9 4L28 25zm-21.9-4.1.6 1.2-3.5 1.9-.6-1.2c-1.6-3-2.6-4.7-3.5-5.2C8.7 23 7 23 4 23v-4c7.7 0 8.4.4 12.5 7.9"/><path d="m20 25-1.4 10-9-4z"/></g></svg>

Before

Width:  |  Height:  |  Size: 491 B

After

Width:  |  Height:  |  Size: 484 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#FBC02D" d="M37 22c0-7.7-6.6-13.8-14.5-12.9-6 .7-10.8 5.5-11.4 11.5-.5 4.6 1.4 8.7 4.6 11.3 1.4 1.2 2.3 2.9 2.3 4.8v.3h12v-.1c0-1.8.8-3.6 2.2-4.8 2.9-2.4 4.8-6 4.8-10.1"/><path fill="#FFF59D" d="m30.6 20.2-3-2c-.3-.2-.8-.2-1.1 0L24 19.8l-2.4-1.6c-.3-.2-.8-.2-1.1 0l-3 2c-.2.2-.4.4-.4.7s0 .6.2.8l3.8 4.7V37h2V26c0-.2-.1-.4-.2-.6l-3.3-4.1 1.5-1 2.4 1.6c.3.2.8.2 1.1 0l2.4-1.6 1.5 1-3.3 4.1c-.1.2-.2.4-.2.6v11h2V26.4l3.8-4.7c.2-.2.3-.5.2-.8s-.2-.6-.4-.7"/><circle cx="24" cy="44" r="3" fill="#5C6BC0"/><path fill="#9FA8DA" d="M26 45h-4c-2.2 0-4-1.8-4-4v-5h12v5c0 2.2-1.8 4-4 4"/><path fill="#5C6BC0" d="m30 41-11.6 1.6c.3.7.9 1.4 1.6 1.8l9.4-1.3c.4-.6.6-1.3.6-2.1m-12-2.3v2L30 39v-2z"/><path fill="#37474F" d="M3.563 6.396 6.39 3.568l37.966 37.966-2.828 2.828z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#FBC02D" d="M37 22c0-7.7-6.6-13.8-14.5-12.9-6 .7-10.8 5.5-11.4 11.5-.5 4.6 1.4 8.7 4.6 11.3 1.4 1.2 2.3 2.9 2.3 4.8v.3h12v-.1c0-1.8.8-3.6 2.2-4.8 2.9-2.4 4.8-6 4.8-10.1"/><path fill="#FFF59D" d="m30.6 20.2-3-2c-.3-.2-.8-.2-1.1 0L24 19.8l-2.4-1.6c-.3-.2-.8-.2-1.1 0l-3 2c-.2.2-.4.4-.4.7s0 .6.2.8l3.8 4.7V37h2V26c0-.2-.1-.4-.2-.6l-3.3-4.1 1.5-1 2.4 1.6c.3.2.8.2 1.1 0l2.4-1.6 1.5 1-3.3 4.1c-.1.2-.2.4-.2.6v11h2V26.4l3.8-4.7c.2-.2.3-.5.2-.8s-.2-.6-.4-.7"/><circle cx="24" cy="44" r="3" fill="#5C6BC0"/><path fill="#9FA8DA" d="M26 45h-4c-2.2 0-4-1.8-4-4v-5h12v5c0 2.2-1.8 4-4 4"/><path fill="#5C6BC0" d="m30 41-11.6 1.6c.3.7.9 1.4 1.6 1.8l9.4-1.3q.6-.9.6-2.1m-12-2.3v2L30 39v-2z"/><path fill="#37474F" d="M3.563 6.396 6.39 3.568l37.966 37.966-2.828 2.828z"/></svg>

Before

Width:  |  Height:  |  Size: 850 B

After

Width:  |  Height:  |  Size: 844 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#546E7A" d="M38 33V5h-4v28h-6l8 10 8-10z"/><path fill="#2196F3" d="M16.4 20h-3V8.6L9.9 9.7V7.3L16 5.1h.3V20zm3 23H9.2v-2l4.8-5.1c.4-.4.7-.8.9-1.1.2-.3.5-.6.6-.9.2-.3.3-.5.3-.8.1-.2.1-.5.1-.7 0-.7-.2-1.2-.5-1.6-.3-.4-.8-.6-1.4-.6-.3 0-.7.1-.9.2-.3.1-.5.3-.7.5-.2.2-.3.5-.4.8s-.1.6-.1 1h-3c0-.7.1-1.3.4-1.9.2-.6.6-1.1 1-1.6.5-.4 1-.8 1.6-1.1.6-.3 1.4-.4 2.2-.4.8 0 1.5.1 2.1.3.6.2 1.1.5 1.5.8s.7.8.9 1.3.3 1.1.3 1.8c0 .5-.1 1-.2 1.4s-.4 1.2-.7 1.7-.6.9-1 1.4c-.4.5-.9 1-1.4 1.5L13 40.6h6.4z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#546E7A" d="M38 33V5h-4v28h-6l8 10 8-10z"/><path fill="#2196F3" d="M16.4 20h-3V8.6L9.9 9.7V7.3L16 5.1h.3V20zm3 23H9.2v-2l4.8-5.1c.4-.4.7-.8.9-1.1s.5-.6.6-.9c.2-.3.3-.5.3-.8.1-.2.1-.5.1-.7 0-.7-.2-1.2-.5-1.6s-.8-.6-1.4-.6c-.3 0-.7.1-.9.2-.3.1-.5.3-.7.5s-.3.5-.4.8-.1.6-.1 1h-3c0-.7.1-1.3.4-1.9.2-.6.6-1.1 1-1.6.5-.4 1-.8 1.6-1.1s1.4-.4 2.2-.4q1.2 0 2.1.3c.6.2 1.1.5 1.5.8s.7.8.9 1.3.3 1.1.3 1.8c0 .5-.1 1-.2 1.4s-.4 1.2-.7 1.7-.6.9-1 1.4-.9 1-1.4 1.5L13 40.6h6.4z"/></svg>

Before

Width:  |  Height:  |  Size: 581 B

After

Width:  |  Height:  |  Size: 555 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#546E7A" d="M38 33V5h-4v28h-6l8 10 8-10z"/><path fill="#2196F3" d="M19.2 20H9v-2l4.8-5.1c.4-.4.7-.8.9-1.1.2-.3.5-.6.6-.9.2-.3.3-.5.3-.8.1-.2.1-.5.1-.7 0-.7-.2-1.2-.5-1.6-.3-.4-.8-.6-1.4-.6-.3 0-.7.1-.9.2-.3.1-.5.3-.7.5-.2.2-.3.5-.4.8s-.1.6-.1 1h-3c0-.7.1-1.3.4-1.9.2-.6.6-1.1 1-1.6.5-.4 1-.8 1.6-1.1.6-.3 1.4-.4 2.2-.4.8 0 1.5.1 2.1.3.6.2 1.1.5 1.5.8s.7.8.9 1.3c.2.5.3 1.1.3 1.8 0 .5-.1 1-.2 1.4s-.4.9-.7 1.4-.6.9-1 1.4c-.4.5-.9 1-1.4 1.5l-2.6 2.8h6.4zm-3 23h-3V31.6l-3.5 1.1v-2.4l6.2-2.2h.3z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#546E7A" d="M38 33V5h-4v28h-6l8 10 8-10z"/><path fill="#2196F3" d="M19.2 20H9v-2l4.8-5.1c.4-.4.7-.8.9-1.1s.5-.6.6-.9c.2-.3.3-.5.3-.8.1-.2.1-.5.1-.7 0-.7-.2-1.2-.5-1.6s-.8-.6-1.4-.6c-.3 0-.7.1-.9.2-.3.1-.5.3-.7.5s-.3.5-.4.8-.1.6-.1 1h-3c0-.7.1-1.3.4-1.9.2-.6.6-1.1 1-1.6.5-.4 1-.8 1.6-1.1s1.4-.4 2.2-.4q1.2 0 2.1.3c.6.2 1.1.5 1.5.8s.7.8.9 1.3q.3.75.3 1.8c0 .5-.1 1-.2 1.4s-.4.9-.7 1.4-.6.9-1 1.4-.9 1-1.4 1.5l-2.6 2.8h6.4zm-3 23h-3V31.6l-3.5 1.1v-2.4l6.2-2.2h.3z"/></svg>

Before

Width:  |  Height:  |  Size: 585 B

After

Width:  |  Height:  |  Size: 554 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#546E7A" d="M14 13H8v-1.8c0-.7.5-1.2 1.2-1.2h3.6c.7 0 1.2.5 1.2 1.2z"/><path fill="#5E35B1" d="M40 40H8c-2.2 0-4-1.8-4-4V22h40v14c0 2.2-1.8 4-4 4"/><path fill="#42257A" d="M12.7 22c-.4 1.3-.7 2.6-.7 4 0 6.6 5.4 12 12 12s12-5.4 12-12c0-1.4-.3-2.7-.7-4z"/><path fill="#78909C" d="M8 12h32c2.2 0 4 1.8 4 4v6H4v-6c0-2.2 1.8-4 4-4"/><path fill="#78909C" d="M33.9 13.1H14.2L17.6 8c.4-.6 1-.9 1.7-.9h9.6c.7 0 1.3.3 1.7.9z"/><path fill="#455A64" d="M35.3 22c-1.6-4.7-6.1-8-11.3-8s-9.7 3.3-11.3 8z"/><circle cx="24" cy="26" r="9" fill="#B388FF"/><path fill="#C7A7FF" d="M29 23c-1.2-1.4-3-2.2-4.8-2.2-1.8 0-3.6.8-4.8 2.2-.5.5-.4 1.3.1 1.8s1.3.4 1.8-.1c1.5-1.7 4.3-1.7 5.8 0 .3.3.6.4 1 .4.3 0 .6-.1.9-.3.4-.4.5-1.3 0-1.8"/><path fill="#DBE2E5" d="M36 15h5v4h-5z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#546E7A" d="M14 13H8v-1.8c0-.7.5-1.2 1.2-1.2h3.6c.7 0 1.2.5 1.2 1.2z"/><path fill="#5E35B1" d="M40 40H8c-2.2 0-4-1.8-4-4V22h40v14c0 2.2-1.8 4-4 4"/><path fill="#42257A" d="M12.7 22c-.4 1.3-.7 2.6-.7 4 0 6.6 5.4 12 12 12s12-5.4 12-12c0-1.4-.3-2.7-.7-4z"/><path fill="#78909C" d="M8 12h32c2.2 0 4 1.8 4 4v6H4v-6c0-2.2 1.8-4 4-4"/><path fill="#78909C" d="M33.9 13.1H14.2L17.6 8c.4-.6 1-.9 1.7-.9h9.6c.7 0 1.3.3 1.7.9z"/><path fill="#455A64" d="M35.3 22c-1.6-4.7-6.1-8-11.3-8s-9.7 3.3-11.3 8z"/><circle cx="24" cy="26" r="9" fill="#B388FF"/><path fill="#C7A7FF" d="M29 23c-1.2-1.4-3-2.2-4.8-2.2s-3.6.8-4.8 2.2c-.5.5-.4 1.3.1 1.8s1.3.4 1.8-.1c1.5-1.7 4.3-1.7 5.8 0 .3.3.6.4 1 .4q.45 0 .9-.3c.4-.4.5-1.3 0-1.8"/><path fill="#DBE2E5" d="M36 15h5v4h-5z"/></svg>

Before

Width:  |  Height:  |  Size: 843 B

After

Width:  |  Height:  |  Size: 837 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#2E7D32" d="M25.4 5.6c-.8-.8-2-.8-2.8 0l-12 12c-.8.8-.8 2 0 2.8.4.4.9.6 1.4.6s1-.2 1.4-.6l12-12c.8-.8.8-2 0-2.8"/><path fill="#1B5E20" d="m37.4 17.6-12-12c-.8-.8-2-.8-2.8 0-.8.8-.8 2 0 2.8l12 12c.4.4.9.6 1.4.6s1-.2 1.4-.6c.8-.8.8-2 0-2.8"/><path fill="#388E3C" d="M37.4 41H10.6c-1 0-1.8-.7-2-1.6L5 21h38l-3.7 18.4c-.2.9-1 1.6-1.9 1.6"/><path fill="#4CAF50" d="M43 23H5c-1.1 0-2-.9-2-2v-2c0-1.1.9-2 2-2h38c1.1 0 2 .9 2 2v2c0 1.1-.9 2-2 2"/><path fill="#DCEDC8" d="m30.8 24.8-7.9 7.9-3.7-3.8-2.2 2.2 5.9 5.9L33 26.9z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#2E7D32" d="M25.4 5.6c-.8-.8-2-.8-2.8 0l-12 12c-.8.8-.8 2 0 2.8.4.4.9.6 1.4.6s1-.2 1.4-.6l12-12c.8-.8.8-2 0-2.8"/><path fill="#1B5E20" d="m37.4 17.6-12-12c-.8-.8-2-.8-2.8 0s-.8 2 0 2.8l12 12c.4.4.9.6 1.4.6s1-.2 1.4-.6c.8-.8.8-2 0-2.8"/><path fill="#388E3C" d="M37.4 41H10.6c-1 0-1.8-.7-2-1.6L5 21h38l-3.7 18.4c-.2.9-1 1.6-1.9 1.6"/><path fill="#4CAF50" d="M43 23H5c-1.1 0-2-.9-2-2v-2c0-1.1.9-2 2-2h38c1.1 0 2 .9 2 2v2c0 1.1-.9 2-2 2"/><path fill="#DCEDC8" d="m30.8 24.8-7.9 7.9-3.7-3.8-2.2 2.2 5.9 5.9L33 26.9z"/></svg>

Before

Width:  |  Height:  |  Size: 607 B

After

Width:  |  Height:  |  Size: 603 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#F57C00" d="M4 9v32s8.4-3 20-3 20 3 20 3V9s-6.7 3-20 3S4 9 4 9"/><path fill="#942A09" d="M24 34h.4L15 19 6.9 36.2c3.4-.9 9.6-2.2 17.1-2.2"/><path fill="#BF360C" d="M24 34c3.3 0 6.3.2 9 .6l-8-11.8-7.8 11.5c2.1-.2 4.4-.3 6.8-.3"/><path fill="#E65100" d="M40.7 36 35 26.5l-5 7.8c4.5.4 8.2 1.1 10.7 1.7"/><ellipse cx="36" cy="19.5" fill="#FFF9C4" rx="2" ry="2.5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#F57C00" d="M4 9v32s8.4-3 20-3 20 3 20 3V9s-6.7 3-20 3S4 9 4 9"/><path fill="#942A09" d="M24 34h.4L15 19 6.9 36.2c3.4-.9 9.6-2.2 17.1-2.2"/><path fill="#BF360C" d="M24 34q4.95 0 9 .6l-8-11.8-7.8 11.5c2.1-.2 4.4-.3 6.8-.3"/><path fill="#E65100" d="M40.7 36 35 26.5l-5 7.8c4.5.4 8.2 1.1 10.7 1.7"/><ellipse cx="36" cy="19.5" fill="#FFF9C4" rx="2" ry="2.5"/></svg>

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 445 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><circle cx="24" cy="24" r="21" fill="#9C27B0"/><path fill="#E1BEE7" d="M25 26.8h-4.5v9h-4V12.5h8.2c1.3 0 2.5.2 3.6.5 1 .3 1.9.8 2.6 1.3.7.6 1.3 1.3 1.6 2.2s.6 1.9.6 3c0 1.6-.4 2.9-1.1 3.9-.8 1-1.8 1.9-3.1 2.4l5.2 9.7v.2h-4.3zm-4.5-3.2h4.2c.7 0 1.4-.1 1.9-.3.5-.2 1-.5 1.4-.8.4-.3.6-.7.8-1.2.2-.5.3-1 .3-1.6 0-.6-.1-1.1-.3-1.6-.2-.5-.4-.9-.8-1.2-.4-.3-.8-.6-1.4-.8-.5-.2-1.2-.3-2-.3h-4.1z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><circle cx="24" cy="24" r="21" fill="#9C27B0"/><path fill="#E1BEE7" d="M25 26.8h-4.5v9h-4V12.5h8.2c1.3 0 2.5.2 3.6.5 1 .3 1.9.8 2.6 1.3.7.6 1.3 1.3 1.6 2.2s.6 1.9.6 3c0 1.6-.4 2.9-1.1 3.9-.8 1-1.8 1.9-3.1 2.4l5.2 9.7v.2h-4.3zm-4.5-3.2h4.2c.7 0 1.4-.1 1.9-.3s1-.5 1.4-.8.6-.7.8-1.2.3-1 .3-1.6-.1-1.1-.3-1.6-.4-.9-.8-1.2-.8-.6-1.4-.8c-.5-.2-1.2-.3-2-.3h-4.1z"/></svg>

Before

Width:  |  Height:  |  Size: 468 B

After

Width:  |  Height:  |  Size: 437 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#1565C0" d="M38.8 28.2C41.5 24.8 45 20.1 45 12c0-.6-.4-1-1-1s-1 .4-1 1c0 6.7-2.5 10.7-5 13.9-.6-1.9-1-4.2-1-6.9 0-.5-.4-1-1-1-.5 0-1 .4-1 1-.1 1.7-.6 3.6-1 3.8-.4 0-.9-1.4-1-2.8 0-.5-.5-.9-1-.9s-1 .3-1 .9c-.3 1.7-1.1 4.1-2 4.1-.4 0-.6-.1-.7-.3-.3-.3-.4-1-.4-1.6 0-.4.1-.8.1-1.2 0-.5-.4-1-.9-1s-1 .3-1.1.8c0 .1-.1.5-.1 1.1-.2 1.7-.8 5.1-2.9 5.1-.7 0-1.1-.2-1.4-.7-.5-.8-.5-2.1 0-3.3v-.1c.1-.1.1-.3.2-.4.8-1.6 1.7-2.5 3.2-2.5.6 0 1-.4 1-1s-.4-1-1-1c-4.2 0-5.4 4.1-6.6 8-1.4 4.8-2.7 8-6.4 8-5.1 0-7-6.6-7-11 0-8.6 4.7-14 9-14 2.9 0 4 2.3 4.1 2.4.2.5.8.7 1.3.5.5-.2.7-.8.5-1.3-.1-.2-1.7-3.6-5.9-3.6C8.6 7 3 13 3 23c0 10.3 5.9 13 9 13 5.1 0 6.8-4.5 8.1-8.5.7.9 1.7 1.5 2.9 1.5 2.2 0 3.5-1.6 4.2-3.6.5.4 1.1.6 1.8.6 1.4 0 2.4-1.2 3-2.4.4.7 1.1 1.2 2 1.2.6 0 1.1-.3 1.5-.7.3 1.4.7 2.7 1 3.8-1.4 1.8-2.5 3.3-2.5 5.1 0 1.7 1.3 3 3 3 1.8 0 3-1.6 3-3 0-1.3-.5-2.7-1.1-4.3 0-.2-.1-.3-.1-.5M37 34c-.7 0-1-.5-1-1 0-.9.5-1.8 1.3-2.9.4 1.2.7 2.1.7 2.9 0 .3-.3 1-1 1"/><path fill="#90A4AE" d="M3 40h42v2H3z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#1565C0" d="M38.8 28.2C41.5 24.8 45 20.1 45 12c0-.6-.4-1-1-1s-1 .4-1 1c0 6.7-2.5 10.7-5 13.9-.6-1.9-1-4.2-1-6.9 0-.5-.4-1-1-1-.5 0-1 .4-1 1-.1 1.7-.6 3.6-1 3.8-.4 0-.9-1.4-1-2.8 0-.5-.5-.9-1-.9s-1 .3-1 .9c-.3 1.7-1.1 4.1-2 4.1-.4 0-.6-.1-.7-.3-.3-.3-.4-1-.4-1.6 0-.4.1-.8.1-1.2 0-.5-.4-1-.9-1s-1 .3-1.1.8c0 .1-.1.5-.1 1.1-.2 1.7-.8 5.1-2.9 5.1-.7 0-1.1-.2-1.4-.7-.5-.8-.5-2.1 0-3.3v-.1c.1-.1.1-.3.2-.4.8-1.6 1.7-2.5 3.2-2.5.6 0 1-.4 1-1s-.4-1-1-1c-4.2 0-5.4 4.1-6.6 8-1.4 4.8-2.7 8-6.4 8-5.1 0-7-6.6-7-11 0-8.6 4.7-14 9-14 2.9 0 4 2.3 4.1 2.4.2.5.8.7 1.3.5s.7-.8.5-1.3C19.8 10.4 18.2 7 14 7 8.6 7 3 13 3 23c0 10.3 5.9 13 9 13 5.1 0 6.8-4.5 8.1-8.5.7.9 1.7 1.5 2.9 1.5 2.2 0 3.5-1.6 4.2-3.6q.75.6 1.8.6c1.4 0 2.4-1.2 3-2.4.4.7 1.1 1.2 2 1.2.6 0 1.1-.3 1.5-.7.3 1.4.7 2.7 1 3.8-1.4 1.8-2.5 3.3-2.5 5.1 0 1.7 1.3 3 3 3 1.8 0 3-1.6 3-3 0-1.3-.5-2.7-1.1-4.3 0-.2-.1-.3-.1-.5M37 34c-.7 0-1-.5-1-1 0-.9.5-1.8 1.3-2.9.4 1.2.7 2.1.7 2.9 0 .3-.3 1-1 1"/><path fill="#90A4AE" d="M3 40h42v2H3z"/></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><circle cx="24" cy="24" r="21" fill="#9C27B0"/><path fill="#E1BEE7" d="M20.7 27.2v8.4h-3.9V12.9h8.7c1.3 0 2.5.2 3.5.5 1 .4 1.9.9 2.6 1.5.7.6 1.2 1.4 1.6 2.3.4.9.6 1.8.6 2.9 0 1.1-.2 2.1-.6 3-.4.9-.9 1.6-1.6 2.2-.7.6-1.6 1.1-2.6 1.4-1 .3-2.2.5-3.5.5zm0-3.2h4.7c.8 0 1.4-.1 2-.3.5-.2 1-.5 1.4-.8.4-.3.6-.8.8-1.2.2-.5.2-1 .2-1.6 0-.5-.1-1-.2-1.5-.2-.5-.4-.9-.8-1.3s-.8-.7-1.4-.9c-.5-.2-1.2-.3-2-.3h-4.7z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><circle cx="24" cy="24" r="21" fill="#9C27B0"/><path fill="#E1BEE7" d="M20.7 27.2v8.4h-3.9V12.9h8.7c1.3 0 2.5.2 3.5.5 1 .4 1.9.9 2.6 1.5s1.2 1.4 1.6 2.3.6 1.8.6 2.9q0 1.65-.6 3c-.4.9-.9 1.6-1.6 2.2S30 26.4 29 26.7s-2.2.5-3.5.5zm0-3.2h4.7c.8 0 1.4-.1 2-.3.5-.2 1-.5 1.4-.8s.6-.8.8-1.2c.2-.5.2-1 .2-1.6 0-.5-.1-1-.2-1.5-.2-.5-.4-.9-.8-1.3s-.8-.7-1.4-.9c-.5-.2-1.2-.3-2-.3h-4.7z"/></svg>

Before

Width:  |  Height:  |  Size: 481 B

After

Width:  |  Height:  |  Size: 456 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#607D8B" d="M44.7 11 36 19.6s-2.6 0-5.2-2.6-2.6-5.2-2.6-5.2l8.7-8.7c-4.9-1.2-10.8.4-14.4 4-5.4 5.4-.6 12.3-2 13.7C12.9 28.7 5.1 34.7 4.9 35c-2.3 2.3-2.4 6-.2 8.2 2.2 2.2 5.9 2.1 8.2-.2.3-.3 6.7-8.4 14.2-15.9 1.4-1.4 8 3.7 13.6-1.8 3.5-3.6 5.2-9.4 4-14.3M9.4 41.1c-1.4 0-2.5-1.1-2.5-2.5C6.9 37.1 8 36 9.4 36s2.5 1.1 2.5 2.5-1.1 2.6-2.5 2.6"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#607D8B" d="M44.7 11 36 19.6s-2.6 0-5.2-2.6-2.6-5.2-2.6-5.2l8.7-8.7c-4.9-1.2-10.8.4-14.4 4-5.4 5.4-.6 12.3-2 13.7C12.9 28.7 5.1 34.7 4.9 35c-2.3 2.3-2.4 6-.2 8.2s5.9 2.1 8.2-.2c.3-.3 6.7-8.4 14.2-15.9 1.4-1.4 8 3.7 13.6-1.8 3.5-3.6 5.2-9.4 4-14.3M9.4 41.1c-1.4 0-2.5-1.1-2.5-2.5C6.9 37.1 8 36 9.4 36s2.5 1.1 2.5 2.5-1.1 2.6-2.5 2.6"/></svg>

Before

Width:  |  Height:  |  Size: 431 B

After

Width:  |  Height:  |  Size: 424 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#546E7A" d="M5 34h6v3H5zm32 0h6v3h-6z"/><path fill="#78909C" d="M44 35H4c-2.2 0-4-1.8-4-4V17c0-2.2 1.8-4 4-4h40c2.2 0 4 1.8 4 4v14c0 2.2-1.8 4-4 4"/><path fill="#37474F" d="M5 19h2v2H5zm0 4h2v2H5zm0 4h2v2H5zm4-8h2v2H9zm0 4h2v2H9zm0 4h2v2H9zm4-8h2v2h-2zm0 4h2v2h-2zm0 4h2v2h-2zm4-8h2v2h-2zm0 4h2v2h-2zm0 4h2v2h-2zm4-8h2v2h-2zm0 4h2v2h-2zm0 4h2v2h-2z"/><circle cx="37" cy="24" r="8" fill="#37474F"/><circle cx="37" cy="24" r="6" fill="#a0f"/><path fill="#EA80FC" d="M40.7 21.7c-1-1.1-2.3-1.7-3.7-1.7s-2.8.6-3.7 1.7c-.4.4-.3 1 .1 1.4.4.4 1 .3 1.4-.1 1.2-1.3 3.3-1.3 4.5 0 .2.2.5.3.7.3.2 0 .5-.1.7-.3.4-.3.4-.9 0-1.3"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#546E7A" d="M5 34h6v3H5zm32 0h6v3h-6z"/><path fill="#78909C" d="M44 35H4c-2.2 0-4-1.8-4-4V17c0-2.2 1.8-4 4-4h40c2.2 0 4 1.8 4 4v14c0 2.2-1.8 4-4 4"/><path fill="#37474F" d="M5 19h2v2H5zm0 4h2v2H5zm0 4h2v2H5zm4-8h2v2H9zm0 4h2v2H9zm0 4h2v2H9zm4-8h2v2h-2zm0 4h2v2h-2zm0 4h2v2h-2zm4-8h2v2h-2zm0 4h2v2h-2zm0 4h2v2h-2zm4-8h2v2h-2zm0 4h2v2h-2zm0 4h2v2h-2z"/><circle cx="37" cy="24" r="8" fill="#37474F"/><circle cx="37" cy="24" r="6" fill="#a0f"/><path fill="#EA80FC" d="M40.7 21.7c-1-1.1-2.3-1.7-3.7-1.7s-2.8.6-3.7 1.7c-.4.4-.3 1 .1 1.4s1 .3 1.4-.1c1.2-1.3 3.3-1.3 4.5 0 .2.2.5.3.7.3s.5-.1.7-.3c.4-.3.4-.9 0-1.3"/></svg>

Before

Width:  |  Height:  |  Size: 705 B

After

Width:  |  Height:  |  Size: 698 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#455A64" d="M36.5 44h-25c-1.1 0-1.8-1.2-1.3-2.2L13 37h22l2.7 4.8c.6 1-.1 2.2-1.2 2.2"/><circle cx="24" cy="23" r="18" fill="#78909C"/><path fill="#455A64" d="M24 35c-6.6 0-12-5.4-12-12s5.4-12 12-12 12 5.4 12 12-5.4 12-12 12"/><circle cx="24" cy="23" r="9" fill="#42A5F5"/><path fill="#90CAF9" d="M28.8 20c-1.2-1.4-3-2.2-4.8-2.2s-3.6.8-4.8 2.2c-.5.5-.4 1.3.1 1.8s1.3.4 1.8-.1c1.5-1.7 4.3-1.7 5.8 0 .3.3.6.4 1 .4.3 0 .6-.1.9-.3.4-.4.5-1.3 0-1.8"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 48 48"><path fill="#455A64" d="M36.5 44h-25c-1.1 0-1.8-1.2-1.3-2.2L13 37h22l2.7 4.8c.6 1-.1 2.2-1.2 2.2"/><circle cx="24" cy="23" r="18" fill="#78909C"/><path fill="#455A64" d="M24 35c-6.6 0-12-5.4-12-12s5.4-12 12-12 12 5.4 12 12-5.4 12-12 12"/><circle cx="24" cy="23" r="9" fill="#42A5F5"/><path fill="#90CAF9" d="M28.8 20c-1.2-1.4-3-2.2-4.8-2.2s-3.6.8-4.8 2.2c-.5.5-.4 1.3.1 1.8s1.3.4 1.8-.1c1.5-1.7 4.3-1.7 5.8 0 .3.3.6.4 1 .4q.45 0 .9-.3c.4-.4.5-1.3 0-1.8"/></svg>

Before

Width:  |  Height:  |  Size: 535 B

After

Width:  |  Height:  |  Size: 533 B

View File

@ -1,30 +0,0 @@
module.exports = {
testEnvironment: 'jsdom',
testMatch: [
'<rootDir>/test/spec/**/*.{spec,test}.{js,jsx,ts,tsx}'
],
transform: {
'^.*PickerTemplate.js$': './config/minifyHtmlInJest.js'
},
moduleFileExtensions: ['js'],
testPathIgnorePatterns: ['node_modules'],
bail: true,
verbose: true,
silent: false,
setupFilesAfterEnv: [
'<rootDir>/config/jest.setup.js'
],
coverageReporters: ['json', 'lcov', 'text', 'html'],
coveragePathIgnorePatterns: [
'bin/',
'test/'
],
coverageThreshold: {
global: {
statements: 100,
branches: 100,
functions: 100,
lines: 100
}
}
}

View File

@ -1,6 +1,6 @@
{
"name": "emoji-picker-element",
"version": "1.21.0",
"version": "1.21.3",
"description": "Lightweight emoji picker distributed as a web component",
"main": "index.js",
"module": "index.js",
@ -44,9 +44,9 @@
"dev:server": "node ./test/adhoc/server.js",
"lint": "standard && stylelint '**/*.scss'",
"lint:fix": "standard --fix && stylelint --fix '**/*.scss'",
"test": "node ./bin/buildStylesForJest.js && NODE_OPTIONS=--experimental-vm-modules jest --runInBand",
"test": "vitest",
"test:adhoc": "node ./test/adhoc/server.js",
"cover": "node ./bin/buildStylesForJest.js && NODE_OPTIONS=--experimental-vm-modules jest --runInBand --coverage",
"cover": "vitest --coverage",
"docs": "node bin/processCustomEmoji.js",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
"version": "run-s changelog docs && git add CHANGELOG.md docs"
@ -71,56 +71,56 @@
},
"homepage": "https://github.com/nolanlawson/emoji-picker-element#readme",
"devDependencies": {
"@peculiar/webcrypto": "^1.4.3",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-inject": "^5.0.5",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-replace": "^5.0.5",
"@rollup/plugin-strip": "^3.0.4",
"@testing-library/dom": "^9.3.3",
"@testing-library/jest-dom": "^6.1.5",
"@testing-library/user-event": "^14.5.1",
"@rollup/plugin-terser": "^0.4.4",
"@testing-library/dom": "^9.3.4",
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/user-event": "^14.5.2",
"@vitest/coverage-istanbul": "^1.4.0",
"@vitest/ui": "^1.4.0",
"blob-util": "^2.0.2",
"compression": "^1.7.4",
"conventional-changelog-cli": "^2.2.2",
"conventional-changelog-cli": "^4.1.0",
"cross-env": "^7.0.3",
"csso": "^5.0.2",
"d2l-resize-aware": "github:BrightspaceUI/resize-aware#semver:^1.2.2",
"emoji-picker-element-data": "^1.6.0",
"emojibase-data": "^5.1.1",
"express": "^4.18.2",
"fake-indexeddb": "^5.0.1",
"express": "^4.19.1",
"fake-indexeddb": "^5.0.2",
"fast-glob": "^3.3.2",
"fetch-mock-jest": "^1.5.1",
"fetch-mock": "^9.11.0",
"flat-color-icons": "^1.1.0",
"focus-visible": "^5.2.0",
"get-folder-size": "^4.0.0",
"husky": "^8.0.3",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"lint-staged": "^15.2.0",
"husky": "^9.0.11",
"jsdom": "^24.0.0",
"lint-staged": "^15.2.2",
"lodash-es": "^4.17.15",
"markdown-table": "^3.0.2",
"markdown-toc": "^1.2.0",
"minify-html-literals": "^1.3.5",
"node-fetch": "^2.7.0",
"npm-run-all": "^4.1.5",
"playwright": "^1.40.1",
"playwright": "^1.42.1",
"pretty-bytes": "^6.1.1",
"puppeteer": "^21.6.0",
"postcss": "^8.4.38",
"puppeteer": "^22.6.0",
"recursive-readdir": "^2.2.3",
"rollup": "^4.7.0",
"rollup": "^4.13.0",
"rollup-plugin-analyzer": "^4.0.0",
"rollup-plugin-terser": "^7.0.2",
"sass": "^1.69.5",
"shx": "^0.3.4",
"sass": "^1.72.0",
"standard": "^17.1.0",
"string.prototype.replaceall": "^1.0.8",
"stylelint": "^15.11.0",
"stylelint-config-recommended-scss": "^13.1.0",
"stylelint-scss": "^5.3.1",
"svgo": "^3.0.5",
"stylelint": "^16.2.1",
"stylelint-config-recommended-scss": "^14.0.0",
"stylelint-scss": "^6.2.1",
"svgo": "^3.2.0",
"tachometer": "^0.7.0",
"terser": "^5.26.0"
"terser": "^5.29.2",
"vitest": "^1.4.0"
},
"//": {
"jsonwebtoken": "comes from tachometer, tachometer is pinned for now due to breaking change, but jsonwebtoken 8 has a vuln"
@ -185,8 +185,5 @@
"*.js": "standard --fix",
"*.(css|scss)": "stylelint --fix '**/*.scss'"
},
"volta": {
"node": "20.9.0",
"yarn": "1.22.19"
}
"packageManager": "pnpm@8.15.5+sha256.4b4efa12490e5055d59b9b9fc9438b7d581a6b7af3b5675eb5c5f447cee1a589"
}

7807
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -3,10 +3,10 @@ import resolve from '@rollup/plugin-node-resolve'
import replace from '@rollup/plugin-replace'
import strip from '@rollup/plugin-strip'
import analyze from 'rollup-plugin-analyzer'
import { buildStyles } from './bin/buildStyles.js'
import { minifyHTMLLiterals } from 'minify-html-literals'
import { minifyHtmlLiteralsRollupPlugin } from './config/minifyHtmlLiteralsRollupPlugin.js'
import { buildStylesRollupPlugin } from './config/buildStylesRollupPlugin.js'
const { NODE_ENV, DEBUG } = process.env
const { NODE_ENV, DEBUG, PERF } = process.env
const dev = NODE_ENV !== 'production'
// Build Database.test.js and Picker.js as separate modules at build times so that they are properly tree-shakeable.
@ -16,9 +16,8 @@ const baseConfig = {
resolve(),
cjs(),
replace({
'process.env.NODE_ENV': dev ? '"development"' : '"production"',
'process.env.PERF': !!process.env.PERF,
'process.env.STYLES': JSON.stringify(buildStyles()),
'import.meta.env.MODE': dev ? '"development"' : '"production"',
'import.meta.env.PERF': !!PERF,
preventAssignment: true
}),
replace({
@ -26,20 +25,12 @@ const baseConfig = {
delimiters: ['', ''],
preventAssignment: true
}),
{
name: 'minify-html-in-tag-template-literals',
transform (content, id) {
if (id.includes('PickerTemplate.js')) {
return minifyHTMLLiterals(content, {
fileName: id
})
}
}
},
minifyHtmlLiteralsRollupPlugin(),
buildStylesRollupPlugin(),
strip({
include: ['**/*.js'],
functions: [
(!dev && !process.env.PERF) && 'performance.*',
(!dev && !PERF) && 'performance.*',
!dev && 'console.log'
].filter(Boolean)
}),

View File

@ -29,7 +29,7 @@ async function doFullDatabaseScanForSingleResult (db, predicate) {
//
// Mini-benchmark for determining the best batch size:
//
// PERF=1 yarn build:rollup && yarn test:adhoc
// PERF=1 pnpm build:rollup && pnpm test:adhoc
//
// (async () => {
// performance.mark('start')

View File

@ -4,7 +4,13 @@ import { binaryStringToArrayBuffer, arrayBufferToBinaryString } from 'blob-util'
export async function jsonChecksum (object) {
performance.mark('jsonChecksum')
const inString = JSON.stringify(object)
const inBuffer = binaryStringToArrayBuffer(inString)
let inBuffer = binaryStringToArrayBuffer(inString)
/* istanbul ignore else */
if (import.meta.env.MODE === 'test') {
// Issue with ArrayBuffer in jsdom https://github.com/vitest-dev/vitest/issues/5365
inBuffer = Buffer.from(new Uint8Array(inBuffer))
}
// this does not need to be cryptographically secure, SHA-1 is fine
const outBuffer = await crypto.subtle.digest('SHA-1', inBuffer)
const outBinString = arrayBufferToBinaryString(outBuffer)

View File

@ -4,6 +4,7 @@ import { DEFAULT_CATEGORY_SORTING, DEFAULT_SKIN_TONE_EMOJI, FONT_FAMILY } from '
import enI18n from './i18n/en.js'
import Database from './ImportedDatabase'
import { queueMicrotask } from './utils/queueMicrotask.js'
import baseStyles from './styles/picker.scss'
const PROPS = [
'customEmoji',
@ -25,7 +26,7 @@ export default class PickerElement extends HTMLElement {
super()
this.attachShadow({ mode: 'open' })
const style = document.createElement('style')
style.textContent = process.env.STYLES + EXTRA_STYLES
style.textContent = baseStyles + EXTRA_STYLES
this.shadowRoot.appendChild(style)
this._ctx = {
// Set defaults

View File

@ -29,7 +29,7 @@ const EMPTY_ARRAY = []
const { assign } = Object
export function createRoot (target, props) {
export function createRoot (shadowRoot, props) {
const refs = {}
const abortController = new AbortController()
const abortSignal = abortController.signal
@ -91,9 +91,11 @@ export function createRoot (target, props) {
//
const focus = id => {
refs.rootElement.getRootNode().getElementById(id).focus()
shadowRoot.getElementById(id).focus()
}
const emojiToDomNode = emoji => shadowRoot.getElementById(`emo-${emoji.id}`)
// fire a custom event that crosses the shadow boundary
const fireEvent = (name, detail) => {
refs.rootElement.dispatchEvent(new CustomEvent(name, {
@ -184,7 +186,7 @@ export function createRoot (target, props) {
let firstRender = true
createEffect(() => {
render(target, state, helpers, events, actions, refs, abortSignal, firstRender)
render(shadowRoot, state, helpers, events, actions, refs, abortSignal, firstRender)
firstRender = false
})
@ -195,7 +197,7 @@ export function createRoot (target, props) {
// mount logic
if (!state.emojiVersion) {
detectEmojiSupportLevel().then(level => {
// Can't actually test emoji support in Jest/JSDom, emoji never render in color in Cairo
// Can't actually test emoji support in Jest/Vitest/JSDom, emoji never render in color in Cairo
/* istanbul ignore next */
if (!level) {
state.message = state.i18n.emojiUnsupportedMessage
@ -310,7 +312,7 @@ export function createRoot (target, props) {
const { database } = state
const favs = (await Promise.all(MOST_COMMONLY_USED_EMOJI.map(unicode => (
database.getEmojiByUnicodeOrName(unicode)
)))).filter(Boolean) // filter because in Jest tests we don't have all the emoji in the DB
)))).filter(Boolean) // filter because in Jest/Vitest tests we don't have all the emoji in the DB
state.defaultFavoriteEmojis = favs
}
@ -360,7 +362,7 @@ export function createRoot (target, props) {
function calculateEmojiGridStyle (node) {
calculateWidth(node, abortSignal, width => {
/* istanbul ignore next */
if (process.env.NODE_ENV !== 'test') { // jsdom throws errors for this kind of fancy stuff
if (import.meta.env.MODE !== 'test') { // jsdom throws errors for this kind of fancy stuff
// read all the style/layout calculations we need to make
const style = getComputedStyle(refs.rootElement)
const newNumColumns = parseInt(style.getPropertyValue('--num-columns'), 10)
@ -431,8 +433,6 @@ export function createRoot (target, props) {
})
function checkZwjSupportAndUpdate (zwjEmojisToCheck) {
const shadowRootNode = refs.rootElement.getRootNode()
const emojiToDomNode = emoji => shadowRootNode.getElementById(`emo-${emoji.id}`)
checkZwjSupport(zwjEmojisToCheck, refs.baselineEmoji, emojiToDomNode)
// force update
// eslint-disable-next-line no-self-assign
@ -467,7 +467,7 @@ export function createRoot (target, props) {
createEffect(() => {
// consider initialLoad to be complete when the first tabpanel and favorites are rendered
/* istanbul ignore next */
if (process.env.NODE_ENV !== 'production' || process.env.PERF) {
if (import.meta.env.MODE !== 'production' || import.meta.env.PERF) {
if (state.currentEmojis.length && state.currentFavorites.length && state.initialLoad) {
state.initialLoad = false
requestPostAnimationFrame(() => performance.measure('initialLoad', 'initialLoad'))

View File

@ -2,11 +2,12 @@ import { getFromMap, parseTemplate, toString } from './utils.js'
const parseCache = new WeakMap()
const domInstancesCache = new WeakMap()
// This needs to be a symbol because it needs to be different from any possible output of a key function
const unkeyedSymbol = Symbol('un-keyed')
// for debugging
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
if (import.meta.env.MODE !== 'production') {
window.parseCache = parseCache
window.domInstancesCache = domInstancesCache
}
@ -37,7 +38,7 @@ function doChildrenNeedRerender (parentNode, newChildren) {
oldChildrenCount++
}
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && oldChildrenCount !== parentNode.children.length) {
if (import.meta.env.MODE !== 'production' && oldChildrenCount !== parentNode.children.length) {
throw new Error('parentNode.children.length is different from oldChildrenCount, it should not be')
}
// if new children length is different from old, we must re-render
@ -54,7 +55,7 @@ function patchChildren (newChildren, instanceBinding) {
needsRerender = doChildrenNeedRerender(targetParentNode, newChildren)
} else { // first render of list
needsRerender = true
instanceBinding.targetNode = undefined // placeholder comment not needed anymore, free memory
instanceBinding.targetNode = undefined // placeholder node not needed anymore, free memory
instanceBinding.targetParentNode = targetParentNode = targetNode.parentNode
}
// avoid re-rendering list if the dom nodes are exactly the same before and after
@ -94,20 +95,16 @@ function patch (expressions, instanceBindings) {
} else if (expression instanceof Element) { // html tag template returning a DOM element
newNode = expression
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && newNode === targetNode) {
if (import.meta.env.MODE !== 'production' && newNode === targetNode) {
// it seems impossible for the framework to get into this state, may as well assert on it
// worst case scenario is we lose focus if we call replaceWith on the same node
throw new Error('the newNode and targetNode are the same, this should never happen')
}
targetNode.replaceWith(newNode)
} else { // primitive - string, number, etc
if (targetNode.nodeType === Node.TEXT_NODE) { // already transformed into a text node
// nodeValue is faster than textContent supposedly https://www.youtube.com/watch?v=LY6y3HbDVmg
targetNode.nodeValue = toString(expression)
} else { // replace comment or whatever was there before with a text node
newNode = document.createTextNode(toString(expression))
targetNode.replaceWith(newNode)
}
// nodeValue is faster than textContent supposedly https://www.youtube.com/watch?v=LY6y3HbDVmg
// note we may be replacing the value in a placeholder text node
targetNode.nodeValue = toString(expression)
}
if (newNode) {
instanceBinding.targetNode = newNode
@ -140,7 +137,7 @@ function parse (tokens) {
case '<': {
const nextChar = token.charAt(j + 1)
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && !/[/a-z]/.test(nextChar)) {
if (import.meta.env.MODE !== 'production' && !/[/a-z]/.test(nextChar)) {
// we don't need to support comments ('<!') because we always use html-minify-literals
// also we don't support '<' inside tags, e.g. '<div> 2 < 3 </div>'
throw new Error('framework currently only supports a < followed by / or a-z')
@ -161,7 +158,7 @@ function parse (tokens) {
}
case '=': {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && !withinTag) {
if (import.meta.env.MODE !== 'production' && !withinTag) {
// we don't currently support '=' anywhere but inside a tag, e.g.
// we don't support '<div>2 + 2 = 4</div>'
throw new Error('framework currently does not support = anywhere but inside a tag')
@ -194,15 +191,17 @@ function parse (tokens) {
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
if (import.meta.env.MODE !== 'production') {
// remind myself that this object is supposed to be immutable
Object.freeze(binding)
}
bindings.push(binding)
// add a placeholder comment that we can find later
htmlString += (!withinTag && !withinAttribute) ? `<!--${bindings.length - 1}-->` : ''
if (!withinTag && !withinAttribute) {
// Add a placeholder text node, so we can find it later. Note we only support one dynamic child text node
htmlString += ' '
}
}
const template = parseTemplate(htmlString)
@ -213,21 +212,6 @@ function parse (tokens) {
}
}
function findPlaceholderComment (element, bindingId) {
// If we had a lot of placeholder comments to find, it would make more sense to build up a map once
// rather than search the DOM every time. But it turns out that we always only have one child,
// and it's the comment node, so searching every time is actually faster.
let childNode = element.firstChild
while (childNode) {
// Note that minify-html-literals has already removed all non-framework comments
// So we just need to look for comments that have exactly the bindingId as its text content
if (childNode.nodeType === Node.COMMENT_NODE && childNode.nodeValue === toString(bindingId)) {
return childNode
}
childNode = childNode.nextSibling
}
}
function traverseAndSetupBindings (dom, elementsToBindings) {
const instanceBindings = []
// traverse dom
@ -243,11 +227,23 @@ function traverseAndSetupBindings (dom, elementsToBindings) {
const targetNode = binding.attributeName
? element // attribute binding, just use the element itself
: findPlaceholderComment(element, i) // not an attribute binding, so has a placeholder comment
: element.firstChild // not an attribute binding, so has a placeholder text node
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && !targetNode) {
throw new Error('targetNode should not be undefined')
if (import.meta.env.MODE !== 'production') {
// We only support exactly one placeholder text node inside an element, which simplifies
// the implementation a lot. Also, minify-html-literals should handle any whitespace
// around the expression, so we should only ever see e.g. `<div>${expr}</div>`
if (
!binding.attributeName &&
!(element.childNodes.length === 1 && element.childNodes[0].nodeType === Node.TEXT_NODE)
) {
throw new Error('framework only supports exactly one dynamic child text node')
}
if (!targetNode) {
throw new Error('targetNode should not be undefined')
}
}
const instanceBinding = {
@ -258,7 +254,7 @@ function traverseAndSetupBindings (dom, elementsToBindings) {
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
if (import.meta.env.MODE !== 'production') {
// remind myself that this object is supposed to be monomorphic (for better JS engine perf)
Object.seal(instanceBinding)
}

View File

@ -17,7 +17,7 @@ export function createState (abortSignal) {
return
}
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && recursionDepth === MAX_RECURSION_DEPTH) {
if (import.meta.env.MODE !== 'production' && recursionDepth === MAX_RECURSION_DEPTH) {
throw new Error('max recursion depth, you probably didn\'t mean to do this')
}
const observersToRun = [...dirtyObservers]
@ -38,7 +38,6 @@ export function createState (abortSignal) {
const state = new Proxy({}, {
get (target, prop) {
// console.log('reactivity: get', prop)
if (currentObserver) {
let observers = propsToObservers.get(prop)
if (!observers) {
@ -50,7 +49,6 @@ export function createState (abortSignal) {
return target[prop]
},
set (target, prop, newValue) {
// console.log('reactivity: set', prop, newValue)
target[prop] = newValue
const observers = propsToObservers.get(prop)
if (observers) {
@ -82,7 +80,7 @@ export function createState (abortSignal) {
// for debugging
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
if (import.meta.env.MODE !== 'production') {
window.state = state
}
@ -91,7 +89,7 @@ export function createState (abortSignal) {
destroyed = true
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
if (import.meta.env.MODE !== 'production') {
delete window.state
}
})

View File

@ -16,7 +16,7 @@ export function parseTemplate (htmlString) {
template.innerHTML = htmlString
/* istanbul ignore next */
if (process.env.NODE_ENV !== 'production') {
if (import.meta.env.MODE !== 'production') {
if (template.content.children.length !== 1) {
throw new Error('only 1 child allowed for now')
}

View File

@ -1,18 +1,18 @@
export default {
categoriesLabel: 'Catégories',
emojiUnsupportedMessage: 'Votre navigateur ne soutient pas les emojis en couleur.',
emojiUnsupportedMessage: 'Votre navigateur ne supporte pas les emojis en couleur.',
favoritesLabel: 'Favoris',
loadingMessage: 'Chargement en cours…',
networkErrorMessage: 'Impossible de charger les emojis.',
regionLabel: 'Choisir un emoji',
searchDescription: 'Quand les résultats sont disponisbles, appuyez la fleche vers le haut ou le bas et la touche entrée pour choisir.',
searchDescription: 'Lorsque les résultats sont affichés, utilisez les flèches haut/bas pour naviguer et la touche entrée pour sélectionner.',
searchLabel: 'Rechercher',
searchResultsLabel: 'Résultats',
skinToneDescription: 'Quand disponible, appuyez la fleche vers le haut ou le bas et la touch entrée pour choisir.',
skinToneDescription: 'Quand disponible, utilisez les flèches haut/bas pour naviguer et la touche entrée pour sélectionner.',
skinToneLabel: 'Choisir une couleur de peau (actuellement {skinTone})',
skinTonesLabel: 'Couleurs de peau',
skinTones: [
'Défaut',
'Par défaut',
'Clair',
'Moyennement clair',
'Moyen',
@ -20,15 +20,15 @@ export default {
'Sombre'
],
categories: {
custom: 'Customisé',
'smileys-emotion': 'Les smileyes et les émoticônes',
'people-body': 'Les gens et le corps',
'animals-nature': 'Les animaux et la nature',
'food-drink': 'La nourriture et les boissons',
'travel-places': 'Les voyages et les endroits',
activities: 'Les activités',
objects: 'Les objets',
symbols: 'Les symbols',
flags: 'Les drapeaux'
custom: 'Personnalisé',
'smileys-emotion': 'Émoticônes',
'people-body': 'Corps et métiers',
'animals-nature': 'Animaux et nature',
'food-drink': 'Nourriture et boissons',
'travel-places': 'Voyages et lieux',
activities: 'Activités',
objects: 'Objets',
symbols: 'Symboles',
flags: 'Drapeaux'
}
}

View File

@ -1,7 +1,13 @@
// get the width of the text inside of a DOM node, via https://stackoverflow.com/a/59525891/680742
export function calculateTextWidth (node) {
// skip running this in jest/vitest because we don't need to check for emoji support in that environment
/* istanbul ignore else */
if (process.env.NODE_ENV === 'test') {
if (import.meta.env.MODE === 'test') {
// sanity check to make sure the node is defined properly
/* istanbul ignore if */
if (!node) {
throw new Error('node should not be undefined/null')
}
return 1
} else {
const range = document.createRange()

View File

@ -15,7 +15,7 @@ export const detectEmojiSupportLevel = () => {
))
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
if (import.meta.env.MODE !== 'production') {
promise.then(emojiSupportLevel => {
console.log('emoji support level', emojiSupportLevel)
})

View File

@ -8,7 +8,7 @@
import { FONT_FAMILY } from '../constants'
import { versionsAndTestEmoji } from '../../../bin/versionsAndTestEmoji'
// only used in jest tests
// only used in jest/vitest tests
let simulateCanvasError = false
export function setSimulateCanvasError (value) {
simulateCanvasError = value
@ -42,7 +42,7 @@ const compareFeatures = (feature1, feature2) => {
}
export function testColorEmojiSupported (text) {
if (process.env.NODE_ENV === 'test') {
if (import.meta.env.MODE === 'test') {
if (simulateCanvasError) {
throw new Error('canvas error')
} else if (simulateOldBrowser) {
@ -51,7 +51,7 @@ export function testColorEmojiSupported (text) {
.map(([emoji]) => emoji)
.includes(text)
}
return true // avoid using canvas in jest
return true // avoid using canvas in jest/vitest
}
// Render white and black and then compare them to each other and ensure they're the same
// color, and neither one is black. This shows that the emoji was rendered in color.

View File

@ -6,7 +6,7 @@ import { requestAnimationFrame } from './requestAnimationFrame'
export let resizeObserverSupported = typeof ResizeObserver === 'function'
// only used in jest tests
// only used in jest/vitest tests
export const resetResizeObserverSupported = () => {
resizeObserverSupported = typeof ResizeObserver === 'function'
}

View File

@ -47,7 +47,6 @@
</head>
<body>
<button class="delete">Delete database</button>
<script src="/node_modules/focus-visible/dist/focus-visible.js"></script>
<script type="module">
import { Picker, Database } from '/index.js'
(async () => {
@ -89,7 +88,6 @@
}
const picker = new Picker(opts)
picker.addEventListener('emoji-click', e => console.log(e))
applyFocusVisiblePolyfill(picker.shadowRoot)
document.body.appendChild(picker)
})()
</script>

View File

@ -1,4 +1,4 @@
import { terser } from 'rollup-plugin-terser'
import terser from '@rollup/plugin-terser'
export default {
input: './index.js',

View File

@ -79,7 +79,7 @@ async function addAndRemovePicker (page) {
async function main () {
await waitForServerReady()
const browser = await puppeteer.launch({ headless: 'new' })
const context = await browser.createIncognitoBrowserContext() // not sure why Addy uses incognito, but sure
const context = await browser.createBrowserContext()
const page = await context.newPage()
await page.goto('http://localhost:3000/')

View File

@ -4,6 +4,7 @@ import {
ALL_EMOJI_NO_ETAG, tick, mockFrenchDataSource, FR_EMOJI, truncatedEmoji, NO_SHORTCODES, mockDataSourceWithNoShortcodes
} from '../shared'
import trimEmojiData from '../../../src/trimEmojiData'
import { mockFetch, mockGetAndHead } from '../mockFetch.js'
describe('database tests', () => {
beforeEach(basicBeforeEach)
@ -19,57 +20,67 @@ describe('database tests', () => {
test('calls GET first and HEAD afterwards', async () => {
let db = new Database({ dataSource: ALL_EMOJI })
await db.ready()
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(ALL_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
await db.close()
db = new Database({ dataSource: ALL_EMOJI })
await db.ready()
await tick(5) // the HEAD request is done asynchronously, so wait for it
expect(fetch).toHaveBeenCalledTimes(2)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI, { method: 'HEAD' })
expect(fetch.calls().length).toBe(2)
expect(fetch.lastUrl()).toBe(ALL_EMOJI)
expect(fetch.lastOptions()).toEqual({ method: 'HEAD' })
await db.delete()
})
test('calls GET first and tries HEAD if ETag unavailable', async () => {
let db = new Database({ dataSource: ALL_EMOJI_NO_ETAG })
await db.ready()
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI_NO_ETAG, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(ALL_EMOJI_NO_ETAG)
expect(fetch.lastOptions()).toBe(undefined)
await db.close()
db = new Database({ dataSource: ALL_EMOJI_NO_ETAG })
await db.ready()
await tick(5) // the request is done asynchronously, so wait for it
expect(fetch).toHaveBeenCalledTimes(3)
expect(fetch).toHaveBeenNthCalledWith(2, ALL_EMOJI_NO_ETAG, { method: 'HEAD' })
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI_NO_ETAG, undefined)
expect(fetch.calls().length).toBe(3)
expect(fetch.calls().at(-2)[0]).toBe(ALL_EMOJI_NO_ETAG)
expect(fetch.calls().at(-2)[1]).toEqual({ method: 'HEAD' })
expect(fetch.lastUrl()).toBe(ALL_EMOJI_NO_ETAG)
expect(fetch.lastOptions()).toBe(undefined)
await db.delete()
})
test('database deletion actually deletes and causes re-fetch', async () => {
let db = new Database({ dataSource: ALL_EMOJI })
await db.ready()
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(ALL_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
await db.delete()
db = new Database({ dataSource: ALL_EMOJI })
await db.ready()
expect(fetch).toHaveBeenCalledTimes(2)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI, undefined)
expect(fetch.calls().length).toBe(2)
expect(fetch.lastUrl()).toBe(ALL_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
await db.delete()
})
test('misconfigured server where ETag in GET but not HEAD still works', async () => {
let db = new Database({ dataSource: ALL_EMOJI_MISCONFIGURED_ETAG })
await db.ready()
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI_MISCONFIGURED_ETAG, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(ALL_EMOJI_MISCONFIGURED_ETAG)
expect(fetch.lastOptions()).toBe(undefined)
await db.close()
db = new Database({ dataSource: ALL_EMOJI_MISCONFIGURED_ETAG })
await db.ready()
await tick(5) // the request is done asynchronously, so wait for it
expect(fetch).toHaveBeenCalledTimes(3)
expect(fetch).toHaveBeenNthCalledWith(2, ALL_EMOJI_MISCONFIGURED_ETAG, { method: 'HEAD' })
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI_MISCONFIGURED_ETAG, undefined)
expect(fetch.calls().length).toBe(3)
expect(fetch.calls().at(-2)[0]).toBe(ALL_EMOJI_MISCONFIGURED_ETAG)
expect(fetch.calls().at(-2)[1]).toEqual({ method: 'HEAD' })
expect(fetch.lastUrl()).toBe(ALL_EMOJI_MISCONFIGURED_ETAG)
expect(fetch.lastOptions()).toBe(undefined)
await db.delete()
})
@ -79,11 +90,11 @@ describe('database tests', () => {
const EMPTY = 'empty.json'
const NULL_ARRAY = 'null-array.json'
const BAD_OBJECT = 'bad-object.json'
fetch.get(NULL, () => new Response('null'))
fetch.get(NOT_ARRAY, () => new Response('{}'))
fetch.get(EMPTY, () => new Response('[]'))
fetch.get(NULL_ARRAY, () => new Response('[null]'))
fetch.get(BAD_OBJECT, () => new Response('[{"missing": true}]'))
mockFetch('get', NULL, 'null')
mockFetch('get', NOT_ARRAY, '{}')
mockFetch('get', EMPTY, '[]')
mockFetch('get', NULL_ARRAY, '[null]')
mockFetch('get', BAD_OBJECT, '[{"missing": true}]')
const makeDB = async (dataSource) => {
const db = new Database({ dataSource })
@ -142,6 +153,7 @@ describe('database tests', () => {
await db1.ready()
const db2 = new Database({ dataSource: ALL_EMOJI })
await db2.ready()
await db2._lazyUpdate // TODO [#407] Skipping this causes an InvalidStateError in IDB
await db1.close()
expect((await db1.getEmojiByUnicodeOrName('🐵')).annotation).toBe('monkey face')
await db2.close()
@ -185,8 +197,7 @@ describe('database tests', () => {
test('basic trimEmojiData test', async () => {
const trimmed = trimEmojiData(truncatedEmoji)
const dataSource = 'trimmed.js'
fetch.get(dataSource, () => new Response(JSON.stringify(trimmed), { headers: { ETag: 'W/trim' } }))
fetch.head(dataSource, () => new Response(null, { headers: { ETag: 'W/trim' } }))
mockGetAndHead(dataSource, trimmed, { headers: { ETag: 'W/trim' } })
const db = new Database({ dataSource })
const emojis = await db.getEmojiBySearchQuery('face')

View File

@ -4,29 +4,32 @@ describe('basic fetch tests', () => {
beforeEach(basicBeforeEach)
afterEach(basicAfterEach)
test('make sure fetch-mock-jest is working correctly', async () => {
expect(fetch).toHaveBeenCalledTimes(0)
test('make sure fetch-mock is working correctly', async () => {
expect(fetch.calls().length).toBe(0)
const resp = await fetch(ALL_EMOJI)
expect(resp.headers.get('etag')).toBe('W/xxx')
expect(await (resp).json()).toEqual(truncatedEmoji)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(ALL_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
})
test('make sure fetch-mock-jest is working correctly 2', async () => {
expect(fetch).toHaveBeenCalledTimes(0)
test('make sure fetch-mock is working correctly 2', async () => {
expect(fetch.calls().length).toBe(0)
const resp = await fetch(ALL_EMOJI_NO_ETAG)
expect(resp.headers.get('etag')).toBeFalsy()
expect(await (resp).json()).toEqual(truncatedEmoji)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI_NO_ETAG, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(ALL_EMOJI_NO_ETAG)
expect(fetch.lastOptions()).toBe(undefined)
})
test('make sure fetch-mock-jest is working correctly 3', async () => {
expect(fetch).toHaveBeenCalledTimes(0)
test('make sure fetch-mock is working correctly 3', async () => {
expect(fetch.calls().length).toBe(0)
const resp = await fetch(ALL_EMOJI, { method: 'HEAD' })
expect(resp.headers.get('etag')).toBe('W/xxx')
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI, { method: 'HEAD' })
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(ALL_EMOJI)
expect(fetch.lastOptions()).toEqual({ method: 'HEAD' })
})
})

View File

@ -2,6 +2,7 @@ import allEmoji from 'emoji-picker-element-data/en/emojibase/data.json'
import Database from '../../../src/database/Database'
import { pick, omit } from 'lodash-es'
import { basicAfterEach, basicBeforeEach, ALL_EMOJI, truncatedEmoji } from '../shared'
import { mockGetAndHead } from '../mockFetch.js'
// order can change from version to version
const expectToBeSorted = results => {
@ -128,10 +129,7 @@ describe('getEmojiBySearchQuery', () => {
const EMOJI_WITH_APOS = 'http://localhost/apos.json'
fetch.get(EMOJI_WITH_APOS, () => new Response(JSON.stringify(emojiWithTwelveOclock), {
headers: { ETag: 'W/apos' }
}))
fetch.head(EMOJI_WITH_APOS, () => new Response(null, { headers: { ETag: 'W/apos' } }))
mockGetAndHead(EMOJI_WITH_APOS, emojiWithTwelveOclock, { headers: { ETag: 'W/apos' } })
const db = new Database({ dataSource: EMOJI_WITH_APOS })
@ -159,10 +157,7 @@ describe('getEmojiBySearchQuery', () => {
const EMOJI = 'http://localhost/apos.json'
fetch.get(EMOJI, () => new Response(JSON.stringify(emoji), {
headers: { ETag: 'W/blond' }
}))
fetch.head(EMOJI, () => new Response(null, { headers: { ETag: 'W/blond' } }))
mockGetAndHead(EMOJI, emoji, { headers: { ETag: 'W/blond' } })
const db = new Database({ dataSource: EMOJI })

View File

@ -1,5 +1,6 @@
import { ALL_EMOJI, basicAfterEach, basicBeforeEach, truncatedEmoji } from '../shared'
import Database from '../../../src/database/Database'
import { mockGetAndHead } from '../mockFetch.js'
describe('getEmojiByShortcode', () => {
beforeEach(basicBeforeEach)
@ -63,9 +64,7 @@ describe('getEmojiByShortcode', () => {
}
}
fetch
.get(dataSource, () => new Response(JSON.stringify(emojis), { headers: { ETag: 'W/optional' } }))
.head(dataSource, () => new Response(null, { headers: { ETag: 'W/optional' } }))
mockGetAndHead(dataSource, emojis, { headers: { ETag: 'W/optional' } })
const db = new Database({ dataSource })

View File

@ -1,6 +1,7 @@
import allEmoji from 'emoji-picker-element-data/en/emojibase/data.json'
import { ALL_EMOJI, basicAfterEach, basicBeforeEach, truncatedEmoji } from '../shared'
import Database from '../../../src/database/Database'
import { mockGetAndHead } from '../mockFetch.js'
describe('getEmojiByUnicode', () => {
beforeEach(basicBeforeEach)
@ -14,10 +15,7 @@ describe('getEmojiByUnicode', () => {
]
const EMOJI_WITH_PIRATES = 'http://localhost/pirate.json'
fetch.get(EMOJI_WITH_PIRATES, () => new Response(JSON.stringify(emojiPlusPirateFlag), {
headers: { ETag: 'W/yarrr' }
}))
fetch.head(EMOJI_WITH_PIRATES, () => new Response(null, { headers: { ETag: 'W/yarrr' } }))
mockGetAndHead(EMOJI_WITH_PIRATES, emojiPlusPirateFlag, { headers: { ETag: 'W/yarrr' } })
const db = new Database({ dataSource: EMOJI_WITH_PIRATES })

View File

@ -1,22 +1,21 @@
import { jest } from '@jest/globals'
import { vi } from 'vitest'
import { ALL_EMOJI, basicAfterEach, basicBeforeEach } from '../shared'
import Database from '../../../src/database/Database'
import { mock500GetAndHead } from '../mockFetch.js'
describe('offline first', () => {
beforeEach(() => {
basicBeforeEach()
jest.spyOn(console, 'warn').mockImplementation(() => {})
vi.spyOn(console, 'warn').mockImplementation(() => undefined)
})
afterEach(basicAfterEach)
test('basic offline first test', async () => {
let db = new Database({ dataSource: ALL_EMOJI })
await db.close()
fetch.mockClear()
fetch.reset()
fetch.get(ALL_EMOJI, { body: null, status: 500 })
fetch.head(ALL_EMOJI, { body: null, status: 500 })
mock500GetAndHead(ALL_EMOJI)
db = new Database({ dataSource: ALL_EMOJI })
await db.ready()
@ -28,8 +27,7 @@ describe('offline first', () => {
test('basic error test', async () => {
const ERROR = 'error.json'
fetch.get(ERROR, { body: null, status: 500 })
fetch.head(ERROR, { body: null, status: 500 })
mock500GetAndHead(ERROR)
const db = new Database({ dataSource: ERROR })
await (expect(() => db.ready())).rejects.toThrow()

View File

@ -1,12 +1,11 @@
import { ALL_EMOJI, ALL_EMOJI_NO_ETAG, basicAfterEach, basicBeforeEach, tick, truncatedEmoji } from '../shared'
import Database from '../../../src/database/Database'
import allEmoji from 'emoji-picker-element-data/en/emojibase/data.json'
import { mockGetAndHead } from '../mockFetch.js'
function mockEmoji (dataSource, data, etag) {
fetch.mockClear()
fetch.reset()
fetch.get(dataSource, () => new Response(JSON.stringify(data), etag && { headers: { ETag: etag } }))
fetch.head(dataSource, () => new Response(null, etag && { headers: { ETag: etag } }))
mockGetAndHead(dataSource, data, etag && { headers: { ETag: etag } })
}
async function testDataChange (firstData, secondData, firstCallback, secondCallback, thirdCallback) {
@ -51,21 +50,25 @@ describe('database second load and update', () => {
await testDataChange(truncatedEmoji, changedEmoji, async (db, dataSource) => {
// first load
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(dataSource, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(dataSource)
expect(fetch.lastOptions()).toBe(undefined)
expect((await db.getEmojiByShortcode('rofl')).annotation).toBe('rolling on the floor laughing')
expect(await db.getEmojiByShortcode('weary_cat')).toBeFalsy()
}, async (db, dataSource) => {
// second load
expect(fetch).toHaveBeenCalledTimes(2)
expect(fetch).toHaveBeenLastCalledWith(dataSource, undefined)
expect(fetch).toHaveBeenNthCalledWith(1, dataSource, { method: 'HEAD' })
expect(fetch.calls().length).toBe(2)
expect(fetch.lastUrl()).toBe(dataSource)
expect(fetch.lastOptions()).toBe(undefined)
expect(fetch.calls().at(-2)[0]).toBe(dataSource)
expect(fetch.calls().at(-2)[1]).toEqual({ method: 'HEAD' })
expect((await db.getEmojiByShortcode('rofl'))).toBeFalsy()
expect((await db.getEmojiByShortcode('pineapple')).annotation).toBe('pineapple')
}, async (db, dataSource) => {
// third load
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(dataSource, { method: 'HEAD' })
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(dataSource)
expect(fetch.lastOptions()).toEqual({ method: 'HEAD' })
expect((await db.getEmojiByShortcode('rofl'))).toBeFalsy()
expect((await db.getEmojiByShortcode('pineapple')).annotation).toBe('pineapple')
})
@ -133,8 +136,9 @@ describe('database second load and update', () => {
let db = new Database({ dataSource })
await db.ready()
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(dataSource, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(dataSource)
expect(fetch.lastOptions()).toBe(undefined)
expect((await db.getEmojiByShortcode('rofl')).annotation).toBe('rolling on the floor laughing')
expect(await db.getEmojiByShortcode('weary_cat')).toBeFalsy()
@ -151,9 +155,11 @@ describe('database second load and update', () => {
db = new Database({ dataSource })
await db.ready()
await db._lazyUpdate
expect(fetch).toHaveBeenCalledTimes(2)
expect(fetch).toHaveBeenLastCalledWith(dataSource, undefined)
expect(fetch).toHaveBeenNthCalledWith(1, dataSource, { method: 'HEAD' })
expect(fetch.calls().length).toBe(2)
expect(fetch.lastUrl()).toBe(dataSource)
expect(fetch.lastOptions()).toBe(undefined)
expect(fetch.calls().at(-2)[0]).toBe(dataSource)
expect(fetch.calls().at(-2)[1]).toEqual({ method: 'HEAD' })
expect((await db.getEmojiByShortcode('rofl'))).toBeFalsy()
expect((await db.getEmojiByShortcode('pineapple')).annotation).toBe('pineapple')
await db.close()
@ -164,9 +170,11 @@ describe('database second load and update', () => {
db = new Database({ dataSource })
await db.ready()
await db._lazyUpdate
expect(fetch).toHaveBeenCalledTimes(2)
expect(fetch).toHaveBeenLastCalledWith(dataSource, undefined)
expect(fetch).toHaveBeenNthCalledWith(1, dataSource, { method: 'HEAD' })
expect(fetch.calls().length).toBe(2)
expect(fetch.lastUrl()).toBe(dataSource)
expect(fetch.lastOptions()).toBe(undefined)
expect(fetch.calls().at(-2)[0]).toBe(dataSource)
expect(fetch.calls().at(-2)[1]).toEqual({ method: 'HEAD' })
expect((await db.getEmojiByShortcode('rofl'))).toBeFalsy()
expect((await db.getEmojiByShortcode('pineapple')).annotation).toBe('pineapple')
await db.delete()
@ -177,13 +185,13 @@ describe('database second load and update', () => {
const dataSource2 = 'http://localhost/will-change2.json'
// first time - data is v1
fetch.get(dataSource, () => new Response(JSON.stringify(truncatedEmoji), { headers: { ETag: 'W/xxx' } }))
fetch.head(dataSource, () => new Response(null, { headers: { ETag: 'W/xxx' } }))
mockGetAndHead(dataSource, truncatedEmoji, { headers: { ETag: 'W/xxx' } })
let db = new Database({ dataSource })
await db.ready()
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(dataSource, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(dataSource)
expect(fetch.lastOptions()).toBe(undefined)
expect((await db.getEmojiByShortcode('rofl')).annotation).toBe('rolling on the floor laughing')
expect(await db.getEmojiByShortcode('weary_cat')).toBeFalsy()
@ -195,31 +203,30 @@ describe('database second load and update', () => {
changedEmoji[roflIndex] = allEmoji.find(_ => _.annotation === 'pineapple') // replace rofl
// second time - update, data is v2
fetch.mockClear()
fetch.reset()
fetch.get(dataSource2, () => new Response(JSON.stringify(changedEmoji), { headers: { ETag: 'W/yyy' } }))
fetch.head(dataSource2, () => new Response(null, { headers: { ETag: 'W/yyy' } }))
mockGetAndHead(dataSource2, changedEmoji, { headers: { ETag: 'W/yyy' } })
db = new Database({ dataSource: dataSource2 })
await db.ready()
await db._lazyUpdate
expect(fetch).toHaveBeenCalledTimes(2)
expect(fetch).toHaveBeenLastCalledWith(dataSource2, undefined)
expect(fetch).toHaveBeenNthCalledWith(1, dataSource2, { method: 'HEAD' })
expect(fetch.calls().length).toBe(2)
expect(fetch.lastUrl()).toBe(dataSource2)
expect(fetch.lastOptions()).toBe(undefined)
expect(fetch.calls().at(-2)[0]).toBe(dataSource2)
expect(fetch.calls().at(-2)[1]).toEqual({ method: 'HEAD' })
expect((await db.getEmojiByShortcode('rofl'))).toBeFalsy()
expect((await db.getEmojiByShortcode('pineapple')).annotation).toBe('pineapple')
// third time - no update, data is v2
fetch.mockClear()
fetch.reset()
fetch.get(dataSource2, () => new Response(JSON.stringify(changedEmoji), { headers: { ETag: 'W/yyy' } }))
fetch.head(dataSource2, () => new Response(null, { headers: { ETag: 'W/yyy' } }))
mockGetAndHead(dataSource2, changedEmoji, { headers: { ETag: 'W/yyy' } })
db = new Database({ dataSource: dataSource2 })
await db.ready()
await db._lazyUpdate
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(dataSource2, { method: 'HEAD' })
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(dataSource2)
expect(fetch.lastOptions()).toEqual({ method: 'HEAD' })
expect((await db.getEmojiByShortcode('rofl'))).toBeFalsy()
expect((await db.getEmojiByShortcode('pineapple')).annotation).toBe('pineapple')
@ -236,9 +243,11 @@ describe('database second load and update', () => {
db = new Database({ dataSource: otherSource })
await db.ready()
await tick(5) // the request is done asynchronously, so wait for it
expect(fetch).toHaveBeenCalledTimes(2)
expect(fetch).toHaveBeenNthCalledWith(1, otherSource, { method: 'HEAD' })
expect(fetch).toHaveBeenLastCalledWith(otherSource, undefined)
expect(fetch.calls().length).toBe(2)
expect(fetch.calls().at(-2)[0]).toBe(otherSource)
expect(fetch.calls().at(-2)[1]).toEqual({ method: 'HEAD' })
expect(fetch.lastUrl()).toBe(otherSource)
expect(fetch.lastOptions()).toBe(undefined)
await db.delete()
})
@ -251,10 +260,8 @@ describe('database second load and update', () => {
changedEmoji[roflIndex] = allEmoji.find(_ => _.annotation === 'pineapple') // replace rofl
// second time - update, data is v2
fetch.mockClear()
fetch.reset()
fetch.get(ALL_EMOJI, () => new Response(JSON.stringify(changedEmoji), { headers: { ETag: 'W/yyy' } }))
fetch.head(ALL_EMOJI, () => new Response(null, { headers: { ETag: 'W/yyy' } }))
mockGetAndHead(ALL_EMOJI, changedEmoji, { headers: { ETag: 'W/yyy' } })
// open two at once
const dbs = [

31
test/spec/mockFetch.js Normal file
View File

@ -0,0 +1,31 @@
// centralize all our fetch mocks in one place so we can have
// consistent timeouts, and smooth over some of the boilerplate
export function mockFetch (method, url, response, { headers, delay } = {}) {
let responseToUse
if (!response) {
responseToUse = null
} else if (typeof response === 'string') {
responseToUse = response
} else {
responseToUse = JSON.stringify(response)
}
fetch[method](
url,
() => new Response(responseToUse, { headers }),
// use a delay of 1 because it's more realistic than a fetch() that resolves in a microtask
{ delay: typeof delay === 'number' ? delay : 1 }
)
}
// convenience util for mocking a typical get and a head
export function mockGetAndHead (url, response, options = {}) {
mockFetch('get', url, response, options)
mockFetch('head', url, null, options)
}
export function mock500GetAndHead (url) {
fetch.get(url, { body: null, status: 500 })
fetch.head(url, { body: null, status: 500 })
}

View File

@ -31,8 +31,9 @@ describe('attributes tests', () => {
document.body.appendChild(picker)
await tick(20)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(FR_EMOJI, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(FR_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
expect(picker.locale).toEqual('fr')
expect(picker.dataSource).toEqual(FR_EMOJI)
@ -169,8 +170,9 @@ describe('attributes tests', () => {
expect(getByRole(picker.shadowRoot, 'button', { name: /Choose a skin tone/ }).innerHTML)
.toContain('✌')
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(ALL_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
document.body.removeChild(div)
await tick(20)

View File

@ -16,8 +16,9 @@ describe('constructor', () => {
await waitFor(() => expect(getByRole(container, 'menuitem', { name: /😀/ })).toBeVisible())
expect(getByRole(container, 'menuitem', { name: /😀/ })).toBeVisible()
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(DEFAULT_DATA_SOURCE, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(DEFAULT_DATA_SOURCE)
expect(fetch.lastOptions()).toBe(undefined)
document.body.removeChild(picker)
await tick(20)

View File

@ -13,6 +13,7 @@ import enI18n from '../../../src/picker/i18n/en'
import Database from '../../../src/database/Database'
import { DEFAULT_SKIN_TONE_EMOJI } from '../../../src/picker/constants'
import { DEFAULT_DATA_SOURCE } from '../../../src/database/constants'
import { mockGetAndHead } from '../mockFetch.js'
const { type } = userEvent
// Workaround for clear() not working in shadow roots: https://github.com/testing-library/user-event/issues/1143
@ -53,8 +54,9 @@ describe('element tests', () => {
test('changing locale/dataSource prop causes only one network request', async () => {
await tick(120)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(ALL_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
await type(getByRole(container, 'combobox'), 'monkey face')
await waitFor(() => expect(getAllByRole(container, 'option')).toHaveLength(1), {
timeout: 2000
@ -64,8 +66,9 @@ describe('element tests', () => {
picker.locale = 'fr'
picker.dataSource = FR_EMOJI
await tick(120)
expect(fetch).toHaveBeenCalledTimes(2)
expect(fetch).toHaveBeenLastCalledWith(FR_EMOJI, undefined)
expect(fetch.calls().length).toBe(2)
expect(fetch.lastUrl()).toBe(FR_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
await clear(getByRole(container, 'combobox'))
await type(getByRole(container, 'combobox'), 'singe tête')
await waitFor(() => expect(getAllByRole(container, 'option')).toHaveLength(1))
@ -74,8 +77,9 @@ describe('element tests', () => {
test('changing locale/dataSource attr causes only one network request', async () => {
await tick(120)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(ALL_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
await type(getByRole(container, 'combobox'), 'monkey face')
await waitFor(() => expect(getAllByRole(container, 'option')).toHaveLength(1), {
timeout: 2000
@ -85,8 +89,9 @@ describe('element tests', () => {
picker.setAttribute('locale', 'fr')
picker.setAttribute('data-source', FR_EMOJI)
await tick(120)
expect(fetch).toHaveBeenCalledTimes(2)
expect(fetch).toHaveBeenLastCalledWith(FR_EMOJI, undefined)
expect(fetch.calls().length).toBe(2)
expect(fetch.lastUrl()).toBe(FR_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
await clear(getByRole(container, 'combobox'))
await type(getByRole(container, 'combobox'), 'singe tête')
await waitFor(() => expect(getAllByRole(container, 'option')).toHaveLength(1))
@ -119,8 +124,7 @@ describe('element tests', () => {
describe('defaults test', () => {
beforeEach(() => {
fetch.get(DEFAULT_DATA_SOURCE, () => new Response(JSON.stringify(truncatedEmoji), { headers: { ETag: 'W/aaa' } }))
fetch.head(DEFAULT_DATA_SOURCE, () => new Response(null, { headers: { ETag: 'W/aaa' } }))
mockGetAndHead(DEFAULT_DATA_SOURCE, truncatedEmoji, { headers: { ETag: 'W/aaa' } })
})
afterEach(basicAfterEach)

View File

@ -1,15 +1,16 @@
import { jest } from '@jest/globals'
import { vi } from 'vitest'
import Picker from '../../../src/picker/PickerElement'
import { ALL_EMOJI, basicAfterEach, basicBeforeEach, tick, truncatedEmoji } from '../shared'
import Database from '../../../src/database/Database'
import { getByRole, waitFor } from '@testing-library/dom'
import { mock500GetAndHead, mockGetAndHead } from '../mockFetch.js'
describe('errors', () => {
let errorSpy
beforeEach(async () => {
await basicBeforeEach()
errorSpy = jest.spyOn(global.console, 'error').mockImplementation()
errorSpy = vi.spyOn(global.console, 'error').mockImplementation(() => undefined)
await tick(40)
})
afterEach(async () => {
@ -19,7 +20,6 @@ describe('errors', () => {
await tick(40)
})
// seems not possible to do
test('throws error when setting the database', async () => {
const picker = new Picker({ dataSource: ALL_EMOJI, locale: 'en' })
document.body.appendChild(picker)
@ -32,12 +32,10 @@ describe('errors', () => {
await tick(20)
})
// can't seem to get jest to ignore these expected errors
test('offline shows an error', async () => {
const dataSource = 'error.json'
fetch.get(dataSource, { body: null, status: 500 })
fetch.head(dataSource, { body: null, status: 500 })
mock500GetAndHead(dataSource)
const picker = new Picker({ dataSource })
const container = picker.shadowRoot
@ -57,10 +55,7 @@ describe('errors', () => {
test('slow networks show "Loading"', async () => {
const dataSource = 'slow.json'
fetch.get(dataSource, () => new Response(JSON.stringify(truncatedEmoji), { headers: { ETag: 'W/slow' } }),
{ delay: 1500 })
fetch.head(dataSource, () => new Response(null, { headers: { ETag: 'W/slow' } }),
{ delay: 1500 })
mockGetAndHead(dataSource, truncatedEmoji, { headers: { ETag: 'W/slow' }, delay: 1500 })
const picker = new Picker({ dataSource })
const container = picker.shadowRoot

View File

@ -8,6 +8,7 @@ import allData from 'emoji-picker-element-data/en/emojibase/data.json'
import { MOST_COMMONLY_USED_EMOJI } from '../../../src/picker/constants'
import { uniqBy } from '../../../src/shared/uniqBy'
import { groups } from '../../../src/picker/groups'
import { mockGetAndHead } from '../mockFetch.js'
const dataSource = 'with-favs.json'
@ -23,8 +24,7 @@ describe('Favorites UI', () => {
...allData.filter(_ => MOST_COMMONLY_USED_EMOJI.includes(_.emoji))
], _ => _.emoji)
fetch.get(dataSource, () => new Response(JSON.stringify(dataWithFavorites), { headers: { ETag: 'W/favs' } }))
fetch.head(dataSource, () => new Response(null, { headers: { ETag: 'W/favs' } }))
mockGetAndHead(dataSource, dataWithFavorites, { headers: { ETag: 'W/favs' } })
picker = new Picker({ dataSource, locale: 'en' })
document.body.appendChild(picker)

View File

@ -58,7 +58,26 @@ describe('framework', () => {
expect(node.outerHTML).toBe('<div><span>foo</span></div>')
})
test('render two dynamic expressions inside the same element', () => {
test('dynamic expression with whitespace around it - minifier should be working', () => {
const state = { name: 'foo' }
const { html } = createFramework(state)
let node
const render = () => {
node = html`<div> ${state.name}\t\n</div>`
}
render()
expect(node.outerHTML).toBe('<div>foo</div>')
state.name = 'baz'
render()
expect(node.outerHTML).toBe('<div>baz</div>')
})
// Framework no longer supports this since we switched from HTML comments to text nodes
test.skip('render two dynamic expressions inside the same element', () => {
const state = { name1: 'foo', name2: 'bar' }
const { html } = createFramework(state)
@ -77,7 +96,8 @@ describe('framework', () => {
expect(node.outerHTML).toBe('<div>bazquux</div>')
})
test('render a mix of dynamic and static text nodes in the same element', () => {
// Framework no longer supports this since we switched from HTML comments to text nodes
test.skip('render a mix of dynamic and static text nodes in the same element', () => {
const state = { name1: 'foo', name2: 'bar' }
const { html } = createFramework(state)

View File

@ -1,4 +1,4 @@
import { jest } from '@jest/globals'
import { vi } from 'vitest'
import { basicAfterEach, basicBeforeEach, tick } from '../shared'
import Picker from '../../../src/picker/PickerElement'
import { getByRole, waitFor } from '@testing-library/dom'
@ -20,8 +20,9 @@ describe('lifecycle', () => {
await waitFor(() => expect(getByRole(container, 'menuitem', { name: /😀/ })).toBeVisible())
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(DEFAULT_DATA_SOURCE, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(DEFAULT_DATA_SOURCE)
expect(fetch.lastOptions()).toBe(undefined)
document.body.removeChild(picker)
await tick(40)
@ -30,8 +31,9 @@ describe('lifecycle', () => {
await waitFor(() => expect(getByRole(container, 'menuitem', { name: /😀/ })).toBeVisible())
// fetch is called once again after re-insertion
expect(fetch).toHaveBeenCalledTimes(2)
expect(fetch).toHaveBeenLastCalledWith(DEFAULT_DATA_SOURCE, { method: 'HEAD' })
expect(fetch.calls().length).toBe(2)
expect(fetch.lastUrl()).toBe(DEFAULT_DATA_SOURCE)
expect(fetch.lastOptions()).toEqual({ method: 'HEAD' })
document.body.removeChild(picker)
await tick(60)
@ -45,7 +47,7 @@ describe('lifecycle', () => {
await waitFor(() => expect(getByRole(container, 'menuitem', { name: /😀/ })).toBeVisible())
const spy = jest.spyOn(picker.database, 'close')
const spy = vi.spyOn(picker.database, 'close')
document.body.removeChild(picker)
await tick(60)
@ -64,8 +66,9 @@ describe('lifecycle', () => {
await tick(60)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(DEFAULT_DATA_SOURCE, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(DEFAULT_DATA_SOURCE)
expect(fetch.lastOptions()).toBe(undefined)
expect(Object.keys(openIndexedDBRequests).length).toBe(0) // no open IDB connections
})
@ -78,8 +81,9 @@ describe('lifecycle', () => {
await tick(120)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(DEFAULT_DATA_SOURCE, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(DEFAULT_DATA_SOURCE)
expect(fetch.lastOptions()).toBe(undefined)
expect(Object.keys(openIndexedDBRequests).length).toBe(0) // no open IDB connections
})
@ -105,8 +109,9 @@ describe('lifecycle', () => {
document.body.appendChild(picker)
await tick(40)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(DEFAULT_DATA_SOURCE, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(DEFAULT_DATA_SOURCE)
expect(fetch.lastOptions()).toBe(undefined)
await expect(() => (
expect(getByRole(picker.shadowRoot, 'option', { name: /😀/ })).toBeVisible()
))
@ -116,7 +121,7 @@ describe('lifecycle', () => {
await tick(40)
expect(fetch).toHaveBeenCalledTimes(1) // fetch is not called again because no re-render
expect(fetch.calls().length).toBe(1) // fetch is not called again because no re-render
await expect(() => (
expect(getByRole(picker.shadowRoot, 'option', { name: /😀/ })).toBeVisible()
))
@ -132,8 +137,9 @@ describe('lifecycle', () => {
document.body.appendChild(picker)
await tick(40)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(DEFAULT_DATA_SOURCE, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(DEFAULT_DATA_SOURCE)
expect(fetch.lastOptions()).toBe(undefined)
await expect(() => (
expect(getByRole(picker.shadowRoot, 'option', { name: /😀/ })).toBeVisible()
))
@ -144,8 +150,9 @@ describe('lifecycle', () => {
await tick(40)
expect(fetch).toHaveBeenCalledTimes(2) // fetch is called again due to re-render
expect(fetch).toHaveBeenLastCalledWith(DEFAULT_DATA_SOURCE, { method: 'HEAD' }) // cached, so does a HEAD
expect(fetch.calls().length).toBe(2) // fetch is called again due to re-render
expect(fetch.lastUrl()).toBe(DEFAULT_DATA_SOURCE)
expect(fetch.lastOptions()).toEqual({ method: 'HEAD' }) // cached, so does a HEAD
await expect(() => (
expect(getByRole(picker.shadowRoot, 'option', { name: /😀/ })).toBeVisible()
))
@ -166,8 +173,9 @@ describe('lifecycle', () => {
await tick(40)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(DEFAULT_DATA_SOURCE, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(DEFAULT_DATA_SOURCE)
expect(fetch.lastOptions()).toBe(undefined)
await expect(() => (
expect(getByRole(picker.shadowRoot, 'option', { name: /😀/ })).toBeVisible()
))

View File

@ -23,8 +23,9 @@ describe('properties', () => {
await tick(40)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(FR_EMOJI, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(FR_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
expect(picker.locale).toEqual('fr')
expect(picker.dataSource).toEqual(FR_EMOJI)
@ -43,8 +44,9 @@ describe('properties', () => {
await tick(40)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(FR_EMOJI, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(FR_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
expect(picker.locale).toEqual('en')
expect(picker.dataSource).toEqual(FR_EMOJI)
@ -63,8 +65,9 @@ describe('properties', () => {
await tick(40)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(DEFAULT_DATA_SOURCE, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(DEFAULT_DATA_SOURCE)
expect(fetch.lastOptions()).toBe(undefined)
expect(picker.locale).toEqual('fr')
expect(picker.dataSource).toEqual(DEFAULT_DATA_SOURCE)

View File

@ -29,7 +29,7 @@ describe('upgrade tests', () => {
await tick(20)
expect(fetch).not.toHaveBeenCalled()
expect(fetch.calls().length).toBe(0)
await import('../../../src/picker/PickerElement')
@ -37,8 +37,9 @@ describe('upgrade tests', () => {
const container = picker.shadowRoot
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(FR_EMOJI, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(FR_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
expect(getByRole(container, 'button', { name: /Choose a skin tone/ }).innerHTML).toContain('👍')

View File

@ -2,6 +2,7 @@ import allEmoji from 'emoji-picker-element-data/en/emojibase/data.json'
import frEmoji from 'emoji-picker-element-data/fr/cldr/data.json'
import allEmojibaseV5Emoji from 'emojibase-data/en/data.json'
import { DEFAULT_DATA_SOURCE } from '../../src/database/constants'
import { mockFetch, mockGetAndHead } from './mockFetch.js'
export function truncateEmoji (allEmoji) {
// just take the first few emoji from each category, or else it takes forever to insert
@ -36,25 +37,14 @@ export const EMOJIBASE_V5 = 'http://localhost/emojibase'
export const WITH_ARRAY_SKIN_TONES = 'http://localhost/with-array-skin-tones'
export function basicBeforeEach () {
fetch
.get(ALL_EMOJI, () => new Response(JSON.stringify(truncatedEmoji), {
headers: { ETag: 'W/xxx' }
}))
.head(ALL_EMOJI, () => new Response(null, {
headers: { ETag: 'W/xxx' }
}))
.get(ALL_EMOJI_NO_ETAG, truncatedEmoji)
.head(ALL_EMOJI_NO_ETAG, () => new Response(null))
.get(ALL_EMOJI_MISCONFIGURED_ETAG, () => new Response(JSON.stringify(truncatedEmoji), {
headers: { ETag: 'W/xxx' }
}))
.head(ALL_EMOJI_MISCONFIGURED_ETAG, () => new Response(null))
.get(DEFAULT_DATA_SOURCE, () => new Response(JSON.stringify(truncatedEmoji), { headers: { ETag: 'W/def' } }))
.head(DEFAULT_DATA_SOURCE, () => new Response(null, { headers: { ETag: 'W/def' } }))
mockGetAndHead(ALL_EMOJI, truncatedEmoji, { headers: { ETag: 'W/xxx' } })
mockGetAndHead(ALL_EMOJI_NO_ETAG, truncatedEmoji)
mockGetAndHead(DEFAULT_DATA_SOURCE, truncatedEmoji, { headers: { ETag: 'W/def' } })
mockFetch('get', ALL_EMOJI_MISCONFIGURED_ETAG, truncatedEmoji, { headers: { ETag: 'W/xxx' } })
mockFetch('head', ALL_EMOJI_MISCONFIGURED_ETAG, null)
}
export async function basicAfterEach () {
fetch.mockClear()
fetch.reset()
await tick(20)
}
@ -66,8 +56,7 @@ export async function tick (times = 1) {
}
export function mockFrenchDataSource () {
fetch.get(FR_EMOJI, () => new Response(JSON.stringify(truncatedFrEmoji), { headers: { ETag: 'W/zzz' } }))
fetch.head(FR_EMOJI, () => new Response(null, { headers: { ETag: 'W/zzz' } }))
mockGetAndHead(FR_EMOJI, truncatedFrEmoji, { headers: { ETag: 'W/zzz' } })
}
export function mockDataSourceWithNoShortcodes () {
@ -76,22 +65,16 @@ export function mockDataSourceWithNoShortcodes () {
delete res.shortcodes
return res
})
fetch.get(NO_SHORTCODES, () => new Response(JSON.stringify(noShortcodeEmoji), { headers: { ETag: 'W/noshort' } }))
fetch.head(NO_SHORTCODES, () => new Response(null, { headers: { ETag: 'W/noshort' } }))
mockGetAndHead(NO_SHORTCODES, noShortcodeEmoji, { headers: { ETag: 'W/noshort' } })
}
export function mockEmojibaseV5DataSource () {
fetch.get(EMOJIBASE_V5, () => new Response(JSON.stringify(emojibaseV5Emoji), { headers: { ETag: 'W/emojibase' } }))
fetch.head(EMOJIBASE_V5, () => new Response(null, { headers: { ETag: 'W/emojibase' } }))
mockGetAndHead(EMOJIBASE_V5, emojibaseV5Emoji, { headers: { ETag: 'W/emojibase' } })
}
export function mockDataSourceWithArraySkinTones () {
const emojis = JSON.parse(JSON.stringify(truncatedEmoji))
emojis.push(allEmoji.find(_ => _.annotation === 'people holding hands')) // has two skin tones, one for each person
fetch
.get(WITH_ARRAY_SKIN_TONES, () => (
new Response(JSON.stringify(emojis), { headers: { ETag: 'W/noshort' } }))
)
.head(WITH_ARRAY_SKIN_TONES, () => new Response(null, { headers: { ETag: 'W/noshort' } }))
mockGetAndHead(WITH_ARRAY_SKIN_TONES, emojis, { headers: { ETag: 'W/noshort' } })
}

30
vitest.config.js Normal file
View File

@ -0,0 +1,30 @@
import { defineConfig } from 'vitest/config'
import { minifyHtmlLiteralsRollupPlugin } from './config/minifyHtmlLiteralsRollupPlugin.js'
import { buildStylesRollupPlugin } from './config/buildStylesRollupPlugin.js'
export default defineConfig({
plugins: [
minifyHtmlLiteralsRollupPlugin(),
buildStylesRollupPlugin()
],
test: {
globals: true,
environment: 'jsdom',
setupFiles: [
'./config/vitest.setup.js'
],
testTimeout: 60000,
coverage: {
provider: 'istanbul',
include: [
'src/'
],
thresholds: {
statements: 100,
branches: 100,
functions: 100,
lines: 100
}
}
}
})

8474
yarn.lock

File diff suppressed because it is too large Load Diff