Add IconsResolver for auto-import, explicit imports only for dynamic maps

This commit is contained in:
Danila Poyarkov 2026-02-28 08:29:39 +03:00
parent 3ddc671bc5
commit bcd8bf6bca
10 changed files with 45 additions and 19 deletions

View file

@ -28,6 +28,7 @@
"oxlint": "^1.50.0",
"typescript": "~5.8.3",
"unplugin-icons": "^23.0.1",
"unplugin-vue-components": "^31.0.0",
"vite": "^7.0.4",
},
},
@ -371,6 +372,8 @@
"canvaskit-wasm": ["canvaskit-wasm@0.40.0", "", { "dependencies": { "@webgpu/types": "0.1.21" } }, "sha512-Od2o+ZmoEw9PBdN/yCGvzfu0WVqlufBPEWNG452wY7E9aT8RBE+ChpZF526doOlg7zumO4iCS+RAeht4P0Gbpw=="],
"chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
"confbox": ["confbox@0.2.4", "", {}, "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ=="],
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
@ -461,6 +464,8 @@
"quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="],
"readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
"reka-ui": ["reka-ui@2.8.2", "", { "dependencies": { "@floating-ui/dom": "^1.6.13", "@floating-ui/vue": "^1.1.6", "@internationalized/date": "^3.5.0", "@internationalized/number": "^3.5.0", "@tanstack/vue-virtual": "^3.12.0", "@vueuse/core": "^14.1.0", "@vueuse/shared": "^14.1.0", "aria-hidden": "^1.2.4", "defu": "^6.1.4", "ohash": "^2.0.11" }, "peerDependencies": { "vue": ">= 3.2.0" } }, "sha512-8lTKcJhmG+D3UyJxhBnNnW/720sLzm0pbA9AC1MWazmJ5YchJAyTSl+O00xP/kxBmEN0fw5JqWVHguiFmsGjzA=="],
"rollup": ["rollup@4.59.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.59.0", "@rollup/rollup-android-arm64": "4.59.0", "@rollup/rollup-darwin-arm64": "4.59.0", "@rollup/rollup-darwin-x64": "4.59.0", "@rollup/rollup-freebsd-arm64": "4.59.0", "@rollup/rollup-freebsd-x64": "4.59.0", "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", "@rollup/rollup-linux-arm-musleabihf": "4.59.0", "@rollup/rollup-linux-arm64-gnu": "4.59.0", "@rollup/rollup-linux-arm64-musl": "4.59.0", "@rollup/rollup-linux-loong64-gnu": "4.59.0", "@rollup/rollup-linux-loong64-musl": "4.59.0", "@rollup/rollup-linux-ppc64-gnu": "4.59.0", "@rollup/rollup-linux-ppc64-musl": "4.59.0", "@rollup/rollup-linux-riscv64-gnu": "4.59.0", "@rollup/rollup-linux-riscv64-musl": "4.59.0", "@rollup/rollup-linux-s390x-gnu": "4.59.0", "@rollup/rollup-linux-x64-gnu": "4.59.0", "@rollup/rollup-linux-x64-musl": "4.59.0", "@rollup/rollup-openbsd-x64": "4.59.0", "@rollup/rollup-openharmony-arm64": "4.59.0", "@rollup/rollup-win32-arm64-msvc": "4.59.0", "@rollup/rollup-win32-ia32-msvc": "4.59.0", "@rollup/rollup-win32-x64-gnu": "4.59.0", "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg=="],
@ -487,6 +492,10 @@
"unplugin-icons": ["unplugin-icons@23.0.1", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/utils": "^3.1.0", "local-pkg": "^1.1.2", "obug": "^2.1.1", "unplugin": "^2.3.11" }, "peerDependencies": { "@svgr/core": ">=7.0.0", "@svgx/core": "^1.0.1", "@vue/compiler-sfc": "^3.0.2", "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["@svgr/core", "@svgx/core", "@vue/compiler-sfc", "svelte"] }, "sha512-rv0XEJepajKzDLvRUWASM8K+8+/CCfZn2jtogXqg6RIp7kpatRc/aFrVJn8ANQA09e++lPEEv9yX8cC9enc+QQ=="],
"unplugin-utils": ["unplugin-utils@0.3.1", "", { "dependencies": { "pathe": "^2.0.3", "picomatch": "^4.0.3" } }, "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog=="],
"unplugin-vue-components": ["unplugin-vue-components@31.0.0", "", { "dependencies": { "chokidar": "^5.0.0", "local-pkg": "^1.1.2", "magic-string": "^0.30.21", "mlly": "^1.8.0", "obug": "^2.1.1", "picomatch": "^4.0.3", "tinyglobby": "^0.2.15", "unplugin": "^2.3.11", "unplugin-utils": "^0.3.1" }, "peerDependencies": { "@nuxt/kit": "^3.2.2 || ^4.0.0", "vue": "^3.0.0" }, "optionalPeers": ["@nuxt/kit"] }, "sha512-4ULwfTZTLuWJ7+S9P7TrcStYLsSRkk6vy2jt/WTfgUEUb0nW9//xxmrfhyHUEVpZ2UKRRwfRb8Yy15PDbVZf+Q=="],
"vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="],
"vue": ["vue@3.5.29", "", { "dependencies": { "@vue/compiler-dom": "3.5.29", "@vue/compiler-sfc": "3.5.29", "@vue/runtime-dom": "3.5.29", "@vue/server-renderer": "3.5.29", "@vue/shared": "3.5.29" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-BZqN4Ze6mDQVNAni0IHeMJ5mwr8VAJ3MQC9FmprRhcBYENw+wOAAjRj8jfmN6FLl0j96OXbR+CjWhmAmM+QGnA=="],

22
components.d.ts vendored Normal file
View file

@ -0,0 +1,22 @@
/* eslint-disable */
// @ts-nocheck
// biome-ignore lint: disable
// oxlint-disable
// ------
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
ColorPicker: typeof import('./src/components/ColorPicker.vue')['default']
EditorCanvas: typeof import('./src/components/EditorCanvas.vue')['default']
IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default']
LayersPanel: typeof import('./src/components/LayersPanel.vue')['default']
PropertiesPanel: typeof import('./src/components/PropertiesPanel.vue')['default']
ScrubInput: typeof import('./src/components/ScrubInput.vue')['default']
Toolbar: typeof import('./src/components/Toolbar.vue')['default']
}
}

View file

@ -42,6 +42,7 @@
"oxlint": "^1.50.0",
"typescript": "~5.8.3",
"unplugin-icons": "^23.0.1",
"unplugin-vue-components": "^31.0.0",
"vite": "^7.0.4"
}
}

