diff --git a/plugins/tailwind/src/iconify.ts b/plugins/tailwind/src/iconify.ts index 4b7024c..961f0a5 100644 --- a/plugins/tailwind/src/iconify.ts +++ b/plugins/tailwind/src/iconify.ts @@ -148,3 +148,47 @@ export function getCSSRules( return rules; } + +/** + * Get dynamic CSS rule + */ +export function getDynamicCSSRules( + selector: string, + icon: string, + options: IconifyPluginOptions = {} +): Record { + const nameParts = icon.split('--'); + let nameError = `Invalid icon name: "${icon}"`; + if (nameParts.length !== 2) { + if (nameParts.length === 1 && icon.indexOf(':') !== -1) { + nameError += `. "{prefix}:{name}" is not supported because of Tailwind limitations, use "{prefix}--{name}" (use double dash!) instead.`; + } + throw new Error(nameError); + } + + const [prefix, name] = nameParts; + if (!prefix.match(matchIconName) || !name.match(matchIconName)) { + throw new Error(nameError); + } + + const iconSet = loadIconSet(prefix, options); + if (!iconSet) { + throw new Error(`Cannot load icon set for "${prefix}"`); + } + + console.log('Selector:', selector); + const generated = getIconsCSSData(iconSet, [name], { + ...options, + // One selector + iconSelector: selector, + commonSelector: selector, + overrideSelector: selector, + }); + if (generated.css.length !== 1) { + throw new Error(`Something went wrong generating "${icon}"`); + } + return { + ...(generated.common?.rules || {}), + ...generated.css[0].rules, + }; +} diff --git a/plugins/tailwind/src/options.ts b/plugins/tailwind/src/options.ts index 3ebe0a6..d79de86 100644 --- a/plugins/tailwind/src/options.ts +++ b/plugins/tailwind/src/options.ts @@ -8,11 +8,22 @@ export interface IconifyPluginFileOptions { files?: Record; } +/** + * Options for matching dynamic icon names + */ +export interface IconifyPluginDynamicPrefixOptions { + // Dynamic prefix for selectors. Default is `icon` + // Allows using icon names like ` + // Where prefix and name are separated by '--' because Tailwind does not allow ':' + dynamicPrefix?: string; +} + /** * All options */ export interface IconifyPluginOptions extends IconCSSIconSetOptions, + IconifyPluginDynamicPrefixOptions, IconifyPluginFileOptions { // } diff --git a/plugins/tailwind/src/plugin.ts b/plugins/tailwind/src/plugin.ts index fb3920b..2d81502 100644 --- a/plugins/tailwind/src/plugin.ts +++ b/plugins/tailwind/src/plugin.ts @@ -1,17 +1,41 @@ import plugin from 'tailwindcss/plugin'; -import { getCSSRules } from './iconify'; +import { getCSSRules, getDynamicCSSRules } from './iconify'; import type { IconifyPluginOptions } from './options'; /** * Iconify plugin */ function iconifyPlugin( - icons: string[] | string, - options: IconifyPluginOptions = {} + icons?: string[] | string, + options?: IconifyPluginOptions ) { - const rules = getCSSRules(icons, options); - return plugin(({ addUtilities }) => { - addUtilities(rules); + const passedOptions = + typeof icons === 'object' && !(icons instanceof Array) + ? icons + : options || {}; + const passedIcons = + typeof icons !== 'object' || icons instanceof Array ? icons : void 0; + + // Get selector for dynamic classes + const dynamicSelector = passedOptions.dynamicPrefix || 'icon'; + + // Get hardcoded list of icons + const rules = passedIcons + ? getCSSRules(passedIcons, passedOptions) + : void 0; + + return plugin(({ addUtilities, matchComponents }) => { + if (rules) { + addUtilities(rules); + } + matchComponents({ + [dynamicSelector]: (icon: string) => + getDynamicCSSRules( + `.${dynamicSelector}-[${icon}]`, + icon, + passedOptions + ), + }); }); }