Skip to content

Commit

Permalink
feat: nuxt icon
Browse files Browse the repository at this point in the history
  • Loading branch information
kingyue737 committed Aug 28, 2024
1 parent 50d8539 commit ef61e2e
Show file tree
Hide file tree
Showing 11 changed files with 197 additions and 139 deletions.
14 changes: 5 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,12 @@
### Nuxt Modules

- [Vuetify Nuxt Module](https://github.com/userquin/vuetify-nuxt-module) - Zero-config Nuxt Module for Vuetify
- [VueUse](https://github.com/vueuse/vueuse) - collection of useful composition APIs
- [Pinia](https://github.com/vuejs/pinia) - intuitive, type-safe, light and flexible Store for Vue
- [DevTools](https://github.com/nuxt/devtools) - unleash Nuxt Developer Experience
- [VueUse](https://github.com/vueuse/vueuse) - Collection of useful composition APIs
- [Pinia](https://github.com/vuejs/pinia) - Intuitive, type-safe, light and flexible Store for Vue
- [Nuxt Icon](https://github.com/nuxt/icon) - Icon module for Nuxt with 200,000+ ready to use icons from Iconify
- [Nuxt ECharts](https://github.com/kingyue737/nuxt-echarts) - Nuxt module for Apache ECharts™
- [Nuxt Auth Utils](https://github.com/Atinux/nuxt-auth-utils) - Minimalist Authentication module for Nuxt

### Plugins

- [`rollup-plugin-regexp`](https://github.com/kingyue737/rollup-plugin-regexp) - auto replace [`@mdi/font`](https://github.com/Templarian/MaterialDesign-Webfont) with [`@mdi/js`](https://github.com/Templarian/MaterialDesign-JS) in `<VIcon>`, reduce bundle size

### Coding Style

- [Prettier](https://prettier.io/), single quotes, no semi
Expand All @@ -91,11 +88,10 @@
- [pnpm](https://pnpm.js.org/) - Fast, disk space efficient package manager
- [Netlify](https://www.netlify.com/) - zero-config deployment
- [VS Code Extensions](./.vscode/extensions.json)
- [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) - TypeScript support inside Vue SFCs
- [Vue - Official](https://marketplace.visualstudio.com/items?itemName=Vue.volar) - TypeScript support inside Vue SFCs
- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) - Find and fix problems in your code
- [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) - Code formatter
- [EditorConfig for VS Code](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig)
- [Material Design Icons Intellisense](https://marketplace.visualstudio.com/items?itemName=lukas-tr.materialdesignicons-intellisense)

## Try it now!

Expand Down
3 changes: 1 addition & 2 deletions assets/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ html {
}
}

.v-icon svg {
fill: currentColor;
.v-icon .iconify {
width: 100%;
height: 100%;
}
1 change: 1 addition & 0 deletions augmentation.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ declare module '#app' {
declare module '#auth-utils' {
interface User {
login: string
avatar_url: string
}
}

Expand Down
2 changes: 1 addition & 1 deletion components/App/AppBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const { loggedIn, clear, user } = useUserSession()
<v-btn icon v-bind="mergeProps(menu, tooltip)" class="ml-1">
<v-icon v-if="!loggedIn" icon="mdi-account-circle" size="36" />
<v-avatar v-else color="primary" size="36">
<v-img :src="`https://github.com/${user!.login}.png`" />
<v-img :src="user?.avatar_url" />
</v-avatar>
</v-btn>
</template>
Expand Down
96 changes: 50 additions & 46 deletions components/App/AppNotification.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,54 +27,58 @@ function toggleAll() {
:rounded="0"
@click="toggleAll"
/>
<teleport to="body">
<v-card
elevation="6"
width="400"
class="d-flex flex-column notification-card"
:class="{ 'notification-card--open': showAll }"
>
<v-toolbar flat density="compact">
<v-toolbar-title
class="font-weight-light text-body-1"
:text="notifications.length ? 'Notification' : 'No New Notifications'"
/>
<v-btn
v-tooltip="{ text: 'Clear All Notifications' }"
size="small"
icon="mdi-bell-remove"
@click="emptyNotifications"
/>
<v-btn
v-tooltip="{ text: 'Hide Notifications' }"
class="mr-0"
size="small"
icon="$expand"
@click="toggleAll"
/>
</v-toolbar>
<v-slide-y-reverse-transition
tag="div"
class="d-flex flex-column notification-box"
group
hide-on-leave
<ClientOnly>
<teleport to="body">
<v-card
elevation="6"
width="400"
class="d-flex flex-column notification-card"
:class="{ 'notification-card--open': showAll }"
>
<div
v-for="notification in notificationsShown"
:key="notification.id"
class="notification-item-wrapper"
>
<AppNotificationItem
v-model="notification.show"
:notification="notification"
:timeout="timeout"
class="notification-item"
@close="deleteNotification(notification.id)"
<v-toolbar flat density="compact">
<v-toolbar-title
class="font-weight-light text-body-1"
:text="
notifications.length ? 'Notification' : 'No New Notifications'
"
/>
<v-btn
v-tooltip="{ text: 'Clear All Notifications' }"
size="small"
icon="mdi-bell-remove"
@click="emptyNotifications"
/>
</div>
</v-slide-y-reverse-transition>
</v-card>
</teleport>
<v-btn
v-tooltip="{ text: 'Hide Notifications' }"
class="mr-0"
size="small"
icon="$expand"
@click="toggleAll"
/>
</v-toolbar>
<v-slide-y-reverse-transition
tag="div"
class="d-flex flex-column notification-box"
group
hide-on-leave
>
<div
v-for="notification in notificationsShown"
:key="notification.id"
class="notification-item-wrapper"
>
<AppNotificationItem
v-model="notification.show"
:notification="notification"
:timeout="timeout"
class="notification-item"
@close="deleteNotification(notification.id)"
/>
</div>
</v-slide-y-reverse-transition>
</v-card>
</teleport>
</ClientOnly>
</template>

<style scoped>
Expand Down
43 changes: 14 additions & 29 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,3 @@
import regexpPlugin from 'rollup-plugin-regexp'
import * as mdicons from '@mdi/js'

const mdi: Record<string, string> = {}
Object.keys(mdicons).forEach((key) => {
const value = (mdicons as Record<string, string>)[key]
mdi[
key
.replace(/([A-Z])/g, '-$1')
.toLowerCase()
.replace(/([0-9]+)/g, '-$1')
] = value
})

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
devtools: { enabled: true },
Expand All @@ -21,6 +7,7 @@ export default defineNuxtConfig({
'vuetify-nuxt-module',
'nuxt-auth-utils',
'nuxt-echarts',
'@nuxt/icon',
'@nuxt/eslint',
'@nuxt/test-utils/module',
],
Expand All @@ -39,6 +26,19 @@ export default defineNuxtConfig({
},
},
},
icon: {
mode: 'svg',
clientBundle: {
// scan all components in the project and include icons
// scan: true,
},
customCollections: [
{
prefix: 'custom',
dir: './assets/icons',
},
],
},
echarts: {
charts: ['LineChart', 'BarChart', 'PieChart', 'RadarChart'],
renderer: 'svg',
Expand All @@ -54,21 +54,6 @@ export default defineNuxtConfig({
],
},
vite: {
plugins: [
regexpPlugin({
exclude: ['node_modules/**'],
find: /\b(?<![/\w])(mdi-[\w-]+)\b(?!\.)/,
replace: (match: string) => {
if (mdi[match]) {
return mdi[match]
} else {
console.warn('[plugin-regexp] No matched svg icon for ' + match)
return match
}
},
sourcemap: false,
}),
],
build: { sourcemap: false },
},
runtimeConfig: {
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
"format": "prettier . --write"
},
"devDependencies": {
"@mdi/js": "^7.4.47",
"@iconify-json/mdi": "^1.1.68",
"@nuxt/eslint": "^0.5.3",
"@nuxt/icon": "^1.5.0",
"@nuxt/test-utils": "^3.14.1",
"@vue/test-utils": "^2.4.6",
"eslint": "^9.9.1",
Expand All @@ -24,7 +25,6 @@
"nuxt-echarts": "^0.2.2",
"playwright-core": "^1.46.1",
"prettier": "^3.3.3",
"rollup-plugin-regexp": "^5.0.1",
"vitest": "^2.0.5",
"vue-tsc": "^2.0.29",
"vuetify-nuxt-module": "^0.17.1"
Expand Down
37 changes: 14 additions & 23 deletions plugins/vuetify.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,23 @@
import { type IconSet, type IconProps } from 'vuetify'
import type { IconProps } from 'vuetify'
import { Icon } from '#components'
import type { VDataTable } from 'vuetify/components'
import { useStorage } from '@vueuse/core'
export type DataTableHeaders = VDataTable['$props']['headers']

function filename(path: string) {
return path
.split(/(\\|\/)/g)
.pop()!
.replace(/\.[^/.]+$/, '')
}
import { aliases } from 'vuetify/iconsets/mdi'

const svgIcons = Object.fromEntries(
Object.entries(
import.meta.glob('~/assets/icons/*.svg', {
eager: true,
query: '?raw',
import: 'default',
}),
).map(([k, v]) => [filename(k), v]),
)
const custom: IconSet = {
component: (props: IconProps) =>
h(props.tag, { innerHTML: svgIcons[props.icon as string] }),
}
export type DataTableHeaders = VDataTable['$props']['headers']

export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('vuetify:configuration', ({ vuetifyOptions }) => {
vuetifyOptions.icons!.sets!['custom'] = custom
vuetifyOptions.icons = {
defaultSet: 'nuxtIcon',
sets: {
nuxtIcon: {
component: ({ icon, tag, ...rest }: IconProps) =>
h(tag, rest, [h(Icon, { name: aliases[icon as string] ?? icon })]),
},
},
aliases,
}
const primary = useStorage('theme-primary', '#1697f6').value
vuetifyOptions.theme = {
themes: {
Expand Down
Loading

0 comments on commit ef61e2e

Please sign in to comment.