Add download multiple support

This commit is contained in:
LeoKu 2022-06-12 14:38:40 +08:00
parent 7e865e8bd4
commit 9bd0a50f23
17 changed files with 437 additions and 77 deletions

View File

@ -4,10 +4,13 @@
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="A pure front-end avatar generator." />
<meta
name="description"
content="An online avatar generator just for fun. Made with Vue3 and Vite."
/>
<meta
name="keywords"
content="vector avatar,illustrations,avatar generator,avatar creator,fun avatar,随机头像,卡通头像在线免费生成工具"
content="vector avatar,illustrations,avatar generator,avatar creator,fun avatar,随机头像,卡通头像在线免费生成工具,头像生成器"
/>
<meta content="dark" name="color-scheme" />

View File

@ -22,6 +22,8 @@
"canvas-confetti": "^1.4.0",
"clipboard": "^2.0.8",
"html2canvas": "^1.3.2",
"jszip": "^3.10.0",
"object-hash": "^3.0.0",
"perfect-scrollbar": "^1.5.2",
"vue": "^3.2.30",
"vue-i18n": "^9.2.0-beta.9",
@ -33,6 +35,7 @@
"@babel/preset-typescript": "^7.16.7",
"@types/canvas-confetti": "^1.4.2",
"@types/jest": "^27.0.2",
"@types/object-hash": "^2.2.1",
"@typescript-eslint/eslint-plugin": "^5.11.0",
"@typescript-eslint/parser": "^5.11.0",
"@vitejs/plugin-vue": "^2.1.0",

View File

@ -20,11 +20,17 @@
<ActionBar @action="handleAction" />
<div class="action-group">
<button class="action-randomize" @click="handleGenerate">
<button
type="button"
class="action-btn action-randomize"
@click="handleGenerate"
>
{{ t('action.randomize') }}
</button>
<button
class="action-download"
type="button"
class="action-btn action-download"
:disabled="downloading"
@click="handleDownload"
>
@ -34,6 +40,14 @@
: t('action.download')
}}
</button>
<button
type="button"
class="action-btn action-multiple"
@click="() => generateMultiple()"
>
{{ t('action.downloadMultiple') }}
</button>
</div>
</div>
@ -57,6 +71,12 @@
</div>
</Container>
<BatchDownloadModal
:visible="avatarListVisible"
:avatar-list="avatarList"
@close=";(avatarListVisible = false), (avatarList = [])"
/>
<Sider>
<Configurator />
</Sider>
@ -64,13 +84,14 @@
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { ref, watchEffect } from 'vue'
import { useI18n } from 'vue-i18n'
import ActionBar from '@/components/ActionBar.vue'
import CodeModal from '@/components/CodeModal.vue'
import Configurator from '@/components/Configurator.vue'
import DownloadModal from '@/components/DownloadModal.vue'
import BatchDownloadModal from '@/components/Modal/BatchDownloadModal.vue'
import CodeModal from '@/components/Modal/CodeModal.vue'
import DownloadModal from '@/components/Modal/DownloadModal.vue'
import VueColorAvatar, {
type VueColorAvatarRef,
} from '@/components/VueColorAvatar.vue'
@ -94,7 +115,9 @@ import {
} from '@/utils/constant'
import { recordEvent } from '@/utils/ga'
import { name as appName } from '../package.json'
import ConfettiCanvas from './components/ConfettiCanvas.vue'
import type { AvatarOption } from './types'
const store = useStore()
@ -152,7 +175,7 @@ async function handleDownload() {
} else {
const trigger = document.createElement('a')
trigger.href = dataURL
trigger.download = 'vue-color-avatar.png'
trigger.download = `${appName}.png`
trigger.click()
}
}
@ -205,6 +228,44 @@ function handleAction(actionType: ActionType) {
break
}
}
const avatarListVisible = ref(false)
const avatarList = ref<AvatarOption[]>([])
watchEffect(() => {
avatarListVisible.value =
Array.isArray(avatarList.value) && avatarList.value.length > 0
})
async function generateMultiple(count = 5 * 6) {
const { default: hash } = await import('object-hash')
const avatarMap = [...Array(count)].reduce<Map<string, AvatarOption>>(
(res) => {
let randomAvatarOption: AvatarOption
let hashKey: string
do {
randomAvatarOption = getRandomAvatarOption(avatarOption.value)
hashKey = hash.sha1(randomAvatarOption)
} while (
randomAvatarOption.background.color === 'transparent' ||
res.has(hashKey)
)
res.set(hashKey, randomAvatarOption)
return res
},
new Map()
)
avatarList.value = Array.from(avatarMap.values())
recordEvent('click_generate_multiple', {
event_category: 'click',
})
}
</script>
<style lang="scss" scoped>
@ -255,12 +316,17 @@ function handleAction(actionType: ActionType) {
align-items: center;
justify-content: center;
margin-top: 4rem;
column-gap: 1rem;
.action-randomize,
.action-download {
@supports not (column-gap: 1rem) {
.action-btn {
margin: 0 0.5rem;
}
}
.action-btn {
min-width: 6rem;
height: 2.5rem;
margin: 0 1rem;
padding: 0 1rem;
color: var.$color-text;
font-weight: bold;
@ -280,6 +346,12 @@ function handleAction(actionType: ActionType) {
cursor: default;
}
}
@media screen and (max-width: var.$screen-sm) {
.action-multiple {
display: none;
}
}
}
}

