mirror of
https://github.com/lucide-icons/lucide.git
synced 2025-12-16 20:37:41 +01:00
docs(icons): External Lucide icons like from lab on lucide.dev (#2194)
* Add section title * Add external libs list in sidebar * Make external lib work * Adds external lib to detail view * fix lint issues * Update to https
This commit is contained in:
@@ -28,6 +28,10 @@ export default defineConfig({
|
|||||||
new URL('./theme/components/overrides/VPFooter.vue', import.meta.url),
|
new URL('./theme/components/overrides/VPFooter.vue', import.meta.url),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
find: '~/.vitepress',
|
||||||
|
replacement: fileURLToPath(new URL('./', import.meta.url)),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
186
docs/.vitepress/data/categoriesData.json
Normal file
186
docs/.vitepress/data/categoriesData.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -10,18 +10,24 @@ type CodeExampleType = {
|
|||||||
const getIconCodes = (): CodeExampleType => {
|
const getIconCodes = (): CodeExampleType => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
language: 'html',
|
language: 'js',
|
||||||
title: 'HTML',
|
title: 'Vanilla',
|
||||||
code: `<i data-lucide="Name"></i>`,
|
code: `\
|
||||||
|
import { createIcons, icons } from 'lucide';
|
||||||
|
|
||||||
|
createIcons({ icons });
|
||||||
|
|
||||||
|
document.body.append('<i data-lucide="$Name"></i>');\
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
language: 'tsx',
|
language: 'tsx',
|
||||||
title: 'React',
|
title: 'React',
|
||||||
code: `import { PascalCase } from 'lucide-react';
|
code: `import { $PascalCase } from 'lucide-react';
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
return (
|
return (
|
||||||
<PascalCase />
|
<$PascalCase />
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,11 +38,11 @@ export default App;
|
|||||||
language: 'vue',
|
language: 'vue',
|
||||||
title: 'Vue',
|
title: 'Vue',
|
||||||
code: `<script setup>
|
code: `<script setup>
|
||||||
import { PascalCase } from 'lucide-vue-next';
|
import { $PascalCase } from 'lucide-vue-next';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PascalCase />
|
<$PascalCase />
|
||||||
</template>
|
</template>
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
@@ -44,20 +50,20 @@ export default App;
|
|||||||
language: 'svelte',
|
language: 'svelte',
|
||||||
title: 'Svelte',
|
title: 'Svelte',
|
||||||
code: `<script>
|
code: `<script>
|
||||||
import { PascalCase } from 'lucide-svelte';
|
import { $PascalCase } from 'lucide-svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<PascalCase />
|
<$PascalCase />
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
language: 'tsx',
|
language: 'tsx',
|
||||||
title: 'Preact',
|
title: 'Preact',
|
||||||
code: `import { PascalCase } from 'lucide-preact';
|
code: `import { $PascalCase } from 'lucide-preact';
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
return (
|
return (
|
||||||
<PascalCase />
|
<$PascalCase />
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -67,11 +73,11 @@ export default App;
|
|||||||
{
|
{
|
||||||
language: 'tsx',
|
language: 'tsx',
|
||||||
title: 'Solid',
|
title: 'Solid',
|
||||||
code: `import { PascalCase } from 'lucide-solid';
|
code: `import { $PascalCase } from 'lucide-solid';
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
return (
|
return (
|
||||||
<PascalCase />
|
<$PascalCase />
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -82,16 +88,16 @@ export default App;
|
|||||||
language: 'tsx',
|
language: 'tsx',
|
||||||
title: 'Angular',
|
title: 'Angular',
|
||||||
code: `// app.module.ts
|
code: `// app.module.ts
|
||||||
import { LucideAngularModule, PascalCase } from 'lucide-angular';
|
import { LucideAngularModule, $PascalCase } from 'lucide-angular';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
LucideAngularModule.pick({ PascalCase })
|
LucideAngularModule.pick({ $PascalCase })
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// app.component.html
|
// app.component.html
|
||||||
<lucide-icon name="Name"></lucide-icon>
|
<lucide-icon name="$Name"></lucide-icon>
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -101,7 +107,7 @@ import { LucideAngularModule, PascalCase } from 'lucide-angular';
|
|||||||
@import ('~lucide-static/font/Lucide.css');
|
@import ('~lucide-static/font/Lucide.css');
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="icon-Name"></div>
|
<div class="icon-$Name"></div>
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
161
docs/.vitepress/lib/codeExamples/createLabCodeExamples.ts
Normal file
161
docs/.vitepress/lib/codeExamples/createLabCodeExamples.ts
Normal file
@@ -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('<i data-lucide="$Name"></i>');\
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
language: 'tsx',
|
||||||
|
title: 'React',
|
||||||
|
code: `import { Icon } from 'lucide-react';
|
||||||
|
import { $Name } from '@lucide/lab';
|
||||||
|
|
||||||
|
const App = () => {
|
||||||
|
return (
|
||||||
|
<Icon iconNode={$Name} />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
language: 'vue',
|
||||||
|
title: 'Vue',
|
||||||
|
code: `<script setup>
|
||||||
|
import { Icon } from 'lucide-vue-next';
|
||||||
|
import { $Name } from '@lucide/lab';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Icon :iconNode="burger" />
|
||||||
|
</template>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
language: 'svelte',
|
||||||
|
title: 'Svelte',
|
||||||
|
code: `<script>
|
||||||
|
import { Icon } from 'lucide-svelte';
|
||||||
|
import { $Name } from '@lucide/lab';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Icon iconNode={burger} />
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
language: 'tsx',
|
||||||
|
title: 'Preact',
|
||||||
|
code: `import { Icon } from 'lucide-preact';
|
||||||
|
import { $Name } from '@lucide/lab';
|
||||||
|
|
||||||
|
const App = () => {
|
||||||
|
return (
|
||||||
|
<Icon iconNode={$Name} />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
language: 'tsx',
|
||||||
|
title: 'Solid',
|
||||||
|
code: `import { Icon } from 'lucide-solid';
|
||||||
|
import { $Name } from '@lucide/lab';
|
||||||
|
|
||||||
|
const App = () => {
|
||||||
|
return (
|
||||||
|
<Icon iconNode={$Name} />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
<lucide-icon name="$Name"></lucide-icon>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
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 `<div class="language-${lang} ${active ? 'active' : ''}">
|
||||||
|
<button title="Copy Code" class="copy"></button>
|
||||||
|
<span class="lang">${lang}</span>
|
||||||
|
${highlightedCode}
|
||||||
|
</div>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
32
docs/.vitepress/lib/codeExamples/highLightCode.ts
Normal file
32
docs/.vitepress/lib/codeExamples/highLightCode.ts
Normal file
@@ -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 `<div class="language-${lang} ${active ? 'active' : ''}">
|
||||||
|
<button title="Copy Code" class="copy"></button>
|
||||||
|
<span class="lang">${lang}</span>
|
||||||
|
${highlightedCode}
|
||||||
|
</div>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default highLightCode;
|
||||||
5
docs/.vitepress/lib/codeExamples/types.ts
Normal file
5
docs/.vitepress/lib/codeExamples/types.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export type CodeExampleType = {
|
||||||
|
title: string;
|
||||||
|
language: string;
|
||||||
|
code: string;
|
||||||
|
}[];
|
||||||
90
docs/.vitepress/theme/components/base/Checkbox.vue
Normal file
90
docs/.vitepress/theme/components/base/Checkbox.vue
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
label: string
|
||||||
|
id: string
|
||||||
|
value: string
|
||||||
|
modelValue: string | string[]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits(['change', 'input', 'update:modelValue'])
|
||||||
|
|
||||||
|
const model = computed({
|
||||||
|
get: () => {
|
||||||
|
if (Array.isArray(props.modelValue)) {
|
||||||
|
return props.modelValue.includes(props.value)
|
||||||
|
}
|
||||||
|
return props.modelValue === props.value
|
||||||
|
|
||||||
|
},
|
||||||
|
set: (value: string) => {
|
||||||
|
if (Array.isArray(props.modelValue)) {
|
||||||
|
const newValue = [...props.modelValue]
|
||||||
|
const index = newValue.indexOf(props.value)
|
||||||
|
if (value) {
|
||||||
|
if (index === -1) {
|
||||||
|
newValue.push(props.value)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (index !== -1) {
|
||||||
|
newValue.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit('update:modelValue', newValue)
|
||||||
|
} else {
|
||||||
|
emit('update:modelValue', value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="checkbox-wrapper">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="checkbox"
|
||||||
|
ref="input"
|
||||||
|
:id="id"
|
||||||
|
v-model="model"
|
||||||
|
v-bind="$attrs"
|
||||||
|
/>
|
||||||
|
<label :for="id" class="checkbox-label">
|
||||||
|
{{ label }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.checkbox-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label {
|
||||||
|
line-height: 20px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--vt-c-text-1);
|
||||||
|
transition: color .5s;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid var(--vp-input-border-color);
|
||||||
|
background-color: var(--vp-input-switch-bg-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox:checked {
|
||||||
|
border-color: transparent;
|
||||||
|
background: url("data:image/svg+xml,%3Csvg width='12px' height='12px' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='4' stroke-linecap='round' stroke-linejoin='round' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E")
|
||||||
|
center no-repeat var(--vp-c-brand);;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -3,7 +3,7 @@ export interface TeamMember {
|
|||||||
name: string
|
name: string
|
||||||
title: string
|
title: string
|
||||||
image: string
|
image: string
|
||||||
sponsor: string
|
sponsor?: string
|
||||||
socialLinks: DefaultTheme.SocialLink[]
|
socialLinks: DefaultTheme.SocialLink[]
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { isActive } from 'vitepress/dist/client/shared'
|
|||||||
import { useActiveAnchor } from '../../composables/useActiveAnchor'
|
import { useActiveAnchor } from '../../composables/useActiveAnchor'
|
||||||
import { data } from './CategoryList.data'
|
import { data } from './CategoryList.data'
|
||||||
import CategoryListItem from './CategoryListItem.vue'
|
import CategoryListItem from './CategoryListItem.vue'
|
||||||
|
import SidebarTitle from './SidebarTitle.vue'
|
||||||
import { useCategoryView } from '../../composables/useCategoryView'
|
import { useCategoryView } from '../../composables/useCategoryView'
|
||||||
|
|
||||||
const { page } = useData()
|
const { page } = useData()
|
||||||
@@ -46,10 +47,13 @@ watch(headers, () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="category-list" ref="container">
|
<div class="category-list" ref="container">
|
||||||
<VPLink class="sidebar-title" href="/icons/" :class="{ 'active': overviewIsActive } ">
|
<SidebarTitle>
|
||||||
|
View
|
||||||
|
</SidebarTitle>
|
||||||
|
<VPLink class="sidebar-link sidebar-text" href="/icons/" :class="{ 'active': overviewIsActive } ">
|
||||||
All
|
All
|
||||||
</VPLink>
|
</VPLink>
|
||||||
<VPLink class="sidebar-title" href="/icons/categories" :class="{ 'active': categoriesIsActive } ">
|
<VPLink class="sidebar-link sidebar-text" href="/icons/categories" :class="{ 'active': categoriesIsActive } ">
|
||||||
Categories
|
Categories
|
||||||
</VPLink>
|
</VPLink>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@@ -62,17 +66,20 @@ watch(headers, () => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.sidebar-title {
|
.sidebar-text {
|
||||||
font-weight: 500;
|
|
||||||
color: var(--vp-c-text-2);
|
|
||||||
margin-bottom: 6px;
|
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
display: block;
|
display: block;
|
||||||
transition: color 0.25s;
|
transition: color 0.25s;
|
||||||
|
padding: 4px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-title:hover, .sidebar-title.active {
|
.sidebar-link {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--vp-c-text-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-link:hover, .sidebar-link.active {
|
||||||
color: var(--vp-c-brand);
|
color: var(--vp-c-brand);
|
||||||
}
|
}
|
||||||
.content {
|
.content {
|
||||||
|
|||||||
@@ -11,19 +11,30 @@ import IconInfo from './IconInfo.vue';
|
|||||||
import Badge from '../base/Badge.vue';
|
import Badge from '../base/Badge.vue';
|
||||||
import { computedAsync } from '@vueuse/core';
|
import { computedAsync } from '@vueuse/core';
|
||||||
import { satisfies } from 'semver';
|
import { satisfies } from 'semver';
|
||||||
|
import { useExternalLibs } from '../../composables/useExternalLibs';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
iconName: string | null
|
iconName: string | null
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const { externalIconNodes } = useExternalLibs()
|
||||||
|
|
||||||
const { go } = useRouter()
|
const { go } = useRouter()
|
||||||
|
|
||||||
const icon = computedAsync<IconEntity | null>(async () => {
|
const icon = computedAsync<IconEntity | null>(async () => {
|
||||||
if (props.iconName) {
|
if (props.iconName) {
|
||||||
try {
|
try {
|
||||||
return (await import(`../../../data/iconDetails/${props.iconName}.ts`)).default as IconEntity
|
if (props.iconName.includes(':')) {
|
||||||
|
const [library, name] = props.iconName.split(':')
|
||||||
|
|
||||||
|
return externalIconNodes.value[library].find((icon) => icon.name === name)
|
||||||
|
} else {
|
||||||
|
return (await import(`../../../data/iconDetails/${props.iconName}.ts`)).default as IconEntity
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
go(`/icons/${props.iconName}`)
|
if (!props.iconName.includes(':')) {
|
||||||
|
go(`/icons/${props.iconName}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
@@ -56,7 +67,7 @@ const Expand = createLucideIcon('Expand', expand)
|
|||||||
class="version"
|
class="version"
|
||||||
:href="releaseTagLink(icon.createdRelease.version)"
|
:href="releaseTagLink(icon.createdRelease.version)"
|
||||||
>v{{ icon.createdRelease.version }}</Badge>
|
>v{{ icon.createdRelease.version }}</Badge>
|
||||||
<IconButton @click="go(`/icons/${icon.name}`)">
|
<IconButton @click="go(icon.externalLibrary ? `/icons/${icon.externalLibrary}/${icon.name}` : `/icons/${icon.name}`)">
|
||||||
<component :is="Expand" />
|
<component :is="Expand" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton @click="onClose">
|
<IconButton @click="onClose">
|
||||||
|
|||||||
@@ -25,8 +25,10 @@ function setActiveIcon(name: string) {
|
|||||||
:key="icon.name"
|
:key="icon.name"
|
||||||
>
|
>
|
||||||
<IconItem
|
<IconItem
|
||||||
v-bind="icon"
|
:iconNode="icon.iconNode"
|
||||||
@setActiveIcon="setActiveIcon(icon.name)"
|
:name="icon.name"
|
||||||
|
:externalLibrary="icon.externalLibrary"
|
||||||
|
@setActiveIcon="setActiveIcon"
|
||||||
:active="activeIcon === icon.name"
|
:active="activeIcon === icon.name"
|
||||||
customizable
|
customizable
|
||||||
:overlayMode="overlayMode"
|
:overlayMode="overlayMode"
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import CopyCodeButton from './CopyCodeButton.vue';
|
|||||||
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue';
|
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue';
|
||||||
import {useData, useRouter} from 'vitepress';
|
import {useData, useRouter} from 'vitepress';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
|
||||||
|
import { diamond } from '../../../data/iconNodes'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
icon: IconEntity
|
icon: IconEntity
|
||||||
@@ -20,13 +22,21 @@ const tags = computed(() => {
|
|||||||
if (!props.icon || !props?.icon?.tags) return []
|
if (!props.icon || !props?.icon?.tags) return []
|
||||||
return props.icon.tags.join(' • ')
|
return props.icon.tags.join(' • ')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const DiamondIcon = createLucideIcon('Diamond', diamond)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="icon-info">
|
<div class="icon-info">
|
||||||
<IconDetailName class="icon-name">
|
<div class="icon-name-wrapper">
|
||||||
{{ icon.name }}
|
<IconDetailName class="icon-name">
|
||||||
</IconDetailName>
|
{{ icon.name }}
|
||||||
|
</IconDetailName>
|
||||||
|
<div v-if="icon.externalLibrary" class="icon-external-lib">
|
||||||
|
<DiamondIcon fill="currentColor" :size="12"/>
|
||||||
|
{{ icon.externalLibrary }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="tags-scroller" v-if="tags.length">
|
<div class="tags-scroller" v-if="tags.length">
|
||||||
<p class="icon-tags horizontal-scroller">
|
<p class="icon-tags horizontal-scroller">
|
||||||
{{ tags }}
|
{{ tags }}
|
||||||
@@ -44,10 +54,10 @@ const tags = computed(() => {
|
|||||||
|
|
||||||
<div class="group buttons">
|
<div class="group buttons">
|
||||||
<VPButton
|
<VPButton
|
||||||
v-if="!page?.relativePath?.startsWith?.(`icons/${icon.name}`)"
|
v-if="!page?.relativePath?.startsWith?.(icon.externalLibrary ? `icons/${icon.externalLibrary}/${icon.name}`: `icons/${icon.name}`)"
|
||||||
:href="`/icons/${icon.name}`"
|
:href="icon.externalLibrary ? `/icons/${icon.externalLibrary}/${icon.name}`: `/icons/${icon.name}`"
|
||||||
text="See in action"
|
text="See in action"
|
||||||
@click="go(`/icons/${icon.name}`)"
|
@click="go(icon.externalLibrary ? `/icons/${icon.externalLibrary}/${icon.name}`: `/icons/${icon.name}`)"
|
||||||
/>
|
/>
|
||||||
<CopySVGButton :name="icon.name" :popoverPosition="popoverPosition"/>
|
<CopySVGButton :name="icon.name" :popoverPosition="popoverPosition"/>
|
||||||
<CopyCodeButton :name="icon.name" :popoverPosition="popoverPosition"/>
|
<CopyCodeButton :name="icon.name" :popoverPosition="popoverPosition"/>
|
||||||
@@ -67,9 +77,27 @@ const tags = computed(() => {
|
|||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
.icon-name {
|
.icon-name {
|
||||||
|
margin-right: -36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-name-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-external-lib {
|
||||||
|
color: var(--vp-c-brand-dark);
|
||||||
|
padding: 4px 12px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 28px;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.icon-tags {
|
.icon-tags {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: var(--vp-c-text-2);
|
color: var(--vp-c-text-2);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { useRouter } from 'vitepress';
|
|||||||
import getSVGIcon from '../../utils/getSVGIcon';
|
import getSVGIcon from '../../utils/getSVGIcon';
|
||||||
import useConfetti from '../../composables/useConfetti';
|
import useConfetti from '../../composables/useConfetti';
|
||||||
import Tooltip from '../base/Tooltip.vue';
|
import Tooltip from '../base/Tooltip.vue';
|
||||||
|
import { diamond } from '../../../data/iconNodes'
|
||||||
|
|
||||||
const downloadText = 'Download!'
|
const downloadText = 'Download!'
|
||||||
const copiedText = 'Copied!'
|
const copiedText = 'Copied!'
|
||||||
@@ -16,6 +17,7 @@ const props = defineProps<{
|
|||||||
name: string;
|
name: string;
|
||||||
iconNode: IconNode;
|
iconNode: IconNode;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
|
externalLibrary?: string;
|
||||||
customizable?: boolean;
|
customizable?: boolean;
|
||||||
overlayMode?: boolean
|
overlayMode?: boolean
|
||||||
hideIcon?: boolean
|
hideIcon?: boolean
|
||||||
@@ -33,8 +35,9 @@ const icon = computed(() => {
|
|||||||
return createLucideIcon(props.name, props.iconNode)
|
return createLucideIcon(props.name, props.iconNode)
|
||||||
})
|
})
|
||||||
|
|
||||||
async function navigateToIcon(event) {
|
const href = computed(() => props.externalLibrary ? `/icons/${props.externalLibrary}/${props.name}` : `/icons/${props.name}`)
|
||||||
|
|
||||||
|
async function navigateToIcon(event) {
|
||||||
if (event.shiftKey) {
|
if (event.shiftKey) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
const svgString = getSVGIcon(event.target.firstChild, {
|
const svgString = getSVGIcon(event.target.firstChild, {
|
||||||
@@ -50,13 +53,16 @@ async function navigateToIcon(event) {
|
|||||||
|
|
||||||
if(props.overlayMode && showOverlay.value) {
|
if(props.overlayMode && showOverlay.value) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
window.history.pushState({}, '', `/icons/${props.name}`)
|
|
||||||
emit('setActiveIcon', props.name)
|
window.history.pushState({}, '', props.externalLibrary ? `/icons/${props.externalLibrary}/${props.name}` : `/icons/${props.name}`)
|
||||||
|
emit('setActiveIcon', props.externalLibrary ? `${props.externalLibrary}:${props.name}`: props.name)
|
||||||
} else {
|
} else {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
go(`/icons/${props.name}`)
|
go(props.externalLibrary ? `/icons/${props.externalLibrary}/${props.name}` : `/icons/${props.name}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DiamondIcon = createLucideIcon('Diamond', diamond)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -66,7 +72,7 @@ async function navigateToIcon(event) {
|
|||||||
@click="navigateToIcon"
|
@click="navigateToIcon"
|
||||||
:class="{ active, animate }"
|
:class="{ active, animate }"
|
||||||
:aria-label="name"
|
:aria-label="name"
|
||||||
:href="`/icons/${props.name}`"
|
|
||||||
:data-confetti-text="confettiText"
|
:data-confetti-text="confettiText"
|
||||||
ref="ref"
|
ref="ref"
|
||||||
>
|
>
|
||||||
@@ -80,6 +86,13 @@ async function navigateToIcon(event) {
|
|||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
</KeepAlive>
|
</KeepAlive>
|
||||||
|
<div
|
||||||
|
v-if="externalLibrary"
|
||||||
|
class="floating-diamond"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<DiamondIcon fill="currentColor" :size="8"/>
|
||||||
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</template>
|
</template>
|
||||||
@@ -88,6 +101,7 @@ async function navigateToIcon(event) {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.icon-button {
|
.icon-button {
|
||||||
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -104,6 +118,13 @@ async function navigateToIcon(event) {
|
|||||||
color: var(--vp-c-text-1);
|
color: var(--vp-c-text-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.floating-diamond {
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
right: 4px;
|
||||||
|
color: var(--vp-c-brand);
|
||||||
|
}
|
||||||
|
|
||||||
.confetti-button:before,
|
.confetti-button:before,
|
||||||
.confetti-button:after {
|
.confetti-button:after {
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export type CategoryRow = CategoryNameRow | CategoryIconsRow;
|
|||||||
import IconGrid from './IconGrid.vue'
|
import IconGrid from './IconGrid.vue'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
activeIconName: string
|
activeIconName: string | null
|
||||||
categoryRow: CategoryRow
|
categoryRow: CategoryRow
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, defineAsyncComponent, onMounted } from 'vue';
|
import { ref, computed, defineAsyncComponent, onMounted, watch, watchEffect } from 'vue';
|
||||||
import type { IconEntity, Category } from '../../types';
|
import type { IconEntity, Category } from '../../types';
|
||||||
import useSearch from '../../composables/useSearch';
|
import useSearch from '../../composables/useSearch';
|
||||||
import InputSearch from '../base/InputSearch.vue';
|
import InputSearch from '../base/InputSearch.vue';
|
||||||
@@ -69,7 +69,7 @@ const categories = computed(() => {
|
|||||||
return props.categories
|
return props.categories
|
||||||
.map(({ name, title }) => {
|
.map(({ name, title }) => {
|
||||||
const categoryIcons = props.icons.filter((icon) => {
|
const categoryIcons = props.icons.filter((icon) => {
|
||||||
const iconCategories = props.iconCategories[icon.name];
|
const iconCategories = icon?.externalLibrary ? icon.categories : props.iconCategories[icon.name]
|
||||||
|
|
||||||
return iconCategories?.includes(name);
|
return iconCategories?.includes(name);
|
||||||
});
|
});
|
||||||
@@ -140,6 +140,12 @@ function handleCloseDrawer() {
|
|||||||
|
|
||||||
window.history.pushState({}, '', '/icons/categories');
|
window.history.pushState({}, '', '/icons/categories');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
|
||||||
|
console.log(props.icons.find((icon) => icon.name === 'burger'));
|
||||||
|
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Checkbox from '../base/Checkbox.vue'
|
||||||
|
import SidebarTitle from './SidebarTitle.vue'
|
||||||
|
import { useExternalLibs } from '../../composables/useExternalLibs';
|
||||||
|
import { ExternalLibs } from '../../types';
|
||||||
|
|
||||||
|
interface ExternalLibrary {
|
||||||
|
name: string;
|
||||||
|
value: ExternalLibs;
|
||||||
|
}
|
||||||
|
|
||||||
|
const externalLibraries: ExternalLibrary[] = [
|
||||||
|
{
|
||||||
|
name: 'Lab',
|
||||||
|
value: 'lab'
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const { selectedLibs } = useExternalLibs();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="external-library-select">
|
||||||
|
<SidebarTitle>
|
||||||
|
Include external libs
|
||||||
|
</SidebarTitle>
|
||||||
|
<ul>
|
||||||
|
<li
|
||||||
|
v-for="library in externalLibraries"
|
||||||
|
:key="library.name"
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
:label="library.name"
|
||||||
|
:id="library.name"
|
||||||
|
v-model="selectedLibs"
|
||||||
|
:value="library.value"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.external-library-select {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
19
docs/.vitepress/theme/components/icons/SidebarTitle.vue
Normal file
19
docs/.vitepress/theme/components/icons/SidebarTitle.vue
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<template>
|
||||||
|
<h2 class="sidebar-title sidebar-text">
|
||||||
|
<slot />
|
||||||
|
</h2>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.sidebar-title {
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--vp-c-text-1);
|
||||||
|
}
|
||||||
|
.sidebar-text {
|
||||||
|
line-height: 24px;
|
||||||
|
font-size: 14px;
|
||||||
|
display: block;
|
||||||
|
transition: color 0.25s;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
57
docs/.vitepress/theme/composables/useExternalLibs.ts
Normal file
57
docs/.vitepress/theme/composables/useExternalLibs.ts
Normal file
@@ -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<Record<ExternalLibs, IconEntity[]>>;
|
||||||
|
|
||||||
|
interface ExternalLibContext {
|
||||||
|
selectedLibs: Ref<[ExternalLibs]>;
|
||||||
|
externalIconNodes: Ref<ExternalIconNodes>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const externalLibContext = {
|
||||||
|
selectedLibs: ref([]),
|
||||||
|
externalIconNodes: ref({}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const externalLibIconNodesAPI = {
|
||||||
|
lab: 'https://lab.lucide.dev/api/icon-details',
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useExternalLibs(): ExternalLibContext {
|
||||||
|
const context = inject<ExternalLibContext>(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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
@@ -7,6 +7,7 @@ import HomeHeroIconsCard from './components/home/HomeHeroIconsCard.vue';
|
|||||||
import HomeHeroBefore from './components/home/HomeHeroBefore.vue';
|
import HomeHeroBefore from './components/home/HomeHeroBefore.vue';
|
||||||
import { ICON_STYLE_CONTEXT, iconStyleContext } from './composables/useIconStyle';
|
import { ICON_STYLE_CONTEXT, iconStyleContext } from './composables/useIconStyle';
|
||||||
import { CATEGORY_VIEW_CONTEXT, categoryViewContext } from './composables/useCategoryView';
|
import { CATEGORY_VIEW_CONTEXT, categoryViewContext } from './composables/useCategoryView';
|
||||||
|
import { EXTERNAL_LIBS_CONTEXT, externalLibContext } from './composables/useExternalLibs';
|
||||||
|
|
||||||
const theme: Partial<Theme> = {
|
const theme: Partial<Theme> = {
|
||||||
extends: DefaultTheme,
|
extends: DefaultTheme,
|
||||||
@@ -20,6 +21,7 @@ const theme: Partial<Theme> = {
|
|||||||
enhanceApp({ app }) {
|
enhanceApp({ app }) {
|
||||||
app.provide(ICON_STYLE_CONTEXT, iconStyleContext);
|
app.provide(ICON_STYLE_CONTEXT, iconStyleContext);
|
||||||
app.provide(CATEGORY_VIEW_CONTEXT, categoryViewContext);
|
app.provide(CATEGORY_VIEW_CONTEXT, categoryViewContext);
|
||||||
|
app.provide(EXTERNAL_LIBS_CONTEXT, externalLibContext);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useData } from 'vitepress'
|
|||||||
|
|
||||||
import CategoryList from '../components/icons/CategoryList.vue'
|
import CategoryList from '../components/icons/CategoryList.vue'
|
||||||
import SidebarIconCustomizer from '../components/icons/SidebarIconCustomizer.vue'
|
import SidebarIconCustomizer from '../components/icons/SidebarIconCustomizer.vue'
|
||||||
|
import ExternalLibrarySelect from '../components/icons/SidebarExternalLibrarySelect.vue'
|
||||||
|
|
||||||
const { page } = useData()
|
const { page } = useData()
|
||||||
|
|
||||||
@@ -11,6 +12,7 @@ const { page } = useData()
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<SidebarIconCustomizer v-if="page?.relativePath?.startsWith?.('icons')"/>
|
<SidebarIconCustomizer v-if="page?.relativePath?.startsWith?.('icons')"/>
|
||||||
|
<ExternalLibrarySelect v-if="page?.relativePath?.startsWith?.('icons')"/>
|
||||||
<CategoryList v-if="page?.relativePath?.startsWith?.('icons')"/>
|
<CategoryList v-if="page?.relativePath?.startsWith?.('icons')"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
export type IconNode = [elementName: string, attrs: Record<string, string>][];
|
export type IconNode = [elementName: string, attrs: Record<string, string>][];
|
||||||
export type IconNodeWithKeys = [elementName: string, attrs: Record<string, string>, key: string][];
|
export type IconNodeWithKeys = [elementName: string, attrs: Record<string, string>, key: string][];
|
||||||
|
|
||||||
export interface IconEntity {
|
export interface IconMetaData {
|
||||||
name: string;
|
|
||||||
tags: string[];
|
tags: string[];
|
||||||
categories: string[];
|
categories: string[];
|
||||||
contributors: string[];
|
contributors: string[];
|
||||||
aliases?: string[];
|
aliases?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ExternalLibs = 'lab';
|
||||||
|
export interface IconEntity extends IconMetaData {
|
||||||
|
name: string;
|
||||||
iconNode: IconNode;
|
iconNode: IconNode;
|
||||||
|
externalLibrary?: ExternalLibs;
|
||||||
createdRelease?: Release;
|
createdRelease?: Release;
|
||||||
changedRelease?: Release;
|
changedRelease?: Release;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ import { burger } from '@lucide/lab';
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Icon :iconNode={burger} />
|
<Icon :iconNode="burger" />
|
||||||
</template>
|
</template>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ sidebar: true
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useData } from 'vitepress'
|
import { useData } from 'vitepress'
|
||||||
import IconPreview from '../.vitepress/theme/components/icons/IconPreview.vue'
|
import IconPreview from '~/.vitepress/theme/components/icons/IconPreview.vue'
|
||||||
import IconPreviewSmall from '../.vitepress/theme/components/icons/IconPreviewSmall.vue'
|
import IconPreviewSmall from '~/.vitepress/theme/components/icons/IconPreviewSmall.vue'
|
||||||
import IconInfo from '../.vitepress/theme/components/icons/IconInfo.vue'
|
import IconInfo from '~/.vitepress/theme/components/icons/IconInfo.vue'
|
||||||
import IconContributors from '../.vitepress/theme/components/icons/IconContributors.vue'
|
import IconContributors from '~/.vitepress/theme/components/icons/IconContributors.vue'
|
||||||
import RelatedIcons from '../.vitepress/theme/components/icons/RelatedIcons.vue'
|
import RelatedIcons from '~/.vitepress/theme/components/icons/RelatedIcons.vue'
|
||||||
import CodeGroup from '../.vitepress/theme/components/base/CodeGroup.vue'
|
import CodeGroup from '~/.vitepress/theme/components/base/CodeGroup.vue'
|
||||||
import Badge from '../.vitepress/theme/components/base/Badge.vue'
|
import Badge from '~/.vitepress/theme/components/base/Badge.vue'
|
||||||
import Label from '../.vitepress/theme/components/base/Label.vue'
|
import Label from '~/.vitepress/theme/components/base/Label.vue'
|
||||||
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue';
|
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue';
|
||||||
import { data } from './codeExamples.data'
|
import { data } from './codeExamples.data'
|
||||||
import { camelCase, startCase } from 'lodash-es'
|
import { camelCase, startCase } from 'lodash-es'
|
||||||
@@ -32,7 +32,7 @@ const tabs = computed(() => data.codeExamples?.map(
|
|||||||
const codeExample = computed(() => data.codeExamples?.map(
|
const codeExample = computed(() => data.codeExamples?.map(
|
||||||
(codeExample) => {
|
(codeExample) => {
|
||||||
const pascalCase = startCase(camelCase( params.value.name)).replace(/\s/g, '')
|
const pascalCase = startCase(camelCase( params.value.name)).replace(/\s/g, '')
|
||||||
return codeExample.code.replace(/PascalCase/g, pascalCase).replace(/Name/g, params.value.name)
|
return codeExample.code.replace(/\$PascalCase/g, pascalCase).replace(/\$Name/g, params.value.name)
|
||||||
}
|
}
|
||||||
).join('') ?? []
|
).join('') ?? []
|
||||||
)
|
)
|
||||||
@@ -100,7 +100,10 @@ function releaseTagLink(version) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<RelatedIcons :icons="params.relatedIcons" />
|
<RelatedIcons
|
||||||
|
v-if="params.relatedIcons"
|
||||||
|
:icons="params.relatedIcons"
|
||||||
|
/>
|
||||||
|
|
||||||
<style module>
|
<style module>
|
||||||
.preview {
|
.preview {
|
||||||
|
|||||||
@@ -10,13 +10,16 @@ import { data } from './icons.data.ts'
|
|||||||
import { data as categoriesData } from './categories.data.ts'
|
import { data as categoriesData } from './categories.data.ts'
|
||||||
import PageContainer from '../.vitepress/theme/components/PageContainer.vue'
|
import PageContainer from '../.vitepress/theme/components/PageContainer.vue'
|
||||||
import IconsCategoryOverview from '../.vitepress/theme/components/icons/IconsCategoryOverview.vue'
|
import IconsCategoryOverview from '../.vitepress/theme/components/icons/IconsCategoryOverview.vue'
|
||||||
|
import useIconsWithExternalLibs from '~/.vitepress/theme/composables/useIconsWithExternalLibs'
|
||||||
|
|
||||||
|
const icons = useIconsWithExternalLibs(data.icons)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="VPDoc content">
|
<div class="VPDoc content">
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<IconsCategoryOverview
|
<IconsCategoryOverview
|
||||||
:categories="categoriesData.categories"
|
:categories="categoriesData.categories"
|
||||||
:icons="data.icons"
|
:icons="icons"
|
||||||
:iconCategories="categoriesData.iconCategories"
|
:iconCategories="categoriesData.iconCategories"
|
||||||
/>
|
/>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import createCodeExamples from '../.vitepress/lib/createCodeExamples';
|
import createCodeExamples from '../.vitepress/lib/codeExamples/createCodeExamples';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
async load() {
|
async load() {
|
||||||
const codeExamples = await createCodeExamples();
|
const codeExamples = await createCodeExamples();
|
||||||
|
|
||||||
// const randomIcons = Array.from({ length: 200 }, () => getRandomItem(icons))
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
codeExamples,
|
codeExamples,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,13 +10,17 @@ head:
|
|||||||
---
|
---
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
import { data } from './icons.data.ts'
|
import { data } from './icons.data.ts'
|
||||||
import IconsOverview from '../.vitepress/theme/components/icons/IconsOverview.vue'
|
import IconsOverview from '~/.vitepress/theme/components/icons/IconsOverview.vue'
|
||||||
import PageContainer from '../.vitepress/theme/components/PageContainer.vue'
|
import PageContainer from '~/.vitepress/theme/components/PageContainer.vue'
|
||||||
|
import useIconsWithExternalLibs from '~/.vitepress/theme/composables/useIconsWithExternalLibs'
|
||||||
|
|
||||||
|
const icons = useIconsWithExternalLibs(data.icons)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="VPDoc content">
|
<div class="VPDoc content">
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<IconsOverview :icons="data.icons" />
|
<IconsOverview :icons="icons" />
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
10
docs/icons/lab/[name].md
Normal file
10
docs/icons/lab/[name].md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
layout: doc
|
||||||
|
footer: false
|
||||||
|
aside: false
|
||||||
|
editLink: false
|
||||||
|
next: false
|
||||||
|
prev: false
|
||||||
|
sidebar: true
|
||||||
|
---
|
||||||
|
<!--@include: ../[name].md -->
|
||||||
19
docs/icons/lab/[name].paths.ts
Normal file
19
docs/icons/lab/[name].paths.ts
Normal file
@@ -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<string, IconEntity>;
|
||||||
|
|
||||||
|
return Object.values(iconDetails).map((iconEntity) => {
|
||||||
|
const params = {
|
||||||
|
externalLibrary: 'lab',
|
||||||
|
...iconEntity,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
params,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
11
docs/icons/lab/codeExamples.data.ts
Normal file
11
docs/icons/lab/codeExamples.data.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import createCodeExamples from '../../.vitepress/lib/codeExamples/createLabCodeExamples';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
async load() {
|
||||||
|
const codeExamples = await createCodeExamples();
|
||||||
|
|
||||||
|
return {
|
||||||
|
codeExamples,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -5,5 +5,8 @@
|
|||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
|
"paths": {
|
||||||
|
"~/.vitepress/*": ["./.vitepress/*"],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user