mirror of
https://github.com/Codennnn/vue-color-avatar.git
synced 2024-12-22 03:32:38 +00:00
Add download multiple support
This commit is contained in:
parent
7e865e8bd4
commit
9bd0a50f23
@ -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" />
|
||||
|
||||
|
@ -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",
|
||||
|
90
src/App.vue
90
src/App.vue
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
:title="t(`wrapperShape.${wrapperShape}`)"
|
||||
@click="switchWrapperShape(wrapperShape)"
|
||||
>
|
||||
<i
|
||||
<div
|
||||
class="shape"
|
||||
:class="[
|
||||
wrapperShape,
|
||||
|
183
src/components/Modal/BatchDownloadModal.vue
Normal file
183
src/components/Modal/BatchDownloadModal.vue
Normal 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>
|
@ -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">
|
@ -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>
|
40
src/components/Modal/ModalWrapper.vue
Normal file
40
src/components/Modal/ModalWrapper.vue
Normal 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>
|
@ -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'
|
||||
|
@ -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()
|
||||
|
@ -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',
|
||||
},
|
||||
}
|
||||
|
@ -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: '已为你自动生成头像',
|
||||
},
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
rel="nofollow noopener noreferrer"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="github-button"
|
||||
@click="
|
||||
recordEvent('click_github', {
|
||||
|
@ -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'
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { type AvatarOption, type AvatarSettings } from '@/types'
|
||||
import type { AvatarOption, AvatarSettings } from '@/types'
|
||||
|
||||
import {
|
||||
BeardShape,
|
||||
|
@ -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'
|
||||
|
||||
|
80
yarn.lock
80
yarn.lock
@ -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=
|
||||
|
Loading…
Reference in New Issue
Block a user