Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom Icons #12

Closed
NoelDeMartin opened this issue Feb 3, 2021 · 14 comments
Closed

Custom Icons #12

NoelDeMartin opened this issue Feb 3, 2021 · 14 comments
Labels
enhancement New feature or request help wanted Extra attention is needed

Comments

@NoelDeMartin
Copy link

This is great, I've tried using the vscode-iconify with Vitesse and the DX is super good, I don't think I'll want to go back to the older days!

Which raises the following question: Is it possible to configure custom svgs to be loaded using the same system? I know since the icons are my own, I can just pack them in the project or create my own component to load them (which is what I've been doing so far). But it'd be great to be able of using the same affordances as the other icons, like seeing the icon preview in vscode and such.

@antfu
Copy link
Member

antfu commented Feb 3, 2021

Thanks, glad you enjoy the workflow!

For the custom icon sets, it's indeed possible for vite-plugin-icons to support it. But we'd need to design a good API and conventions for loading the custom icons. What would you expect for how the API looks like to you can configure it?

@NoelDeMartin
Copy link
Author

NoelDeMartin commented Feb 3, 2021

Well, using Vitesse as an example, and assuming I have a custom icon called my-custom-icon.svg, I'd like to use it like this:

<vitesse-my-custom-icon />

Where "vitesse" is the namespace of my app, maybe by default it could be "app" if it isn't specified, so you'd write:

<app-my-custom-icon />

And I think it's pretty common to have icons under src/assets/icons, so that could also be a good default. Although I'm not sure if it's possible to know where the root of the project is.

With these, I think the configuration could be something like this:

// Specifying everything...
ViteIconsResolver({
    customIcons: {
        folder: '/~/assets/icons', // defaults to 'src/assets/icons'
        componentPrefix: 'vitesse', // defaults to 'app'
    },
});

// Or using all the defaults
ViteIconsResolver({
    customIcons: true;
});

And I would expect this to make any .svg from that folder available as an icon, using the file name as the icon name :).

Edit: Maybe they could be called localIcons or something else, if that's more clear. Naming is hard 😅.

@JohnCampionJr
Copy link

Could I also suggest an ability to import an Iconify JSON bundle? As an example, Font Awesome Pro JSONs are available but not part of the public Iconify bundles.

@chojnicki
Copy link
Contributor

chojnicki commented Mar 2, 2021

I resolved problem, but not by using this package.

Vite-plugin-icons uses vite-plugin-components from same author (thanks @antfu for awesome work) for auto-discovery components, so Installed this instead. Then I combined this with vite-plugin-vue-svg for importing .svg as components. Simple as that, now we have universal package for any icons pack :)

After doing everything ready to work, I found that actually @antfu did exactly that in example from 3 days ago....
https://github.com/antfu/vite-plugin-components/blob/master/examples/vue3/vite.config.ts

But sure similar functionality should be available in vite-plugin-icons because it would be more straightforward. Anyone should be using any icons they want. After all this package is named "-icons" and not "-iconify" ;)

@oliverpool
Copy link

oliverpool commented Aug 20, 2021