View file

@ -1,16 +1,15 @@
<script setup lang="ts">
import { ref, computed, type Component } from 'vue'
import { ref, computed } from 'vue'
import { useEventListener } from '@vueuse/core'
import { TreeRoot, TreeItem } from 'reka-ui'
import IconSquare from '~icons/lucide/square'
import IconCircle from '~icons/lucide/circle'
import IconFrame from '~icons/lucide/frame'
import IconGroup from '~icons/lucide/group'
import IconMinus from '~icons/lucide/minus'
import IconType from '~icons/lucide/type'
import IconPenTool from '~icons/lucide/pen-tool'
import IconChevronRight from '~icons/lucide/chevron-right'
import IconSquare from '~icons/lucide/square'
import IconType from '~icons/lucide/type'
import { useEditorStore } from '../stores/editor'
@ -23,7 +22,7 @@ interface LayerNode {
children?: LayerNode[]
}
const NODE_ICONS: Record<string, Component> = {
const nodeIcons: Record<string, typeof IconSquare> = {
ELLIPSE: IconCircle,
FRAME: IconFrame,
GROUP: IconGroup,
@ -33,10 +32,6 @@ const NODE_ICONS: Record<string, Component> = {
RECTANGLE: IconSquare
}
function nodeIcon(type: string): Component {
return NODE_ICONS[type] ?? IconSquare
}
function buildTree(parentId: string): LayerNode[] {
const parent = store.graph.getNode(parentId)
if (!parent) return []
@ -240,10 +235,10 @@ function updateDropTarget(ev: PointerEvent) {
:class="isExpanded ? 'rotate-90' : 'rotate-0'"
@click.stop="toggleExpand(item.value.id)"
>
<IconChevronRight class="size-3 rotate-90" />
<icon-lucide-chevron-right class="size-3 rotate-90" />
</span>
<span v-else class="w-4 shrink-0" />
<component :is="nodeIcon(item.value.type)" class="size-3 shrink-0 opacity-70" />
<component :is="nodeIcons[item.value.type] ?? IconSquare" class="size-3 shrink-0 opacity-70" />
<span class="truncate">{{ item.value.name }}</span>
</button>
</TreeItem>

View file

@ -1,11 +1,7 @@
<script setup lang="ts">
import { type Component } from 'vue'
import IconMousePointer from '~icons/lucide/mouse-pointer'
import IconFrame from '~icons/lucide/frame'
import IconSquare from '~icons/lucide/square'
import IconCircle from '~icons/lucide/circle'
import IconMinus from '~icons/lucide/minus'
import IconPenTool from '~icons/lucide/pen-tool'
import IconType from '~icons/lucide/type'
import IconHand from '~icons/lucide/hand'
@ -16,12 +12,12 @@ import type { Tool } from '../stores/editor'
const store = useEditorStore()
const TOOL_ICONS: Record<Tool, Component> = {
const toolIcons: Record<Tool, typeof IconSquare> = {
SELECT: IconMousePointer,
FRAME: IconFrame,
RECTANGLE: IconSquare,
ELLIPSE: IconCircle,
LINE: IconMinus,
ELLIPSE: IconSquare,
LINE: IconSquare,
PEN: IconPenTool,
TEXT: IconType,
HAND: IconHand
@ -41,7 +37,7 @@ const TOOL_ICONS: Record<Tool, Component> = {
:title="`${tool.label} (${tool.shortcut})`"
@click="store.setTool(tool.key)"
>
<component :is="TOOL_ICONS[tool.key]" class="size-4" />
<component :is="toolIcons[tool.key]" class="size-4" />
</button>
</div>
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -2,6 +2,8 @@ import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import tailwindcss from '@tailwindcss/vite'
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
import Components from 'unplugin-vue-components/vite'
import { copyFileSync, existsSync } from 'fs'
// @ts-expect-error process is a nodejs global
@ -21,6 +23,7 @@ export default defineConfig(async () => ({
},
tailwindcss(),
Icons({ compiler: 'vue3' }),
Components({ resolvers: [IconsResolver({ prefix: 'icon' })] }),
vue()
],
clearScreen: false,