optimize home card

This commit is contained in:
Eric Fennis
2023-04-22 22:05:24 +02:00
parent e46acf4e9a
commit af27f26885
20 changed files with 548 additions and 121 deletions
@@ -0,0 +1,22 @@
import fs from 'fs'
import path from 'path'
export function getAllCategoryFiles() {
const fileNames = fs.readdirSync(path.join('../categories')).filter((file) => path.extname(file) === '.json');
return fileNames
.map((fileName) => path.basename(fileName, '.json'))
.map((fileName) => [fileName, fs.readFileSync(path.join('../categories', fileName + '.json'), 'utf8')])
.map(([name, file]) => ({
name,
...JSON.parse(file)
}));
}
export default {
async load() {
return {
categories: getAllCategoryFiles(),
}
}
}
@@ -0,0 +1,103 @@
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useData } from 'vitepress'
import VPDocOutlineItem from 'vitepress/dist/client/theme-default/components/VPDocOutlineItem.vue'
import { onContentUpdated } from 'vitepress'
import { useActiveAnchor } from '../composables/useActiveAnchor'
import { data } from './CategoriesList.data'
const { frontmatter, theme, params, page } = useData()
interface Header {
level: number
title: string
slug: string
link: string
children: Header[]
}
type MenuItem = Omit<Header, 'slug' | 'children'> & {
children?: MenuItem[]
}
// const headers = shallowRef<MenuItem[]>([])
// onContentUpdated(() => {
// headers.value = data.categories.map(({ name, title }) => ({
// level: 2,
// link: `#${name}`,
// title,
// }))
// })
const headers = computed(() => {
const linkPrefix = page.value.relativePath.startsWith('icons/categories')
? '' : '/icons/categories'
return data.categories.map(({ name, title }) => ({
level: 2,
link: `${linkPrefix}#${name}`,
title,
}))
})
const container = ref()
const marker = ref()
useActiveAnchor(container, marker)
</script>
<template>
<div class="category-list" ref="container">
<h2 class="sidebar-title">
Categories
</h2>
<div class="content">
<div class="outline-marker" ref="marker" />
<nav aria-labelledby="doc-outline-aria-label">
<VPDocOutlineItem :headers="headers" :root="true" />
</nav>
</div>
</div>
</template>
<style scoped>
.category-list {
margin-top: -16px;
}
.sidebar-title {
font-weight: 700;
color: var(--vp-c-text-1);
margin-bottom: 12px;
line-height: 24px;
font-size: 14px;
}
.content {
position: relative;
border-left: 1px solid var(--vp-c-divider);
padding-left: 16px;
font-size: 13px;
font-weight: 500;
}
.outline-marker {
position: absolute;
top: 32px;
left: -1px;
z-index: 0;
opacity: 0;
width: 1px;
height: 18px;
background-color: var(--vp-c-brand);
transition: top 0.25s cubic-bezier(0, 1, 0.5, 1), background-color 0.5s, opacity 0.25s;
}
.outline-title {
letter-spacing: 0.4px;
line-height: 28px;
font-size: 13px;
font-weight: 600;
}
.root {
z-index: 0;
}
</style>
@@ -6,7 +6,7 @@ export default {
async load() {
const icons = await getAllData()
const randomIcons = Array.from({ length: 55 }, () => getRandomItem(icons))
const randomIcons = Array.from({ length: 100 }, () => getRandomItem(icons))
return {
icons: randomIcons,
@@ -1,33 +1,60 @@
<script setup lang="ts">
import { data } from './HomeHeroIconsCard.data'
import IconButton from './IconButton.vue';
import LucideIcon from './LucideIcon.vue'
console.log(data);
import { useRouter } from 'vitepress';
const { go } = useRouter()
</script>
<template>
<div class="icons-card">
<TransitionGroup name="list">
<LucideIcon
v-for="icon in data.icons"
v-bind="icon"
/>
</TransitionGroup>
<div class="card-wrapper">
<div class="icons-card">
<div class="card-grid">
<TransitionGroup name="list">
<IconButton v-for="icon in data.icons" @click="go(`/icons/${props.name}`)">
<LucideIcon
v-bind="icon"
/>
</IconButton>
</TransitionGroup>
</div>
</div>
</div>
</template>
<style scoped>
.card-wrapper {
/* padding: 0 24px; */
margin-left: auto;
}
.icons-card {
background: var(--vp-input-bg-color);
padding: 24px;
border-radius: 8px;
width: 100%;
height:100%;
box-shadow: var(--vp-shadow-4);
max-height: 220px;
max-width: 560px;
margin: 0 auto;
/* max-height: 240px; */
/* margin-top: 96px; */
}
.card-grid {
display: grid;
gap: 8px;
grid-template-columns: repeat(auto-fill, minmax(36px, 1fr));
grid-template-rows: repeat(auto-fill, minmax(36px, 1fr));
box-shadow: var(--vp-shadow-4);
grid-template-columns: repeat(auto-fill, minmax(38px, 1fr));
grid-template-rows: repeat(auto-fill, minmax(38px, 1fr));
width: 100%;
height:100%;
max-height: 172px;
max-width: 512px;
overflow: hidden;
}
@@ -1,19 +1,6 @@
<template>
<button v-bind="$listeners" class="icon-button">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
<slot />
</button>
</template>
@@ -2,7 +2,7 @@
import type { IconEntity } from '../types'
import { ref, computed } from 'vue'
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
import CloseButton from './CloseButton.vue';
import IconButton from './IconButton.vue';
import IconPreview from './IconPreview.vue';
const props = defineProps<{
@@ -36,7 +36,22 @@ import IconPreview from './IconPreview.vue';
<Transition name="drawer">
<div class="overlay-container" v-if="props.icon">
<div class="overlay-panel">
<CloseButton class="close-button" @click="onClose" />
<IconButton class="close-button" @click="onClose">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</IconButton>
<IconPreview :name="props.icon.name" :iconNode="props.icon.iconNode"/>
<div class="icon-info">
<h1 class="icon-name">
+7 -5
View File
@@ -4,14 +4,12 @@ import IconItem from './IconItem.vue'
const emit = defineEmits(['setActiveIcon'])
defineProps<{
const props = defineProps<{
icons: IconEntity[]
activeIcon: string
activeIcon?: string
}>()
function setActiveIcon(name: string) {
console.log(name);
emit('setActiveIcon', name)
}
@@ -20,7 +18,11 @@ function setActiveIcon(name: string) {
<template>
<div class="icons">
<div class="icon" v-for="icon in icons" :key="icon.name">
<IconItem v-bind="icon" @setActiveIcon="setActiveIcon(icon.name)" :active="activeIcon === icon.name"/>
<IconItem
v-bind="icon"
@setActiveIcon="setActiveIcon(icon.name)"
:active="activeIcon === icon.name"
/>
</div>
</div>
</template>
+48 -4
View File
@@ -1,6 +1,8 @@
<script setup lang="ts">
// import type { IconEntity } from '../types';
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
import { useMediaQuery } from '@vueuse/core';
import { useAside } from 'vitepress/dist/client/theme-default/composables/aside.js'
import { useRouter } from 'vitepress';
export type IconNode = [elementName: string, attrs: Record<string, string>][]
@@ -17,18 +19,32 @@ const props = defineProps<{
const emit = defineEmits(['setActiveIcon'])
const { go } = useRouter()
const showOverlay = useMediaQuery('(min-width: 720px)');
const icon = createLucideIcon(props.name, props.iconNode)
function navigateToIcon() {
window.history.pushState({}, '', `/icons/${props.name}`)
emit('setActiveIcon', props.name)
if(showOverlay.value) {
window.history.pushState({}, '', `/icons/${props.name}`)
emit('setActiveIcon', props.name)
}
else {
go(`/icons/${props.name}`)
}
}
</script>
<template>
<button class="icon-button" @click="navigateToIcon" :class="{ 'active' : active }">
<component :is="icon" />
<button
class="icon-button"
@click="navigateToIcon"
:class="{ 'active' : active }"
:data-title="name"
:aria-label="name"
>
<KeepAlive>
<component :is="icon" />
</KeepAlive>
</button>
</template>
@@ -50,6 +66,34 @@ function navigateToIcon() {
font-size: 24px;
}
.icon-button:hover:before {
opacity: 1;
transform: translate(-50%, 48px) scale(1);
}
.icon-button:before {
content: attr(data-title);
display: block;
font-size: 12px;
line-height: 20px;
margin-left: 12px;
transform: translate(-50%, 48px) scale(0.9);
font-weight: 400;
position: absolute;
background: var(--vp-c-brand-dark);
color: white;
z-index: 10;
white-space: nowrap;
padding: 2px 8px;
border-radius: 4px;
box-shadow: var(--vp-shadow-1);
opacity: 0;
pointer-events: none;
transition: cubic-bezier(0.19, 1, 0.22, 1) .2s;
transition-property: opacity, transform;
}
.icon-button:active {
transition: color 0.1s, border-color 0.1s, background-color 0.1s;
}
@@ -13,6 +13,8 @@ const props = defineProps<{
const activeIconName = ref('')
function setActiveIconName(name: string) {
console.log('activeIcon', name);
activeIconName.value = name
}
@@ -38,15 +40,18 @@ const categories = computed(() => {
})
const activeIcon = computed(() =>
props.categories?.find((icon) => icon.name === activeIconName.value)
props.icons?.find((icon) => icon.name === activeIconName.value)
)
</script>
<template>
<section class="category" v-for="category in categories" :key="category.name">
<h2 class="title">{{ category.title }}</h2>
<IconGrid :activeIcon="activeIcon" :icons="category.icons" @setActiveIcon="setActiveIconName"/>
<h2 class="title" :id="category.name">
<a class="header-anchor" :href="`#${category.name}`" :aria-label="`Permalink to &quot;${category.title}&quot;`">&ZeroWidthSpace;</a>
{{ category.title }}
</h2>
<IconGrid :activeIcon="activeIconName" :icons="category.icons" @setActiveIcon="setActiveIconName"/>
</section>
<IconDetailOverlay :icon="activeIcon" @close="setActiveIconName('')"/>
</template>
+36 -2
View File
@@ -1,15 +1,29 @@
<script setup lang="ts">
import { ref, computed } from 'vue'
import { ref, computed, watch } from 'vue'
import type { IconEntity } from '../types'
import IconItem from './IconItem.vue'
import IconDetailOverlay from './IconDetailOverlay.vue'
import IconGrid from './IconGrid.vue'
import Input from './Input.vue'
import useSearch from '../lib/useSearch'
const props = defineProps<{
icons: IconEntity[]
}>()
const activeIconName = ref('')
const searchQuery = ref(
typeof window === 'undefined'
? ''
: (
new URLSearchParams(window.location.search).get('search')
|| ''
)
)
const searchResults = useSearch(searchQuery, props.icons, [
{ name: 'name', weight: 2 },
{ name: 'tags', weight: 1 },
])
function setActiveIconName(name: string) {
activeIconName.value = name
@@ -19,10 +33,20 @@ const activeIcon = computed(() =>
props.icons.find((icon) => icon.name === activeIconName.value)
)
watch(searchQuery, (searchString) => {
const newUrl = new URL(window.location.href);
newUrl.searchParams.set('search', searchString);
window.history.replaceState({}, '', newUrl)
})
</script>
<template>
<IconGrid :activeIcon="activeIconName" :icons="icons" @setActiveIcon="setActiveIconName"/>
<Input type="search" placeholder="Search icons..." v-model="searchQuery" class="input-wrapper"/>
<IconGrid :activeIcon="activeIconName" :icons="searchResults" @setActiveIcon="setActiveIconName"/>
<IconDetailOverlay :icon="activeIcon" @close="setActiveIconName('')"/>
</template>
@@ -38,4 +62,14 @@ const activeIcon = computed(() =>
.icon {
aspect-ratio: 1/1;
}
.input-wrapper .input {
padding: 12px 24px;
font-size: 14px;
height: 48px;
}
.input-wrapper {
margin-bottom: 8px;
}
</style>
+48
View File
@@ -0,0 +1,48 @@
<script lang="ts">
export default {
inheritAttrs: false,
}
</script>
<script setup lang="ts">
interface Props {
type: string
modelValue: string
}
const props = withDefaults(defineProps<Props>(), {
type: 'text'
})
defineEmits(['change', 'input'])
</script>
<template>
<div class="input-wrapper">
<input
:type="type"
class="input"
:value="modelValue"
v-bind="$attrs"
@input="$emit('update:modelValue', $event.target.value)"
/>
</div>
</template>
<style scoped>
.input {
justify-content: flex-start;
border: 1px solid transparent;
border-radius: 8px;
padding: 0 10px 0 12px;
width: 100%;
height: 40px;
background-color: var(--vp-c-bg-alt);
}
.input:hover, .input:focus {
border-color: var(--vp-c-brand);
background: var(--vp-c-bg-alt);
}
</style>
@@ -0,0 +1,83 @@
import { onMounted, onUpdated, onUnmounted } from 'vue';
import { throttleAndDebounce } from 'vitepress/dist/client/theme-default/support/utils'
export function useActiveAnchor(container, marker) {
const onScroll = throttleAndDebounce(setActiveLink, 100);
let prevActiveLink = null;
onMounted(() => {
requestAnimationFrame(setActiveLink);
window.addEventListener('scroll', onScroll);
});
onUpdated(() => {
// sidebar update means a route change
activateLink(location.hash);
});
onUnmounted(() => {
window.removeEventListener('scroll', onScroll);
});
function setActiveLink() {
const links = [].slice.call(container.value.querySelectorAll('.outline-link'));
const anchors = [].slice
.call(document.querySelectorAll('.content .header-anchor'))
.filter((anchor) => {
return links.some((link) => {
return link.hash === anchor.hash && anchor.offsetParent !== null;
});
});
const scrollY = window.scrollY;
const innerHeight = window.innerHeight;
const offsetHeight = document.body.offsetHeight;
const isBottom = Math.abs(scrollY + innerHeight - offsetHeight) < 1;
// page bottom - highlight last one
if (anchors.length && isBottom) {
activateLink(anchors[anchors.length - 1].hash);
return;
}
for (let i = 0; i < anchors.length; i++) {
const anchor = anchors[i];
const nextAnchor = anchors[i + 1];
const [isActive, hash] = isAnchorActive(i, anchor, nextAnchor);
if (isActive) {
activateLink(hash);
return;
}
}
}
function activateLink(hash) {
if (prevActiveLink) {
prevActiveLink.classList.remove('active');
}
if (hash !== null) {
prevActiveLink = container.value.querySelector(`a[href="${decodeURIComponent(hash)}"]`);
}
const activeLink = prevActiveLink;
if (activeLink) {
activeLink.classList.add('active');
marker.value.style.top = activeLink.offsetTop + 5 + 'px';
marker.value.style.opacity = '1';
}
else {
marker.value.style.top = '33px';
marker.value.style.opacity = '0';
}
}
}
const PAGE_OFFSET = 64;
function getAnchorTop(anchor) {
return anchor.parentElement.offsetTop - PAGE_OFFSET;
}
function isAnchorActive(index, anchor, nextAnchor) {
const scrollTop = window.scrollY;
if (index === 0 && scrollTop === 0) {
return [true, null];
}
if (scrollTop < getAnchorTop(anchor)) {
return [false, null];
}
if (!nextAnchor || scrollTop < getAnchorTop(nextAnchor)) {
return [true, anchor.hash];
}
return [false, null];
}
+1 -1
View File
@@ -86,7 +86,7 @@ export default defineConfig({
],
'icons': [
{ text: 'All', link: '/icons/' },
{ text: 'Categorized', link: '/icons/categorized' },
// { text: 'Categorized', link: '/icons/categorized' },
// {
// text: 'Categories',
// items: [
+12 -12
View File
@@ -1,21 +1,21 @@
import Fuse from 'fuse.js';
import { useMemo } from 'react';
import { shallowRef, computed, Ref } from 'vue';
const useSearch = <T>(query = '', collection: T[], keys: Fuse.FuseOptionKey<T>[] = []) => {
const index = useMemo(() => {
return new Fuse(collection, {
threshold: 0.2,
keys,
});
}, [collection]);
const useSearch = <T>(query: Ref<string>, collection: T[], keys: Fuse.FuseOptionKey<T>[] = []) => {
const index = shallowRef(
new Fuse(collection, {
threshold: 0.2,
keys,
})
)
const results = useMemo(() => {
if (query) {
return index.search(query).map((result) => result.item);
const results = computed(() => {
if (query.value) {
return index.value.search(query.value).map((result) => result.item);
}
return collection;
}, [query, index]);
});
return results;
};
@@ -1,21 +1,22 @@
<script setup>
import { onMounted } from 'vue'
import { onMounted } from 'vue'
import { useData } from 'vitepress'
import VPDocAsideOutline from 'vitepress/dist/client/theme-default/components/VPDocAsideOutline.vue'
import { useSidebar } from 'vitepress/dist/client/theme-default/composables/sidebar.js'
import CategoriesList from '../../components/CategoriesList.vue'
const data = useData()
const { frontmatter, page } = useData()
const { isOpen, isSidebarEnabled } = useSidebar()
onMounted(() => {
console.log('Data',data)
console.log('Data',frontmatter)
})
</script>
<template>
<div>
Categories
<VPDocAsideOutline v-if="data.page?.relativePath?.startsWith?.('icons')"/>
<CategoriesList v-if="page?.relativePath?.startsWith?.('icons')"/>
</div>
</template>
+31 -1
View File
@@ -20,6 +20,36 @@
.VPHomeHero .image-container {
transform: none;
width: auto;
width: 100%;
/* padding: 0 24px; */
}
/* .VPHomeHero .container {
flex-direction: column-reverse;
} */
.VPHomeHero .container .main {
flex:1;
flex-shirk: 0;
}
.VPHomeHero .container .image {
margin: 0;
order: 2;
flex: 1;
margin-top: 32px;
}
.VPHomeHero .container .image-container {
height: auto;
justify-content: flex-end;
}
@media (min-width: 960px) {
.VPHomeHero .container .image {
order: 1;
margin-top: auto;
}
}
html:has(* .outline-link:target) {
scroll-behavior: smooth;
}
-2
View File
@@ -29,5 +29,3 @@ const tags = computed(() => {
# {{ $params.name }} {#name}
<IconPreview :name="$params.name" :iconNode="$params.iconNode" />
@@ -1,5 +1,6 @@
---
layout: page
outline: 2
outlineTitle: Categories
---
+4 -1
View File
@@ -23,8 +23,11 @@
},
"dependencies": {
"@headlessui/vue": "^1.7.13",
"@vueuse/core": "^10.1.0",
"fuse.js": "^6.5.3",
"js-yaml": "^4.1.0",
"jszip": "^3.7.0",
"lucide-vue-next": "workspace:*"
"lucide-vue-next": "workspace:*",
"vue": "^3.2.47"
}
}
+80 -56
View File
@@ -45,7 +45,13 @@ importers:
dependencies:
'@headlessui/vue':
specifier: ^1.7.13
version: 1.7.13(vue@3.2.45)
version: 1.7.13(vue@3.2.47)
'@vueuse/core':
specifier: ^10.1.0
version: 10.1.0(vue@3.2.47)
fuse.js:
specifier: ^6.5.3
version: 6.6.2
js-yaml:
specifier: ^4.1.0
version: 4.1.0
@@ -55,6 +61,9 @@ importers:
lucide-vue-next:
specifier: workspace:*
version: link:../packages/lucide-vue-next
vue:
specifier: ^3.2.47
version: 3.2.47
devDependencies:
'@lucide/docs':
specifier: workspace:*
@@ -1225,7 +1234,7 @@ packages:
'@babel/helper-compilation-targets': 7.20.0(@babel/core@7.18.10)
'@babel/helper-module-transforms': 7.20.2
'@babel/helpers': 7.20.1
'@babel/parser': 7.20.3
'@babel/parser': 7.20.7
'@babel/template': 7.18.10
'@babel/traverse': 7.20.1
'@babel/types': 7.20.7
@@ -4731,7 +4740,7 @@ packages:
'@babel/helper-module-imports': 7.18.6
'@babel/helper-plugin-utils': 7.20.2
'@babel/plugin-syntax-jsx': 7.18.6(@babel/core@7.20.2)
'@babel/types': 7.20.2
'@babel/types': 7.20.7
dev: false
/@babel/plugin-transform-react-jsx@7.19.0(@babel/core@7.18.13):
@@ -5676,7 +5685,7 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
'@babel/parser': 7.20.3
'@babel/parser': 7.20.7
'@babel/types': 7.20.7
/@babel/template@7.20.7:
@@ -5697,7 +5706,7 @@ packages:
'@babel/helper-function-name': 7.18.9
'@babel/helper-hoist-variables': 7.18.6
'@babel/helper-split-export-declaration': 7.18.6
'@babel/parser': 7.20.3
'@babel/parser': 7.20.7
'@babel/types': 7.20.7
debug: 4.3.4
globals: 11.12.0
@@ -7235,13 +7244,13 @@ packages:
'@hapi/hoek': 9.3.0
dev: true
/@headlessui/vue@1.7.13(vue@3.2.45):
/@headlessui/vue@1.7.13(vue@3.2.47):
resolution: {integrity: sha512-obG5TdPdBDfs+jiA1mY29LPFqyJl93Q90EL86ontfRe1B6XvbjPkx+x1aAC5DA18bXbb0Juni1ayDbXo0w1u0A==}
engines: {node: '>=10'}
peerDependencies:
vue: ^3.2.0
dependencies:
vue: 3.2.45
vue: 3.2.47
dev: false
/@humanwhocodes/config-array@0.10.4:
@@ -9796,7 +9805,6 @@ packages:
/@types/web-bluetooth@0.0.16:
resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
dev: true
/@types/ws@8.5.3:
resolution: {integrity: sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==}
@@ -10055,6 +10063,7 @@ packages:
'@vue/shared': 3.2.45
estree-walker: 2.0.2
source-map: 0.6.1
dev: true
/@vue/compiler-core@3.2.47:
resolution: {integrity: sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==}
@@ -10063,20 +10072,19 @@ packages:
'@vue/shared': 3.2.47
estree-walker: 2.0.2
source-map: 0.6.1
dev: true
/@vue/compiler-dom@3.2.45:
resolution: {integrity: sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==}
dependencies:
'@vue/compiler-core': 3.2.45
'@vue/shared': 3.2.45
dev: true
/@vue/compiler-dom@3.2.47:
resolution: {integrity: sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==}
dependencies:
'@vue/compiler-core': 3.2.47
'@vue/shared': 3.2.47
dev: true
/@vue/compiler-sfc@2.7.14:
resolution: {integrity: sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==}
@@ -10099,6 +10107,7 @@ packages:
magic-string: 0.25.9
postcss: 8.4.16
source-map: 0.6.1
dev: true
/@vue/compiler-sfc@3.2.47:
resolution: {integrity: sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==}
@@ -10113,20 +10122,19 @@ packages:
magic-string: 0.25.9
postcss: 8.4.21
source-map: 0.6.1
dev: true
/@vue/compiler-ssr@3.2.45:
resolution: {integrity: sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==}
dependencies:
'@vue/compiler-dom': 3.2.45
'@vue/shared': 3.2.45
dev: true
/@vue/compiler-ssr@3.2.47:
resolution: {integrity: sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==}
dependencies:
'@vue/compiler-dom': 3.2.47
'@vue/shared': 3.2.47
dev: true
/@vue/devtools-api@6.5.0:
resolution: {integrity: sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==}
@@ -10140,6 +10148,7 @@ packages:
'@vue/shared': 3.2.45
estree-walker: 2.0.2
magic-string: 0.25.9
dev: true
/@vue/reactivity-transform@3.2.47:
resolution: {integrity: sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==}
@@ -10149,31 +10158,30 @@ packages:
'@vue/shared': 3.2.47
estree-walker: 2.0.2
magic-string: 0.25.9
dev: true
/@vue/reactivity@3.2.45:
resolution: {integrity: sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==}
dependencies:
'@vue/shared': 3.2.45
dev: true
/@vue/reactivity@3.2.47:
resolution: {integrity: sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==}
dependencies:
'@vue/shared': 3.2.47
dev: true
/@vue/runtime-core@3.2.45:
resolution: {integrity: sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==}
dependencies:
'@vue/reactivity': 3.2.45
'@vue/shared': 3.2.45
dev: true
/@vue/runtime-core@3.2.47:
resolution: {integrity: sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==}
dependencies:
'@vue/reactivity': 3.2.47
'@vue/shared': 3.2.47
dev: true
/@vue/runtime-dom@3.2.45:
resolution: {integrity: sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA==}
@@ -10181,6 +10189,7 @@ packages:
'@vue/runtime-core': 3.2.45
'@vue/shared': 3.2.45
csstype: 2.6.20
dev: true
/@vue/runtime-dom@3.2.47:
resolution: {integrity: sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==}
@@ -10188,7 +10197,6 @@ packages:
'@vue/runtime-core': 3.2.47
'@vue/shared': 3.2.47
csstype: 2.6.20
dev: true
/@vue/server-renderer@3.2.45(vue@3.2.45):
resolution: {integrity: sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==}
@@ -10198,6 +10206,7 @@ packages:
'@vue/compiler-ssr': 3.2.45
'@vue/shared': 3.2.45
vue: 3.2.45
dev: true
/@vue/server-renderer@3.2.47(vue@3.2.47):
resolution: {integrity: sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==}
@@ -10207,14 +10216,13 @@ packages:
'@vue/compiler-ssr': 3.2.47
'@vue/shared': 3.2.47
vue: 3.2.47
dev: true
/@vue/shared@3.2.45:
resolution: {integrity: sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==}
dev: true
/@vue/shared@3.2.47:
resolution: {integrity: sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==}
dev: true
/@vue/test-utils@1.3.0(vue-template-compiler@2.7.14)(vue@2.7.14):
resolution: {integrity: sha512-Xk2Xiyj2k5dFb8eYUKkcN9PzqZSppTlx7LaQWBbdA8tqh3jHr/KHX2/YLhNFc/xwDrgeLybqd+4ZCPJSGPIqeA==}
@@ -10237,26 +10245,51 @@ packages:
vue: 3.2.45
dev: true
/@vueuse/core@10.1.0(vue@3.2.47):
resolution: {integrity: sha512-3Znoa5m5RO+z4/C9w6DRaKTR3wCVJvD5rav8HTDGsr+7rOZRHtcgFJ8NcCs0ZvIpmev2kExTa311ns5j2RbzDQ==}
dependencies:
'@types/web-bluetooth': 0.0.16
'@vueuse/metadata': 10.1.0
'@vueuse/shared': 10.1.0(vue@3.2.47)
vue-demi: 0.14.0(vue@3.2.47)
transitivePeerDependencies:
- '@vue/composition-api'
- vue
dev: false
/@vueuse/core@9.13.0(vue@3.2.47):
resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==}
dependencies:
'@types/web-bluetooth': 0.0.16
'@vueuse/metadata': 9.13.0
'@vueuse/shared': 9.13.0(vue@3.2.47)
vue-demi: 0.13.11(vue@3.2.47)
vue-demi: 0.14.0(vue@3.2.47)
transitivePeerDependencies:
- '@vue/composition-api'
- vue
dev: true
/@vueuse/metadata@10.1.0:
resolution: {integrity: sha512-cM28HjDEw5FIrPE9rgSPFZvQ0ZYnOLAOr8hl1XM6tFl80U3WAR5ROdnAqiYybniwP5gt9MKKAJAqd/ab2aHkqg==}
dev: false
/@vueuse/metadata@9.13.0:
resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==}
dev: true
/@vueuse/shared@10.1.0(vue@3.2.47):
resolution: {integrity: sha512-2X52ogu12i9DkKOQ01yeb/BKg9UO87RNnpm5sXkQvyORlbq8ONS5l39MYkjkeVWWjdT0teJru7a2S41dmHmqjQ==}
dependencies:
vue-demi: 0.14.0(vue@3.2.47)
transitivePeerDependencies:
- '@vue/composition-api'
- vue
dev: false
/@vueuse/shared@9.13.0(vue@3.2.47):
resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==}
dependencies:
vue-demi: 0.13.11(vue@3.2.47)
vue-demi: 0.14.0(vue@3.2.47)
transitivePeerDependencies:
- '@vue/composition-api'
- vue
@@ -12373,7 +12406,7 @@ packages:
css-select: 4.3.0
parse5: 6.0.1
parse5-htmlparser2-tree-adapter: 6.0.1
postcss: 8.4.19
postcss: 8.4.21
pretty-bytes: 5.6.0
dev: true
@@ -12431,14 +12464,14 @@ packages:
peerDependencies:
webpack: ^5.0.0
dependencies:
icss-utils: 5.1.0(postcss@8.4.19)
postcss: 8.4.19
postcss-modules-extract-imports: 3.0.0(postcss@8.4.19)
postcss-modules-local-by-default: 4.0.0(postcss@8.4.19)
postcss-modules-scope: 3.0.0(postcss@8.4.19)
postcss-modules-values: 4.0.0(postcss@8.4.19)
icss-utils: 5.1.0(postcss@8.4.21)
postcss: 8.4.21
postcss-modules-extract-imports: 3.0.0(postcss@8.4.21)
postcss-modules-local-by-default: 4.0.0(postcss@8.4.21)
postcss-modules-scope: 3.0.0(postcss@8.4.21)
postcss-modules-values: 4.0.0(postcss@8.4.21)
postcss-value-parser: 4.2.0
semver: 7.3.8
semver: 7.4.0
webpack: 5.74.0(@swc/core@1.3.35)
dev: true
@@ -15991,13 +16024,13 @@ packages:
safer-buffer: 2.1.2
dev: true
/icss-utils@5.1.0(postcss@8.4.19):
/icss-utils@5.1.0(postcss@8.4.21):
resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==}
engines: {node: ^10 || ^12 || >= 14}
peerDependencies:
postcss: ^8.1.0
dependencies:
postcss: 8.4.19
postcss: 8.4.21
dev: true
/ieee754@1.2.1:
@@ -17365,7 +17398,7 @@ packages:
optional: true
dependencies:
'@babel/core': 7.20.2
'@babel/parser': 7.20.3
'@babel/parser': 7.20.7
'@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.20.2)
'@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.20.2)
'@babel/plugin-proposal-optional-chaining': 7.18.9(@babel/core@7.20.2)
@@ -20210,7 +20243,7 @@ packages:
cosmiconfig: 7.0.1
klona: 2.0.6
postcss: 8.4.16
semver: 7.3.8
semver: 7.4.0
webpack: 5.74.0(@swc/core@1.3.35)
dev: true
@@ -20232,45 +20265,45 @@ packages:
postcss: 8.4.16
dev: true
/postcss-modules-extract-imports@3.0.0(postcss@8.4.19):
/postcss-modules-extract-imports@3.0.0(postcss@8.4.21):
resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==}
engines: {node: ^10 || ^12 || >= 14}
peerDependencies:
postcss: ^8.1.0
dependencies:
postcss: 8.4.19
postcss: 8.4.21
dev: true
/postcss-modules-local-by-default@4.0.0(postcss@8.4.19):
/postcss-modules-local-by-default@4.0.0(postcss@8.4.21):
resolution: {integrity: sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==}
engines: {node: ^10 || ^12 || >= 14}
peerDependencies:
postcss: ^8.1.0
dependencies:
icss-utils: 5.1.0(postcss@8.4.19)
postcss: 8.4.19
icss-utils: 5.1.0(postcss@8.4.21)
postcss: 8.4.21
postcss-selector-parser: 6.0.10
postcss-value-parser: 4.2.0
dev: true
/postcss-modules-scope@3.0.0(postcss@8.4.19):
/postcss-modules-scope@3.0.0(postcss@8.4.21):
resolution: {integrity: sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==}
engines: {node: ^10 || ^12 || >= 14}
peerDependencies:
postcss: ^8.1.0
dependencies:
postcss: 8.4.19
postcss: 8.4.21
postcss-selector-parser: 6.0.10
dev: true
/postcss-modules-values@4.0.0(postcss@8.4.19):
/postcss-modules-values@4.0.0(postcss@8.4.21):
resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==}
engines: {node: ^10 || ^12 || >= 14}
peerDependencies:
postcss: ^8.1.0
dependencies:
icss-utils: 5.1.0(postcss@8.4.19)
postcss: 8.4.19
icss-utils: 5.1.0(postcss@8.4.21)
postcss: 8.4.21
dev: true
/postcss-nesting@10.2.0(postcss@8.4.16):
@@ -20502,6 +20535,7 @@ packages:
nanoid: 3.3.4
picocolors: 1.0.0
source-map-js: 1.0.2
dev: true
/postcss@8.4.19:
resolution: {integrity: sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==}
@@ -20519,7 +20553,6 @@ packages:
nanoid: 3.3.4
picocolors: 1.0.0
source-map-js: 1.0.2
dev: true
/potrace@2.1.8:
resolution: {integrity: sha512-V9hI7UMJyEhNZjM8CbZaP/804ZRLgzWkCS9OOYnEZkszzj3zKR/erRdj0uFMcN3pp6x4B+AIZebmkQgGRinG/g==}
@@ -21527,7 +21560,7 @@ packages:
adjust-sourcemap-loader: 4.0.0
convert-source-map: 1.8.0
loader-utils: 2.0.2
postcss: 8.4.19
postcss: 8.4.21
source-map: 0.6.1
dev: true
@@ -22037,14 +22070,6 @@ packages:
lru-cache: 6.0.0
dev: true
/semver@7.3.8:
resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==}
engines: {node: '>=10'}
hasBin: true
dependencies:
lru-cache: 6.0.0
dev: true
/semver@7.4.0:
resolution: {integrity: sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==}
engines: {node: '>=10'}
@@ -24508,8 +24533,8 @@ packages:
resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==}
dev: true
/vue-demi@0.13.11(vue@3.2.47):
resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==}
/vue-demi@0.14.0(vue@3.2.47):
resolution: {integrity: sha512-gt58r2ogsNQeVoQ3EhoUAvUsH9xviydl0dWJj7dabBC/2L4uBId7ujtCwDRD0JhkGsV1i0CtfLAeyYKBht9oWg==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
@@ -24521,7 +24546,6 @@ packages:
optional: true
dependencies:
vue: 3.2.47
dev: true
/vue-template-compiler@2.7.14:
resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==}
@@ -24545,6 +24569,7 @@ packages:
'@vue/runtime-dom': 3.2.45
'@vue/server-renderer': 3.2.45(vue@3.2.45)
'@vue/shared': 3.2.45
dev: true
/vue@3.2.47:
resolution: {integrity: sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==}
@@ -24554,7 +24579,6 @@ packages:
'@vue/runtime-dom': 3.2.47
'@vue/server-renderer': 3.2.47(vue@3.2.47)
'@vue/shared': 3.2.47
dev: true
/w3c-hr-time@1.0.2:
resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==}