diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 17023ef932..8f75fa0050 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -28,6 +28,10 @@ export default defineConfig({ new URL('./theme/components/overrides/VPFooter.vue', import.meta.url), ), }, + { + find: '~/.vitepress', + replacement: fileURLToPath(new URL('./', import.meta.url)), + }, ], }, }, diff --git a/docs/.vitepress/data/categoriesData.json b/docs/.vitepress/data/categoriesData.json new file mode 100644 index 0000000000..03712cc237 --- /dev/null +++ b/docs/.vitepress/data/categoriesData.json @@ -0,0 +1,186 @@ +[ + { + "name": "accessibility", + "title": "Accessibility" + }, + { + "name": "account", + "title": "Accounts & access" + }, + { + "name": "animals", + "title": "Animals" + }, + { + "name": "arrows", + "title": "Arrows" + }, + { + "name": "brands", + "title": "Brands" + }, + { + "name": "buildings", + "title": "Buildings" + }, + { + "name": "charts", + "title": "Charts" + }, + { + "name": "communication", + "title": "Communication" + }, + { + "name": "connectivity", + "title": "Connectivity" + }, + { + "name": "currency", + "title": "Currency" + }, + { + "name": "cursors", + "title": "Cursors" + }, + { + "name": "design", + "title": "Design" + }, + { + "name": "development", + "title": "Coding & development" + }, + { + "name": "devices", + "title": "Devices" + }, + { + "name": "emoji", + "title": "Emoji" + }, + { + "name": "files", + "title": "File icons" + }, + { + "name": "food-beverage", + "title": "Food & beverage" + }, + { + "name": "furniture", + "title": "Furniture" + }, + { + "name": "gaming", + "title": "Gaming" + }, + { + "name": "home", + "title": "Home" + }, + { + "name": "layout", + "title": "Layout" + }, + { + "name": "mail", + "title": "Mail" + }, + { + "name": "maps", + "title": "Maps" + }, + { + "name": "maths", + "title": "Maths" + }, + { + "name": "medical", + "title": "Medical" + }, + { + "name": "money", + "title": "Money" + }, + { + "name": "multimedia", + "title": "Multimedia" + }, + { + "name": "nature", + "title": "Nature" + }, + { + "name": "navigation", + "title": "Navigation" + }, + { + "name": "notifications", + "title": "Notifications" + }, + { + "name": "people", + "title": "People" + }, + { + "name": "photography", + "title": "Photography" + }, + { + "name": "science", + "title": "Science" + }, + { + "name": "seasons", + "title": "Seasons" + }, + { + "name": "security", + "title": "Security" + }, + { + "name": "shapes", + "title": "Shapes" + }, + { + "name": "shopping", + "title": "Shopping" + }, + { + "name": "social", + "title": "Social" + }, + { + "name": "sports", + "title": "Sports" + }, + { + "name": "sustainability", + "title": "Sustainability" + }, + { + "name": "text", + "title": "Text formatting" + }, + { + "name": "time", + "title": "Time & calendar" + }, + { + "name": "tools", + "title": "Tools" + }, + { + "name": "transportation", + "title": "Transportation" + }, + { + "name": "travel", + "title": "Travel" + }, + { + "name": "weather", + "title": "Weather" + } +] \ No newline at end of file diff --git a/docs/.vitepress/lib/createCodeExamples.ts b/docs/.vitepress/lib/codeExamples/createCodeExamples.ts similarity index 76% rename from docs/.vitepress/lib/createCodeExamples.ts rename to docs/.vitepress/lib/codeExamples/createCodeExamples.ts index ee9e4bd4fd..7848841c2e 100644 --- a/docs/.vitepress/lib/createCodeExamples.ts +++ b/docs/.vitepress/lib/codeExamples/createCodeExamples.ts @@ -10,18 +10,24 @@ type CodeExampleType = { const getIconCodes = (): CodeExampleType => { return [ { - language: 'html', - title: 'HTML', - code: ``, + language: 'js', + title: 'Vanilla', + code: `\ +import { createIcons, icons } from 'lucide'; + +createIcons({ icons }); + +document.body.append('');\ + `, }, { language: 'tsx', title: 'React', - code: `import { PascalCase } from 'lucide-react'; + code: `import { $PascalCase } from 'lucide-react'; const App = () => { return ( - + <$PascalCase /> ); }; @@ -32,11 +38,11 @@ export default App; language: 'vue', title: 'Vue', code: ` `, }, @@ -44,20 +50,20 @@ export default App; language: 'svelte', title: 'Svelte', code: ` - +<$PascalCase /> `, }, { language: 'tsx', title: 'Preact', - code: `import { PascalCase } from 'lucide-preact'; + code: `import { $PascalCase } from 'lucide-preact'; const App = () => { return ( - + <$PascalCase /> ); }; @@ -67,11 +73,11 @@ export default App; { language: 'tsx', title: 'Solid', - code: `import { PascalCase } from 'lucide-solid'; + code: `import { $PascalCase } from 'lucide-solid'; const App = () => { return ( - + <$PascalCase /> ); }; @@ -82,16 +88,16 @@ export default App; language: 'tsx', title: 'Angular', code: `// app.module.ts -import { LucideAngularModule, PascalCase } from 'lucide-angular'; +import { LucideAngularModule, $PascalCase } from 'lucide-angular'; @NgModule({ imports: [ - LucideAngularModule.pick({ PascalCase }) + LucideAngularModule.pick({ $PascalCase }) ], }) // app.component.html - + `, }, { @@ -101,7 +107,7 @@ import { LucideAngularModule, PascalCase } from 'lucide-angular'; @import ('~lucide-static/font/Lucide.css'); -
+
`, }, ]; diff --git a/docs/.vitepress/lib/codeExamples/createLabCodeExamples.ts b/docs/.vitepress/lib/codeExamples/createLabCodeExamples.ts new file mode 100644 index 0000000000..91b5190160 --- /dev/null +++ b/docs/.vitepress/lib/codeExamples/createLabCodeExamples.ts @@ -0,0 +1,161 @@ +import { bundledLanguages, type ThemeRegistration } from 'shikiji'; +import { getHighlighter } from 'shikiji'; + +type CodeExampleType = { + title: string; + language: string; + code: string; +}[]; + +const getIconCodes = (): CodeExampleType => { + return [ + { + language: 'js', + title: 'Vanilla', + code: `\ +import { createIcons, icons } from 'lucide'; +import { $Name } from '@lucide/lab'; + +createIcons({ + icons: { + $Name + } +}); + +document.body.append('');\ + `, + }, + { + language: 'tsx', + title: 'React', + code: `import { Icon } from 'lucide-react'; +import { $Name } from '@lucide/lab'; + +const App = () => { + return ( + + ); +}; + +export default App; +`, + }, + { + language: 'vue', + title: 'Vue', + code: ` + + +`, + }, + { + language: 'svelte', + title: 'Svelte', + code: ` + + +`, + }, + { + language: 'tsx', + title: 'Preact', + code: `import { Icon } from 'lucide-preact'; +import { $Name } from '@lucide/lab'; + +const App = () => { + return ( + + ); +}; + +export default App; +`, + }, + { + language: 'tsx', + title: 'Solid', + code: `import { Icon } from 'lucide-solid'; +import { $Name } from '@lucide/lab'; + +const App = () => { + return ( + + ); +}; + +export default App; +`, + }, + { + language: 'tsx', + title: 'Angular', + code: `// app.module.ts +import { LucideAngularModule, $PascalCase } from 'lucide-angular'; +import { $Name } from '@lucide/lab'; + +@NgModule({ + imports: [ + LucideAngularModule.pick({ $Name }) + ], +}) + +// app.component.html + +`, + }, + ]; +}; + +export type ThemeOptions = + | ThemeRegistration + | { light: ThemeRegistration; dark: ThemeRegistration }; + +const highLightCode = async (code: string, lang: string, active?: boolean) => { + const highlighter = await getHighlighter({ + themes: ['github-light', 'github-dark'], + langs: Object.keys(bundledLanguages), + }); + + const highlightedCode = highlighter + .codeToHtml(code, { + lang, + themes: { + light: 'github-light', + dark: 'github-dark', + }, + defaultColor: false, + }) + .replace('shiki-themes', 'shiki-themes vp-code'); + + return `
+ + ${lang} + ${highlightedCode} +
`; +}; + +export default async function createCodeExamples() { + const codes = getIconCodes(); + + const codeExamplePromises = codes.map(async ({ title, language, code }, index) => { + const isFirst = index === 0; + + const codeString = await highLightCode(code, language, isFirst); + + return { + title, + language: language, + code: codeString, + }; + }); + + return Promise.all(codeExamplePromises); +} diff --git a/docs/.vitepress/lib/codeExamples/highLightCode.ts b/docs/.vitepress/lib/codeExamples/highLightCode.ts new file mode 100644 index 0000000000..ed51ce21bc --- /dev/null +++ b/docs/.vitepress/lib/codeExamples/highLightCode.ts @@ -0,0 +1,32 @@ +import { bundledLanguages, type ThemeRegistration } from 'shikiji'; +import { getHighlighter } from 'shikiji'; + +export type ThemeOptions = + | ThemeRegistration + | { light: ThemeRegistration; dark: ThemeRegistration }; + +const highLightCode = async (code: string, lang: string, active?: boolean) => { + const highlighter = await getHighlighter({ + themes: ['github-light', 'github-dark'], + langs: Object.keys(bundledLanguages), + }); + + const highlightedCode = highlighter + .codeToHtml(code, { + lang, + themes: { + light: 'github-light', + dark: 'github-dark', + }, + defaultColor: false, + }) + .replace('shiki-themes', 'shiki-themes vp-code'); + + return `
+ + ${lang} + ${highlightedCode} +
`; +}; + +export default highLightCode; diff --git a/docs/.vitepress/lib/codeExamples/types.ts b/docs/.vitepress/lib/codeExamples/types.ts new file mode 100644 index 0000000000..1ab47b4696 --- /dev/null +++ b/docs/.vitepress/lib/codeExamples/types.ts @@ -0,0 +1,5 @@ +export type CodeExampleType = { + title: string; + language: string; + code: string; +}[]; diff --git a/docs/.vitepress/theme/components/base/Checkbox.vue b/docs/.vitepress/theme/components/base/Checkbox.vue new file mode 100644 index 0000000000..a13e9469aa --- /dev/null +++ b/docs/.vitepress/theme/components/base/Checkbox.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/docs/.vitepress/theme/components/home/TeamMemberCard.vue b/docs/.vitepress/theme/components/home/TeamMemberCard.vue index 8d472e8ea5..44adef886d 100644 --- a/docs/.vitepress/theme/components/home/TeamMemberCard.vue +++ b/docs/.vitepress/theme/components/home/TeamMemberCard.vue @@ -3,7 +3,7 @@ export interface TeamMember { name: string title: string image: string - sponsor: string + sponsor?: string socialLinks: DefaultTheme.SocialLink[] } diff --git a/docs/.vitepress/theme/components/icons/CategoryList.vue b/docs/.vitepress/theme/components/icons/CategoryList.vue index 56dc570cf0..49452e2709 100644 --- a/docs/.vitepress/theme/components/icons/CategoryList.vue +++ b/docs/.vitepress/theme/components/icons/CategoryList.vue @@ -6,6 +6,7 @@ import { isActive } from 'vitepress/dist/client/shared' import { useActiveAnchor } from '../../composables/useActiveAnchor' import { data } from './CategoryList.data' import CategoryListItem from './CategoryListItem.vue' +import SidebarTitle from './SidebarTitle.vue' import { useCategoryView } from '../../composables/useCategoryView' const { page } = useData() @@ -46,10 +47,13 @@ watch(headers, () => { diff --git a/docs/.vitepress/theme/components/icons/SidebarTitle.vue b/docs/.vitepress/theme/components/icons/SidebarTitle.vue new file mode 100644 index 0000000000..f3b358b3e1 --- /dev/null +++ b/docs/.vitepress/theme/components/icons/SidebarTitle.vue @@ -0,0 +1,19 @@ + + + diff --git a/docs/.vitepress/theme/composables/useExternalLibs.ts b/docs/.vitepress/theme/composables/useExternalLibs.ts new file mode 100644 index 0000000000..84a4259d76 --- /dev/null +++ b/docs/.vitepress/theme/composables/useExternalLibs.ts @@ -0,0 +1,57 @@ +import { ref, inject, Ref, watch } from 'vue'; +import { ExternalLibs, IconEntity } from '../types'; + +export const EXTERNAL_LIBS_CONTEXT = Symbol('externalLibs'); + +type ExternalIconNodes = Partial>; + +interface ExternalLibContext { + selectedLibs: Ref<[ExternalLibs]>; + externalIconNodes: Ref; +} + +export const externalLibContext = { + selectedLibs: ref([]), + externalIconNodes: ref({}), +}; + +const externalLibIconNodesAPI = { + lab: 'https://lab.lucide.dev/api/icon-details', +}; + +export function useExternalLibs(): ExternalLibContext { + const context = inject(EXTERNAL_LIBS_CONTEXT); + + watch(context?.selectedLibs, async (selectedLibs) => { + const savedIconNodes = { ...context?.externalIconNodes.value }; + const newExternalIconNodes: ExternalIconNodes = {}; + + try { + for (const lib of selectedLibs) { + if (savedIconNodes[lib]) { + newExternalIconNodes[lib] = savedIconNodes[lib]; + } else { + const response = await fetch(externalLibIconNodesAPI[lib]); + const iconNodes = await response.json(); + + if (iconNodes) { + newExternalIconNodes[lib] = Object.values(iconNodes).map((iconEntity: IconEntity) => ({ + ...iconEntity, + externalLibrary: lib, + })); + } + } + } + + context.externalIconNodes.value = newExternalIconNodes; + } catch (error) { + console.error(error); + } + }); + + if (!context) { + throw new Error('useExternalLibs must be used with externalLibs context'); + } + + return context; +} diff --git a/docs/.vitepress/theme/composables/useIconsWithExternalLibs.ts b/docs/.vitepress/theme/composables/useIconsWithExternalLibs.ts new file mode 100644 index 0000000000..dfc7e18c48 --- /dev/null +++ b/docs/.vitepress/theme/composables/useIconsWithExternalLibs.ts @@ -0,0 +1,29 @@ +import { computed } from 'vue'; +import { useExternalLibs } from '~/.vitepress/theme/composables/useExternalLibs'; +import { IconEntity } from '../types'; + +const useIconsWithExternalLibs = (initialIcons?: IconEntity[]) => { + const { externalIconNodes } = useExternalLibs(); + + return computed(() => { + let icons = []; + + if (initialIcons) { + icons = icons.concat(initialIcons); + } + + const externalIconNodesArray = Object.values(externalIconNodes.value); + + if (externalIconNodesArray?.length) { + externalIconNodesArray.forEach((iconNodes) => { + if (iconNodes?.length) { + icons = icons.concat(iconNodes); + } + }); + } + + return icons; + }); +}; + +export default useIconsWithExternalLibs; diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index 9ba9b2d098..143228cf11 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -7,6 +7,7 @@ import HomeHeroIconsCard from './components/home/HomeHeroIconsCard.vue'; import HomeHeroBefore from './components/home/HomeHeroBefore.vue'; import { ICON_STYLE_CONTEXT, iconStyleContext } from './composables/useIconStyle'; import { CATEGORY_VIEW_CONTEXT, categoryViewContext } from './composables/useCategoryView'; +import { EXTERNAL_LIBS_CONTEXT, externalLibContext } from './composables/useExternalLibs'; const theme: Partial = { extends: DefaultTheme, @@ -20,6 +21,7 @@ const theme: Partial = { enhanceApp({ app }) { app.provide(ICON_STYLE_CONTEXT, iconStyleContext); app.provide(CATEGORY_VIEW_CONTEXT, categoryViewContext); + app.provide(EXTERNAL_LIBS_CONTEXT, externalLibContext); }, }; diff --git a/docs/.vitepress/theme/layouts/IconsSidebarNavAfter.vue b/docs/.vitepress/theme/layouts/IconsSidebarNavAfter.vue index caaa7ff802..b0b758f3b0 100644 --- a/docs/.vitepress/theme/layouts/IconsSidebarNavAfter.vue +++ b/docs/.vitepress/theme/layouts/IconsSidebarNavAfter.vue @@ -3,6 +3,7 @@ import { useData } from 'vitepress' import CategoryList from '../components/icons/CategoryList.vue' import SidebarIconCustomizer from '../components/icons/SidebarIconCustomizer.vue' +import ExternalLibrarySelect from '../components/icons/SidebarExternalLibrarySelect.vue' const { page } = useData() @@ -11,6 +12,7 @@ const { page } = useData() diff --git a/docs/.vitepress/theme/types.ts b/docs/.vitepress/theme/types.ts index 88a02750b8..4ceeff181f 100644 --- a/docs/.vitepress/theme/types.ts +++ b/docs/.vitepress/theme/types.ts @@ -1,13 +1,18 @@ export type IconNode = [elementName: string, attrs: Record][]; export type IconNodeWithKeys = [elementName: string, attrs: Record, key: string][]; -export interface IconEntity { - name: string; +export interface IconMetaData { tags: string[]; categories: string[]; contributors: string[]; aliases?: string[]; +} + +export type ExternalLibs = 'lab'; +export interface IconEntity extends IconMetaData { + name: string; iconNode: IconNode; + externalLibrary?: ExternalLibs; createdRelease?: Release; changedRelease?: Release; } diff --git a/docs/guide/packages/lucide-vue-next.md b/docs/guide/packages/lucide-vue-next.md index 367a4aafd4..4d406d0cb4 100644 --- a/docs/guide/packages/lucide-vue-next.md +++ b/docs/guide/packages/lucide-vue-next.md @@ -87,7 +87,7 @@ import { burger } from '@lucide/lab'; ``` diff --git a/docs/icons/[name].md b/docs/icons/[name].md index 33d6101d9c..bf9b2acffd 100644 --- a/docs/icons/[name].md +++ b/docs/icons/[name].md @@ -10,14 +10,14 @@ sidebar: true
diff --git a/docs/icons/codeExamples.data.ts b/docs/icons/codeExamples.data.ts index ce8ac59419..3c65b5ba9f 100644 --- a/docs/icons/codeExamples.data.ts +++ b/docs/icons/codeExamples.data.ts @@ -1,11 +1,9 @@ -import createCodeExamples from '../.vitepress/lib/createCodeExamples'; +import createCodeExamples from '../.vitepress/lib/codeExamples/createCodeExamples'; export default { async load() { const codeExamples = await createCodeExamples(); - // const randomIcons = Array.from({ length: 200 }, () => getRandomItem(icons)) - return { codeExamples, }; diff --git a/docs/icons/index.md b/docs/icons/index.md index 020819f809..bb4ca3dc52 100644 --- a/docs/icons/index.md +++ b/docs/icons/index.md @@ -10,13 +10,17 @@ head: ---
- +
diff --git a/docs/icons/lab/[name].md b/docs/icons/lab/[name].md new file mode 100644 index 0000000000..a3e215a526 --- /dev/null +++ b/docs/icons/lab/[name].md @@ -0,0 +1,10 @@ +--- +layout: doc +footer: false +aside: false +editLink: false +next: false +prev: false +sidebar: true +--- + diff --git a/docs/icons/lab/[name].paths.ts b/docs/icons/lab/[name].paths.ts new file mode 100644 index 0000000000..733760c3b0 --- /dev/null +++ b/docs/icons/lab/[name].paths.ts @@ -0,0 +1,19 @@ +import { IconEntity } from '../../.vitepress/theme/types'; + +export default { + paths: async () => { + const iconDetailsResponse = await fetch('https://lab.lucide.dev/api/icon-details'); + const iconDetails = (await iconDetailsResponse.json()) as Record; + + return Object.values(iconDetails).map((iconEntity) => { + const params = { + externalLibrary: 'lab', + ...iconEntity, + }; + + return { + params, + }; + }); + }, +}; diff --git a/docs/icons/lab/codeExamples.data.ts b/docs/icons/lab/codeExamples.data.ts new file mode 100644 index 0000000000..95b5c3dcde --- /dev/null +++ b/docs/icons/lab/codeExamples.data.ts @@ -0,0 +1,11 @@ +import createCodeExamples from '../../.vitepress/lib/codeExamples/createLabCodeExamples'; + +export default { + async load() { + const codeExamples = await createCodeExamples(); + + return { + codeExamples, + }; + }, +}; diff --git a/docs/tsconfig.json b/docs/tsconfig.json index 9a675fb7f4..3402131d10 100644 --- a/docs/tsconfig.json +++ b/docs/tsconfig.json @@ -5,5 +5,8 @@ "allowImportingTsExtensions": true, "allowSyntheticDefaultImports": true, "noEmit": true, + "paths": { + "~/.vitepress/*": ["./.vitepress/*"], + }, }, }