I really like this package, since it supports both vue 2 and 3 (maybe the compiler part could be integrated into https://github.com/vueuse/vue-demi ;-).

we'd need to design a good API and conventions for loading the custom icons. What would you expect for how the API looks like to you can configure it?

After thinking about it, I would propose to add one configuration key customCollections (or some other name) of type Record<string, IconCollection>:

export interface IconCollection {
  getIconData(icon: string): object | null; // like Iconify, { "body": "<path ...>", "width": 20, "height": 20} should be enough
}
export default {
  plugins: [
    Vue(),
    Icons({
      customCollections: {
          'app': new LazyCollection('./folder'),
      }
    })
  ],
}

This customCollection would be checked before (or inside) the getCollection(name) call.

In this case, LazyCollection could make a lookup in the ./folder on every call to getIconData. However I would expressly let implementations of IconCollection outside of this package, since there might be a lot of usecases (caching / svgo compression / FontAwesomePro / fallback...).
Maybe one implementation could live in this repo to serve as a base for the users to implement their own IconCollection.


Maybe the getIconData should be async (since loading the file would be done in a non-blocking way), but I don't have enough package experience to know if this is a good idea or not.


I think this very minimal API would be quite a powerful addition to this package, since it would make it pluggable to a lot of icon sources.

If enough people think that this would be a good idea, I would be glad to craft a PR.

@antfu thank you for you awesome work for the vue community!

@antfu
Copy link
Member

antfu commented Aug 30, 2021

Could anyone offer me a small sample of custom icons (with the folder/file structure you would like) for me to experiment with the API design? It would be great if their license allow me to include them as examples in this repo in the future. Thanks!

@antfu antfu added enhancement New feature or request help wanted Extra attention is needed labels Aug 30, 2021
@antfu antfu pinned this issue Aug 30, 2021
@m5o
Copy link

m5o commented Aug 31, 2021

💡 The Popular category from Icons8 is free, for example.
They only want to include a link and the License seems open source friendly for all the other categories.
Icons8 key feature: they offer icons in many different styles, icon naming and meaning are consistent. Useful for theming applications, you just need to change the icon style/file structure. Maybe this is a resource you’ll find interesting. ✌️

@Akryum
Copy link
Contributor

Akryum commented Sep 14, 2021

Here are some icons I made, you are free to use them however you want:

~/src/assets/icons/SteeringWheel.svg

<svg
    fill="none"
    stroke-linecap="round"
    stroke-linejoin="round"
    stroke-width="2"
    viewBox="0 0 24 24"
    stroke="currentColor"
  ><path d="M12 14V20M10 12L4 10M14 12L20 10M21 12a9 9 0 11-18 0 9 9 0 0118 0zM14 12a2 2 0 11-4 0 2 2 0 014 0z" /></svg>

~/src/assets/icons/Car.svg

<svg
    fill="none"
    stroke-linecap="round"
    stroke-linejoin="round"
    stroke-width="2"
    viewBox="0 0 24 24"
    stroke="currentColor"
  ><path d="M17 10a4 4 0 0 0-4-4h-2a4 4 0 0 0-4 4m5-4v4M3 16h2m4 0h6m4 0h2v-3a3 3 0 0 0-3-3h-12a3 3 0 0 0-3 3v3M9 16a2 2 0 1 1-4 0a2 2 0 0 1 4 0zM19 16a2 2 0 1 1-4 0a2 2 0 0 1 4 0z" /></svg>

@antfu antfu closed this as completed in c1e2e6e Sep 14, 2021
@antfu
Copy link
Member

antfu commented Sep 14, 2021

Thanks everyone, it's now landed as v0.11!
https://github.com/antfu/unplugin-icons#custom-icons

@oliverpool
Copy link

Works perfectly, thank you so much!

@jceb
Copy link
Contributor

jceb commented Oct 7, 2021

Just as a reference, in order to integrate FontAwesome Pro icons via vite use some configuration like this.

Add npm dependencies, esp. @fortawesome/fontawesome-pro.

vite.config.js:

import { defineConfig } from "vite";
import Icons from "unplugin-icons/vite";
import IconsResolver from "unplugin-icons/resolver";
import AutoImport from "unplugin-auto-import/vite";
import { FileSystemIconLoader } from "unplugin-icons/loaders";
import { promises as fs } from "fs";

export default defineConfig({
  plugins: [
    ...,
    AutoImport({
      resolvers: [
        IconsResolver({
          prefix: "Icon",
          extension: "jsx",
          customCollections: [
            "fap-brands",
            "fap-duotone",
            "fap-light",
            "fap-regular",
            "fap-solid",
          ],
        }),
      ],
    }),

    Icons({
      compiler: "solid",
      customCollections: {
        "fap-brands": FileSystemIconLoader(
          "PATH_TO/node_modules/@fortawesome/fontawesome-pro/svgs/brands",
        ),
        "fap-duotone": FileSystemIconLoader(
          "PATH_TO/node_modules/@fortawesome/fontawesome-pro/svgs/duotone",
        ),
        "fap-regular": FileSystemIconLoader(
          "PATH_TO/node_modules/@fortawesome/fontawesome-pro/svgs/regular",
        ),
        "fap-solid": FileSystemIconLoader(
          "PATH_TO/node_modules/@fortawesome/fontawesome-pro/svgs/solid",
        ),
        "fap-light": FileSystemIconLoader(
          "PATH_TO/node_modules/@fortawesome/fontawesome-pro/svgs/light",
        ),
      },
    }),
  ],
});

Then import icons by placing this in your react/solid code: <IconFapSolidEnveleope />.

@jceb
Copy link
Contributor

jceb commented Oct 8, 2021

Actually, the above mentioned approach has one big disadvantage: the fontawesome icons don't come with the SVG attribute fill="currentColor" so the color can't be changed.

In order to make it work one has to build the icons first with a build script that's described here: iconify/iconify#4 (comment)

However, the compiled json files can be loaded directly into unplugin-icons because the loader (https://github.com/antfu/unplugin-icons/blob/e0cb01043b6eb462f4ea42b9b054382874495285/src/core/modern.ts#L51) is hard-coded receive only icons that are part of @iconify/json (https://github.com/antfu/unplugin-icons/blob/e0cb01043b6eb462f4ea42b9b054382874495285/src/core/loader.ts#L74). The customCollections attribute doesn't accept the JSON data structure that @iconify uses, instead it directly expects SVGs.

To make it work, I created a custom loader function that uses the same code as searchForIcon. Here's the whole code in vite.config.js:

import { defineConfig } from "vite";
import solid from "solid-start";
import Icons from "unplugin-icons/vite";
import IconsResolver from "unplugin-icons/resolver";
import AutoImport from "unplugin-auto-import/vite";
import _fab from "./json/fab.json";
import _fad from "./json/fad.json";
import _fal from "./json/fal.json";
import _far from "./json/far.json";
import _fas from "./json/fas.json";
import { iconToSVG } from "@iconify/utils/lib/svg/build";
import { getIconData } from "@iconify/utils/lib/icon-set/get-icon";
import { defaults as DefaultIconCustomizations } from "@iconify/utils/lib/customisations";

const getIcon = (iconSet) => (iconName) => {
  const iconData = getIconData(iconSet, iconName, true);
  if (iconData) {
    const scale = 1;
    const { attributes, body } = iconToSVG(iconData, {
      ...DefaultIconCustomizations,
      height: `${scale}em`,
      width: `${scale}em`,
    });
    return `<svg ${Object.entries(attributes)
      .map((i) => `${i[0]}="${i[1]}"`)
      .join(" ")}>${body}</svg>`;
  }
};

const fab = getIcon(_fab);
const fad = getIcon(_fad);
const fal = getIcon(_fal);
const far = getIcon(_far);
const fas = getIcon(_fas);


export default defineConfig({
  plugins: [
    solid(),
    AutoImport({
      resolvers: [
        IconsResolver({
          prefix: "Icon",
          extension: "jsx",
          customCollections: [
            "fap-brands",
            "fap-duotone",
            "fap-light",
            "fap-regular",
            "fap-solid",
          ],
        }),
      ],
    }),

    Icons({
      compiler: "solid",
      customCollections: {
        "fap-brands": fab,
        "fap-duotone": fad,
        "fap-light": fal,
        "fap-regular": far,
        "fap-solid": fas,
      },
    }),
  ],
});

@antfu
Copy link
Member

antfu commented Oct 8, 2021

@jceb thanks for the update! If you see a way to make it general, I am happy to have a PR to include this loader into the plugin directly.

@jceb
Copy link
Contributor

jceb commented Oct 8, 2021

@antfu I created an issue and would appreciate your input on the "unplugin-icons" way of doing things.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

8 participants