mirror of
https://github.com/antfu-collective/icones.git
synced 2026-01-09 07:40:49 +08:00
refactor: extract logic
This commit is contained in:
parent
1c48271b1e
commit
68aef90254
@ -17,11 +17,11 @@
|
||||
- Direct download
|
||||
- Mobile friendly
|
||||
- Powered by [Vite](https://github.com/vitejs/vite)
|
||||
- Bookmarks
|
||||
- Collection bookmarks
|
||||
- Categories filters
|
||||
|
||||
### TODOs
|
||||
|
||||
- Tags filter
|
||||
- Dark mode
|
||||
- Full-offline mode - pack all the icons
|
||||
- Electron client
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
|
||||
<script lang='ts'>
|
||||
import { defineComponent, ref, computed } from 'vue'
|
||||
import Base64 from '../utils/base64'
|
||||
import { getIconSnippet, getIconDownloadLink } from '../utils/icons'
|
||||
import { previewColor } from '../store'
|
||||
|
||||
export default defineComponent({
|
||||
@ -57,44 +57,19 @@ export default defineComponent({
|
||||
setup(props) {
|
||||
const copied = ref(false)
|
||||
|
||||
const getSvg = () =>
|
||||
fetch(`https://api.iconify.design/${props.icon}.svg?inline=false&height=auto`)
|
||||
.then(r => r.text())
|
||||
|
||||
const copy = async(type: string) => {
|
||||
if (!props.icon) return
|
||||
|
||||
let text = props.icon
|
||||
switch (type) {
|
||||
case 'url':
|
||||
text = `https://api.iconify.design/${props.icon}.svg`
|
||||
break
|
||||
case 'html':
|
||||
text = `<span class="iconify" data-icon="${props.icon}" data-inline="false"></span>`
|
||||
break
|
||||
case 'css':
|
||||
text = `background: url('https://api.iconify.design/${props.icon}.svg') no-repeat center center / contain;`
|
||||
break
|
||||
case 'svg':
|
||||
text = await getSvg()
|
||||
break
|
||||
case 'data_url':
|
||||
text = `data:image/svg+xml;base64,${Base64.encode(await getSvg())}`
|
||||
break
|
||||
}
|
||||
const text = await getIconSnippet(props.icon, type)
|
||||
if (!text)
|
||||
return
|
||||
|
||||
await navigator.clipboard.writeText(text)
|
||||
copied.value = true
|
||||
|
||||
setTimeout(() => {
|
||||
copied.value = false
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
const downlodUrl = computed(
|
||||
() =>
|
||||
`https://api.iconify.design/${props.icon}.svg?download=true&inline=false&height=auto`,
|
||||
)
|
||||
const downlodUrl = computed(() => getIconDownloadLink(props.icon))
|
||||
|
||||
return {
|
||||
copy,
|
||||
|
||||
6
src/global.d.ts
vendored
Normal file
6
src/global.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
interface Window {
|
||||
Iconify: {
|
||||
getSVG: (icon: string) => string | false
|
||||
getSVGObject: (icon: string) => any
|
||||
}
|
||||
}
|
||||
49
src/hooks/search.ts
Normal file
49
src/hooks/search.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import Fuse from 'fuse.js'
|
||||
import { ref, computed, markRaw, Ref, watch } from 'vue'
|
||||
import { useThrottle } from '@vueuse/core'
|
||||
import { collections, all } from '../data'
|
||||
import { showCategories } from '../store'
|
||||
|
||||
export function useSearch(id: Ref<string>, defaultCategory = '', defaultSearch = '') {
|
||||
const category = ref(defaultCategory)
|
||||
const search = ref(defaultSearch)
|
||||
const throttledSearch = useThrottle(search, 150)
|
||||
|
||||
const collection = computed(() => {
|
||||
return id.value === 'all'
|
||||
? all
|
||||
: collections.find(c => c.id === id.value)!
|
||||
})
|
||||
|
||||
const iconSource = computed(() => {
|
||||
if (category.value && showCategories.value)
|
||||
return collection.value.categories?.[category.value] || []
|
||||
else
|
||||
return collection.value.icons
|
||||
})
|
||||
|
||||
const fuse = computed(() => {
|
||||
const icons = iconSource.value.map(icon => ({ icon }))
|
||||
return markRaw(new Fuse(icons, {
|
||||
includeScore: false,
|
||||
keys: ['icon'],
|
||||
}))
|
||||
})
|
||||
|
||||
const icons = computed(() => {
|
||||
const searchString = throttledSearch.value.trim().toLowerCase()
|
||||
if (!searchString)
|
||||
return iconSource.value
|
||||
else
|
||||
return fuse.value.search(searchString).map(i => i.item.icon)
|
||||
})
|
||||
|
||||
watch(id, () => { category.value = defaultCategory })
|
||||
|
||||
return {
|
||||
collection,
|
||||
search,
|
||||
category,
|
||||
icons,
|
||||
}
|
||||
}
|
||||
20
src/store.ts
20
src/store.ts
@ -6,6 +6,7 @@ export const previewColor = useStorage('explorer-preview-color', '#888')
|
||||
export const listType = useStorage('explorer-list-type', 'grid')
|
||||
export const showCategories = useStorage('explorer-show-categories', true)
|
||||
export const favoritedCollections = useStorage<string[]>('explorer-fav-collections', [])
|
||||
export const iconsCart = useStorage<string[]>('explorer-cart', [])
|
||||
|
||||
export function isFavorited(id: string) {
|
||||
return favoritedCollections.value.includes(id)
|
||||
@ -17,16 +18,15 @@ export function toggleFavorite(id: string) {
|
||||
favoritedCollections.value.splice(index, 1)
|
||||
else
|
||||
favoritedCollections.value.push(id)
|
||||
|
||||
// eslint-disable-next-line no-self-assign
|
||||
favoritedCollections.value = [...favoritedCollections.value]
|
||||
}
|
||||
|
||||
export default {
|
||||
themeColor,
|
||||
iconSize,
|
||||
previewColor,
|
||||
listType,
|
||||
favoritedCollections,
|
||||
showCategories,
|
||||
export function addToCart(id: string) {
|
||||
if (!favoritedCollections.value.includes(id))
|
||||
favoritedCollections.value.push(id)
|
||||
}
|
||||
|
||||
export function removeFromCart(id: string) {
|
||||
const index = favoritedCollections.value.indexOf(id)
|
||||
if (index >= 0)
|
||||
favoritedCollections.value.splice(index, 1)
|
||||
}
|
||||
|
||||
29
src/utils/icons.ts
Normal file
29
src/utils/icons.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import Base64 from './base64'
|
||||
|
||||
const API_ENTRY = 'https://api.iconify.design'
|
||||
|
||||
export async function getSvg(icon: string) {
|
||||
return window.Iconify.getSVG(icon) || await fetch(`${API_ENTRY}/${icon}.svg?inline=false&height=auto`).then(r => r.text()) || ''
|
||||
}
|
||||
|
||||
export async function getIconSnippet(icon: string, type: string): Promise<string | undefined> {
|
||||
if (!icon)
|
||||
return
|
||||
|
||||
switch (type) {
|
||||
case 'url':
|
||||
return `${API_ENTRY}/${icon}.svg`
|
||||
case 'html':
|
||||
return `<span class="iconify" data-icon="${icon}" data-inline="false"></span>`
|
||||
case 'css':
|
||||
return `background: url('${API_ENTRY}/${icon}.svg') no-repeat center center / contain;`
|
||||
case 'svg':
|
||||
return await getSvg(icon)
|
||||
case 'data_url':
|
||||
return `data:image/svg+xml;base64,${Base64.encode(await getSvg(icon))}`
|
||||
}
|
||||
}
|
||||
|
||||
export function getIconDownloadLink(icon: string) {
|
||||
return `${API_ENTRY}/${icon}.svg?download=true&inline=false&height=auto`
|
||||
}
|
||||
@ -113,11 +113,9 @@
|
||||
</template>
|
||||
|
||||
<script lang='ts'>
|
||||
import { defineComponent, ref, computed, markRaw, watch } from 'vue'
|
||||
import Fuse from 'fuse.js'
|
||||
import { useThrottle } from '@vueuse/core'
|
||||
import { collections, all } from '../data'
|
||||
import { defineComponent, ref, toRefs } from 'vue'
|
||||
import { iconSize, listType, showCategories } from '../store'
|
||||
import { useSearch } from '../utils/search'
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@ -127,11 +125,11 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const search = ref('')
|
||||
const throttledSearch = useThrottle(search, 150)
|
||||
const { id } = toRefs(props)
|
||||
const { search, icons, collection, category } = useSearch(id)
|
||||
|
||||
const selected = ref<string | null>(null)
|
||||
const max = ref(200)
|
||||
const category = ref('')
|
||||
|
||||
const toggleCategory = (cat: string) => {
|
||||
if (category.value === cat)
|
||||
@ -140,39 +138,6 @@ export default defineComponent({
|
||||
category.value = cat
|
||||
}
|
||||
|
||||
const collection = computed(() => {
|
||||
return props.id === 'all'
|
||||
? all
|
||||
: collections.find(c => c.id === props.id)!
|
||||
})
|
||||
|
||||
const iconSource = computed(() => {
|
||||
if (category.value && showCategories.value)
|
||||
return collection.value.categories?.[category.value] || []
|
||||
else
|
||||
return collection.value.icons
|
||||
})
|
||||
|
||||
const fuse = computed(() => {
|
||||
const icons = iconSource.value.map(icon => ({ icon }))
|
||||
return markRaw(new Fuse(icons, {
|
||||
includeScore: false,
|
||||
keys: ['icon'],
|
||||
}))
|
||||
})
|
||||
|
||||
const icons = computed(() => {
|
||||
const searchString = throttledSearch.value.trim().toLowerCase()
|
||||
if (!searchString)
|
||||
return iconSource.value
|
||||
else
|
||||
return fuse.value.search(searchString).map(i => i.item.icon)
|
||||
})
|
||||
|
||||
watch(() => props.id, () => {
|
||||
category.value = ''
|
||||
})
|
||||
|
||||
const onSelect = (icon: string) => {
|
||||
selected.value = icon
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user