feat: 离线图标+在线图标
parent
256884d15b
commit
c117a90750
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 25 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 10 KiB |
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10a10 10 0 0 0 10-10c0-5.53-4.5-10-10-10M6.76 8.82l1.06-1.06l1.06 1.06l1.06-1.06L11 8.82L9.94 9.88L11 10.94L9.94 12l-1.06-1.06L7.82 12l-1.06-1.06l1.06-1.06l-1.06-1.06m.13 8.68C7.69 15.46 9.67 14 12 14c2.33 0 4.31 1.46 5.11 3.5H6.89m10.35-6.56L16.18 12l-1.06-1.06L14.06 12L13 10.94l1.06-1.06L13 8.82l1.06-1.06l1.06 1.06l1.06-1.06l1.06 1.06l-1.06 1.06l1.06 1.06Z"/></svg>
|
After Width: | Height: | Size: 517 B |
@ -0,0 +1 @@
|
|||||||
|
保持此文件夹,用于生成文件
|
@ -0,0 +1,18 @@
|
|||||||
|
import { addCollection } from '@iconify/vue'
|
||||||
|
import antdesignIcons from '@iconify/json/json/ant-design.json'
|
||||||
|
import gridiconsIcons from '@iconify/json/json/gridicons.json'
|
||||||
|
import mdiIcons from '@iconify/json/json/mdi.json'
|
||||||
|
import ionIcons from '@iconify/json/json/ion.json'
|
||||||
|
import carbonIcons from '@iconify/json/json/carbon.json'
|
||||||
|
import emojioneIcons from '@iconify/json/json/emojione.json'
|
||||||
|
|
||||||
|
import CustomSvgJSON from '/build/_generated/svg.json'
|
||||||
|
|
||||||
|
addCollection(antdesignIcons)
|
||||||
|
addCollection(gridiconsIcons)
|
||||||
|
addCollection(mdiIcons)
|
||||||
|
addCollection(ionIcons)
|
||||||
|
addCollection(carbonIcons)
|
||||||
|
addCollection(emojioneIcons)
|
||||||
|
|
||||||
|
addCollection(CustomSvgJSON)
|
@ -0,0 +1,15 @@
|
|||||||
|
import antdesignIcons from '@iconify/json/json/ant-design.json'
|
||||||
|
import gridiconsIcons from '@iconify/json/json/gridicons.json'
|
||||||
|
import mdiIcons from '@iconify/json/json/mdi.json'
|
||||||
|
import ionIcons from '@iconify/json/json/ion.json'
|
||||||
|
import carbonIcons from '@iconify/json/json/carbon.json'
|
||||||
|
import emojioneIcons from '@iconify/json/json/emojione.json'
|
||||||
|
|
||||||
|
import CustomSvgJSON from '/build/_generated/svg.json'
|
||||||
|
|
||||||
|
const collections = [antdesignIcons, gridiconsIcons, mdiIcons, ionIcons, carbonIcons, emojioneIcons, CustomSvgJSON]
|
||||||
|
const ret: string[] = []
|
||||||
|
collections.forEach((item) => {
|
||||||
|
ret.push(...Object.keys(item.icons).map(key => `${item.prefix}:${key}`))
|
||||||
|
})
|
||||||
|
export default ret
|
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"icons": {},
|
||||||
|
"prefix": "nl-svg"
|
||||||
|
}
|
@ -0,0 +1,306 @@
|
|||||||
|
/**
|
||||||
|
* This is an advanced example for creating icon bundles for Iconify SVG Framework.
|
||||||
|
*
|
||||||
|
* It creates a bundle from:
|
||||||
|
* - All SVG files in a directory.
|
||||||
|
* - Custom JSON files.
|
||||||
|
* - Iconify icon sets.
|
||||||
|
* - SVG framework.
|
||||||
|
*
|
||||||
|
* This example uses Iconify Tools to import and clean up icons.
|
||||||
|
* For Iconify Tools documentation visit https://docs.iconify.design/tools/tools2/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { promises as fs } from 'node:fs'
|
||||||
|
import { dirname } from 'node:path'
|
||||||
|
import type { IconifyJSON, IconifyMetaData } from '@iconify/types'
|
||||||
|
|
||||||
|
// Installation: npm install --save-dev @iconify/tools @iconify/utils @iconify/json @iconify/iconify
|
||||||
|
import {
|
||||||
|
cleanupSVG,
|
||||||
|
importDirectory,
|
||||||
|
isEmptyColor,
|
||||||
|
parseColors,
|
||||||
|
runSVGO,
|
||||||
|
} from '@iconify/tools'
|
||||||
|
import { getIcons, minifyIconSet, stringToIcon } from '@iconify/utils'
|
||||||
|
|
||||||
|
import { buildUtilsReadFile, buildUtilsWriteFile } from '../../utils'
|
||||||
|
import {
|
||||||
|
IconLog,
|
||||||
|
SvgPrefix,
|
||||||
|
iconBundlePath,
|
||||||
|
iconListPath,
|
||||||
|
iconSVGPath,
|
||||||
|
} from '../src'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Script configuration
|
||||||
|
*/
|
||||||
|
interface BundleScriptCustomSVGConfig {
|
||||||
|
// Path to SVG files
|
||||||
|
dir: string
|
||||||
|
|
||||||
|
// True if icons should be treated as monotone: colors replaced with currentColor
|
||||||
|
monotone: boolean
|
||||||
|
|
||||||
|
// Icon set prefix
|
||||||
|
prefix: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BundleScriptCustomJSONConfig {
|
||||||
|
// Path to JSON file
|
||||||
|
filename: string
|
||||||
|
|
||||||
|
// List of icons to import. If missing, all icons will be imported
|
||||||
|
icons?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BundleScriptConfig {
|
||||||
|
// Custom SVG to import and bundle
|
||||||
|
svg?: BundleScriptCustomSVGConfig[]
|
||||||
|
|
||||||
|
// Icons to bundled from @iconify/json packages
|
||||||
|
icons?: string[]
|
||||||
|
|
||||||
|
// List of JSON files to bundled
|
||||||
|
// Entry can be a string, pointing to filename or a BundleScriptCustomJSONConfig object (see type above)
|
||||||
|
// If entry is a string or object without 'icons' property, an entire JSON file will be bundled
|
||||||
|
json?: (string | BundleScriptCustomJSONConfig)[]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iconify component (this changes import statement in generated file)
|
||||||
|
// Available options: '@iconify/react' for React, '@iconify/vue' for Vue 3, '@iconify/vue2' for Vue 2, '@iconify/svelte' for Svelte
|
||||||
|
const component = '@iconify/vue'
|
||||||
|
|
||||||
|
// Set to true to use require() instead of import
|
||||||
|
const CommonJS = false
|
||||||
|
|
||||||
|
// File to save bundle to
|
||||||
|
const target = iconBundlePath
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do stuff!
|
||||||
|
*/
|
||||||
|
export async function generateIconUsedBundle() {
|
||||||
|
const iconPools = await buildUtilsReadFile(iconListPath)
|
||||||
|
|
||||||
|
const allIconsArr = Array.from<string>(
|
||||||
|
JSON.parse(iconPools.replace('export default ', '')),
|
||||||
|
)
|
||||||
|
|
||||||
|
const sources: BundleScriptConfig = {
|
||||||
|
// svg: [
|
||||||
|
// {
|
||||||
|
// dir: 'svg',
|
||||||
|
// monotone: true,
|
||||||
|
// prefix: 'svg',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// dir: 'emojis',
|
||||||
|
// monotone: false,
|
||||||
|
// prefix: 'emoji',
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
|
||||||
|
icons: allIconsArr,
|
||||||
|
|
||||||
|
// json: [
|
||||||
|
// // Custom JSON file
|
||||||
|
// 'json/gg.json',
|
||||||
|
// // Iconify JSON file (@iconify/json is a package name, /json/ is directory where files are, then filename)
|
||||||
|
// require.resolve('@iconify/json/json/tabler.json'),
|
||||||
|
// // Custom file with only few icons
|
||||||
|
// {
|
||||||
|
// filename: require.resolve('@iconify/json/json/line-md.json'),
|
||||||
|
// icons: [
|
||||||
|
// 'home-twotone-alt',
|
||||||
|
// 'github',
|
||||||
|
// 'document-list',
|
||||||
|
// 'document-code',
|
||||||
|
// 'image-twotone',
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
}
|
||||||
|
|
||||||
|
let bundle = CommonJS
|
||||||
|
? `const { addCollection } = require('${component}');\n\n`
|
||||||
|
: `import { addCollection } from '${component}';\n\n`
|
||||||
|
|
||||||
|
// Create directory for output if missing
|
||||||
|
const dir = dirname(target)
|
||||||
|
try {
|
||||||
|
await fs.mkdir(dir, {
|
||||||
|
recursive: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert sources.icons to sources.json
|
||||||
|
*/
|
||||||
|
if (sources.icons) {
|
||||||
|
const sourcesJSON = sources.json ? sources.json : (sources.json = [])
|
||||||
|
|
||||||
|
// Sort icons by prefix
|
||||||
|
const organizedList = organizeIconsList(sources.icons)
|
||||||
|
for (const prefix in organizedList) {
|
||||||
|
if (prefix !== SvgPrefix) {
|
||||||
|
const filename = require.resolve(`@iconify/json/json/${prefix}.json`)
|
||||||
|
sourcesJSON.push({
|
||||||
|
filename,
|
||||||
|
icons: organizedList[prefix],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sourcesJSON.push({
|
||||||
|
filename: iconSVGPath,
|
||||||
|
icons: organizedList[prefix],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bundle JSON files
|
||||||
|
*/
|
||||||
|
if (sources.json) {
|
||||||
|
for (let i = 0; i < sources.json.length; i++) {
|
||||||
|
const item = sources.json[i]
|
||||||
|
|
||||||
|
// Load icon set
|
||||||
|
const filename = typeof item === 'string' ? item : item.filename
|
||||||
|
let content = JSON.parse(
|
||||||
|
await buildUtilsReadFile(filename),
|
||||||
|
) as IconifyJSON
|
||||||
|
|
||||||
|
// Filter icons
|
||||||
|
if (typeof item !== 'string' && item.icons?.length) {
|
||||||
|
const filteredContent = getIcons(content, item.icons)
|
||||||
|
if (!filteredContent)
|
||||||
|
throw new Error(`Cannot find required icons in ${filename}`)
|
||||||
|
|
||||||
|
content = filteredContent
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove metadata and add to bundle
|
||||||
|
removeMetaData(content)
|
||||||
|
minifyIconSet(content)
|
||||||
|
bundle += `addCollection(${JSON.stringify(content)});\n`
|
||||||
|
IconLog('Icon Bundle', `Bundled icons from ${filename}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom SVG
|
||||||
|
*/
|
||||||
|
if (sources.svg) {
|
||||||
|
for (let i = 0; i < sources.svg.length; i++) {
|
||||||
|
const source = sources.svg[i]
|
||||||
|
|
||||||
|
// Import icons
|
||||||
|
const iconSet = await importDirectory(source.dir, {
|
||||||
|
prefix: source.prefix,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Validate, clean up, fix palette and optimise
|
||||||
|
await iconSet.forEach(async (name, type) => {
|
||||||
|
if (type !== 'icon')
|
||||||
|
return
|
||||||
|
|
||||||
|
// Get SVG instance for parsing
|
||||||
|
const svg = iconSet.toSVG(name)
|
||||||
|
if (!svg) {
|
||||||
|
// Invalid icon
|
||||||
|
iconSet.remove(name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up and optimise icons
|
||||||
|
try {
|
||||||
|
// Clean up icon code
|
||||||
|
await cleanupSVG(svg)
|
||||||
|
|
||||||
|
if (source.monotone) {
|
||||||
|
// Replace color with currentColor, add if missing
|
||||||
|
// If icon is not monotone, remove this code
|
||||||
|
await parseColors(svg, {
|
||||||
|
defaultColor: 'currentColor',
|
||||||
|
callback: (attr, colorStr, color) => {
|
||||||
|
return !color || isEmptyColor(color) ? colorStr : 'currentColor'
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimise
|
||||||
|
runSVGO(svg)
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
// Invalid icon
|
||||||
|
console.error(`Error parsing ${name} from ${source.dir}:`, err)
|
||||||
|
iconSet.remove(name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update icon from SVG instance
|
||||||
|
iconSet.fromSVG(name, svg)
|
||||||
|
})
|
||||||
|
console.log(`Bundled ${iconSet.count()} icons from ${source.dir}`)
|
||||||
|
|
||||||
|
// Export to JSON
|
||||||
|
const content = iconSet.export()
|
||||||
|
bundle += `addCollection(${JSON.stringify(content)});\n`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save to file
|
||||||
|
await buildUtilsWriteFile(target, bundle)
|
||||||
|
|
||||||
|
IconLog(
|
||||||
|
'Icon Bundle',
|
||||||
|
`Saved bundle icons at: ${target} (${Number(bundle.length / 1024).toFixed(
|
||||||
|
2,
|
||||||
|
)} KB)`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove metadata from icon set
|
||||||
|
*/
|
||||||
|
function removeMetaData(iconSet: IconifyJSON) {
|
||||||
|
const props: (keyof IconifyMetaData)[] = [
|
||||||
|
'info',
|
||||||
|
'chars',
|
||||||
|
'categories',
|
||||||
|
'themes',
|
||||||
|
'prefixes',
|
||||||
|
'suffixes',
|
||||||
|
]
|
||||||
|
props.forEach((prop) => {
|
||||||
|
delete iconSet[prop]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort icon names by prefix
|
||||||
|
*/
|
||||||
|
function organizeIconsList(icons: string[]): Record<string, string[]> {
|
||||||
|
const sorted: Record<string, string[]> = Object.create(null)
|
||||||
|
icons.forEach((icon) => {
|
||||||
|
const item = stringToIcon(icon)
|
||||||
|
if (!item)
|
||||||
|
return
|
||||||
|
|
||||||
|
const prefix = item.prefix
|
||||||
|
const prefixList = sorted[prefix] ? sorted[prefix] : (sorted[prefix] = [])
|
||||||
|
|
||||||
|
const name = item.name
|
||||||
|
if (!prefixList.includes(name))
|
||||||
|
prefixList.push(name)
|
||||||
|
})
|
||||||
|
|
||||||
|
return sorted
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
import { buildUtilsWriteFile } from '../../utils'
|
||||||
|
import {
|
||||||
|
IconBundleConfig,
|
||||||
|
SvgPrefix,
|
||||||
|
iconBundlePath,
|
||||||
|
iconListPath,
|
||||||
|
iconSVGPath,
|
||||||
|
} from '../src'
|
||||||
|
|
||||||
|
export async function generateIconDev() {
|
||||||
|
const names = IconBundleConfig.list.filter(i => i !== SvgPrefix)
|
||||||
|
|
||||||
|
const JSONName = (i: string) => `${i.replace('-', '')}Icons`
|
||||||
|
|
||||||
|
const customJSONName = 'CustomSvgJSON'
|
||||||
|
|
||||||
|
const importJSON = names.map(
|
||||||
|
i =>
|
||||||
|
`import ${JSONName(i)
|
||||||
|
} from '@iconify/json/json/${i}.json' \n`,
|
||||||
|
)
|
||||||
|
|
||||||
|
const addCollections = names.map(
|
||||||
|
i => `addCollection(${JSONName(i)}) \n`,
|
||||||
|
)
|
||||||
|
|
||||||
|
const generateListFromJSON = `const collections = [${names.map(i => JSONName(i))}, ${customJSONName}];
|
||||||
|
const ret: string[] = []
|
||||||
|
collections.forEach((item) => {
|
||||||
|
ret.push(...Object.keys(item.icons).map(key => \`\${item.prefix}:\${key}\`))
|
||||||
|
})
|
||||||
|
export default ret
|
||||||
|
`
|
||||||
|
|
||||||
|
const addImport = 'import { addCollection } from \'@iconify/vue\''
|
||||||
|
|
||||||
|
const importString = `
|
||||||
|
${importJSON.join('')}
|
||||||
|
import ${customJSONName} from '/${iconSVGPath}'\n
|
||||||
|
`
|
||||||
|
|
||||||
|
const addCollection = `${addCollections.join('')}
|
||||||
|
addCollection(${customJSONName})`
|
||||||
|
|
||||||
|
// addCollection bundle
|
||||||
|
await buildUtilsWriteFile(iconBundlePath, addImport + importString + addCollection)
|
||||||
|
|
||||||
|
// generate icon list
|
||||||
|
await buildUtilsWriteFile(iconListPath, importString + generateListFromJSON)
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
import fg from 'fast-glob'
|
||||||
|
import { IconSet } from '@iconify/tools'
|
||||||
|
import type { IconifyJSON } from '@iconify/types'
|
||||||
|
|
||||||
|
import { buildUtilsReadFile, buildUtilsWriteFile } from '../../utils'
|
||||||
|
|
||||||
|
import {
|
||||||
|
IconBundleConfig,
|
||||||
|
IconLog,
|
||||||
|
SvgPrefix,
|
||||||
|
iconListPath,
|
||||||
|
iconSVGPath,
|
||||||
|
} from '../src'
|
||||||
|
|
||||||
|
export async function getIconListAllArray() {
|
||||||
|
let arr: string[] = []
|
||||||
|
|
||||||
|
const allCollectionPaths = await fg(
|
||||||
|
'node_modules/@iconify/json/json/*.json',
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
IconBundleConfig.list.map(async (i) => {
|
||||||
|
const res = allCollectionPaths
|
||||||
|
.map(path => path.split('/').slice(-1)[0])
|
||||||
|
.filter(p => p === `${i}.json`)
|
||||||
|
|
||||||
|
const file = await buildUtilsReadFile(
|
||||||
|
i === SvgPrefix
|
||||||
|
? iconSVGPath
|
||||||
|
: `node_modules/@iconify/json/json/${res[0]}`,
|
||||||
|
)
|
||||||
|
|
||||||
|
const fileJSON: IconifyJSON = JSON.parse(file.toString())
|
||||||
|
|
||||||
|
const iconSet = new IconSet(fileJSON)
|
||||||
|
|
||||||
|
const iconArr = iconSet.list().map(i => `${fileJSON.prefix}:${i}`)
|
||||||
|
|
||||||
|
arr = arr.concat(iconArr)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function generateIconListAll() {
|
||||||
|
const arr = await getIconListAllArray()
|
||||||
|
|
||||||
|
const str = JSON.stringify(arr)
|
||||||
|
|
||||||
|
await buildUtilsWriteFile(iconListPath, `export default ${str}`)
|
||||||
|
|
||||||
|
IconLog(
|
||||||
|
'Icon List',
|
||||||
|
`Generating icon list... Total number: ${arr.length}, writing into file: ${iconListPath}`,
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
import fg from 'fast-glob'
|
||||||
|
|
||||||
|
import { buildUtilsReadFile, buildUtilsWriteFile } from '../../utils'
|
||||||
|
import { IconLog, iconListPath } from '../src/index'
|
||||||
|
import { getIconListAllArray } from './icon-list-all'
|
||||||
|
|
||||||
|
export async function generateIconListScan() {
|
||||||
|
const iconPool = await getIconListAllArray()
|
||||||
|
|
||||||
|
const files = await fg('src/**/*.{vue,ts,tsx}', { dot: true })
|
||||||
|
|
||||||
|
IconLog(
|
||||||
|
'Icon Scan',
|
||||||
|
`Scanning all 'vue, ts, tsx' files under 'src'. File number: ${files.length}`,
|
||||||
|
)
|
||||||
|
|
||||||
|
const buffers = await Promise.all(files.map(i => buildUtilsReadFile(i)))
|
||||||
|
|
||||||
|
const ret = [
|
||||||
|
...new Set(
|
||||||
|
buffers
|
||||||
|
.map(i =>
|
||||||
|
iconPool
|
||||||
|
.map(q => i.toString().match(new RegExp(q, 'gm')))
|
||||||
|
.filter(i => i),
|
||||||
|
)
|
||||||
|
.filter(i => i.length !== 0)
|
||||||
|
.flat(Number.POSITIVE_INFINITY),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
await buildUtilsWriteFile(
|
||||||
|
iconListPath,
|
||||||
|
`export default ${JSON.stringify(ret)}`,
|
||||||
|
)
|
||||||
|
|
||||||
|
IconLog(
|
||||||
|
'Icon Scan',
|
||||||
|
`Detected ${ret.length} matched icons, writing into file: ${iconListPath}`,
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
import { buildUtilsReadFile, buildUtilsWriteFile } from '../../utils'
|
||||||
|
import { IconLog, SvgPrefix, iconBundlePath, iconListPath, iconSVGPath } from '../src'
|
||||||
|
import { generateSvgJSON } from './icon-svg-json'
|
||||||
|
|
||||||
|
export async function writeSvgJSONBundle() {
|
||||||
|
const result = `import { addCollection } from '@iconify/vue'
|
||||||
|
import CustomSvgJson from '/${iconSVGPath}'\n
|
||||||
|
addCollection(CustomSvgJson);`
|
||||||
|
|
||||||
|
IconLog('Svg Bundle', `Bundle svg icons from ${iconSVGPath}`)
|
||||||
|
|
||||||
|
await buildUtilsWriteFile(iconBundlePath, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function rewriteSvgJSON() {
|
||||||
|
const usedIconList = await buildUtilsReadFile(iconListPath)
|
||||||
|
|
||||||
|
const arr = Array.from<string>(JSON.parse(usedIconList.replace('export default ', '')))
|
||||||
|
|
||||||
|
const usedSvgIcons: string[] = arr.filter(i => i.startsWith(SvgPrefix))
|
||||||
|
|
||||||
|
await generateSvgJSON(usedSvgIcons.map(i => i.replace(`${SvgPrefix}:`, '')))
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
import {
|
||||||
|
cleanupSVG,
|
||||||
|
importDirectory,
|
||||||
|
parseColors,
|
||||||
|
runSVGO,
|
||||||
|
} from '@iconify/tools'
|
||||||
|
|
||||||
|
import { buildUtilsReadFile, buildUtilsWriteFile } from '../../utils'
|
||||||
|
import { IconLog, SvgPrefix, iconSVGPath } from '../src'
|
||||||
|
|
||||||
|
export async function generateSvgJSON(whiteList?: string[]) {
|
||||||
|
// build the empty json file
|
||||||
|
await buildUtilsWriteFile(
|
||||||
|
iconSVGPath,
|
||||||
|
JSON.stringify({ icons: {}, prefix: SvgPrefix }),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Import icons
|
||||||
|
const iconSet = await importDirectory('.svg', {
|
||||||
|
prefix: SvgPrefix,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Validate, clean up, fix palette and optimise
|
||||||
|
await iconSet.forEach(async (name, type) => {
|
||||||
|
if (type !== 'icon')
|
||||||
|
return
|
||||||
|
|
||||||
|
// white list limit
|
||||||
|
if (whiteList && !whiteList.includes(name))
|
||||||
|
iconSet.remove(name)
|
||||||
|
|
||||||
|
const svg = iconSet.toSVG(name)
|
||||||
|
if (!svg) {
|
||||||
|
// Invalid icon
|
||||||
|
iconSet.remove(name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up and optimise icons
|
||||||
|
try {
|
||||||
|
// Cleanup icon code
|
||||||
|
await cleanupSVG(svg)
|
||||||
|
|
||||||
|
// Assume icon is monotone: replace color with currentColor, add if missing
|
||||||
|
// If icon is not monotone, remove this code
|
||||||
|
await parseColors(svg, {
|
||||||
|
defaultColor: 'currentColor',
|
||||||
|
|
||||||
|
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||||
|
callback: (attr, colorStr, color) => {
|
||||||
|
// return !color || isEmptyColor(color) ? colorStr : 'currentColor'
|
||||||
|
return colorStr || 'currentColor'
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Optimise
|
||||||
|
runSVGO(svg)
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
// Invalid icon
|
||||||
|
console.error(`Error parsing ${name}:`, err)
|
||||||
|
iconSet.remove(name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update icon
|
||||||
|
iconSet.fromSVG(name, svg)
|
||||||
|
})
|
||||||
|
|
||||||
|
const data = JSON.parse(await buildUtilsReadFile(iconSVGPath))
|
||||||
|
|
||||||
|
// Generate to icon list
|
||||||
|
await iconSet.forEach((name) => {
|
||||||
|
// auto remove width and height
|
||||||
|
const body = String(iconSet.toString(name))
|
||||||
|
.replaceAll(/width="(.[0-9]*)"/gi, '')
|
||||||
|
.replaceAll(/height="(.[0-9]*)"/gi, '')
|
||||||
|
|
||||||
|
data.icons[name] = {
|
||||||
|
body,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// write into json
|
||||||
|
await buildUtilsWriteFile(iconSVGPath, JSON.stringify(data, null, 2))
|
||||||
|
|
||||||
|
IconLog(
|
||||||
|
'Icon SVG',
|
||||||
|
`Detecting ${iconSet.count()} custom svg icon, writing into file: ${iconSVGPath}`,
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
import process from 'node:process'
|
||||||
|
import { writeIntoLog } from '../utils'
|
||||||
|
import { iconLogPath } from './src/path'
|
||||||
|
|
||||||
|
writeIntoLog(
|
||||||
|
'Icon Bundle',
|
||||||
|
`npx esno build/icon/src/core.ts ${process.argv.slice(-1)[0]}`,
|
||||||
|
iconLogPath,
|
||||||
|
)
|
@ -0,0 +1,17 @@
|
|||||||
|
export const SvgPrefix = 'nl'
|
||||||
|
|
||||||
|
export const IconBundleConfig: IconConfig = {
|
||||||
|
online: false,
|
||||||
|
treeShake: true,
|
||||||
|
cache: false,
|
||||||
|
// see full icon in https://icon-sets.iconify.design
|
||||||
|
list: [
|
||||||
|
SvgPrefix,
|
||||||
|
'ant-design',
|
||||||
|
'gridicons',
|
||||||
|
'mdi',
|
||||||
|
'ion',
|
||||||
|
'carbon',
|
||||||
|
'emojione',
|
||||||
|
],
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
import process from 'node:process'
|
||||||
|
import { generateIconUsedBundle } from '../generate/icon-bundle'
|
||||||
|
import { generateIconListAll } from '../generate/icon-list-all'
|
||||||
|
import { generateIconListScan } from '../generate/icon-list-scan'
|
||||||
|
import { generateSvgJSON } from '../generate/icon-svg-json'
|
||||||
|
import { generateIconDev } from '../generate/icon-dev'
|
||||||
|
import { rewriteSvgJSON, writeSvgJSONBundle } from '../generate/icon-svg-bundle'
|
||||||
|
import { buildUtilsWarn } from '../../utils'
|
||||||
|
import { IconBundleConfig } from './config'
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const arg = process.argv.slice(-1)[0]
|
||||||
|
|
||||||
|
// always excute the logic to transform custom svg icons into iconify json
|
||||||
|
await generateSvgJSON()
|
||||||
|
|
||||||
|
// dev env, just bundle all collection icons
|
||||||
|
if (arg === 'dev') {
|
||||||
|
// when dev, just import json from `node_modules` and call `addCollection` api
|
||||||
|
// also generate icon list from imported jsons
|
||||||
|
await generateIconDev()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (IconBundleConfig.online) {
|
||||||
|
// online but not treeshake
|
||||||
|
if (!IconBundleConfig.treeShake) {
|
||||||
|
// Step 1 - Icon List should be all
|
||||||
|
await generateIconListAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
// online also treeshake
|
||||||
|
if (IconBundleConfig.treeShake) {
|
||||||
|
// Step 1 - Use fast-glob to scan and write the used icon into icon list
|
||||||
|
await generateIconListScan()
|
||||||
|
|
||||||
|
// Step 2 - Used last step generated icon list to rewrite the svg json file
|
||||||
|
await rewriteSvgJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add svg icons with `addCollection`
|
||||||
|
await writeSvgJSONBundle()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// not online and not treeshake
|
||||||
|
// WARNING - highly not recommended to do so
|
||||||
|
// cause it will bundle all icons into final output which would increase the bundle size a lot
|
||||||
|
if (!IconBundleConfig.treeShake) {
|
||||||
|
// not recomended to do so
|
||||||
|
buildUtilsWarn('Not online and Not treeshake \n WARNING - It\'s highly not recommended to do so \n Cause it will bundle all icons into final output which would increase the bundle size a lot \n Normally more than 1MB')
|
||||||
|
process.exitCode = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// not online but treeshake
|
||||||
|
if (IconBundleConfig.treeShake) {
|
||||||
|
// Step 1 - Use fast-glob to scan and write the used icon into icon list
|
||||||
|
await generateIconListScan()
|
||||||
|
|
||||||
|
// Step 2 - Excute the iconify bundle logic from
|
||||||
|
// https://docs.iconify.design/icon-components/bundles/examples/component-full.html
|
||||||
|
await generateIconUsedBundle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})()
|
@ -0,0 +1,3 @@
|
|||||||
|
export * from './path'
|
||||||
|
export * from './log'
|
||||||
|
export * from './config'
|
@ -0,0 +1,15 @@
|
|||||||
|
import { buildUtilsLog } from '../../utils'
|
||||||
|
|
||||||
|
export function IconLog(title: string, data: string) {
|
||||||
|
buildUtilsLog(
|
||||||
|
`
|
||||||
|
/**
|
||||||
|
* ==============================================
|
||||||
|
* =============== ${title} ==================
|
||||||
|
* ==============================================
|
||||||
|
* ${data}
|
||||||
|
* ==============================================
|
||||||
|
*/
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
export const iconListPath = 'build/_generated/icon-list.ts'
|
||||||
|
export const iconBundlePath = 'build/_generated/icon-bundle.ts'
|
||||||
|
export const iconSVGPath = 'build/_generated/svg.json'
|
||||||
|
export const iconLogPath = 'build/_generated/icon-build.log'
|
@ -0,0 +1,10 @@
|
|||||||
|
export {}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface IconConfig {
|
||||||
|
online: boolean
|
||||||
|
treeShake: boolean
|
||||||
|
cache: boolean
|
||||||
|
list: string[]
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
import { promisify } from 'node:util'
|
||||||
|
import { exec } from 'node:child_process'
|
||||||
|
import { buildUtilsLog } from './'
|
||||||
|
|
||||||
|
const promisifyExec = promisify(exec)
|
||||||
|
|
||||||
|
export async function buildUtilsExec(file: string) {
|
||||||
|
const { stdout, stderr } = await promisifyExec(file)
|
||||||
|
|
||||||
|
buildUtilsLog(stdout)
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
import { promises as fs } from 'node:fs'
|
||||||
|
import { buildUtilsLog } from './'
|
||||||
|
|
||||||
|
const stage = 'File'
|
||||||
|
|
||||||
|
export async function buildUtilsReadFile(path: string) {
|
||||||
|
buildUtilsLog(`Reading File: ${path}`, stage)
|
||||||
|
return await fs.readFile(path, 'utf8')
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function buildUtilsWriteFile(path: string, data: any) {
|
||||||
|
buildUtilsLog(`Writing data into File: ${path}`, stage)
|
||||||
|
return await fs.writeFile(path, data, 'utf8')
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
export * from './log'
|
||||||
|
export * from './cp'
|
||||||
|
export * from './fs'
|
||||||
|
export * from './paths'
|
@ -0,0 +1,83 @@
|
|||||||
|
import process from 'node:process'
|
||||||
|
import fs from 'node:fs'
|
||||||
|
import cp from 'node:child_process'
|
||||||
|
|
||||||
|
import chalk from 'chalk'
|
||||||
|
import { getNow } from 'easy-fns-ts/dist/lib'
|
||||||
|
import pkg from '../../package.json'
|
||||||
|
|
||||||
|
function title(stage: string) {
|
||||||
|
return chalk.magenta.bgBlack(
|
||||||
|
`[${pkg.name.toUpperCase()}] - [${getNow()}] - [${stage}]`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildUtilsLog(msg: string, stage = 'log') {
|
||||||
|
console.log(`${title(stage)}: ${msg}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildUtilsWarn(warns: string) {
|
||||||
|
console.warn(chalk.yellow(`[${pkg.name.toUpperCase()} Warning] - [${getNow()}] \n ${warns}`))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function writeIntoLog(title: string, command: string, path: string) {
|
||||||
|
const prefix = (msg: string, emoji: string) => `
|
||||||
|
/**
|
||||||
|
* ==============================================
|
||||||
|
* ==============================================
|
||||||
|
* ${title} - ${msg} - ${emoji} - ${getNow()}
|
||||||
|
* ==============================================
|
||||||
|
* ==============================================
|
||||||
|
*/
|
||||||
|
`
|
||||||
|
|
||||||
|
const log_file = fs.createWriteStream(path, {
|
||||||
|
flags: 'w',
|
||||||
|
})
|
||||||
|
|
||||||
|
const start = new Date().getTime()
|
||||||
|
|
||||||
|
cp.exec(command, (error, stdout, stderr) => {
|
||||||
|
prefix(`Excuting command: ${command}`, '⚡⚡⚡⚡⚡')
|
||||||
|
|
||||||
|
const end = new Date().getTime()
|
||||||
|
|
||||||
|
const cost = new Date(end - start).getSeconds()
|
||||||
|
|
||||||
|
buildUtilsLog(chalk.blue(`${title} done in ${cost}s`))
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
process.exitCode = 1
|
||||||
|
log_file.write(
|
||||||
|
prefix('Error', '😈😈😈😈😈') + JSON.stringify(error),
|
||||||
|
() => {
|
||||||
|
// // recover icon file if failed
|
||||||
|
// cp.execSync('npm run postbuild')
|
||||||
|
|
||||||
|
buildUtilsLog(
|
||||||
|
chalk.red.bgBlack(`${title} Failed, see more in ${path}`),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buildUtilsLog(chalk.green.bgBlack(`${title} Successfully! ✨✨✨✨✨ `))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
log_file.write(prefix('Std Out', '✨✨✨✨✨') + stdout, () => {
|
||||||
|
buildUtilsLog(
|
||||||
|
chalk.magenta.bgBlack(`${title} Std out, see more in ${path}`),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
log_file.write(prefix('Std Err', '💊💊💊💊💊') + stderr, () => {
|
||||||
|
buildUtilsLog(
|
||||||
|
chalk.yellow.bgBlack(`${title} Std Error, see more in ${path}`),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
export const stagingBuildLogPath = 'report/stage.log'
|
||||||
|
export const typesCheckLogPath = 'report/tsc.log'
|
||||||
|
export const bundleSizeStatsLogPath = 'report/stats.html'
|
||||||
|
|
||||||
|
// paths.ts
|
||||||
|
export const generatedPathsFilePath = 'build/_generated/paths.ts'
|
||||||
|
|
||||||
|
// publish.bat
|
||||||
|
export const generatedPublishBatFileName = 'build/_generated/publish.bat'
|
||||||
|
|
||||||
|
// vscode setting file path
|
||||||
|
export const VScodeSettingsFilePath = '.vscode/settings.json'
|
||||||
|
|
||||||
|
// app setting file path
|
||||||
|
export const AppSettingsJSONFilePath = 'src/settings.json'
|
||||||
|
|
||||||
|
// app setting interface file path
|
||||||
|
export const AppSettingsInterfaceFilePath = 'types/settings.d.ts'
|
@ -0,0 +1,12 @@
|
|||||||
|
import { IconBundleConfig } from '/build/icon/src/config'
|
||||||
|
import { disableCache, enableCache } from '@iconify/vue'
|
||||||
|
import '/build/_generated/icon-bundle.ts'
|
||||||
|
|
||||||
|
if (!IconBundleConfig.cache) {
|
||||||
|
disableCache('all')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
enableCache('local')
|
||||||
|
}
|
||||||
|
|
||||||
|
export { default } from './src/icon.vue'
|
@ -0,0 +1,175 @@
|
|||||||
|
<script setup lang="ts" name="MyIcon">
|
||||||
|
import { Icon as Iconify, iconExists, loadIcon } from '@iconify/vue'
|
||||||
|
import type { CSSProperties } from 'vue'
|
||||||
|
import { createNamespace } from '~/utils'
|
||||||
|
|
||||||
|
defineOptions({ name: 'LIcon' })
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
icon: { type: String as PropType<string> },
|
||||||
|
size: { type: [String, Number] as PropType<string | number>, default: 16 },
|
||||||
|
color: { type: String },
|
||||||
|
depth: { type: Number as PropType<1 | 2 | 3 | 4 | 5> },
|
||||||
|
spin: { type: Boolean, default: false },
|
||||||
|
transition: { type: Boolean, default: false },
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['click'])
|
||||||
|
|
||||||
|
const { VITE_ICON_UNOCSS_PREFIX: UnoCSSPrefix, VITE_ICON_LOCAL_PREFIX: LocalPrefix } = import.meta.env
|
||||||
|
|
||||||
|
function getPrefixRegex(prefix: string) {
|
||||||
|
return new RegExp(`^${prefix}[:-]`)
|
||||||
|
}
|
||||||
|
// ^nl[-:]
|
||||||
|
const UnoCSSPrefixRegex = getPrefixRegex(UnoCSSPrefix)
|
||||||
|
const LocalPrefixRegex = getPrefixRegex(LocalPrefix)
|
||||||
|
|
||||||
|
enum IconType {
|
||||||
|
Unknown = 0,
|
||||||
|
UnoCSS = 1,
|
||||||
|
Local = 2,
|
||||||
|
Iconify = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
let loaded = $ref(false)
|
||||||
|
|
||||||
|
function click(e: MouseEvent) {
|
||||||
|
emit('click', e)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getIconTypeRef = computed((): IconType => {
|
||||||
|
const { icon } = props
|
||||||
|
if (!icon) {
|
||||||
|
return IconType.Unknown
|
||||||
|
}
|
||||||
|
if (LocalPrefixRegex.test(icon)) {
|
||||||
|
return IconType.Local
|
||||||
|
}
|
||||||
|
if (UnoCSSPrefixRegex.test(icon)) {
|
||||||
|
return IconType.UnoCSS
|
||||||
|
}
|
||||||
|
return IconType.Iconify
|
||||||
|
})
|
||||||
|
|
||||||
|
function checkAndLoad(icon: string) {
|
||||||
|
if (unref(getIconTypeRef) === IconType.Iconify) {
|
||||||
|
const isLoaded = (loaded = iconExists(icon))
|
||||||
|
if (!isLoaded) {
|
||||||
|
loadIcon(icon)
|
||||||
|
.then(() => loaded = true)
|
||||||
|
.catch((_) => {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测 icon 变化
|
||||||
|
watch(
|
||||||
|
() => props.icon,
|
||||||
|
v => checkAndLoad(v!),
|
||||||
|
{ immediate: true },
|
||||||
|
)
|
||||||
|
|
||||||
|
const getIconRef = computed((): string => {
|
||||||
|
const iconType = unref(getIconTypeRef)
|
||||||
|
switch (iconType) {
|
||||||
|
case IconType.Local:
|
||||||
|
{
|
||||||
|
let icon = props.icon || 'no-icon'
|
||||||
|
if (icon.startsWith(LocalPrefix)) {
|
||||||
|
icon = icon.slice(icon.indexOf(LocalPrefix) + LocalPrefix.length + 1)
|
||||||
|
}
|
||||||
|
return `#${LocalPrefix}-${icon}`
|
||||||
|
}
|
||||||
|
case IconType.UnoCSS:
|
||||||
|
{
|
||||||
|
let icon = props.icon!
|
||||||
|
if (icon.startsWith(UnoCSSPrefix)) {
|
||||||
|
icon = icon.slice(icon.indexOf(UnoCSSPrefix) + UnoCSSPrefix.length + 1)
|
||||||
|
}
|
||||||
|
return `${UnoCSSPrefix}-${icon}`
|
||||||
|
}
|
||||||
|
case IconType.Iconify:
|
||||||
|
return props.icon!
|
||||||
|
default:
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const attrs = useAttrs()
|
||||||
|
const bindAttrs = computed<{ class: string; style: string }>(() => ({
|
||||||
|
class: (attrs.class as string) || '',
|
||||||
|
style: (attrs.style as string) || '',
|
||||||
|
}))
|
||||||
|
const getStyle = computed((): CSSProperties => {
|
||||||
|
const { size } = props
|
||||||
|
let s = `${size}`
|
||||||
|
s = `${s.replace('px', '')}px`
|
||||||
|
return {
|
||||||
|
width: s,
|
||||||
|
height: s,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const getClass = computed(() => {
|
||||||
|
return {
|
||||||
|
'animate-spin animate-duration-1000': props.spin,
|
||||||
|
'transition-all ease-in-out-300': props.transition,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const { bem } = createNamespace('l-icon')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="$attrs.class || {}" @click="click($event)">
|
||||||
|
<template v-if="getIconTypeRef === IconType.Unknown">
|
||||||
|
<NSkeleton animated circle :width="size" :height="size" class="inline-block" />
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<template v-if="getIconTypeRef === IconType.Local">
|
||||||
|
<svg
|
||||||
|
:class="[bem(), getClass]"
|
||||||
|
:style="getStyle"
|
||||||
|
aria-hidden="true"
|
||||||
|
v-bind="bindAttrs"
|
||||||
|
>
|
||||||
|
<use :xlink:href="getIconRef" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
<template v-if="getIconTypeRef === IconType.Iconify">
|
||||||
|
<NIcon
|
||||||
|
v-if="loaded"
|
||||||
|
:class="[bem(), getClass]"
|
||||||
|
:style="getStyle"
|
||||||
|
:color="color"
|
||||||
|
:depth="depth"
|
||||||
|
:size="size"
|
||||||
|
v-bind="bindAttrs"
|
||||||
|
>
|
||||||
|
<Iconify :icon="getIconRef" />
|
||||||
|
</NIcon>
|
||||||
|
<NSkeleton v-else animated circle :width="size" :height="size" class="inline-block" />
|
||||||
|
</template>
|
||||||
|
<template v-if="getIconTypeRef === IconType.UnoCSS">
|
||||||
|
<NIcon
|
||||||
|
:class="[bem(), getClass]"
|
||||||
|
:style="getStyle"
|
||||||
|
:color="color"
|
||||||
|
:depth="depth"
|
||||||
|
:size="size"
|
||||||
|
v-bind="bindAttrs"
|
||||||
|
>
|
||||||
|
<div :class="getIconRef" />
|
||||||
|
</NIcon>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.l-icon {
|
||||||
|
display: inline-block;
|
||||||
|
overflow: hidden;
|
||||||
|
fill: currentcolor;
|
||||||
|
vertical-align: -0.15em;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,29 @@
|
|||||||
|
import LIcon from '~/components/LIcon'
|
||||||
|
|
||||||
|
export function useIcon() {
|
||||||
|
interface IconRenderOption {
|
||||||
|
icon?: string
|
||||||
|
color?: string
|
||||||
|
size?: string | number
|
||||||
|
depth?: 1 | 2 | 3 | 4 | 5
|
||||||
|
spin?: boolean
|
||||||
|
transition?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderIcon(val: IconRenderOption | string) {
|
||||||
|
let opt: IconRenderOption = {}
|
||||||
|
if (val instanceof String) {
|
||||||
|
opt = { icon: val as string }
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
opt = val as IconRenderOption
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opt.icon) {
|
||||||
|
return h(LIcon, { ...opt })
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return { renderIcon }
|
||||||
|
}
|
Loading…
Reference in New Issue