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-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">
|
||||
<a href="https://iconify.antfu.me/">⇁ iconify.antfu.me</a>
|
||||
<a href="https://iconify.antfu.me/">iconify.antfu.me</a>
|
||||
</p>
|
||||
|
||||

|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "electron-webpack-quick-start",
|
||||
"name": "iconify-explorer-electron",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
@ -9,6 +9,7 @@
|
||||
"dist:dir": "yarn dist --dir -c.compression=store -c.mac.identity=null"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-loader": "^8.1.0",
|
||||
"electron-debug": "^3.1.0",
|
||||
"electron-serve": "^1.0.0",
|
||||
"electron-util": "^0.14.2",
|
||||
|
||||
@ -17,7 +17,7 @@ const createMainWindow = async() => {
|
||||
height: 500,
|
||||
minWidth: 200,
|
||||
minHeight: 200,
|
||||
titleBarStyle: 'hidden',
|
||||
titleBarStyle: 'hiddenInset',
|
||||
})
|
||||
|
||||
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 name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Iconify Explorer</title>
|
||||
<script src="https://code.iconify.design/1/1.0.7/iconify.without-api.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/svg-packer@latest/dist/index.browser.min.js" defer></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
|
||||
<script src="/lib/iconify.min.js"></script>
|
||||
<script src="/lib/svg-packer.js" defer></script>
|
||||
</head>
|
||||
<body class="dragging">
|
||||
<div id="app"></div>
|
||||
|
||||
@ -3,8 +3,10 @@
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"postinstall": "ts-node -P tsconfig.tsnode.json scripts/prepare.ts",
|
||||
"dev": "vite",
|
||||
"dev:electron": "pnpm -C ./electron dev",
|
||||
"build": "vite build"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -15,6 +17,8 @@
|
||||
"vue-router": "next"
|
||||
},
|
||||
"devDependencies": {
|
||||
"svg-packer": "^0.0.3",
|
||||
"@iconify/iconify": "^1.0.7",
|
||||
"@antfu/eslint-config-vue": "^0.2.13",
|
||||
"@iconify/json": "^1.1.186",
|
||||
"@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 fs from 'fs-extra'
|
||||
|
||||
const dir = path.resolve(__dirname, '../node_modules/@iconify/json')
|
||||
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'))
|
||||
await fs.ensureDir(collectionsDir)
|
||||
|
||||
@ -21,9 +22,12 @@ async function prepare() {
|
||||
|
||||
const icons = Object.keys(setData.icons)
|
||||
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)
|
||||
collectionsMeta.push(meta)
|
||||
|
||||
@ -34,4 +38,29 @@ async function prepare() {
|
||||
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()
|
||||
|
||||
17
src/App.vue
17
src/App.vue
@ -1,10 +1,7 @@
|
||||
<template>
|
||||
<div class="flex flex-col h-screen overflow-hidden" :style="style">
|
||||
<div class="flex flex-auto overflow-hidden">
|
||||
<Drawer v-if="!isRoot" class="h-full overflow-auto flex-none hidden md:block" style="width:280px" />
|
||||
<div class="h-full flex-auto overflow-auto">
|
||||
<router-view />
|
||||
</div>
|
||||
<div class="h-full flex-auto overflow-auto">
|
||||
<router-view />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -16,17 +13,13 @@ import { themeColor } from './store'
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
const isRoot = computed(() => route.path === '/')
|
||||
|
||||
const style = computed(() => ({
|
||||
'--theme-color': themeColor.value,
|
||||
'--theme-color': themeColor.value
|
||||
}))
|
||||
|
||||
return {
|
||||
isRoot,
|
||||
style,
|
||||
style
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<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>
|
||||
<div class="text-gray-700 text-lg">
|
||||
@ -10,7 +10,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<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')" />
|
||||
</div>
|
||||
<template v-if="bags.length">
|
||||
@ -18,8 +18,8 @@
|
||||
<Icons :icons="bags" />
|
||||
</div>
|
||||
<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" icon="carbon:download" text="Download All SVGs" :active="true" @click="wip" />
|
||||
<IconButton class="p-1 cursor-pointer" icon="carbon:function" text="Generate Icon Fonts" :active="true" @click="pack()" />
|
||||
<IconButton class="p-1 cursor-pointer" icon="carbon:download" text="Download All SVGs" :active="true" @click="wip" />
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
<template>
|
||||
<div class="border-r border-gray-200">
|
||||
<!-- Back button, Mobile Only -->
|
||||
<div
|
||||
v-if='!isElectron'
|
||||
class="border-b border-gray-200"
|
||||
>
|
||||
<IconButton
|
||||
@ -10,6 +12,8 @@
|
||||
@click="$router.replace('/')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Collections -->
|
||||
<router-link
|
||||
v-for="collection in collections"
|
||||
:key="collection.id"
|
||||
@ -46,6 +50,7 @@ import { defineComponent, computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { sortedCollectionsInfo } from '../data'
|
||||
import { isFavorited, toggleFavorite } from '../store'
|
||||
import { isElectron } from '../env'
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
@ -64,6 +69,7 @@ export default defineComponent({
|
||||
current,
|
||||
toggleFavorite,
|
||||
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>
|
||||
<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" />
|
||||
<div
|
||||
v-if="number"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<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' "
|
||||
>
|
||||
<Icon
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
<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
|
||||
v-for="icon of icons"
|
||||
:key="icon"
|
||||
class="non-dragging icons-item cursor-pointer flex m-2"
|
||||
:class="selected.includes(namespace+icon) ? 'active': ''"
|
||||
class="non-dragging icons-item flex"
|
||||
:class="[spacing, selected.includes(namespace+icon) ? 'active': '']"
|
||||
@click="$emit('select', namespace+icon)"
|
||||
>
|
||||
<Icon class="non-dragging" :icon="namespace+icon" />
|
||||
@ -33,14 +33,14 @@ export default defineComponent({
|
||||
type: String,
|
||||
default: '2xl',
|
||||
},
|
||||
spacing: {
|
||||
type: String,
|
||||
default: 'm-2'
|
||||
},
|
||||
search: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#555',
|
||||
},
|
||||
display: {
|
||||
type: String,
|
||||
default: 'grid',
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
@click="$emit('close')"
|
||||
/>
|
||||
<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"
|
||||
: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>
|
||||
<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">
|
||||
<slot />
|
||||
</div>
|
||||
@ -9,13 +10,23 @@
|
||||
|
||||
<script lang='ts'>
|
||||
import { defineComponent } from 'vue'
|
||||
import ElectronNav from './ElectronNav.vue'
|
||||
import { isElectron } from '../env'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
ElectronNav
|
||||
},
|
||||
props: {
|
||||
navClass: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
setup(){
|
||||
return {
|
||||
isElectron,
|
||||
}
|
||||
}
|
||||
})
|
||||
</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 Drawer from './components/Drawer.vue'
|
||||
import Bag from './components/Bag.vue'
|
||||
import ViewControls from './components/ViewControls.vue'
|
||||
import './main.css'
|
||||
|
||||
const app = createApp(App)
|
||||
@ -52,5 +53,6 @@ app.component('Footer', Footer)
|
||||
app.component('Drawer', Drawer)
|
||||
app.component('FAB', FAB)
|
||||
app.component('Bag', Bag)
|
||||
app.component('ViewControls', ViewControls)
|
||||
|
||||
app.mount('#app')
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div v-if="!collection" class="p-4 text-gray-700">
|
||||
Loading...
|
||||
</div>
|
||||
<WithNavbar v-if="!collection">
|
||||
<div class="py-8 px-4 text-gray-700 text-center">Loading...</div>
|
||||
</WithNavbar>
|
||||
<IconSet v-else :collection="collection" :installed="installed" />
|
||||
</template>
|
||||
|
||||
@ -13,19 +13,19 @@ import {
|
||||
getFullMeta,
|
||||
install,
|
||||
getMeta,
|
||||
CollectionMeta,
|
||||
CollectionMeta
|
||||
} from '../data'
|
||||
import IconSet from './IconSet.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
IconSet,
|
||||
IconSet
|
||||
},
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const loaded = ref(isMetaLoaded(props.id))
|
||||
@ -34,7 +34,7 @@ export default defineComponent({
|
||||
|
||||
watch(
|
||||
() => props.id,
|
||||
async() => {
|
||||
async () => {
|
||||
loaded.value = false
|
||||
|
||||
if (props.id === 'all') {
|
||||
@ -42,23 +42,22 @@ export default defineComponent({
|
||||
collection.value = {
|
||||
id: '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)
|
||||
collection.value = await getMeta(props.id)
|
||||
}
|
||||
loaded.value = true
|
||||
},
|
||||
{ immediate: true },
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
return {
|
||||
collection,
|
||||
loaded,
|
||||
installed,
|
||||
installed
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -1,150 +1,107 @@
|
||||
<template>
|
||||
<WithNavbar nav-class="md:hidden">
|
||||
<div class="py-5 px-5 md:px-8">
|
||||
<div class="flex">
|
||||
<!-- Left -->
|
||||
<div class="flex-auto">
|
||||
<div class="text-gray-900 text-xl flex">
|
||||
{{ collection.name }}
|
||||
<a
|
||||
v-if="collection.url"
|
||||
class="text-gray-500 hover:text-gray-900 mt-1 text-base"
|
||||
:href="collection.url"
|
||||
target="_blank"
|
||||
>
|
||||
<Icon icon="la:external-link-square-alt-solid" />
|
||||
</a>
|
||||
<div class="flex flex-auto h-full overflow-hidden">
|
||||
<Drawer class="h-full overflow-auto flex-none hidden md:block" style="width:280px" />
|
||||
<div class="py-5 px-5 md:px-8 h-full overflow-y-auto">
|
||||
<div class="flex">
|
||||
<!-- Left -->
|
||||
<div class="flex-auto">
|
||||
<div class="text-gray-900 text-xl flex">
|
||||
{{ collection.name }}
|
||||
<a
|
||||
v-if="collection.url"
|
||||
class="text-gray-500 hover:text-gray-900 mt-1 text-base"
|
||||
:href="collection.url"
|
||||
target="_blank"
|
||||
>
|
||||
<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>
|
||||
<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">
|
||||
<div class="px-1 text-xl text-gray-800 flex">
|
||||
<IconButton
|
||||
class="hidden md:block mr-3"
|
||||
icon="carbon:checkbox-checked"
|
||||
:active="selectingMode"
|
||||
@click="selectingMode = !selectingMode"
|
||||
/>
|
||||
<div class="hidden md:block mx-1 h-full m-auto bg-gray-200" style="width:1px;" />
|
||||
<template v-if="collection.categories">
|
||||
<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" />
|
||||
<!-- Categories -->
|
||||
<div class="py-3 pr-3 overflow-x-auto flex flex-no-wrap">
|
||||
<template v-if="collection.categories">
|
||||
<div
|
||||
v-for="c of Object.keys(collection.categories)"
|
||||
:key="c"
|
||||
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"
|
||||
:class="c === category ? 'text-primary border-primary' : ''"
|
||||
@click="toggleCategory(c)"
|
||||
>{{ c }}</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Categories -->
|
||||
<div class="py-2">
|
||||
<template v-if="collection.categories && showCategories">
|
||||
<div
|
||||
v-for="c of Object.keys(collection.categories)"
|
||||
:key="c"
|
||||
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' : ''"
|
||||
@click="toggleCategory(c)"
|
||||
>
|
||||
{{ c }}
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<!-- Search -->
|
||||
<div class="flex">
|
||||
<input
|
||||
v-model="search"
|
||||
class="shadow rounded outline-none py-2 px-4 flex-auto"
|
||||
placeholder="Search..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Search -->
|
||||
<div class="flex">
|
||||
<input
|
||||
v-model="search"
|
||||
class="shadow rounded outline-none py-2 px-4 flex-auto"
|
||||
placeholder="Search..."
|
||||
>
|
||||
</div>
|
||||
<!-- Icons -->
|
||||
<div class="py-4 text-center">
|
||||
<Icons
|
||||
:icons="icons.slice(0, max)"
|
||||
:selected="selectedIcons"
|
||||
:size="iconSize"
|
||||
:display="listType"
|
||||
: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 -->
|
||||
<div class="py-4 text-center">
|
||||
<Icons
|
||||
:icons="icons.slice(0, max)"
|
||||
:selected="selectedIcons"
|
||||
:size="iconSize"
|
||||
:display="listType"
|
||||
:search="search"
|
||||
:namespace="namespace"
|
||||
@select="onSelect"
|
||||
<Footer />
|
||||
|
||||
<!-- Details -->
|
||||
<Modal :value="!!current" @close="current = null">
|
||||
<IconDetail :icon="current" />
|
||||
</Modal>
|
||||
|
||||
<!-- Bag -->
|
||||
<Modal :value="showBag" direction="right" @close="showBag = false">
|
||||
<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 />
|
||||
|
||||
<!-- Details -->
|
||||
<Modal :value="!!current" @close="current = null">
|
||||
<IconDetail :icon="current" />
|
||||
</Modal>
|
||||
|
||||
<!-- Bag -->
|
||||
<Modal :value="showBag" direction="right" @close="showBag = false">
|
||||
<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" />
|
||||
<!-- 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>
|
||||
</WithNavbar>
|
||||
@ -152,16 +109,25 @@
|
||||
|
||||
<script lang='ts'>
|
||||
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 { CollectionMeta } from '../data'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { isElectron } from '../env'
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
collection: {
|
||||
type: Object as PropType<CollectionMeta>,
|
||||
required: true,
|
||||
},
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const { collection } = toRefs(props)
|
||||
@ -169,13 +135,11 @@ export default defineComponent({
|
||||
const showBag = ref(false)
|
||||
|
||||
const current = ref<string | null>(null)
|
||||
const max = ref(200)
|
||||
const max = ref(isElectron ? 500 : 200)
|
||||
|
||||
const toggleCategory = (cat: string) => {
|
||||
if (category.value === cat)
|
||||
category.value = ''
|
||||
else
|
||||
category.value = cat
|
||||
if (category.value === cat) category.value = ''
|
||||
else category.value = cat
|
||||
}
|
||||
|
||||
const namespace = computed(() => {
|
||||
@ -183,39 +147,19 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const onSelect = (icon: string) => {
|
||||
if (selectingMode.value)
|
||||
toggleBag(icon)
|
||||
else
|
||||
current.value = icon
|
||||
if (selectingMode.value) toggleBag(icon)
|
||||
else current.value = icon
|
||||
}
|
||||
|
||||
const selectedIcons = computed(() => {
|
||||
if (selectingMode.value)
|
||||
return bags.value
|
||||
else
|
||||
return current.value ? [] : [current.value]
|
||||
if (selectingMode.value) return bags.value
|
||||
else return current.value ? [] : [current.value]
|
||||
})
|
||||
|
||||
const loadMore = () => {
|
||||
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 {
|
||||
current,
|
||||
search,
|
||||
@ -227,7 +171,6 @@ export default defineComponent({
|
||||
loadMore,
|
||||
iconSize,
|
||||
listType,
|
||||
setGrid,
|
||||
namespace,
|
||||
selectedIcons,
|
||||
|
||||
@ -238,8 +181,8 @@ export default defineComponent({
|
||||
// bags
|
||||
showBag,
|
||||
bags,
|
||||
selectingMode,
|
||||
selectingMode
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -1,36 +1,35 @@
|
||||
<template>
|
||||
<WithNavbar>
|
||||
<div class="">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||
<div class="collections-list grid">
|
||||
<div
|
||||
v-for="collection in collections"
|
||||
:key="collection.id"
|
||||
class="px-2 py-4 border-r border-b border-gray-200"
|
||||
>
|
||||
<router-link
|
||||
v-for="collection in collections"
|
||||
:key="collection.id"
|
||||
class="flex flex-col relative transition-all duration-300 text-gray-900 text-center justify-center hover:text-primary"
|
||||
: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 bg-opacity-50 text-center">
|
||||
<div class="text-lg">
|
||||
{{ collection.name }}
|
||||
</div>
|
||||
<div class="text-gray-500 text-sm block leading-none">
|
||||
{{ collection.author }}
|
||||
</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 class="flex-auto text-lg">{{ collection.name }}</div>
|
||||
<div class="flex-auto opacity-50 text-xs">
|
||||
<span>{{ collection.author }}</span>
|
||||
<span class="px-1 opacity-25">/</span>
|
||||
<span>{{ collection.license }}</span>
|
||||
<span class="px-1 opacity-25">/</span>
|
||||
<span>{{ collection.total }} icons</span>
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
@ -49,8 +48,14 @@ export default defineComponent({
|
||||
return {
|
||||
collections: sortedCollectionsInfo,
|
||||
isFavorited,
|
||||
sample,
|
||||
sample
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.collections-list {
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
}
|
||||
</style>
|
||||
@ -24,9 +24,6 @@ module.exports = {
|
||||
},
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['Roboto', ...defaultTheme.fontFamily.sans],
|
||||
},
|
||||
fontSize: {
|
||||
'7xl': '5rem',
|
||||
'8xl': '6rem',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user