Skip to content

Commit

Permalink
feat: support iconify customization (#199)
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Fu <[email protected]>
  • Loading branch information
ezequidias and antfu committed Jul 22, 2024
1 parent 1ccb7c2 commit 1b28ec2
Show file tree
Hide file tree
Showing 11 changed files with 94 additions and 13 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"@iconify/collections": "^1.0.441",
"@iconify/types": "^2.0.0",
"@iconify/utils": "^2.1.25",
"@iconify/vue": "^4.1.2",
"@iconify/vue": "4.1.3-beta.1",
"@nuxt/devtools-kit": "^1.3.9",
"@nuxt/kit": "^3.12.4",
"consola": "^3.2.3",
Expand Down
2 changes: 1 addition & 1 deletion playground/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export default defineAppConfig({
aliases: {
github: 'carbon:logo-github',
nuxt: 'logos:nuxt-icon',
rocket: 'fluent-emoji:rocket',
rocket: 'skill-icons:rocket',
nxt: 'NuxtLogo',
},
},
Expand Down
39 changes: 38 additions & 1 deletion playground/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ const mode = useCookie<'svg' | 'css'>('nuxt-icon-demo-mode', {
const iconsRaw = useCookie<string[]>('nuxt-icon-demo-icons', {
default: () => ['logos-nuxt-icon', 'logos-vitejs', 'logos-vue'],
})
const stroke = useCookie<number>('nuxt-icon-demo-stroke', {
default: () => 0.5,
})
const customize = (content: string) => {
return content.replace(/stroke-width="[^"]*"/g, `stroke-width="${stroke.value}"`)
}
const icons = ref<string[]>(iconsRaw.value)
Expand All @@ -36,6 +43,7 @@ function addRandom() {
function clear() {
icons.value = []
iconsRaw.value = icons.value
stroke.value = 0.5
}
</script>

Expand All @@ -51,6 +59,17 @@ function clear() {
CSS
</option>
</select>
<div class="flex gap-2 mb-0.5 items-center">
<div>Stroke:</div>
<input
v-model="stroke"
type="range"
class="mt-1.5"
min="0.5"
step="0.1"
max="3"
>
</div>
<button
class="flex items-center gap-1"
@click="addRandom"
Expand Down Expand Up @@ -92,6 +111,7 @@ function clear() {
<Icon
:name="icon"
:mode="mode"
:customize
/>
</div>
<div class="text-xs font-mono opacity-50">
Expand All @@ -109,33 +129,39 @@ function clear() {
<Icon
name="uil:github"
:mode
:customize
/>
<Icon
name="uil:github"
size="24"
:mode
:customize
/>
<Icon
name="uil:github"
size="48"
:mode
:customize
/>
</p>
<p>
Global components:
<Icon
name="NuxtLogo"
:mode
:customize
/>
<Icon
name="NuxtLogo"
size="24"
:mode
:customize
/>
<Icon
name="NuxtLogo"
size="48"
:mode
:customize
/>
</p>
<p>
Expand All @@ -144,6 +170,7 @@ function clear() {
name="custom1:nuxt-v1"
size="64"
:mode
:customize
/>
<Icon
name="custom1:nuxt-v2"
Expand All @@ -154,11 +181,13 @@ function clear() {
name="custom1:nuxt-v3-beta"
size="64"
:mode
:customize
/>
<Icon
name="custom1:nuxt-v3"
size="64"
:mode
:customize
/>
</p>
<p>
Expand All @@ -167,6 +196,7 @@ function clear() {
name="layer:layer"
size="64"
:mode
:customize
/>
</p>
<!-- <p>
Expand All @@ -187,27 +217,32 @@ function clear() {
name="github"
size="24"
:mode
:customize
/>
<Icon
name="nuxt"
size="24"
:mode
:customize
/>
<Icon
name="rocket"
size="24"
:mode
:customize
/>
<Icon
name="nxt"
size="24"
:customize
/>
</p>
<p>
`i-` syntax:
<Icon
name="i-fluent-emoji-high-contrast-1st-place-medal"
name="i-solar:medal-ribbon-bold-duotone"
:mode
:customize
/>
</p>
<!-- <p>
Expand All @@ -223,10 +258,12 @@ function clear() {
<Icon
name="i-ph-code"
:mode
:customize
/>
<Icon
name="ph:table"
:mode
:customize
/>
</p>
</div>
Expand Down
10 changes: 5 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export default defineNuxtModule<ModuleOptions>({
mode: schema['mode'].$default,
attrs: schema['attrs'].$default,
collections: schema['collections'].$default,
customize: schema['customize'].$default,
},
async setup(options, nuxt) {
const resolver = createResolver(import.meta.url)
Expand Down
10 changes: 8 additions & 2 deletions src/runtime/components/css.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { addIcon, getIcon as _getIcon } from '@iconify/vue'
import { computed, watch, h, defineComponent } from 'vue'
import { getIconCSS } from '@iconify/utils/lib/css/icon'
import type { PropType } from 'vue'
import type { IconifyIcon } from '@iconify/types'
import type { NuxtIconRuntimeOptions, NuxtIconRuntimeServerOptions } from '../../types'
import type { NuxtIconRuntimeOptions, NuxtIconRuntimeServerOptions, IconifyIconCustomizeCallback } from '../../types'
import { loadIcon } from './shared'
import { useAppConfig, useNuxtApp, useHead, useRuntimeConfig } from '#imports'

Expand Down Expand Up @@ -65,9 +66,13 @@ export const NuxtIconCss = /* @__PURE__ */ defineComponent({
name: 'NuxtIconCss',
props: {
name: {
type: String,
type: String as PropType<string>,
required: true,
},
customize: {
type: Function as PropType<IconifyIconCustomizeCallback>,
required: false,
},
},
async setup(props) {
const nuxt = useNuxtApp()
Expand Down Expand Up @@ -95,6 +100,7 @@ export const NuxtIconCss = /* @__PURE__ */ defineComponent({
const css = getIconCSS(icon, {
iconSelector,
format: 'compressed',
customise: props.customize,
})
if (options.cssLayer && withLayer) {
return `@layer ${options.cssLayer} { ${css} }`
Expand Down
8 changes: 7 additions & 1 deletion src/runtime/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getIcon as _getIcon } from '@iconify/vue'
import type { PropType } from 'vue'
import { computed, defineComponent, h } from 'vue'
import type { NuxtIconRuntimeOptions } from '../../types'
import type { NuxtIconRuntimeOptions, IconifyIconCustomizeCallback } from '../../types'
import { NuxtIconCss } from './css'
import { NuxtIconSvg } from './svg'
import { useResolvedName } from './shared'
Expand All @@ -24,6 +24,10 @@ export default defineComponent({
required: false,
default: null,
},
customize: {
type: Function as PropType<IconifyIconCustomizeCallback>,
required: false,
},
},
async setup(props, { slots }) {
const nuxtApp = useNuxtApp()
Expand All @@ -41,6 +45,7 @@ export default defineComponent({
? { fontSize: Number.isNaN(+size) ? size : size + 'px' }
: null
})
const customize = props.customize || options.customize

return () => h(
component.value,
Expand All @@ -49,6 +54,7 @@ export default defineComponent({
name: name.value,
class: options.class,
style: style.value,
customize,
},
slots,
)
Expand Down
11 changes: 9 additions & 2 deletions src/runtime/components/svg.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import { Icon as Iconify, addIcon } from '@iconify/vue'
import { h } from 'vue'
import type { NuxtIconRuntimeOptions } from '../../types'
import type { PropType } from 'vue'
import type { NuxtIconRuntimeOptions, IconifyIconCustomizeCallback } from '../../types'
import { loadIcon, useResolvedName } from './shared'
import { useAsyncData, useNuxtApp, defineComponent, useAppConfig } from '#imports'

export const NuxtIconSvg = /* @__PURE__ */ defineComponent({
name: 'NuxtIconSvg',
props: {
name: {
type: String,
type: String as PropType<string>,
required: true,
},
customize: {
type: Function as PropType<IconifyIconCustomizeCallback>,
required: false,
},
},
async setup(props, { slots }) {
const nuxt = useNuxtApp()
Expand Down Expand Up @@ -40,6 +45,8 @@ export const NuxtIconSvg = /* @__PURE__ */ defineComponent({
icon: name.value,
ssr: true,
class: options.class,
// Iconify uses `customise`, where we expose `customize` for consistency
customise: props.customize,
}, slots)
},
})
9 changes: 9 additions & 0 deletions src/schema-types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// This file is generated from scripts/schema.ts

import type { IconifyIconCustomizeCallback } from './types'

export interface NuxtIconRuntimeOptions {
/**
* Icon Size
Expand Down Expand Up @@ -124,4 +126,11 @@ export interface NuxtIconRuntimeOptions {
* @default "/api/_nuxt_icon"
*/
localApiEndpoint: string

/**
* Callback to customize iconify data
*
* @default undefined
*/
customize: IconifyIconCustomizeCallback
}
8 changes: 8 additions & 0 deletions src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,12 @@ export const schema = {
tags: ['@studioIcon material-symbols:api'],
},
},
customize: {
$default: undefined,
$schema: {
title: 'Customize callback',
description: 'Customize icon content (replace stroke-width, colors, etc...).',
tags: ['@studioIcon material-symbols:edit'],
},
},
} satisfies SchemaDefinition
7 changes: 7 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ import type { NuxtIconRuntimeOptions } from './schema-types'

export type { NuxtIconRuntimeOptions }

export type IconifyIconCustomizeCallback = (
content: string,
name?: string,
prefix?: string,
provider?: string
) => string

export interface NuxtIconRuntimeServerOptions {
/**
* List of pre-compiled CSS classnames to be used for server-side CSS icon rendering
Expand Down

0 comments on commit 1b28ec2

Please sign in to comment.