View File

@ -10,7 +10,7 @@
:title="t(`wrapperShape.${wrapperShape}`)"
@click="switchWrapperShape(wrapperShape)"
>
<i
<div
class="shape"
:class="[
wrapperShape,

View File

@ -0,0 +1,183 @@
<template>
<ModalWrapper :visible="props.visible" @close="emit('close')">
<div class="container">
<div class="top-bar">
<div>{{ t('text.downloadMultipleTip') }}</div>
<div class="right">
<button
type="button"
:disabled="making"
class="download-btn"
@click="make"
>
{{
making
? `${t('text.downloadingMultiple')}(${madeCount}/${
avatarList?.length
})`
: t(`text.downloadMultiple`)
}}
</button>
</div>
</div>
<div class="content-box">
<PerfectScrollbar
style="height: 100%; overflow: hidden"
:options="{ suppressScrollX: false }"
>
<div class="content">
<template v-for="(opt, i) in props.avatarList" :key="i">
<div
class="avatar-box"
:style="{ opacity: making && i + 1 > madeCount ? 0.5 : 1 }"
>
<VueColorAvatar :id="`avatar-${i}`" :option="opt" :size="280" />
</div>
</template>
</div>
</PerfectScrollbar>
</div>
</div>
</ModalWrapper>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import PerfectScrollbar from '@/components/PerfectScrollbar.vue'
import VueColorAvatar from '@/components/VueColorAvatar.vue'
import type { AvatarOption } from '@/types'
import { recordEvent } from '@/utils/ga'
import { name as appName } from '../../../package.json'
import ModalWrapper from './ModalWrapper.vue'
const props = defineProps<{ visible?: boolean; avatarList?: AvatarOption[] }>()
const emit = defineEmits<{
(e: 'close'): void
}>()
const { t } = useI18n()
const making = ref(false)
const madeCount = ref(0)
async function make() {
if (props.avatarList && !making.value) {
making.value = true
madeCount.value = 1
const html2canvas = (await import('html2canvas')).default
const { default: JSZip } = await import('jszip')
const jsZip = new JSZip()
for (let i = 0; i <= props.avatarList.length; i += 1) {
const dom = window.document.querySelector(`#avatar-${i}`)
if (dom instanceof HTMLElement) {
const canvas = await html2canvas(dom, {
backgroundColor: null,
})
const dataUrl = canvas.toDataURL().replace('data:image/png;base64,', '')
jsZip.file(`${i + 1}.png`, dataUrl, { base64: true })
madeCount.value = madeCount.value += 1
}
}
const base64 = await jsZip.generateAsync({ type: 'base64' })
making.value = false
madeCount.value = 0
const a = window.document.createElement('a')
a.href = 'data:application/zip;base64,' + base64
a.download = `${appName}.zip`
a.click()
recordEvent('click_download_multiple', {
event_category: 'click',
})
}
}
</script>
<style lang="scss" scoped>
@use 'src/styles/var';
.container {
position: absolute;
top: 50%;
left: 50%;
height: max(90vh, 1000px);
overflow: hidden;
background-color: lighten(var.$color-dark, 3);
border-radius: 1rem;
transform: translate(-50%, -50%);
$top-bar-height: 3.5rem;
.top-bar {
position: absolute;
right: 0;
left: 0;
z-index: 10;
display: flex;
align-items: center;
height: $top-bar-height;
padding: 1rem 2rem;
background-color: lighten(var.$color-dark, 6);
.right {
display: flex;
align-items: center;
margin-left: auto;
.download-btn {
display: flex;
align-items: center;
justify-content: center;
height: 2rem;
margin-left: 1rem;
padding: 0 1rem;
color: #fff;
background-color: var.$color-accent;
border-radius: 0.4rem;
cursor: pointer;
&:disabled,
&[disabled] {
color: rgba(#fff, 0.8);
cursor: default;
}
}
}
}
.content-box {
height: 100%;
padding: $top-bar-height 0rem 0rem 0rem;
}
.content {
z-index: 10;
display: grid;
grid-auto-rows: min-content;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 2rem;
justify-content: space-between;
width: max(85vw, 1280px);
padding: 2rem;
.avatar-box {
display: flex;
align-items: center;
justify-content: center;
}
}
}
</style>

View File

@ -1,35 +1,33 @@
<template>
<transition name="fade">
<div v-if="props.visible" class="code-modal" @click.self="emit('close')">
<div class="code-box">
<div class="code-header">
<div class="title">{{ t('text.codeModalTitle') }}</div>
<ModalWrapper :visible="props.visible" @close="emit('close')">
<div class="code-box">
<div class="code-header">
<div class="title">{{ t('text.codeModalTitle') }}</div>
<div class="close-btn" @click="emit('close')">
<img :src="IconClose" class="icon-close" :alt="t('action.close')" />
</div>
</div>
<div class="code-content-box">
<PerfectScrollbar
class="code-scroll-wrapper"
:options="{ suppressScrollX: false }"
>
<pre><code class="code-content" v-html="highlightedCode"></code></pre>
</PerfectScrollbar>
<button
id="copy-code-btn"
class="copy-btn"
:class="{ copied: copied }"
:data-clipboard-text="codeJSON"
>
{{ copied ? t('action.copied') : t('action.copyCode') }}
</button>
<div class="close-btn" @click="emit('close')">
<img :src="IconClose" class="icon-close" :alt="t('action.close')" />
</div>
</div>
<div class="code-content-box">
<PerfectScrollbar
class="code-scroll-wrapper"
:options="{ suppressScrollX: false }"
>
<pre><code class="code-content" v-html="highlightedCode"></code></pre>
</PerfectScrollbar>
<button
id="copy-code-btn"
class="copy-btn"
:class="{ copied: copied }"
:data-clipboard-text="codeJSON"
>
{{ copied ? t('action.copied') : t('action.copyCode') }}
</button>
</div>
</div>
</transition>
</ModalWrapper>
</template>
<script lang="ts" setup>
@ -42,6 +40,8 @@ import PerfectScrollbar from '@/components/PerfectScrollbar.vue'
import { useAvatarOption } from '@/hooks'
import { highlightJSON } from '@/utils'
import ModalWrapper from './ModalWrapper.vue'
const props = defineProps<{ visible?: boolean }>()
const emit = defineEmits<{
@ -88,32 +88,23 @@ onUnmounted(() => {
<style lang="scss" scoped>
@use 'src/styles/var';
.code-modal {
position: fixed;
bottom: 0;
left: 50%;
z-index: 999;
width: 100%;
height: 100%;
padding: 2rem 0;
overflow: hidden;
transform: translate(-50%, 0);
backdrop-filter: blur(0.1rem);
}
.code-box {
$code-header-height: 4rem;
$code-box-side-padding-normal: 2rem;
$code-box-side-padding-small: 1rem;
position: relative;
position: absolute;
top: 50%;
left: 50%;
width: 75%;
max-width: 800px;
height: 100%;
height: max(90vh, 1000px);
margin: 0 auto;
padding: $code-header-height $code-box-side-padding-normal 2.5rem
$code-box-side-padding-normal;
overflow: hidden;
background-color: lighten(var.$color-dark, 3);
border-radius: 1rem;
transform: translate(-50%, -50%);
transition: width 0.2s;
@media screen and (max-width: 1200px) {
@ -208,19 +199,6 @@ onUnmounted(() => {
}
}
}
.fade-enter-active,
.fade-leave-active {
@media (prefers-reduced-motion: no-preference) {
transition: opacity 0.25s ease, transform 0.25s;
}
}
.fade-enter-from,
.fade-leave-to {
transform: translate(-50%, 2rem);
opacity: 0;
}
</style>
<style lang="scss">

View File

@ -17,7 +17,7 @@
<p class="tip">{{ t('text.downloadTip') }} 🥳</p>
</div>
<button class="close-btn" @click="emit('close')">
<button type="button" class="close-btn" @click="emit('close')">
{{ t('action.close') }}
</button>
</div>

View File

@ -0,0 +1,40 @@
<template>
<transition name="fade">
<div v-if="props.visible" class="modal" @click.self="emit('close')">
<slot />
</div>
</transition>
</template>
<script lang="ts" setup>
const props = defineProps<{ visible?: boolean }>()
const emit = defineEmits<{
(e: 'close'): void
}>()
</script>
<style lang="scss" scoped>
.modal {
position: fixed;
top: 0;
left: 0;
z-index: 999;
width: 100%;
height: 100%;
overflow: hidden;
backdrop-filter: blur(0.1rem);
}
.fade-enter-active,
.fade-leave-active {
@media (prefers-reduced-motion: no-preference) {
transition: opacity 0.25s ease, transform 0.25s;
}
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>

View File

@ -24,7 +24,7 @@ export interface VueColorAvatarRef {
import { ref, toRefs, watchEffect } from 'vue'
import { WrapperShape } from '@/enums'
import { type AvatarOption } from '@/types'
import type { AvatarOption } from '@/types'
import { getRandomAvatarOption } from '@/utils'
import { AVATAR_LAYER, NONE } from '@/utils/constant'
import { widgetData } from '@/utils/dynamic-data'

View File

@ -2,7 +2,7 @@ import { computed } from 'vue'
import { useStore } from '@/store'
import { SET_AVATAR_OPTION } from '@/store/mutation-type'
import { type AvatarOption } from '@/types'
import type { AvatarOption } from '@/types'
export default function useAvatarOption() {
const store = useStore()

View File

@ -8,6 +8,7 @@ export default {
code: 'code',
randomize: 'Randomize',
download: 'Download',
downloadMultiple: 'Download multiple',
copyCode: 'Copy',
copied: 'Copied',
downloading: 'Downloading',
@ -39,5 +40,8 @@ export default {
text: {
codeModalTitle: 'Code',
downloadTip: 'LONG PRESS or RIGHT CLICK to save',
downloadMultiple: 'Download',
downloadingMultiple: 'Downloading',
downloadMultipleTip: 'Automatically generate avatar for you',
},
}

View File

@ -8,6 +8,7 @@ export default {
code: '配置代码',
randomize: '随机生成',
download: '下载头像',
downloadMultiple: '批量下载',
copyCode: '复制代码',
copied: '已复制',
downloading: '准备下载',
@ -39,5 +40,8 @@ export default {
text: {
codeModalTitle: '配置代码',
downloadTip: '长按图片或右键点击下载至本地相册',
downloadMultiple: '开始下载',
downloadingMultiple: '正在下载',
downloadMultipleTip: '已为你自动生成头像',
},
}

View File

@ -11,6 +11,7 @@
rel="nofollow noopener noreferrer"
>
<button
type="button"
class="github-button"
@click="
recordEvent('click_github', {

View File

@ -2,7 +2,7 @@ import { type InjectionKey } from 'vue'
import { type Store, createStore, useStore as baseUseStore } from 'vuex'
import { WrapperShape } from '@/enums'
import { type AvatarOption } from '@/types'
import type { AvatarOption } from '@/types'
import { getRandomAvatarOption } from '@/utils'
import { SCREEN } from '@/utils/constant'

View File

@ -1,4 +1,4 @@
import { type AvatarOption, type AvatarSettings } from '@/types'
import type { AvatarOption, AvatarSettings } from '@/types'
import {
BeardShape,

View File

@ -5,7 +5,7 @@ import {
Gender,
TopsShape,
} from '@/enums'
import { type AvatarOption, type None } from '@/types'
import type { AvatarOption, None } from '@/types'
import { AVATAR_LAYER, NONE, SETTINGS, SPECIAL_AVATARS } from './constant'

View File

@ -1710,6 +1710,11 @@
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301"
integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==
"@types/object-hash@^2.2.1":
version "2.2.1"
resolved "https://registry.npmmirror.com/@types/object-hash/-/object-hash-2.2.1.tgz#67c169f8f033e0b62abbf81df2d00f4598d540b9"
integrity sha512-i/rtaJFCsPljrZvP/akBqEwUP2y5cZLOmvO+JaYnz01aPknrQ+hB5MRcO7iqCUsFaYfTG8kGfKUyboA07xeDHQ==
"@types/parse-json@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
@ -2845,6 +2850,11 @@ core-js-compat@^3.20.2, core-js-compat@^3.21.0:
browserslist "^4.19.1"
semver "7.0.0"
core-util-is@~1.0.0:
version "1.0.3"
resolved "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
cosmiconfig@^5.0.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a"
@ -4254,6 +4264,11 @@ ignore@^5.2.0:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
immediate@~3.0.5:
version "3.0.6"
resolved "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==
immutable@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0.tgz#b86f78de6adef3608395efb269a91462797e2c23"
@ -4321,7 +4336,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
inherits@2, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3:
inherits@2, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@ -4697,7 +4712,7 @@ is-wsl@^2.1.1:
dependencies:
is-docker "^2.0.0"
isarray@1.0.0:
isarray@1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
@ -5307,6 +5322,16 @@ jstransformer@1.0.0:
is-promise "^2.0.0"
promise "^7.0.1"
jszip@^3.10.0:
version "3.10.0"
resolved "https://registry.npmmirror.com/jszip/-/jszip-3.10.0.tgz#faf3db2b4b8515425e34effcdbb086750a346061"
integrity sha512-LDfVtOLtOxb9RXkYOwPyNBTQDL4eUbqahtoY6x07GiDJHwSYvn8sHHIw8wINImV3MqbMNve2gSuM1DDqEKk09Q==
dependencies:
lie "~3.3.0"
pako "~1.0.2"
readable-stream "~2.3.6"
setimmediate "^1.0.5"
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
version "3.2.2"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
@ -5372,6 +5397,13 @@ levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
lie@~3.3.0:
version "3.3.0"
resolved "https://registry.npmmirror.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a"
integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==
dependencies:
immediate "~3.0.5"
lilconfig@2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082"
@ -5836,6 +5868,11 @@ object-copy@^0.1.0:
define-property "^0.2.5"
kind-of "^3.0.3"
object-hash@^3.0.0:
version "3.0.0"
resolved "https://registry.npmmirror.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9"
integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==
object-inspect@^1.11.0, object-inspect@^1.9.0:
version "1.11.0"
resolved "https://registry.nlark.com/object-inspect/download/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1"
@ -5975,6 +6012,11 @@ p-try@^2.0.0:
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
pako@~1.0.2:
version "1.0.11"
resolved "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
parent-module@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
@ -6301,6 +6343,11 @@ pretty-format@^27.0.0, pretty-format@^27.2.5:
ansi-styles "^5.0.0"
react-is "^17.0.1"
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
promise@^7.0.1:
version "7.3.1"
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
@ -6494,6 +6541,19 @@ readable-stream@^3.1.1:
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
readable-stream@~2.3.6:
version "2.3.7"
resolved "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
readdirp@~3.6.0:
version "3.6.0"
resolved "https://registry.nlark.com/readdirp/download/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
@ -6787,7 +6847,7 @@ rxjs@^7.5.2:
dependencies:
tslib "^2.1.0"
safe-buffer@~5.1.1:
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
@ -6862,6 +6922,11 @@ set-value@^2.0.0, set-value@^2.0.1:
is-plain-object "^2.0.3"
split-string "^3.0.1"
setimmediate@^1.0.5:
version "1.0.5"
resolved "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==
shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
@ -7157,6 +7222,13 @@ string_decoder@^1.1.1:
dependencies:
safe-buffer "~5.2.0"
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
stringify-entities@^1.0.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-1.3.2.tgz#a98417e5471fd227b3e45d3db1861c11caf668f7"
@ -7833,7 +7905,7 @@ use@^3.1.0:
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
util-deprecate@^1.0.1, util-deprecate@^1.0.2:
util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=