diff --git a/README.md b/README.md
index 0bf01323..cbe912a5 100644
--- a/README.md
+++ b/README.md
@@ -19,6 +19,7 @@
- Zero-config required
- Auto-import Ionic components, composables and icons
+- Support SVG Icons
- Ionic Router integration
- Pre-render routes
- Mobile meta tags
diff --git a/docs/content/0.index.md b/docs/content/0.index.md
index c50a9af0..d9c892d9 100755
--- a/docs/content/0.index.md
+++ b/docs/content/0.index.md
@@ -25,6 +25,7 @@ Batteries-included [Ionic](https://ionicframework.com/) integration for Nuxt.
::list
- Zero-config required
- Auto-import Ionic components, composables and icons
+- Support SVG Icons
- Ionic Router integration
- Pre-render routes
- Mobile meta tags
diff --git a/docs/content/1.get-started/1.introduction.md b/docs/content/1.get-started/1.introduction.md
index d84cb10f..ad2ebaed 100644
--- a/docs/content/1.get-started/1.introduction.md
+++ b/docs/content/1.get-started/1.introduction.md
@@ -35,6 +35,7 @@ This module attempts to get you going with Nuxt + Ionic quickly, providing sane
::list{type=success}
- **Ionic router integration:** continue defining routes based on the structure of your `~/pages` directory and using page-level utilities such as `definePageMeta()`.
- **Auto-imports**: Ionic components, composables and icons are all [auto-imported](https://nuxt.com/docs/guide/concepts/auto-imports) for ease of use.
+- **Support SVG Icons**: custom icons in the assets/ionic-icons path
- **Helpful components and utilities**: This module provides components and utilities to accomplish common tasks more easily.
- **PWA support**: out-of-the-box support for progressive web apps, using `nuxt-pwa-module`.
- **Pre-render routes**
diff --git a/docs/content/1.get-started/3.configuration.md b/docs/content/1.get-started/3.configuration.md
index 34ed5b5d..315687f2 100644
--- a/docs/content/1.get-started/3.configuration.md
+++ b/docs/content/1.get-started/3.configuration.md
@@ -50,8 +50,29 @@ Integrations control which other modules this module should enable and setup fro
- **icons**
+ - **ionicons**
+
Default: `true`
- Disable to stop icons from being auto-imported.
+ Disable to stop ionic-icons from being auto-imported.
+
+ - **svg**
+
+ Custom SVG icons and automatic detection with nested folders and icon size optimization in assets/ionic-icons path.
+ ::alert{type="warning"}
+ ⚠️ Note: Please put all the necessary SVG files in the assets/ionic-icons folder before you run your project. Because when a file is added/deleted in this path, your project will be rerendered again.
+ ::
+
+ - **enable**
+
+ Default: `true`
+
+ Disable to stop svg from being auto-imported.
+
+ - **directoryAsNamespace**
+
+ Default: `true`
+
+ Disable to stop svg from being auto-imported.
#### `css`
diff --git a/docs/content/2.overview/5.icons.md b/docs/content/2.overview/5.icons.md
index 2697f12c..41768358 100644
--- a/docs/content/2.overview/5.icons.md
+++ b/docs/content/2.overview/5.icons.md
@@ -6,35 +6,59 @@ navigation.icon: uil:illustration
Icons are auto-imported from [`ionicons/icons`](https://github.com/ionic-team/ionicons) by default, following the pattern of camel case naming with `ionicons` in front of the original icon name, that you can find on the [official ionicons website](https://ionic.io/ionicons).
+::alert{type="info"}
+ℹ️ Note: Please put all the necessary SVG files in the assets/ionic-icons folder before you run your project. Because when a file is added/deleted in this path, your project will be rerendered again.
+::
+
+::alert{type="warning"}
+⚠️ Note: When you use #import , change the name of the icon using "as" and the desired name. Because it doesn't interfere with auto-import.
+::
+
::code-group
```vue [Auto-imported icons]
+
+
+
+
+
+
+
+
+
```
```vue [Manual imports]
+
+
+
+
```
::
-You can opt-out of auto-importing icons by setting the `integrations.icons` module options in your `nuxt.config.ts` to `false`.
+You can opt-out of auto-importing icons by setting the `integrations.icons.ionicons` module options in your `nuxt.config.ts` to `false`.
```js
export default defineNuxtConfig({
ionic: {
integrations: {
- icons: false,
+ icons: {
+ ionicons: false
+ },
},
},
})
diff --git a/package.json b/package.json
index 623e971f..b80bd316 100644
--- a/package.json
+++ b/package.json
@@ -61,6 +61,7 @@
"ionicons": "^7.3.1",
"pathe": "^1.1.2",
"pkg-types": "^1.0.3",
+ "svgo": "^3.2.0",
"ufo": "^1.5.3",
"unimport": "^3.7.1"
},
diff --git a/playground/composables/usePhotoGallery.ts b/playground/composables/usePhotoGallery.ts
index a9c71659..10f3c809 100644
--- a/playground/composables/usePhotoGallery.ts
+++ b/playground/composables/usePhotoGallery.ts
@@ -4,7 +4,7 @@ import { Camera, CameraSource, CameraResultType } from '@capacitor/camera'
import { Filesystem, Directory } from '@capacitor/filesystem'
import { Preferences } from '@capacitor/preferences'
-export function usePhotoGallery() {
+export const usePhotoGallery = () => {
const photos = ref([])
const PHOTO_STORAGE = 'photos'
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5f13d0f4..9c388f64 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -44,6 +44,9 @@ importers:
pkg-types:
specifier: ^1.0.3
version: 1.0.3
+ svgo:
+ specifier: ^3.2.0
+ version: 3.2.0
ufo:
specifier: ^1.5.3
version: 1.5.3
@@ -8108,8 +8111,6 @@ snapshots:
'@tootallnate/quickjs-emscripten@0.23.0': {}
- '@trysound/sax@0.2.0': {}
-
'@tufjs/canonical-json@2.0.0': {}
'@tufjs/models@2.0.0':
@@ -8497,10 +8498,6 @@ snapshots:
'@vue/compiler-core@3.4.24':
dependencies:
'@babel/parser': 7.24.4
- '@vue/shared': 3.4.24
- entities: 4.5.0
- estree-walker: 2.0.2
- source-map-js: 1.2.0
'@vue/compiler-dom@3.4.24':
dependencies:
@@ -8851,8 +8848,6 @@ snapshots:
inherits: 2.0.4
readable-stream: 3.6.2
- boolbase@1.0.0: {}
-
bplist-parser@0.2.0:
dependencies:
big-integer: 1.6.51
@@ -9133,8 +9128,6 @@ snapshots:
commander@2.20.3: {}
- commander@7.2.0: {}
-
commander@8.3.0: {}
commander@9.5.0: {}
@@ -9223,8 +9216,6 @@ snapshots:
mdn-data: 2.0.30
source-map-js: 1.2.0
- css-what@6.1.0: {}
-
cssesc@3.0.0: {}
cssnano-preset-default@6.0.3(postcss@8.4.38):
@@ -9443,8 +9434,6 @@ snapshots:
domhandler: 5.0.3
entities: 4.5.0
- domelementtype@2.3.0: {}
-
domhandler@5.0.3:
dependencies:
domelementtype: 2.3.0
@@ -9522,8 +9511,6 @@ snapshots:
graceful-fs: 4.2.11
tapable: 2.2.1
- entities@4.5.0: {}
-
env-paths@2.2.1: {}
err-code@2.0.3: {}
@@ -11094,10 +11081,6 @@ snapshots:
dependencies:
'@types/mdast': 4.0.1
- mdn-data@2.0.28: {}
-
- mdn-data@2.0.30: {}
-
mdurl@1.0.1: {}
merge-stream@2.0.0: {}
@@ -12592,8 +12575,6 @@ snapshots:
postcss-unique-selectors@7.0.0(postcss@8.4.38):
dependencies:
- postcss: 8.4.38
- postcss-selector-parser: 6.0.16
postcss-value-parser@4.2.0: {}
@@ -13174,8 +13155,6 @@ snapshots:
ip: 2.0.0
smart-buffer: 4.2.0
- source-map-js@1.2.0: {}
-
source-map-support@0.5.21:
dependencies:
buffer-from: 1.1.2
diff --git a/src/module.ts b/src/module.ts
index 11a35545..f1164aec 100644
--- a/src/module.ts
+++ b/src/module.ts
@@ -17,17 +17,28 @@ import { IonicBuiltInComponents, IonicHooks } from './imports'
import { setupUtilityComponents } from './parts/components'
import { useCSSSetup } from './parts/css'
-import { setupIcons } from './parts/icons'
+import { setupIonIcons } from './parts/icons/ionicons'
+import { setupIonicIconsSvg } from './parts/icons/ionic-icons-svg'
import { setupMeta } from './parts/meta'
import { setupPWA } from './parts/pwa'
import { setupRouter } from './parts/router'
+export interface ModuleOptionIconSvg {
+ enable?: boolean
+ directoryAsNamespace?: boolean
+}
+
+export interface ModuleOptionIcon {
+ ionicons?: boolean
+ svg?: ModuleOptionIconSvg
+}
+
export interface ModuleOptions {
integrations?: {
router?: boolean
pwa?: boolean
meta?: boolean
- icons?: boolean
+ icons?: ModuleOptionIcon
}
css?: {
core?: boolean
@@ -93,7 +104,13 @@ export default defineNuxtModule({
meta: true,
pwa: true,
router: true,
- icons: true,
+ icons: {
+ ionicons: true,
+ svg: {
+ enable: false,
+ directoryAsNamespace: true,
+ },
+ },
},
css: {
core: true,
@@ -205,8 +222,13 @@ export default defineNuxtModule({
}
// Add auto-imported icons
- if (options.integrations?.icons) {
- await setupIcons()
+ if (options.integrations?.icons?.ionicons) {
+ await setupIonIcons()
+ }
+
+ // Add auto-imported custom icons
+ if (options.integrations?.icons?.svg?.enable) {
+ setupIonicIconsSvg(options.integrations?.icons?.svg)
}
if (options.integrations?.meta) {
diff --git a/src/parts/icons/ionic-icons-svg.ts b/src/parts/icons/ionic-icons-svg.ts
new file mode 100644
index 00000000..83b9c099
--- /dev/null
+++ b/src/parts/icons/ionic-icons-svg.ts
@@ -0,0 +1,123 @@
+import fs from 'node:fs'
+import { defineUnimportPreset } from 'unimport'
+import { useNuxt, addTemplate, addImportsSources } from '@nuxt/kit'
+import { join, resolve, basename, sep } from 'pathe'
+import svgo from 'svgo'
+import type { ModuleOptionIconSvg } from '../../module'
+
+const getIcons = (dir: string): string[] => {
+ const items = fs.readdirSync(dir)
+ const files: string[] = []
+
+ for (const item of items) {
+ const pathFile = join(dir, item)
+ if (fs.existsSync(pathFile) && fs.lstatSync(pathFile).isDirectory()) {
+ files.push(...getIcons(pathFile))
+ }
+ else if (pathFile.endsWith(`.svg`)) {
+ files.push(pathFile)
+ }
+ }
+ return files
+}
+
+export const setupIonicIconsSvg = async (options: ModuleOptionIconSvg) => {
+ // init
+ const nuxt = useNuxt()
+ const newIcons: Array<{ name: string, path: string }> = []
+ const fileNameConfigIonicIcons = 'ionic-icons.ts'
+ const pathConfigIonicIcons = resolve(nuxt.options.buildDir, fileNameConfigIonicIcons)
+ const pathIonicIcons = join(nuxt?.options?.rootDir, `assets/ionic-icons`)
+ if (!fs.existsSync(pathIonicIcons)) {
+ fs.mkdir(pathIonicIcons, { recursive: true }, (err) => {
+ if (err) {
+ // note: this does NOT get triggered if the directory already existed
+ console.info(
+ 'Unfortunately, a problem has occurred. We cannot create the ionic-icons folder. Please create the ionic-icons folder manually in the assets path.',
+ )
+ }
+ })
+ }
+ if (fs.existsSync(pathIonicIcons)) {
+ const icons = getIcons(pathIonicIcons)
+
+ // check length
+ if (Object.keys(icons).length > 0) {
+ for (const icon of icons) {
+ let fileName = basename(icon)
+ if (options.directoryAsNamespace) {
+ const segments = icon.replace(pathIonicIcons, '').split(sep)
+ segments.splice(0, 1)
+ fileName = segments.map((name: string) => name[0].toUpperCase() + name.slice(1)).join('').replace('.svg', '')
+ }
+ else {
+ fileName = fileName.replace('.svg', '').toUpperCase()
+ }
+ fileName = fileName
+ .split('-')
+ .map((name: string) => name[0].toUpperCase() + name.slice(1))
+ .join('')
+ newIcons.push({
+ name: fileName,
+ path: icon,
+ })
+ }
+
+ let fileText = `/**
+* @description Generated by @nuxtjs/ionic
+* @author rasool-deldar
+*/\n`
+ let count = 0
+ for (const newIcon of newIcons) {
+ fs.readFile(newIcon.path, 'utf8', async (err, data) => {
+ if (err) console.log(err)
+ const resultSvgo = svgo.optimize(data, {
+ multipass: true,
+ js2svg: {
+ indent: 1,
+ pretty: false,
+ },
+ plugins: [
+ 'cleanupAttrs',
+ 'minifyStyles',
+ 'removeDoctype',
+ 'removeEditorsNSData',
+ 'removeXMLProcInst',
+ {
+ name: 'removeComments',
+ params: {
+ preservePatterns: ['