mirror of
https://github.com/antfu-collective/icones.git
synced 2026-01-09 07:40:49 +08:00
feat: more optimize for electron
This commit is contained in:
parent
73c97b2768
commit
c3cf4f048d
3
.gitignore
vendored
3
.gitignore
vendored
@ -7,4 +7,5 @@ src/assets/collections.json
|
|||||||
|
|
||||||
public/collections
|
public/collections
|
||||||
public/collections-info.json
|
public/collections-info.json
|
||||||
public/collections-meta.json
|
public/collections-meta.json
|
||||||
|
public/lib
|
||||||
@ -3,7 +3,7 @@
|
|||||||
<p align="center">Explorer for <a href="https://iconify.design/" target="_blank">Iconify</a> with <b>Instant</b> searching.</p>
|
<p align="center">Explorer for <a href="https://iconify.design/" target="_blank">Iconify</a> with <b>Instant</b> searching.</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://iconify.antfu.me/">⇁ iconify.antfu.me</a>
|
<a href="https://iconify.antfu.me/">iconify.antfu.me</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "electron-webpack-quick-start",
|
"name": "iconify-explorer-electron",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -9,6 +9,7 @@
|
|||||||
"dist:dir": "yarn dist --dir -c.compression=store -c.mac.identity=null"
|
"dist:dir": "yarn dist --dir -c.compression=store -c.mac.identity=null"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"babel-loader": "^8.1.0",
|
||||||
"electron-debug": "^3.1.0",
|
"electron-debug": "^3.1.0",
|
||||||
"electron-serve": "^1.0.0",
|
"electron-serve": "^1.0.0",
|
||||||
"electron-util": "^0.14.2",
|
"electron-util": "^0.14.2",
|
||||||
|
|||||||
@ -17,7 +17,7 @@ const createMainWindow = async() => {
|
|||||||
height: 500,
|
height: 500,
|
||||||
minWidth: 200,
|
minWidth: 200,
|
||||||
minHeight: 200,
|
minHeight: 200,
|
||||||
titleBarStyle: 'hidden',
|
titleBarStyle: 'hiddenInset',
|
||||||
})
|
})
|
||||||
|
|
||||||
win.on('ready-to-show', () => {
|
win.on('ready-to-show', () => {
|
||||||
|
|||||||
6885
electron/yarn.lock
6885
electron/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -4,9 +4,8 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Iconify Explorer</title>
|
<title>Iconify Explorer</title>
|
||||||
<script src="https://code.iconify.design/1/1.0.7/iconify.without-api.min.js"></script>
|
<script src="/lib/iconify.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/svg-packer@latest/dist/index.browser.min.js" defer></script>
|
<script src="/lib/svg-packer.js" defer></script>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
|
|
||||||
</head>
|
</head>
|
||||||
<body class="dragging">
|
<body class="dragging">
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
@ -3,8 +3,10 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"preinstall": "npx only-allow pnpm",
|
||||||
"postinstall": "ts-node -P tsconfig.tsnode.json scripts/prepare.ts",
|
"postinstall": "ts-node -P tsconfig.tsnode.json scripts/prepare.ts",
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
"dev:electron": "pnpm -C ./electron dev",
|
||||||
"build": "vite build"
|
"build": "vite build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -15,6 +17,8 @@
|
|||||||
"vue-router": "next"
|
"vue-router": "next"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"svg-packer": "^0.0.3",
|
||||||
|
"@iconify/iconify": "^1.0.7",
|
||||||
"@antfu/eslint-config-vue": "^0.2.13",
|
"@antfu/eslint-config-vue": "^0.2.13",
|
||||||
"@iconify/json": "^1.1.186",
|
"@iconify/json": "^1.1.186",
|
||||||
"@types/fs-extra": "^9.0.1",
|
"@types/fs-extra": "^9.0.1",
|
||||||
|
|||||||
9822
pnpm-lock.yaml
generated
Normal file
9822
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
2
pnpm-workspace.yaml
Normal file
2
pnpm-workspace.yaml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
packages:
|
||||||
|
- 'electron/'
|
||||||
@ -1,11 +1,12 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
|
|
||||||
const dir = path.resolve(__dirname, '../node_modules/@iconify/json')
|
|
||||||
const out = path.resolve(__dirname, '../public')
|
const out = path.resolve(__dirname, '../public')
|
||||||
const collectionsDir = path.resolve(__dirname, '../public/collections')
|
|
||||||
|
|
||||||
async function prepare() {
|
async function prepareJSON() {
|
||||||
|
const dir = path.resolve(__dirname, '../node_modules/@iconify/json')
|
||||||
|
const collectionsDir = path.resolve(__dirname, '../public/collections')
|
||||||
|
|
||||||
const raw = await fs.readJSON(path.join(dir, 'collections.json'))
|
const raw = await fs.readJSON(path.join(dir, 'collections.json'))
|
||||||
await fs.ensureDir(collectionsDir)
|
await fs.ensureDir(collectionsDir)
|
||||||
|
|
||||||
@ -21,9 +22,12 @@ async function prepare() {
|
|||||||
|
|
||||||
const icons = Object.keys(setData.icons)
|
const icons = Object.keys(setData.icons)
|
||||||
const categories = setData.categories
|
const categories = setData.categories
|
||||||
const meta = {...info, icons, categories }
|
const meta = { ...info, icons, categories }
|
||||||
|
|
||||||
await fs.writeJSON(path.join(collectionsDir, `${info.id}-raw.json`), setData)
|
await fs.writeJSON(
|
||||||
|
path.join(collectionsDir, `${info.id}-raw.json`),
|
||||||
|
setData
|
||||||
|
)
|
||||||
await fs.writeJSON(path.join(collectionsDir, `${info.id}-meta.json`), meta)
|
await fs.writeJSON(path.join(collectionsDir, `${info.id}-meta.json`), meta)
|
||||||
collectionsMeta.push(meta)
|
collectionsMeta.push(meta)
|
||||||
|
|
||||||
@ -34,4 +38,29 @@ async function prepare() {
|
|||||||
await fs.writeJSON(path.join(out, 'collections-info.json'), collections)
|
await fs.writeJSON(path.join(out, 'collections-info.json'), collections)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function copyLibs() {
|
||||||
|
const modules = path.resolve(__dirname, '../node_modules')
|
||||||
|
|
||||||
|
await fs.copy(
|
||||||
|
path.join(modules, '@iconify/iconify/dist/'),
|
||||||
|
path.join(out, 'lib'),
|
||||||
|
{
|
||||||
|
filter: (src) => {
|
||||||
|
if (fs.lstatSync(src).isDirectory()) return true
|
||||||
|
const basename = path.basename(src)
|
||||||
|
return basename.startsWith('iconify') && basename.endsWith('.min.js')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
await fs.copy(
|
||||||
|
path.join(modules, 'svg-packer/dist/index.browser.js'),
|
||||||
|
path.join(out, 'lib/svg-packer.js')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function prepare() {
|
||||||
|
await prepareJSON()
|
||||||
|
await copyLibs()
|
||||||
|
}
|
||||||
|
|
||||||
prepare()
|
prepare()
|
||||||
|
|||||||
17
src/App.vue
17
src/App.vue
@ -1,10 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col h-screen overflow-hidden" :style="style">
|
<div class="flex flex-col h-screen overflow-hidden" :style="style">
|
||||||
<div class="flex flex-auto overflow-hidden">
|
<div class="h-full flex-auto overflow-auto">
|
||||||
<Drawer v-if="!isRoot" class="h-full overflow-auto flex-none hidden md:block" style="width:280px" />
|
<router-view />
|
||||||
<div class="h-full flex-auto overflow-auto">
|
|
||||||
<router-view />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -16,17 +13,13 @@ import { themeColor } from './store'
|
|||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
const route = useRoute()
|
|
||||||
const isRoot = computed(() => route.path === '/')
|
|
||||||
|
|
||||||
const style = computed(() => ({
|
const style = computed(() => ({
|
||||||
'--theme-color': themeColor.value,
|
'--theme-color': themeColor.value
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isRoot,
|
style
|
||||||
style,
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="h-full flex flex-col" style="width:40vw;">
|
<div class="h-full flex flex-col w-screen md:w-16">
|
||||||
<div class="flex-none border-b border-gray-200 py-3 px-6 flex">
|
<div class="flex-none border-b border-gray-200 py-3 px-6 flex">
|
||||||
<div>
|
<div>
|
||||||
<div class="text-gray-700 text-lg">
|
<div class="text-gray-700 text-lg">
|
||||||
@ -10,7 +10,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-auto" />
|
<div class="flex-auto" />
|
||||||
<IconButton class="text-xl mr-4 flex-none" icon="carbon:delete" @click="clear" />
|
<IconButton v-if='bags.length' class="text-xl mr-4 flex-none" icon="carbon:delete" @click="clear" />
|
||||||
<IconButton class="text-2xl flex-none" icon="carbon:close" @click="$emit('close')" />
|
<IconButton class="text-2xl flex-none" icon="carbon:close" @click="$emit('close')" />
|
||||||
</div>
|
</div>
|
||||||
<template v-if="bags.length">
|
<template v-if="bags.length">
|
||||||
@ -18,8 +18,8 @@
|
|||||||
<Icons :icons="bags" />
|
<Icons :icons="bags" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-none border-t border-gray-200 py-3 px-6 text-2xl text-gray-700">
|
<div class="flex-none border-t border-gray-200 py-3 px-6 text-2xl text-gray-700">
|
||||||
<IconButton class="p-1" icon="carbon:function" text="Generate Icon Fonts" :active="true" @click="pack()" />
|
<IconButton class="p-1 cursor-pointer" icon="carbon:function" text="Generate Icon Fonts" :active="true" @click="pack()" />
|
||||||
<IconButton class="p-1" icon="carbon:download" text="Download All SVGs" :active="true" @click="wip" />
|
<IconButton class="p-1 cursor-pointer" icon="carbon:download" text="Download All SVGs" :active="true" @click="wip" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="border-r border-gray-200">
|
<div class="border-r border-gray-200">
|
||||||
|
<!-- Back button, Mobile Only -->
|
||||||
<div
|
<div
|
||||||
|
v-if='!isElectron'
|
||||||
class="border-b border-gray-200"
|
class="border-b border-gray-200"
|
||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
@ -10,6 +12,8 @@
|
|||||||
@click="$router.replace('/')"
|
@click="$router.replace('/')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Collections -->
|
||||||
<router-link
|
<router-link
|
||||||
v-for="collection in collections"
|
v-for="collection in collections"
|
||||||
:key="collection.id"
|
:key="collection.id"
|
||||||
@ -46,6 +50,7 @@ import { defineComponent, computed } from 'vue'
|
|||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { sortedCollectionsInfo } from '../data'
|
import { sortedCollectionsInfo } from '../data'
|
||||||
import { isFavorited, toggleFavorite } from '../store'
|
import { isFavorited, toggleFavorite } from '../store'
|
||||||
|
import { isElectron } from '../env'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
@ -64,6 +69,7 @@ export default defineComponent({
|
|||||||
current,
|
current,
|
||||||
toggleFavorite,
|
toggleFavorite,
|
||||||
isFavorited,
|
isFavorited,
|
||||||
|
isElectron,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
33
src/components/ElectronNav.vue
Normal file
33
src/components/ElectronNav.vue
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<template>
|
||||||
|
<div class="electron-nav dragging md:border-b border-gray-200 flex-none flex justify-start">
|
||||||
|
<div class="mac-controls flex-none" />
|
||||||
|
<IconButton
|
||||||
|
v-show="$route.path !== '/'"
|
||||||
|
class="text-lg mx-1 px-3 flex-none"
|
||||||
|
icon="carbon:chevron-left"
|
||||||
|
@click="$router.replace('/')"
|
||||||
|
/>
|
||||||
|
<div class="inline-block text-sm text-gray-800 my-auto ml-3 font-light"><b class="font-bold">Iconify</b>Explorer</div>
|
||||||
|
<div class="flex-auto"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang='ts'>
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
|
export default defineComponent({})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.electron-nav {
|
||||||
|
height: 37px
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-controls {
|
||||||
|
width: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.electron-nav .icon-button svg {
|
||||||
|
margin-top: -2px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="hidden md:block p-4 m-6 fixed bottom-0 right-0 shadow-lg bg-white rounded-full text-2xl cursor-pointer hover:bg-gray-100">
|
<div class="p-4 m-6 fixed bottom-0 right-0 shadow-lg bg-white rounded-full text-2xl cursor-pointer hover:bg-gray-100">
|
||||||
<Icon class="text-gray-700" :icon="icon" />
|
<Icon class="text-gray-700" :icon="icon" />
|
||||||
<div
|
<div
|
||||||
v-if="number"
|
v-if="number"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="m-auto cursor-pointer"
|
class="icon-button m-auto"
|
||||||
:class="active ? 'opacity-100 hover:opacity-100 hover:text-primary' : 'opacity-25 hover:opacity-50' "
|
:class="active ? 'opacity-100 hover:opacity-100 hover:text-primary' : 'opacity-25 hover:opacity-50' "
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="non-dragging flex flex-wrap select-none justify-center" :class="`text-${size}`" :style="{ color }">
|
<div class="non-dragging flex flex-wrap select-none justify-center" :class="`text-${size}`">
|
||||||
<div
|
<div
|
||||||
v-for="icon of icons"
|
v-for="icon of icons"
|
||||||
:key="icon"
|
:key="icon"
|
||||||
class="non-dragging icons-item cursor-pointer flex m-2"
|
class="non-dragging icons-item flex"
|
||||||
:class="selected.includes(namespace+icon) ? 'active': ''"
|
:class="[spacing, selected.includes(namespace+icon) ? 'active': '']"
|
||||||
@click="$emit('select', namespace+icon)"
|
@click="$emit('select', namespace+icon)"
|
||||||
>
|
>
|
||||||
<Icon class="non-dragging" :icon="namespace+icon" />
|
<Icon class="non-dragging" :icon="namespace+icon" />
|
||||||
@ -33,14 +33,14 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: '2xl',
|
default: '2xl',
|
||||||
},
|
},
|
||||||
|
spacing: {
|
||||||
|
type: String,
|
||||||
|
default: 'm-2'
|
||||||
|
},
|
||||||
search: {
|
search: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
default: '#555',
|
|
||||||
},
|
|
||||||
display: {
|
display: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'grid',
|
default: 'grid',
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
@click="$emit('close')"
|
@click="$emit('close')"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="bg-white absolute shadow-lg transition-all duration-200 ease-out border-gray-200"
|
class="bg-white absolute transition-all duration-200 ease-out border-gray-200"
|
||||||
:class="positionClass"
|
:class="positionClass"
|
||||||
:style="value ? {}: {transform}"
|
:style="value ? {}: {transform}"
|
||||||
>
|
>
|
||||||
|
|||||||
71
src/components/ViewControls.vue
Normal file
71
src/components/ViewControls.vue
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<template>
|
||||||
|
<div class="px-1 text-xl text-gray-800 flex">
|
||||||
|
<IconButton
|
||||||
|
class="mr-3"
|
||||||
|
icon="carbon:checkbox-checked"
|
||||||
|
:active="selectingMode"
|
||||||
|
@click="selectingMode = !selectingMode"
|
||||||
|
/>
|
||||||
|
<div class="mx-1 h-full m-auto bg-gray-200" style="width:1px;" />
|
||||||
|
<IconButton
|
||||||
|
class="ml-3"
|
||||||
|
icon="carbon:hinton-plot"
|
||||||
|
title="Small"
|
||||||
|
:active="listType === 'grid' && iconSize === '2xl'"
|
||||||
|
@click="()=>setGrid('small')"
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
class="ml-3"
|
||||||
|
icon="carbon:app-switcher"
|
||||||
|
title="Large"
|
||||||
|
:active="listType === 'grid' && iconSize === '4xl'"
|
||||||
|
@click="()=>setGrid('large')"
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
class="ml-3"
|
||||||
|
icon="carbon:list"
|
||||||
|
title="List View"
|
||||||
|
:active="listType === 'list'"
|
||||||
|
@click="()=>setGrid('list')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang='ts'>
|
||||||
|
import { defineComponent, PropType } from 'vue'
|
||||||
|
import { iconSize, listType, showCategories, selectingMode } from '../store'
|
||||||
|
import { CollectionMeta } from '../data'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
collection: {
|
||||||
|
type: Object as PropType<CollectionMeta>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const setGrid = (type: string) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'small':
|
||||||
|
iconSize.value = '2xl'
|
||||||
|
listType.value = 'grid'
|
||||||
|
break
|
||||||
|
case 'large':
|
||||||
|
iconSize.value = '4xl'
|
||||||
|
listType.value = 'grid'
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
iconSize.value = '3xl'
|
||||||
|
listType.value = 'list'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
setGrid,
|
||||||
|
listType,
|
||||||
|
iconSize,
|
||||||
|
showCategories,
|
||||||
|
selectingMode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex h-screen flex-col overflow-hidden">
|
<div class="flex h-screen flex-col overflow-hidden">
|
||||||
<Navbar :class="navClass" />
|
<ElectronNav v-if='isElectron'/>
|
||||||
|
<Navbar v-else :class="navClass" />
|
||||||
<div class="flex-auto overflow-auto">
|
<div class="flex-auto overflow-auto">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
@ -9,13 +10,23 @@
|
|||||||
|
|
||||||
<script lang='ts'>
|
<script lang='ts'>
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
|
import ElectronNav from './ElectronNav.vue'
|
||||||
|
import { isElectron } from '../env'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
ElectronNav
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
navClass: {
|
navClass: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
setup(){
|
||||||
|
return {
|
||||||
|
isElectron,
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
1
src/env.ts
Normal file
1
src/env.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const isElectron = location.protocol === 'app:' || (process.env.NODE_ENV === 'development' && navigator.userAgent.indexOf('Electron') >= 0)
|
||||||
@ -15,6 +15,7 @@ import Footer from './components/Footer.vue'
|
|||||||
import FAB from './components/FAB.vue'
|
import FAB from './components/FAB.vue'
|
||||||
import Drawer from './components/Drawer.vue'
|
import Drawer from './components/Drawer.vue'
|
||||||
import Bag from './components/Bag.vue'
|
import Bag from './components/Bag.vue'
|
||||||
|
import ViewControls from './components/ViewControls.vue'
|
||||||
import './main.css'
|
import './main.css'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
@ -52,5 +53,6 @@ app.component('Footer', Footer)
|
|||||||
app.component('Drawer', Drawer)
|
app.component('Drawer', Drawer)
|
||||||
app.component('FAB', FAB)
|
app.component('FAB', FAB)
|
||||||
app.component('Bag', Bag)
|
app.component('Bag', Bag)
|
||||||
|
app.component('ViewControls', ViewControls)
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="!collection" class="p-4 text-gray-700">
|
<WithNavbar v-if="!collection">
|
||||||
Loading...
|
<div class="py-8 px-4 text-gray-700 text-center">Loading...</div>
|
||||||
</div>
|
</WithNavbar>
|
||||||
<IconSet v-else :collection="collection" :installed="installed" />
|
<IconSet v-else :collection="collection" :installed="installed" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -13,19 +13,19 @@ import {
|
|||||||
getFullMeta,
|
getFullMeta,
|
||||||
install,
|
install,
|
||||||
getMeta,
|
getMeta,
|
||||||
CollectionMeta,
|
CollectionMeta
|
||||||
} from '../data'
|
} from '../data'
|
||||||
import IconSet from './IconSet.vue'
|
import IconSet from './IconSet.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
IconSet,
|
IconSet
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
id: {
|
id: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const loaded = ref(isMetaLoaded(props.id))
|
const loaded = ref(isMetaLoaded(props.id))
|
||||||
@ -34,7 +34,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.id,
|
() => props.id,
|
||||||
async() => {
|
async () => {
|
||||||
loaded.value = false
|
loaded.value = false
|
||||||
|
|
||||||
if (props.id === 'all') {
|
if (props.id === 'all') {
|
||||||
@ -42,23 +42,22 @@ export default defineComponent({
|
|||||||
collection.value = {
|
collection.value = {
|
||||||
id: 'all',
|
id: 'all',
|
||||||
name: 'All',
|
name: 'All',
|
||||||
icons: meta.flatMap(c => c.icons.map(i => `${c.id}:${i}`)),
|
icons: meta.flatMap(c => c.icons.map(i => `${c.id}:${i}`))
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
await install(props.id)
|
await install(props.id)
|
||||||
collection.value = await getMeta(props.id)
|
collection.value = await getMeta(props.id)
|
||||||
}
|
}
|
||||||
loaded.value = true
|
loaded.value = true
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
collection,
|
collection,
|
||||||
loaded,
|
loaded,
|
||||||
installed,
|
installed
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,150 +1,107 @@
|
|||||||
<template>
|
<template>
|
||||||
<WithNavbar nav-class="md:hidden">
|
<WithNavbar nav-class="md:hidden">
|
||||||
<div class="py-5 px-5 md:px-8">
|
<div class="flex flex-auto h-full overflow-hidden">
|
||||||
<div class="flex">
|
<Drawer class="h-full overflow-auto flex-none hidden md:block" style="width:280px" />
|
||||||
<!-- Left -->
|
<div class="py-5 px-5 md:px-8 h-full overflow-y-auto">
|
||||||
<div class="flex-auto">
|
<div class="flex">
|
||||||
<div class="text-gray-900 text-xl flex">
|
<!-- Left -->
|
||||||
{{ collection.name }}
|
<div class="flex-auto">
|
||||||
<a
|
<div class="text-gray-900 text-xl flex">
|
||||||
v-if="collection.url"
|
{{ collection.name }}
|
||||||
class="text-gray-500 hover:text-gray-900 mt-1 text-base"
|
<a
|
||||||
:href="collection.url"
|
v-if="collection.url"
|
||||||
target="_blank"
|
class="text-gray-500 hover:text-gray-900 mt-1 text-base"
|
||||||
>
|
:href="collection.url"
|
||||||
<Icon icon="la:external-link-square-alt-solid" />
|
target="_blank"
|
||||||
</a>
|
>
|
||||||
|
<Icon icon="la:external-link-square-alt-solid" />
|
||||||
|
</a>
|
||||||
|
<div class="flex-auto" />
|
||||||
|
</div>
|
||||||
|
<div class="text-gray-500 text-xs block">{{ collection.author }}</div>
|
||||||
|
<div>
|
||||||
|
<a
|
||||||
|
class="text-gray-500 text-xs hover:text-gray-900"
|
||||||
|
:href="collection.licenseURL"
|
||||||
|
target="_blank"
|
||||||
|
>{{ collection.license }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Right -->
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<ViewControls :collection="collection" />
|
||||||
<div class="flex-auto" />
|
<div class="flex-auto" />
|
||||||
</div>
|
</div>
|
||||||
<div class="text-gray-500 text-xs block">
|
|
||||||
{{ collection.author }}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<a
|
|
||||||
class="text-gray-500 text-xs hover:text-gray-900"
|
|
||||||
:href="collection.licenseURL"
|
|
||||||
target="_blank"
|
|
||||||
>{{ collection.license }}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Right -->
|
<!-- Categories -->
|
||||||
<div class="flex flex-col">
|
<div class="py-3 pr-3 overflow-x-auto flex flex-no-wrap">
|
||||||
<div class="px-1 text-xl text-gray-800 flex">
|
<template v-if="collection.categories">
|
||||||
<IconButton
|
<div
|
||||||
class="hidden md:block mr-3"
|
v-for="c of Object.keys(collection.categories)"
|
||||||
icon="carbon:checkbox-checked"
|
:key="c"
|
||||||
:active="selectingMode"
|
class="whitespace-no-wrap text-sm inline-block px-2 border border-gray-200 text-gray-500 rounded-full m-1 hover:bg-gray-100 cursor-pointer"
|
||||||
@click="selectingMode = !selectingMode"
|
:class="c === category ? 'text-primary border-primary' : ''"
|
||||||
/>
|
@click="toggleCategory(c)"
|
||||||
<div class="hidden md:block mx-1 h-full m-auto bg-gray-200" style="width:1px;" />
|
>{{ c }}</div>
|
||||||
<template v-if="collection.categories">
|
</template>
|
||||||
<IconButton
|
|
||||||
class="mx-3"
|
|
||||||
icon="carbon:categories"
|
|
||||||
:active="showCategories"
|
|
||||||
title="Categories"
|
|
||||||
@click="showCategories = !showCategories"
|
|
||||||
/>
|
|
||||||
<div class="mx-1 m-auto h-full bg-gray-200" style="width:1px;" />
|
|
||||||
</template>
|
|
||||||
<IconButton
|
|
||||||
class="ml-3"
|
|
||||||
icon="carbon:hinton-plot"
|
|
||||||
title="Small"
|
|
||||||
:active="listType === 'grid' && iconSize === '2xl'"
|
|
||||||
@click="()=>setGrid('small')"
|
|
||||||
/>
|
|
||||||
<IconButton
|
|
||||||
class="ml-3"
|
|
||||||
icon="carbon:app-switcher"
|
|
||||||
title="Large"
|
|
||||||
:active="listType === 'grid' && iconSize === '4xl'"
|
|
||||||
@click="()=>setGrid('large')"
|
|
||||||
/>
|
|
||||||
<IconButton
|
|
||||||
class="ml-3"
|
|
||||||
icon="carbon:list"
|
|
||||||
title="List View"
|
|
||||||
:active="listType === 'list'"
|
|
||||||
@click="()=>setGrid('list')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex-auto" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Categories -->
|
<!-- Search -->
|
||||||
<div class="py-2">
|
<div class="flex">
|
||||||
<template v-if="collection.categories && showCategories">
|
<input
|
||||||
<div
|
v-model="search"
|
||||||
v-for="c of Object.keys(collection.categories)"
|
class="shadow rounded outline-none py-2 px-4 flex-auto"
|
||||||
:key="c"
|
placeholder="Search..."
|
||||||
class="text-sm inline-block px-2 border border-gray-200 text-gray-500 rounded-full m-1 hover:bg-gray-100 cursor-pointer"
|
/>
|
||||||
:class="c === category ? 'text-primary border-primary' : ''"
|
</div>
|
||||||
@click="toggleCategory(c)"
|
|
||||||
>
|
|
||||||
{{ c }}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Search -->
|
<!-- Icons -->
|
||||||
<div class="flex">
|
<div class="py-4 text-center">
|
||||||
<input
|
<Icons
|
||||||
v-model="search"
|
:icons="icons.slice(0, max)"
|
||||||
class="shadow rounded outline-none py-2 px-4 flex-auto"
|
:selected="selectedIcons"
|
||||||
placeholder="Search..."
|
:size="iconSize"
|
||||||
>
|
:display="listType"
|
||||||
</div>
|
:search="search"
|
||||||
|
:namespace="namespace"
|
||||||
|
style="color: #666"
|
||||||
|
@select="onSelect"
|
||||||
|
/>
|
||||||
|
<button v-if="icons.length > max" class="btn m-2" @click="loadMore">Load More</button>
|
||||||
|
<p class="text-gray-500 text-sm pt-4">{{ icons.length }} icons</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Icons -->
|
<Footer />
|
||||||
<div class="py-4 text-center">
|
|
||||||
<Icons
|
<!-- Details -->
|
||||||
:icons="icons.slice(0, max)"
|
<Modal :value="!!current" @close="current = null">
|
||||||
:selected="selectedIcons"
|
<IconDetail :icon="current" />
|
||||||
:size="iconSize"
|
</Modal>
|
||||||
:display="listType"
|
|
||||||
:search="search"
|
<!-- Bag -->
|
||||||
:namespace="namespace"
|
<Modal :value="showBag" direction="right" @close="showBag = false">
|
||||||
@select="onSelect"
|
<Bag @close="showBag = false" />
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<!-- Bag Fab -->
|
||||||
|
<FAB
|
||||||
|
v-if="bags.length"
|
||||||
|
icon="carbon:shopping-bag"
|
||||||
|
:number="bags.length"
|
||||||
|
@click="showBag = true"
|
||||||
/>
|
/>
|
||||||
<button v-if="icons.length > max" class="btn m-2" @click="loadMore">
|
|
||||||
Load More
|
|
||||||
</button>
|
|
||||||
<p class="text-gray-500 text-sm px-2">
|
|
||||||
{{ icons.length }} icons
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Footer />
|
<!-- Selecting Note -->
|
||||||
|
<div
|
||||||
<!-- Details -->
|
class="fixed top-0 right-0 pl-4 pr-2 py-1 rounded-l-full bg-primary text-white shadow mt-16 cursor-pointer transition-transform duration-300 ease-in-out"
|
||||||
<Modal :value="!!current" @close="current = null">
|
:style="selectingMode ? {} : {transform: 'translateX(120%)'}"
|
||||||
<IconDetail :icon="current" />
|
@click="selectingMode = false"
|
||||||
</Modal>
|
>
|
||||||
|
Selecting Mode
|
||||||
<!-- Bag -->
|
<Icon icon="carbon:close" class="inline-block text-xl align-text-bottom" />
|
||||||
<Modal :value="showBag" direction="right" @close="showBag = false">
|
</div>
|
||||||
<Bag @close="showBag = false" />
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<!-- Bag Fab -->
|
|
||||||
<FAB
|
|
||||||
v-if="bags.length"
|
|
||||||
icon="carbon:shopping-bag"
|
|
||||||
:number="bags.length"
|
|
||||||
@click="showBag = true"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Selecting Note -->
|
|
||||||
<div
|
|
||||||
class="fixed top-0 right-0 pl-4 pr-2 py-1 rounded-l-full bg-primary text-white shadow mt-16 cursor-pointer transition-transform duration-300 ease-in-out"
|
|
||||||
:style="selectingMode ? {} : {transform: 'translateX(120%)'}"
|
|
||||||
@click="selectingMode = false"
|
|
||||||
>
|
|
||||||
Selecting Mode
|
|
||||||
<Icon icon="carbon:close" class="inline-block text-xl align-text-bottom" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</WithNavbar>
|
</WithNavbar>
|
||||||
@ -152,16 +109,25 @@
|
|||||||
|
|
||||||
<script lang='ts'>
|
<script lang='ts'>
|
||||||
import { defineComponent, ref, toRefs, computed, PropType } from 'vue'
|
import { defineComponent, ref, toRefs, computed, PropType } from 'vue'
|
||||||
import { iconSize, listType, showCategories, selectingMode, bags, toggleBag } from '../store'
|
import {
|
||||||
|
iconSize,
|
||||||
|
listType,
|
||||||
|
showCategories,
|
||||||
|
selectingMode,
|
||||||
|
bags,
|
||||||
|
toggleBag
|
||||||
|
} from '../store'
|
||||||
import { useSearch } from '../hooks/search'
|
import { useSearch } from '../hooks/search'
|
||||||
import { CollectionMeta } from '../data'
|
import { CollectionMeta } from '../data'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { isElectron } from '../env'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
collection: {
|
collection: {
|
||||||
type: Object as PropType<CollectionMeta>,
|
type: Object as PropType<CollectionMeta>,
|
||||||
required: true,
|
required: true
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const { collection } = toRefs(props)
|
const { collection } = toRefs(props)
|
||||||
@ -169,13 +135,11 @@ export default defineComponent({
|
|||||||
const showBag = ref(false)
|
const showBag = ref(false)
|
||||||
|
|
||||||
const current = ref<string | null>(null)
|
const current = ref<string | null>(null)
|
||||||
const max = ref(200)
|
const max = ref(isElectron ? 500 : 200)
|
||||||
|
|
||||||
const toggleCategory = (cat: string) => {
|
const toggleCategory = (cat: string) => {
|
||||||
if (category.value === cat)
|
if (category.value === cat) category.value = ''
|
||||||
category.value = ''
|
else category.value = cat
|
||||||
else
|
|
||||||
category.value = cat
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const namespace = computed(() => {
|
const namespace = computed(() => {
|
||||||
@ -183,39 +147,19 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const onSelect = (icon: string) => {
|
const onSelect = (icon: string) => {
|
||||||
if (selectingMode.value)
|
if (selectingMode.value) toggleBag(icon)
|
||||||
toggleBag(icon)
|
else current.value = icon
|
||||||
else
|
|
||||||
current.value = icon
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedIcons = computed(() => {
|
const selectedIcons = computed(() => {
|
||||||
if (selectingMode.value)
|
if (selectingMode.value) return bags.value
|
||||||
return bags.value
|
else return current.value ? [] : [current.value]
|
||||||
else
|
|
||||||
return current.value ? [] : [current.value]
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const loadMore = () => {
|
const loadMore = () => {
|
||||||
max.value += 100
|
max.value += 100
|
||||||
}
|
}
|
||||||
|
|
||||||
const setGrid = (type: string) => {
|
|
||||||
switch (type) {
|
|
||||||
case 'small':
|
|
||||||
iconSize.value = '2xl'
|
|
||||||
listType.value = 'grid'
|
|
||||||
break
|
|
||||||
case 'large':
|
|
||||||
iconSize.value = '4xl'
|
|
||||||
listType.value = 'grid'
|
|
||||||
break
|
|
||||||
default :
|
|
||||||
iconSize.value = '3xl'
|
|
||||||
listType.value = 'list'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
current,
|
current,
|
||||||
search,
|
search,
|
||||||
@ -227,7 +171,6 @@ export default defineComponent({
|
|||||||
loadMore,
|
loadMore,
|
||||||
iconSize,
|
iconSize,
|
||||||
listType,
|
listType,
|
||||||
setGrid,
|
|
||||||
namespace,
|
namespace,
|
||||||
selectedIcons,
|
selectedIcons,
|
||||||
|
|
||||||
@ -238,8 +181,8 @@ export default defineComponent({
|
|||||||
// bags
|
// bags
|
||||||
showBag,
|
showBag,
|
||||||
bags,
|
bags,
|
||||||
selectingMode,
|
selectingMode
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,36 +1,35 @@
|
|||||||
<template>
|
<template>
|
||||||
<WithNavbar>
|
<WithNavbar>
|
||||||
<div class="">
|
<div class="collections-list grid">
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
<div
|
||||||
|
v-for="collection in collections"
|
||||||
|
:key="collection.id"
|
||||||
|
class="px-2 py-4 border-r border-b border-gray-200"
|
||||||
|
>
|
||||||
<router-link
|
<router-link
|
||||||
v-for="collection in collections"
|
class="flex flex-col relative transition-all duration-300 text-gray-900 text-center justify-center hover:text-primary"
|
||||||
:key="collection.id"
|
|
||||||
:to="`/collection/${collection.id}`"
|
:to="`/collection/${collection.id}`"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col py-4 relative border-r border-b border-gray-200 transition-all duration-300 text-gray-900 hover:text-primary">
|
<div class="flex-auto text-lg">{{ collection.name }}</div>
|
||||||
<div class="flex-auto bg-opacity-50 text-center">
|
<div class="flex-auto opacity-50 text-xs">
|
||||||
<div class="text-lg">
|
<span>{{ collection.author }}</span>
|
||||||
{{ collection.name }}
|
<span class="px-1 opacity-25">/</span>
|
||||||
</div>
|
<span>{{ collection.license }}</span>
|
||||||
<div class="text-gray-500 text-sm block leading-none">
|
<span class="px-1 opacity-25">/</span>
|
||||||
{{ collection.author }}
|
<span>{{ collection.total }} icons</span>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Icons
|
|
||||||
:icons="collection.sampleIcons"
|
|
||||||
:namespace="`${collection.id}:`"
|
|
||||||
class="py-2 justify-center overflow-hidden flex-none pointer-events-none"
|
|
||||||
/>
|
|
||||||
<div class="flex-auto bg-opacity-50 text-center">
|
|
||||||
<div class="text-gray-500 text-xs block">
|
|
||||||
{{ collection.license }}
|
|
||||||
</div>
|
|
||||||
<div class="text-gray-500 text-xs block">
|
|
||||||
{{ collection.total }} icons
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<IconButton v-if="isFavorited(collection.id)" class="absolute top-0 right-0 p-2 text-lg" icon="carbon:bookmark" />
|
|
||||||
</div>
|
</div>
|
||||||
|
<Icons
|
||||||
|
:icons="collection.sampleIcons"
|
||||||
|
:namespace="`${collection.id}:`"
|
||||||
|
size="xl"
|
||||||
|
spacing="m-1"
|
||||||
|
class="mt-2 mb-1 justify-center opacity-75 overflow-hidden flex-none pointer-events-none"
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
v-if="isFavorited(collection.id)"
|
||||||
|
class="absolute top-0 right-0 p-2 text-lg"
|
||||||
|
icon="carbon:bookmark"
|
||||||
|
/>
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -49,8 +48,14 @@ export default defineComponent({
|
|||||||
return {
|
return {
|
||||||
collections: sortedCollectionsInfo,
|
collections: sortedCollectionsInfo,
|
||||||
isFavorited,
|
isFavorited,
|
||||||
sample,
|
sample
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.collections-list {
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -24,9 +24,6 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
fontFamily: {
|
|
||||||
sans: ['Roboto', ...defaultTheme.fontFamily.sans],
|
|
||||||
},
|
|
||||||
fontSize: {
|
fontSize: {
|
||||||
'7xl': '5rem',
|
'7xl': '5rem',
|
||||||
'8xl': '6rem',
|
'8xl': '6rem',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user