diff --git a/.eslintrc.json b/.eslintrc.json
index 65f045b8..e8e7c98a 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -87,6 +87,7 @@
]
},
"settings": {
+ "import/core-modules": ["#app"],
"vue-i18n": {
"localeDir": "./src/locales/*.{json,json5,yaml,yml}"
}
diff --git a/components/DropdownMenu.vue b/components/DropdownMenu.vue
index 4ae79fb1..593b4116 100644
--- a/components/DropdownMenu.vue
+++ b/components/DropdownMenu.vue
@@ -1,9 +1,10 @@
@@ -23,12 +21,7 @@ withDefaults(
active-class="bg-tint"
class="group flex gap-2 rounded-xl px-4 py-2"
>
-
-
+
-
-
+
diff --git a/modules/icons/generator/index.ts b/modules/icons/generator/index.ts
new file mode 100644
index 00000000..69cf343a
--- /dev/null
+++ b/modules/icons/generator/index.ts
@@ -0,0 +1,60 @@
+/* eslint-disable import/prefer-default-export */
+/* eslint import/no-extraneous-dependencies: ["error", {"devDependencies": true}] */
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { Nuxt } from "@nuxt/schema";
+import chalk from "chalk";
+import { saveGeneratedFile } from "./output";
+import { constructIconNames } from "./parser";
+
+type CreateTypedIconsArgs = {
+ nuxt: Nuxt;
+ location: string | null;
+ fileExtension: string;
+ isHookCall?: boolean;
+};
+
+export async function CreateTypedIcons({
+ nuxt,
+ location,
+ fileExtension,
+ isHookCall = false,
+}: CreateTypedIconsArgs): Promise {
+ try {
+ if (!isHookCall) {
+ if (location) {
+ nuxt.hook("builder:watch", (_, watchedPath) => {
+ if (watchedPath.startsWith(location)) {
+ CreateTypedIcons({
+ nuxt,
+ location,
+ fileExtension,
+ isHookCall: true,
+ });
+ }
+ });
+ }
+
+ nuxt.hook("modules:done", () => {
+ CreateTypedIcons({ nuxt, location, fileExtension, isHookCall: true });
+ });
+
+ return;
+ }
+
+ if (location) {
+ const iconNames = await constructIconNames(
+ nuxt.options.rootDir,
+ location,
+ fileExtension
+ );
+ await saveGeneratedFile(nuxt, iconNames);
+ } else {
+ await saveGeneratedFile(nuxt, null);
+ }
+ } catch (e) {
+ console.error(
+ chalk.red("Error while generating icons definition model"),
+ `\n${e}`
+ );
+ }
+}
diff --git a/modules/icons/generator/output.ts b/modules/icons/generator/output.ts
new file mode 100644
index 00000000..ad2d26f2
--- /dev/null
+++ b/modules/icons/generator/output.ts
@@ -0,0 +1,27 @@
+/* eslint-disable import/prefer-default-export */
+/* eslint import/no-extraneous-dependencies: ["error", {"devDependencies": true}] */
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { Nuxt } from "@nuxt/schema";
+import { addTemplate } from "@nuxt/kit";
+
+export const saveGeneratedFile = (_: Nuxt, iconNames: string[] | null) => {
+ addTemplate({
+ filename: "nuxt-icons.d.ts",
+ getContents: () => `// @ts-nocheck
+// eslint-disable
+/**
+ * ---------------------------------------------------
+ * 🚗 Generated by nuxt-icons. Do not modify !
+ * ---------------------------------------------------
+ * */
+
+declare module '#app' {
+ type NuxtIconName = ${
+ iconNames === null
+ ? "string"
+ : iconNames.map((p) => `"${p.replace(/[\\"]/g, "\\$&")}"`).join(" | ")
+ }
+}
+`,
+ });
+};
diff --git a/modules/icons/generator/parser.ts b/modules/icons/generator/parser.ts
new file mode 100644
index 00000000..a1f02715
--- /dev/null
+++ b/modules/icons/generator/parser.ts
@@ -0,0 +1,12 @@
+/* eslint-disable import/prefer-default-export */
+/* eslint import/no-extraneous-dependencies: ["error", {"devDependencies": true}] */
+import { glob } from "glob";
+
+export const constructIconNames = async (
+ path: string,
+ location: string,
+ fileExtension: string
+) =>
+ (await glob(`${location}/**/**.${fileExtension}`, { cwd: path }))
+ .map((p) => p.substring(location.length + 1))
+ .map((p) => p.substring(0, p.length - 4));
diff --git a/modules/icons/module.ts b/modules/icons/module.ts
new file mode 100644
index 00000000..c9c6f9fa
--- /dev/null
+++ b/modules/icons/module.ts
@@ -0,0 +1,52 @@
+// This component is a copy of the nuxt-icon component from nuxt-icons (https://nuxt.com/modules/icons)
+/* eslint import/no-extraneous-dependencies: ["error", {"devDependencies": true}] */
+import { defineNuxtModule, createResolver, addComponent } from "@nuxt/kit";
+import { CreateTypedIcons } from "./generator";
+
+export interface ModuleOptions {
+ // dir: string
+ /**
+ * Enables path autocomplete and path validity for programmatic validation
+ *
+ * @default true
+ */
+ pathCheck?: boolean;
+}
+
+export default defineNuxtModule({
+ meta: {
+ name: "nuxt-icons",
+ configKey: "nuxtIcons",
+ compatibility: {
+ nuxt: "^3.0.0",
+ },
+ },
+ defaults: {
+ pathCheck: true,
+ },
+ setup(options, nuxt) {
+ const { resolve } = createResolver(import.meta.url);
+ addComponent({
+ name: "nuxt-icon",
+ global: true,
+ filePath: resolve("./runtime/components/nuxt-icon.vue"),
+ });
+
+ // Force register of type declaration
+ nuxt.hook("prepare:types", ({ references }) => {
+ references.push({
+ path: resolve(nuxt.options.buildDir, "nuxt-icons.d.ts"),
+ });
+ });
+
+ if (options.pathCheck) {
+ CreateTypedIcons({
+ nuxt,
+ location: "assets/icons",
+ fileExtension: "svg",
+ });
+ } else {
+ CreateTypedIcons({ nuxt, location: null, fileExtension: "" });
+ }
+ },
+});
diff --git a/components/IconComponent.vue b/modules/icons/runtime/components/nuxt-icon.vue
similarity index 87%
rename from components/IconComponent.vue
rename to modules/icons/runtime/components/nuxt-icon.vue
index db7a7639..48e8d19e 100644
--- a/components/IconComponent.vue
+++ b/modules/icons/runtime/components/nuxt-icon.vue
@@ -1,11 +1,10 @@