mirror of
https://github.com/lucide-icons/lucide.git
synced 2026-11-14 13:58:21 +00:00
Add related icons
This commit is contained in:
@@ -7,6 +7,7 @@ const emit = defineEmits(['setActiveIcon'])
|
||||
const props = defineProps<{
|
||||
icons: IconEntity[]
|
||||
activeIcon?: string
|
||||
overlayMode?: boolean
|
||||
}>()
|
||||
|
||||
function setActiveIcon(name: string) {
|
||||
@@ -23,6 +24,7 @@ function setActiveIcon(name: string) {
|
||||
@setActiveIcon="setActiveIcon(icon.name)"
|
||||
:active="activeIcon === icon.name"
|
||||
customizable
|
||||
:overlayMode="overlayMode"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,6 +14,7 @@ const props = defineProps<{
|
||||
iconNode: IconNode;
|
||||
active: boolean;
|
||||
customizable?: boolean;
|
||||
overlayMode?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['setActiveIcon'])
|
||||
@@ -24,7 +25,7 @@ const showOverlay = useMediaQuery('(min-width: 860px)');
|
||||
const icon = createLucideIcon(props.name, props.iconNode)
|
||||
|
||||
function navigateToIcon() {
|
||||
if(showOverlay.value) {
|
||||
if(props.overlayMode && showOverlay.value) {
|
||||
window.history.pushState({}, '', `/icons/${props.name}`)
|
||||
emit('setActiveIcon', props.name)
|
||||
}
|
||||
@@ -41,6 +42,7 @@ function navigateToIcon() {
|
||||
:class="{ 'active' : active }"
|
||||
:data-title="name"
|
||||
:aria-label="name"
|
||||
:href="`/icons/${props.name}`"
|
||||
>
|
||||
<KeepAlive>
|
||||
<component :is="icon" class="lucide-icon" :class="{ customizable }" />
|
||||
@@ -63,6 +65,7 @@ function navigateToIcon() {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
font-size: 24px;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.icon-button:hover:before {
|
||||
|
||||
@@ -68,19 +68,13 @@ const activeIcon = computed(() =>
|
||||
:activeIcon="activeIconName"
|
||||
:icons="category.icons"
|
||||
@setActiveIcon="setActiveIconName"
|
||||
overlayMode
|
||||
/>
|
||||
</section>
|
||||
<IconDetailOverlay :icon="activeIcon" @close="setActiveIconName('')"/>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.icons {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(56px, 1fr));
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.icon {
|
||||
aspect-ratio: 1/1;
|
||||
}
|
||||
@@ -88,6 +82,7 @@ const activeIcon = computed(() =>
|
||||
.title {
|
||||
margin-bottom: 24px;
|
||||
font-size: 19px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.category {
|
||||
@@ -95,16 +90,6 @@ const activeIcon = computed(() =>
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.hidden-category {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -40,8 +40,9 @@ const pageSize = computed(() => {
|
||||
|
||||
const { searchInput, searchQuery, searchQueryThrottled } = useSearchInput()
|
||||
const searchResults = useSearch(searchQueryThrottled, props.icons, [
|
||||
{ name: 'name', weight: 2 },
|
||||
{ name: 'tags', weight: 1 },
|
||||
{ name: 'name', weight: 3 },
|
||||
{ name: 'tags', weight: 2 },
|
||||
{ name: 'categories', weight: 1 },
|
||||
])
|
||||
|
||||
const { next, currentPage } = useOffsetPagination( { pageSize })
|
||||
@@ -73,7 +74,12 @@ watch(searchQueryThrottled, (searchString) => {
|
||||
class="input-wrapper"
|
||||
ref="searchInput"
|
||||
/>
|
||||
<IconGrid :activeIcon="activeIconName" :icons="paginatedIcons" @setActiveIcon="setActiveIconName"/>
|
||||
<IconGrid
|
||||
overlayMode
|
||||
:activeIcon="activeIconName"
|
||||
:icons="paginatedIcons"
|
||||
@setActiveIcon="setActiveIconName"
|
||||
/>
|
||||
<EndOfPage @end-of-page="next" class="bottom-page"/>
|
||||
<IconDetailOverlay :icon="activeIcon" @close="setActiveIconName('')"/>
|
||||
</template>
|
||||
@@ -91,7 +97,7 @@ watch(searchQueryThrottled, (searchString) => {
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.bottom-page {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
import type { IconEntity } from '../../types'
|
||||
import IconGrid from './IconGrid.vue'
|
||||
defineProps<{
|
||||
icons: IconEntity[]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="category">
|
||||
<h2 class="title">
|
||||
Related Icons
|
||||
</h2>
|
||||
<IconGrid
|
||||
:icons="icons"
|
||||
/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.icon {
|
||||
aspect-ratio: 1/1;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-bottom: 24px;
|
||||
font-size: 19px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.category {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
</style>
|
||||
@@ -13,6 +13,7 @@ import { useData } from 'vitepress'
|
||||
import IconPreview from '../.vitepress/theme/components/icons/IconPreview.vue'
|
||||
import IconInfo from '../.vitepress/theme/components/icons/IconInfo.vue'
|
||||
import IconContributors from '../.vitepress/theme/components/icons/IconContributors.vue'
|
||||
import RelatedIcons from '../.vitepress/theme/components/icons/RelatedIcons.vue'
|
||||
import CodeGroup from '../.vitepress/theme/components/base/CodeGroup.vue'
|
||||
import Badge from '../.vitepress/theme/components/base/Badge.vue'
|
||||
import Label from '../.vitepress/theme/components/base/Label.vue'
|
||||
@@ -64,10 +65,10 @@ const codeExample = computed(() => data.codeExamples?.map(
|
||||
/>
|
||||
</CodeGroup>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<RelatedIcons :icons="$params.relatedIcons" />
|
||||
|
||||
<style module>
|
||||
.preview {
|
||||
grid-area: preview;
|
||||
|
||||
@@ -1,10 +1,38 @@
|
||||
import Fuse from "fuse.js";
|
||||
import { getAllData } from "../.vitepress/lib/icons";
|
||||
import { IconEntity } from "../.vitepress/theme/types";
|
||||
|
||||
const getRelatedIcons = (name:string, icons: IconEntity[]) => {
|
||||
const index = new Fuse(icons, {
|
||||
threshold: 1,
|
||||
keys: [
|
||||
{ name: 'name', weight: 3 },
|
||||
{ name: 'tags', weight: 2 },
|
||||
{ name: 'categories', weight: 1 },
|
||||
],
|
||||
});
|
||||
|
||||
return index
|
||||
.search(name)
|
||||
.map((result) => result.item)
|
||||
.slice(0, 85)
|
||||
.map(({ name, iconNode }: IconEntity) => ({
|
||||
name,
|
||||
iconNode
|
||||
}))
|
||||
|
||||
}
|
||||
|
||||
export default {
|
||||
paths: async () => {
|
||||
const icons = await getAllData()
|
||||
|
||||
return icons.map((params) => {
|
||||
return icons.map((iconEntity) => {
|
||||
const relatedIcons = getRelatedIcons(iconEntity.name, icons)
|
||||
const params = {
|
||||
...iconEntity,
|
||||
relatedIcons
|
||||
}
|
||||
return {
|
||||
params,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { getAllData } from '../.vitepress/lib/icons'
|
||||
import { getAllCategoryFiles } from '../.vitepress/lib/categories'
|
||||
import { IconEntity } from '../.vitepress/theme/types'
|
||||
|
||||
export default {
|
||||
async load() {
|
||||
|
||||
Reference in New Issue
Block a user