diff --git a/example/vite.config.ts b/example/vite.config.ts index 80374416..2ba47273 100644 --- a/example/vite.config.ts +++ b/example/vite.config.ts @@ -37,4 +37,11 @@ export default defineConfig({ '@': fileURLToPath(new URL('./src', import.meta.url)), }, }, + css: { + preprocessorOptions: { + scss: { + api: 'modern', + }, + }, + }, }); diff --git a/src/components/icons/RuiIcon.vue b/src/components/icons/RuiIcon.vue index 40a212af..30caf71f 100644 --- a/src/components/icons/RuiIcon.vue +++ b/src/components/icons/RuiIcon.vue @@ -25,7 +25,7 @@ const path: ComputedRef = computed(() => { console.warn(`icon ${name} must be a valid RuiIcon`); } const iconName = `ri-${name}`; - const found = get(registeredIcons)[iconName]; + const found = registeredIcons[iconName]; if (!found) { console.error(`Icons "${name}" not found. Make sure that you have register the icon when installing the RuiPlugin`); diff --git a/src/composables/icons.ts b/src/composables/icons.ts index c949096b..df34338b 100644 --- a/src/composables/icons.ts +++ b/src/composables/icons.ts @@ -32,51 +32,68 @@ import { RiRadioButtonLine, } from '@/icons'; import type { GeneratedIcon } from '@/types/icons'; +import type { InjectionKey } from 'vue'; -export const useIcons = createGlobalState(() => { - const requiredIcons: GeneratedIcon[] = [ - RiAlertFill, - RiAlertLine, - RiArrowDownLine, - RiArrowDropDownFill, - RiArrowLeftDoubleLine, - RiArrowLeftLine, - RiArrowLeftSLine, - RiArrowRightDoubleLine, - RiArrowRightLine, - RiArrowRightSLine, - RiCheckLine, - RiCheckboxBlankCircleFill, - RiCheckboxBlankCircleLine, - RiCheckboxBlankLine, - RiCheckboxCircleFill, - RiCheckboxCircleLine, - RiCheckboxFill, - RiCheckboxIndeterminateFill, - RiCloseCircleLine, - RiCloseFill, - RiCloseLine, - RiDatabase2Line, - RiErrorWarningFill, - RiErrorWarningLine, - RiEyeLine, - RiEyeOffLine, - RiInformationFill, - RiInformationLine, - RiRadioButtonLine, - RiMoreFill, - RiExpandUpDownLine, - ]; +const requiredIcons: GeneratedIcon[] = [ + RiAlertFill, + RiAlertLine, + RiArrowDownLine, + RiArrowDropDownFill, + RiArrowLeftDoubleLine, + RiArrowLeftLine, + RiArrowLeftSLine, + RiArrowRightDoubleLine, + RiArrowRightLine, + RiArrowRightSLine, + RiCheckLine, + RiCheckboxBlankCircleFill, + RiCheckboxBlankCircleLine, + RiCheckboxBlankLine, + RiCheckboxCircleFill, + RiCheckboxCircleLine, + RiCheckboxFill, + RiCheckboxIndeterminateFill, + RiCloseCircleLine, + RiCloseFill, + RiCloseLine, + RiDatabase2Line, + RiErrorWarningFill, + RiErrorWarningLine, + RiEyeLine, + RiEyeOffLine, + RiInformationFill, + RiInformationLine, + RiRadioButtonLine, + RiMoreFill, + RiExpandUpDownLine, +]; + +export interface IconsOptions { + registeredIcons: GeneratedIcon[]; +} + +export interface UseIconsReturn { + registeredIcons: Readonly>; +} + +export const IconsSymbol: InjectionKey = Symbol.for('rui:icons'); - const registeredIcons: Ref> = ref({}); - const registerIcons = (iconsToAdd: GeneratedIcon[]) => { - set(registeredIcons, { - ...get(registeredIcons), +export function createIconDefaults(options?: Partial): UseIconsReturn { + const iconsToAdd = options?.registeredIcons || []; + return { + registeredIcons: { ...Object.fromEntries( - [...requiredIcons, ...iconsToAdd].map(({ name, path }) => [name, path]), + [ + ...requiredIcons, + ...iconsToAdd, + ].map(({ name, path }) => [name, path]), ), - }); + }, }; +} - return { registeredIcons, registerIcons }; -}); +export function useIcons(): UseIconsReturn { + const icons = inject(IconsSymbol); + assert(icons, 'Could not find icons injection'); + return icons; +} diff --git a/src/index.ts b/src/index.ts index 9e2c35fb..975ce582 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,7 @@ import { TableSymbol, createTableDefaults, } from '@/composables/defaults/table'; +import { IconsSymbol, createIconDefaults } from '@/composables/icons'; import type { App } from 'vue'; import type { InitThemeOptions } from '@/types/theme'; import '@/style.scss'; @@ -38,15 +39,17 @@ export function createRui(options: RuiOptions = {}) { const { theme } = options; const defaults = Object.freeze({ + icons: createIconDefaults({ + registeredIcons: options.theme?.icons, + }), table: createTableDefaults(options.defaults?.table), }); const install = (app: App) => { - const { registerIcons } = useIcons(); - registerIcons(theme?.icons || []); useRotkiTheme().init({ ...theme }); app.provide(TableSymbol, defaults.table); + app.provide(IconsSymbol, defaults.icons); }; return { diff --git a/tests/setup-files/setup.ts b/tests/setup-files/setup.ts index 2e83e63e..6ac95ea8 100644 --- a/tests/setup-files/setup.ts +++ b/tests/setup-files/setup.ts @@ -1,11 +1,14 @@ // setup.js file import { vi } from 'vitest'; import { promiseTimeout } from '@vueuse/core'; +import { config } from '@vue/test-utils'; +import { IconsSymbol, createIconDefaults } from '../../src/composables/icons'; import * as Icons from '../../src/icons'; -import { useIcons } from '../../src/composables'; -const { registerIcons } = useIcons(); -registerIcons(Object.values(Icons)); +// @ts-expect-error symbol cannot be used as an index. +config.global.provide[IconsSymbol] = createIconDefaults({ + registeredIcons: Object.values(Icons), +}); const ResizeObserverMock = vi.fn(() => ({ observe: vi.fn(), diff --git a/tsconfig.build.json b/tsconfig.build.json index 31e3b5b3..85d37c79 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -17,8 +17,5 @@ ], "tsc-alias": { "resolveFullPaths": true - }, - "vueCompilerOptions": { - "target": 3.4 } } diff --git a/vite.config.ts b/vite.config.ts index feb690ae..edfeb5fa 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,6 +3,7 @@ import vue from '@vitejs/plugin-vue'; import { defineConfig } from 'vitest/config'; import AutoImport from 'unplugin-auto-import/vite'; import fg from 'fast-glob'; +import consola from 'consola'; const entryPoints = [ 'src/components/index.ts', @@ -22,6 +23,41 @@ const entities = files.map((file) => { const entries = Object.fromEntries(entities); +function manualChunks(identifier: string): string { + const relative = identifier.replace(__dirname, ''); + const pathsAfterModule = relative.split('node_modules/'); + + if (pathsAfterModule.length > 1) { + return 'vendor'; + } + else { + const pathWithoutSrc = pathsAfterModule[0].replace('/src/', ''); + if (pathWithoutSrc.startsWith('icons')) { + return pathWithoutSrc.replace('.ts', ''); + } + else if (pathWithoutSrc.startsWith('utils')) { + return 'utils/index'; + } + else if (pathWithoutSrc.startsWith('composables')) { + return 'composables/index'; + } + else if (pathWithoutSrc.startsWith('components') || pathWithoutSrc.includes('plugin-vue:export-helper')) { + return 'components/index'; + } + else if (pathWithoutSrc.startsWith('types')) { + return 'types/index'; + } + else if (pathWithoutSrc.endsWith('.ts')) { + return pathWithoutSrc.replace('.ts', ''); + } + else { + consola.debug(pathWithoutSrc); + } + + return 'index'; + } +} + export default defineConfig({ resolve: { alias: { @@ -34,7 +70,10 @@ export default defineConfig({ AutoImport({ imports: ['vue', '@vueuse/core', { '@vueuse/shared': ['get', 'set'] }], dts: './auto-imports.d.ts', - dirs: ['src/composables/**', 'src/utils/**'], + dirs: [ + 'src/composables/**/!(theme.ts|icons.ts|breakpoint.ts)', + 'src/utils/**', + ], vueTemplate: true, }), ], @@ -47,6 +86,7 @@ export default defineConfig({ }, build: { outDir: './dist', + minify: true, lib: { entry: entries, fileName: (format, entryName) => `${entryName}.${format}.js`, @@ -59,11 +99,13 @@ export default defineConfig({ 'tailwindcss/plugin', '@vueuse/core', '@vueuse/shared', + '@vueuse/math', ], output: { globals: { vue: 'vue', }, + manualChunks, }, }, },