mirror of
https://github.com/lucide-icons/lucide.git
synced 2026-11-15 14:08:49 +00:00
docs(version-1): Version 1 website (#4142)
* Update images * Adjust color * update accessebility * Add matrix hero * Add typescript documentation * Add basics section to vue pages * Add svelte docs * Fix dynamic sidebar * Add animation? * Some adjustments * Adjust animation * Updates docs * Add scaledown animation * Add docs for vue types * Fix layout accessibility page * fix framework select * adjust easing home animation * Write docs for Vue * Adjust animation home * Adjust home hero animation * Finish svelte docs * Add solid pages * remove spiral animation component * Add group icons * Added solid docs * update preact docs * Adjust examples preact docs * Add snackplayer * Get new editor * Save this * Add examples * Adjust styling * setup custom sandpack * Add script * Format files * Make sandpack plugin work * migrate react docs * Fix svelte and solid examples * Migrate to solid packages * Add darkmode package logos * Fix bug in selector * migrate vue examples * migrate advanced vue items * migrate preact and svelte examples * Add astro docs * adding more docs on guide for lucide library * Fix home animation * Cleanup * Added resources page, with more details and content * Add hero badge for Version 1 * Fix vercel json * Update missing paths * Fix build? * Replace lucide-vue-next * Fix build * Add some docs * update markdown * Setup angular docs * Add basic angular docs * Adjust code examples * Update title and descriptions * Update accessibility link * Update title and description * Add og image * Fix alignment * Add migration guides * Adjust version 1 markdown file * Add migration guide to the main list * Minor fixes * Update docs * Apply feedback * Select icons * Add pointer events none * Fix package links homepage * Format code * Fix types * Fix focus * Fix build * Fix focus * Apply feedback * Adjust imports * Adjust imports * Fix search * Apply feedback * Fix import in font docs * Small fixes * docs(guide): added easter egg icon. stay tuned, guys 😉 * Add llm txt plugin * Adjust logo top * docs(version-1): upgraded @lucide/angular guide for v1 website (#4144) * docs(guide/angular): rewrite some angular documentation * docs(guide/angular): extens angular guide with combining icons & icon provider guides * docs(guide/angular): fix angular sandbox, still needs @lucide/angular release tho * docs(guide/angular): rework the getting started page a bit more, refactor some more prop=>input occurences * docs(guide/angular): context provider => provideLucideConfig * Update docs/guide/angular/migration.md Co-authored-by: Eric Fennis <eric.fennis@gmail.com> * Update docs/guide/angular/advanced/combining-icons.md Co-authored-by: Eric Fennis <eric.fennis@gmail.com> * Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * docs(guide/angular): rework a11y for better flow & pacing * docs(guide/angular): salvage original phrase, it has better flow * docs(guide/angular): some more a11y nitpicking * docs(guide/angular): reduce llm fatigue * docs(guides/angular): fix app component selector * docs(guides/angular): fix angular sandpacks * Add sandpack angular to improve tree-shakable stack * Update docs/guide/angular/getting-started.md * docs(guides/angular): fix user import in combinding icons guide * docs(guides/angular): fix nested SVG phrasing * Update docs/guide/angular/advanced/with-lucide-lab.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update docs/guide/angular/advanced/filled-icons.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * docs(guides/angular): fix createLucideIcon guide * docs(guides/angular): upgrade all relevant angular sandpack demos --------- Co-authored-by: Eric Fennis <eric.fennis@gmail.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * fix(docs): add autogenerated OG images to gitignore * fix(docs): unify the case of "Lucide" & "Lucide Lab" over all docs and readmes * Fix build * Add next tag to all installation guides * Add top bar notification * Minor fixes * Update text * Add todos * Update og image * Adjust install command * Color top bar * fix(docs): ignore all OG images, expect general.png * fix(docs): fix the package list, hide @lucide/icons, downgrade angular logo, link to v0 guide, fix package title alignment * Update docs/.vitepress/theme/components/base/LayoutTop.vue Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update docs/guide/react/advanced/dynamic-icon-component.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update CONTRIBUTING.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update docs/.vitepress/sidebar/resources.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fix code example * fix(docs): version 1 => Version 1 * feat(docs): add angular to new features in v1 * Fix next line issue * feat(docs): fix typo (code example_s_) * Fix deadlink * feat(docs): fix some more typos and irky grammar * feat(docs): fix lowercase lucide in sidebar 😅 * Update docs/guide/version-1.md Co-authored-by: Karsa <contact@karsa.org> --------- Co-authored-by: Karsa <contact@karsa.org> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot Autofix powered by AI
Karsa
parent
e715245d62
commit
484f2c9cd3
@@ -1,7 +1,19 @@
|
||||
import { fileURLToPath, URL } from 'node:url';
|
||||
import { defineConfig } from 'vitepress';
|
||||
import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons';
|
||||
import sidebar from './sidebar';
|
||||
import snackPlayer from './markdown/snackPlayer';
|
||||
import sandpackPlugin from './markdown/sandpack';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { resourcesSidebar } from './sidebar/resources';
|
||||
import getStructuredData from './getStructuredData';
|
||||
import { createGeneralOGImage, createIconOGImage } from './createOGImage';
|
||||
import llmstxt from 'vitepress-plugin-llms';
|
||||
|
||||
const defaultSandpackCSS = await readFile(
|
||||
fileURLToPath(new URL('./theme/sandpack-default.css', import.meta.url)),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
const title = 'Lucide';
|
||||
const socialTitle = 'Lucide Icons';
|
||||
@@ -15,6 +27,20 @@ export default defineConfig({
|
||||
cleanUrls: true,
|
||||
outDir: '.vercel/output/static',
|
||||
srcExclude: ['**/README.md'],
|
||||
markdown: {
|
||||
config(md) {
|
||||
md.use(groupIconMdPlugin);
|
||||
md.use(snackPlayer);
|
||||
md.use(sandpackPlugin, {
|
||||
defaultFiles: {
|
||||
'/styles.css': {
|
||||
code: defaultSandpackCSS,
|
||||
hidden: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
vite: {
|
||||
resolve: {
|
||||
alias: [
|
||||
@@ -36,6 +62,19 @@ export default defineConfig({
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
groupIconVitePlugin(),
|
||||
llmstxt({
|
||||
ignoreFiles: [
|
||||
'code-of-conduct.md',
|
||||
'index.md',
|
||||
'packages.md',
|
||||
'showcase.md',
|
||||
'brand-logo-statement.md',
|
||||
'icons/**', // Not working, need investigation
|
||||
],
|
||||
}),
|
||||
],
|
||||
},
|
||||
head: [
|
||||
[
|
||||
@@ -153,6 +192,7 @@ export default defineConfig({
|
||||
],
|
||||
],
|
||||
async transformPageData(pageData) {
|
||||
pageData.frontmatter.head ??= [];
|
||||
if (
|
||||
pageData.relativePath.startsWith('icons/') &&
|
||||
!pageData.relativePath.startsWith('icons/lab/') &&
|
||||
@@ -173,13 +213,58 @@ export default defineConfig({
|
||||
|
||||
const structuredData = await getStructuredData(iconName, pageData);
|
||||
|
||||
pageData.frontmatter.head ??= [];
|
||||
const ogPath = await createIconOGImage(iconName, pageData.params?.tags || []);
|
||||
|
||||
if (ogPath) {
|
||||
const content = `https://lucide.dev${ogPath}`;
|
||||
pageData.frontmatter.head.push(
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:image',
|
||||
content,
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'twitter:image',
|
||||
content,
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
pageData.frontmatter.head.push([
|
||||
'script',
|
||||
{ type: 'application/ld+json' },
|
||||
JSON.stringify(structuredData),
|
||||
]);
|
||||
}
|
||||
|
||||
if (pageData.relativePath.startsWith('guide/')) {
|
||||
const ogPath = await createGeneralOGImage(pageData);
|
||||
|
||||
if (ogPath) {
|
||||
const content = `https://lucide.dev${ogPath}`;
|
||||
pageData.frontmatter.head.push(
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:image',
|
||||
content,
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'twitter:image',
|
||||
content,
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
themeConfig: {
|
||||
logo: {
|
||||
@@ -189,9 +274,15 @@ export default defineConfig({
|
||||
nav: [
|
||||
{ text: 'Icons', link: '/icons/' },
|
||||
{ text: 'Guide', link: '/guide/' },
|
||||
{
|
||||
text: 'Resources',
|
||||
items: [
|
||||
...resourcesSidebar[0].items,
|
||||
{ text: 'Design icons', link: '/contribute/icon-design-guide' },
|
||||
],
|
||||
},
|
||||
{ text: 'Packages', link: '/packages' },
|
||||
{ text: 'Showcase', link: '/showcase' },
|
||||
{ text: 'License', link: '/license' },
|
||||
],
|
||||
sidebar,
|
||||
socialLinks: [
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import { PageData } from 'vitepress';
|
||||
import fs from 'node:fs/promises';
|
||||
import { cwd } from 'node:process';
|
||||
import { join } from 'node:path';
|
||||
import sharp from 'sharp';
|
||||
|
||||
const generalSVGTemplate = fs.readFile(join(cwd(), '.vitepress/lib/guideOGTemplate.svg'), 'utf-8');
|
||||
|
||||
export async function createGeneralOGImage({ title, description, relativePath }: PageData) {
|
||||
const fileName = relativePath.replace(/\.md$/, '.png').replace(/\//g, '-');
|
||||
|
||||
const path = join(cwd(), 'public/og', fileName);
|
||||
|
||||
let desc = description.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
if (!desc) {
|
||||
desc = '';
|
||||
}
|
||||
const lines = desc.replace(/(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n').split('\n');
|
||||
const data = {
|
||||
title: title,
|
||||
line1: lines[0] || '',
|
||||
line2: lines[1] || '',
|
||||
line3: lines[2] || '',
|
||||
line4: lines[3] || '',
|
||||
};
|
||||
const svgTemplate = await generalSVGTemplate;
|
||||
const svg = svgTemplate.replace(/\{\{([^}]+)\}\}/g, (_, name: keyof typeof data) => data[name]);
|
||||
|
||||
console.log(`Generating ${fileName}`);
|
||||
try {
|
||||
await sharp(Buffer.from(svg))
|
||||
.resize(1200 * 1.1, 630 * 1.1)
|
||||
.png()
|
||||
.toFile(path);
|
||||
} catch (e) {
|
||||
console.error('Error generating', { filename: path, ...data, svg });
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
return `/og/${fileName}`;
|
||||
}
|
||||
|
||||
const iconSVGTemplate = fs.readFile(join(cwd(), '.vitepress/lib/iconOGTemplate.svg'), 'utf-8');
|
||||
|
||||
export async function createIconOGImage(iconName: string, tags: string[]) {
|
||||
const path = join(cwd(), 'public/og', `icon-${iconName}.png`);
|
||||
|
||||
let iconSVG = await fs.readFile(join(cwd(), `../icons/${iconName}.svg`), 'utf-8');
|
||||
iconSVG = iconSVG
|
||||
.replace('width="24"', 'width="320"')
|
||||
.replace('height="24"', 'height="320"')
|
||||
.replace('currentColor', '#dfdfd6')
|
||||
.replace(
|
||||
'xmlns="http://www.w3.org/2000/svg"',
|
||||
'xmlns="http://www.w3.org/2000/svg" x="62" y="66"',
|
||||
);
|
||||
|
||||
const data = {
|
||||
name: iconName,
|
||||
tags: tags.join(' • ').replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&'),
|
||||
iconSVG,
|
||||
};
|
||||
|
||||
const svgTemplate = await iconSVGTemplate;
|
||||
const svg = svgTemplate.replace(/\{\{([^}]+)\}\}/g, (_, name: keyof typeof data) => data[name]);
|
||||
|
||||
console.log(`Generating ${path}`);
|
||||
try {
|
||||
await sharp(Buffer.from(svg))
|
||||
.resize(1200 * 1.1, 630 * 1.1)
|
||||
.png()
|
||||
.toFile(path);
|
||||
} catch (e) {
|
||||
console.error('Error generating', { filename: path, ...data, svg });
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
return `/og/icon-${iconName}.png`;
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
"lucide-react": {
|
||||
"order": 1,
|
||||
"icon": "react",
|
||||
"docsAlias": "react",
|
||||
"shields": [
|
||||
{
|
||||
"alt": "npm",
|
||||
@@ -34,14 +35,25 @@
|
||||
"@lucide/vue": {
|
||||
"order": 2,
|
||||
"icon": "vue",
|
||||
"docsAlias": "lucide-vue",
|
||||
"docsAlias": "vue",
|
||||
"packageDirname": "vue",
|
||||
"shields": [
|
||||
{
|
||||
"alt": "npm",
|
||||
"src": "https://img.shields.io/npm/v/@lucide/vue",
|
||||
"href": "https://www.npmjs.com/package/@lucide/vue"
|
||||
},
|
||||
{
|
||||
"alt": "npm",
|
||||
"src": "https://img.shields.io/npm/dw/@lucide/vue",
|
||||
"href": "https://www.npmjs.com/package/@lucide/vue"
|
||||
}
|
||||
]
|
||||
},
|
||||
"lucide-svelte": {
|
||||
"order": 3,
|
||||
"icon": "svelte",
|
||||
"docsAlias": "svelte",
|
||||
"shields": [
|
||||
{
|
||||
"alt": "npm",
|
||||
@@ -58,6 +70,7 @@
|
||||
"lucide-solid": {
|
||||
"order": 4,
|
||||
"icon": "solid",
|
||||
"docsAlias": "solid",
|
||||
"shields": [
|
||||
{
|
||||
"alt": "npm",
|
||||
@@ -74,6 +87,7 @@
|
||||
"lucide-react-native": {
|
||||
"order": 5,
|
||||
"icon": "react-native",
|
||||
"docsAlias": "react-native",
|
||||
"shields": [
|
||||
{
|
||||
"alt": "npm",
|
||||
@@ -90,6 +104,7 @@
|
||||
"@lucide/angular": {
|
||||
"order": 6,
|
||||
"icon": "angular",
|
||||
"docsAlias": "angular",
|
||||
"shields": [
|
||||
{
|
||||
"alt": "npm",
|
||||
@@ -105,7 +120,9 @@
|
||||
},
|
||||
"lucide-angular": {
|
||||
"order": 7,
|
||||
"icon": "angular",
|
||||
"icon": "angular-old",
|
||||
"description": "Our legacy Angular package with a module based API.",
|
||||
"documentation": "https://v0.lucide.dev/guide/packages/lucide-angular",
|
||||
"shields": [
|
||||
{
|
||||
"alt": "npm",
|
||||
@@ -122,6 +139,7 @@
|
||||
"lucide-preact": {
|
||||
"order": 8,
|
||||
"icon": "preact",
|
||||
"docsAlias": "preact",
|
||||
"shields": [
|
||||
{
|
||||
"alt": "npm",
|
||||
@@ -136,7 +154,7 @@
|
||||
]
|
||||
},
|
||||
"@lucide/astro": {
|
||||
"docsAlias": "lucide-astro",
|
||||
"docsAlias": "astro",
|
||||
"packageDirname": "astro",
|
||||
"order": 9,
|
||||
"icon": "astro",
|
||||
@@ -157,6 +175,7 @@
|
||||
"lucide-static": {
|
||||
"order": 10,
|
||||
"icon": "svg",
|
||||
"docsAlias": "static",
|
||||
"shields": [
|
||||
{
|
||||
"alt": "npm",
|
||||
@@ -171,6 +190,7 @@
|
||||
]
|
||||
},
|
||||
"@lucide/icons": {
|
||||
"hide": true,
|
||||
"docsAlias": "icons",
|
||||
"packageDirname": "icons",
|
||||
"order": 10,
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
},
|
||||
{
|
||||
"name": "lucide-slint",
|
||||
"description": "Implementation of the lucide icon library for Slint.",
|
||||
"description": "Implementation of the Lucide icon library for Slint.",
|
||||
"icon": "/framework-logos/slint.svg",
|
||||
"shields": [
|
||||
{
|
||||
|
||||
@@ -459,20 +459,7 @@ const SvgPreview = (
|
||||
/>
|
||||
<ColoredPath
|
||||
paths={paths}
|
||||
colors={[
|
||||
'#1982c4',
|
||||
'#4267AC',
|
||||
'#6a4c93',
|
||||
'#B55379',
|
||||
'#FF595E',
|
||||
'#FF7655',
|
||||
'#ff924c',
|
||||
'#FFAE43',
|
||||
'#ffca3a',
|
||||
'#C5CA30',
|
||||
'#8ac926',
|
||||
'#52A675',
|
||||
]}
|
||||
colors={['#dfdfd6']}
|
||||
/>
|
||||
<Radii
|
||||
paths={paths}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 630" width="1200" height="630" fill="none">
|
||||
<g clip-path="url(#a)">
|
||||
<path fill="#1b1b1f" d="M0 0h1200v630H0z" />
|
||||
<text xml:space="preserve" fill="#fff" font-family="Inter, Helvetica, sans-serif" font-size="44" font-weight="600"
|
||||
letter-spacing=".05em" style="white-space:pre">
|
||||
<tspan x="62" y="118.5">{{title}}</tspan>
|
||||
</text>
|
||||
<text xml:space="preserve" fill="#ebebf5" fill-opacity=".8" font-family="Inter, Helvetica, sans-serif"
|
||||
font-size="32" letter-spacing="0em" opacity=".79" style="white-space:pre">
|
||||
<tspan x="62" y="209.14">{{line1}}</tspan>
|
||||
<tspan x="62" y="257.14">{{line2}}</tspan>
|
||||
<tspan x="62" y="305.14">{{line3}}</tspan>
|
||||
<tspan x="62" y="353.14">{{line4}}</tspan>
|
||||
</text>
|
||||
<path fill="#161618" d="M0 438h1200v192H0z" />
|
||||
<path fill="#e7e7e7"
|
||||
d="M395.33 562.28q-5.97 0-10.31-2.5a17 17 0 0 1-6.64-7.13q-2.32-4.64-2.32-10.91 0-6.18 2.32-10.84a17.7 17.7 0 0 1 6.56-7.29q4.2-2.63 9.9-2.63 3.66 0 6.92 1.2 3.28 1.17 5.78 3.64a17 17 0 0 1 3.98 6.3 26 26 0 0 1 1.45 9.07v2.89h-32.52v-6.36H404q-.03-2.7-1.17-4.81a8.5 8.5 0 0 0-3.17-3.36 9 9 0 0 0-4.7-1.23q-2.88 0-5.04 1.4a9.7 9.7 0 0 0-3.39 3.65q-1.19 2.25-1.21 4.93v5.54q0 3.5 1.27 6a9 9 0 0 0 3.53 3.8q2.29 1.3 5.32 1.3 2.05 0 3.7-.58a8 8 0 0 0 2.87-1.74 7 7 0 0 0 1.83-2.84l8.73 1a14 14 0 0 1-3.15 6.09 15.4 15.4 0 0 1-5.89 4q-3.6 1.41-8.21 1.41M346.67 562.2q-4.67 0-8.37-2.42a16.4 16.4 0 0 1-5.84-7.03q-2.14-4.6-2.14-11.2 0-6.67 2.17-11.25a16 16 0 0 1 5.91-6.95 15 15 0 0 1 8.3-2.37q3.48 0 5.73 1.2 2.25 1.17 3.56 2.84 1.32 1.63 2.05 3.1h.38v-19.95h9.38v53.33h-9.2v-6.3h-.56q-.74 1.45-2.1 3.1-1.37 1.61-3.61 2.76a12.4 12.4 0 0 1-5.66 1.14m2.6-7.73q2.99 0 5.07-1.62a10 10 0 0 0 3.18-4.55 19 19 0 0 0 1.08-6.8 19 19 0 0 0-1.08-6.74 9.6 9.6 0 0 0-3.15-4.46 8 8 0 0 0-5.1-1.59 8.2 8.2 0 0 0-5.2 1.64 10 10 0 0 0-3.16 4.54 19 19 0 0 0-1.06 6.61q0 3.75 1.06 6.7a10.5 10.5 0 0 0 3.18 4.6 8 8 0 0 0 5.19 1.67M312.98 561.5v-40h9.35v40zm4.7-45.68q-2.22 0-3.82-1.48a4.8 4.8 0 0 1-1.6-3.62q0-2.13 1.6-3.62a5.4 5.4 0 0 1 3.82-1.51q2.25 0 3.82 1.51 1.6 1.49 1.6 3.62a4.8 4.8 0 0 1-1.6 3.62 5.4 5.4 0 0 1-3.82 1.48M288.3 562.28q-5.93 0-10.2-2.63-4.23-2.62-6.53-7.26a24 24 0 0 1-2.27-10.73q0-6.1 2.32-10.76a17.4 17.4 0 0 1 6.56-7.29 19 19 0 0 1 10.07-2.63q4.83 0 8.56 1.8a14.6 14.6 0 0 1 5.96 5.02 15 15 0 0 1 2.53 7.55h-8.93a8.6 8.6 0 0 0-2.59-4.81q-2.01-1.95-5.4-1.96a8.4 8.4 0 0 0-5.03 1.57 10 10 0 0 0-3.39 4.42 18 18 0 0 0-1.19 6.93q0 4.1 1.2 7.03 1.19 2.93 3.33 4.5a8.5 8.5 0 0 0 5.08 1.57q2.07 0 3.7-.78a7.5 7.5 0 0 0 2.76-2.32q1.11-1.5 1.53-3.67h8.93a16 16 0 0 1-2.48 7.52 14.7 14.7 0 0 1-5.83 5.1 19.4 19.4 0 0 1-8.68 1.83M252.04 544.68V521.5h9.34v40h-9.06v-7.11h-.41a12 12 0 0 1-4.42 5.5q-3.05 2.13-7.52 2.13-3.9 0-6.9-1.74a12 12 0 0 1-4.64-5.13 18 18 0 0 1-1.68-8.18V521.5h9.35v24.01q0 3.8 2.06 6.04a7 7 0 0 0 5.43 2.24q2.07 0 4-1.01a8 8 0 0 0 3.18-3.02 9.4 9.4 0 0 0 1.26-5.08M185.96 561.5v-53.33h9.58v45.23h23.3v8.1zM117.1 534c0-7.6-6.1-13.75-13.64-13.75A13.7 13.7 0 0 0 89.82 534c0 17.72 14.25 32.08 31.83 32.08s31.82-14.36 31.82-32.08a50.5 50.5 0 0 0-16.67-37.58 4.6 4.6 0 0 1-.36-6.47 4.5 4.5 0 0 1 6.42-.36 59.7 59.7 0 0 1 19.7 44.4l-.05 2.13c-1.1 21.8-18.97 39.13-40.86 39.13-22.6 0-40.92-18.47-40.92-41.25a22.8 22.8 0 0 1 22.73-22.92A22.8 22.8 0 0 1 126.2 534a4.56 4.56 0 0 1-4.55 4.58 4.56 4.56 0 0 1-4.54-4.58" />
|
||||
<path fill="#f56565"
|
||||
d="M98.91 534a4.56 4.56 0 0 1 4.55-4.58 4.57 4.57 0 0 1 4.55 4.58c0 7.6 6.1 13.75 13.63 13.75 7.3 0 13.26-5.78 13.62-13.04l.02-.71c0-17.72-14.24-32.08-31.82-32.08S71.64 516.28 71.64 534a50.5 50.5 0 0 0 16.93 37.81 4.6 4.6 0 0 1 .4 6.48 4.53 4.53 0 0 1-6.42.4 59.7 59.7 0 0 1-19.98-43.03l-.02-1.66c0-22.78 18.31-41.25 40.91-41.25s40.92 18.47 40.92 41.25l-.04 1.18a22.8 22.8 0 0 1-22.7 21.74A22.8 22.8 0 0 1 98.92 534" />
|
||||
<text xml:space="preserve" fill="#ebebf5" fill-opacity=".8" font-family="Inter, Helvetica, sans-serif"
|
||||
font-size="39" font-weight="500" letter-spacing="0em" style="white-space:pre">
|
||||
<tspan x="808.98" y="548.18">https://lucide.dev</tspan>
|
||||
</text>
|
||||
<path fill="url(#b)" d="M1116 0h84v435h-84z" />
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="b" x1="1200" x2="1116" y1="218" y2="218" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#1b1b1f" />
|
||||
<stop offset="1" stop-color="#1b1b1f" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<clipPath id="a">
|
||||
<path fill="#fff" d="M0 0h1200v630H0z" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
@@ -0,0 +1,47 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" fill="none">
|
||||
<g clip-path="url(#a)">
|
||||
<path fill="#1b1b1f" d="M0 0h1200v630H0z" />
|
||||
<g clip-path="url(#b)">
|
||||
<rect width="320" height="320" x="62" y="66" fill="#161618" rx="24" />
|
||||
<path stroke="#202127"
|
||||
d="M62 79.33h320M75.33 66v320M62 92.67h320M88.67 66v320M62 106h320M102 66v320M62 119.33h320M115.33 66v320M62 132.67h320M128.67 66v320M62 146h320M142 66v320M62 159.33h320M155.33 66v320M62 172.67h320M168.67 66v320M62 186h320M182 66v320M62 199.33h320M195.33 66v320M62 212.67h320M208.67 66v320M62 226h320M222 66v320M62 239.33h320M235.33 66v320M62 252.67h320M248.67 66v320M62 266h320M262 66v320M62 279.33h320M275.33 66v320M62 292.67h320M288.67 66v320M62 306h320M302 66v320M62 319.33h320M315.33 66v320M62 332.67h320M328.67 66v320M62 346h320M342 66v320M62 359.33h320M355.33 66v320M62 372.67h320M368.67 66v320" />
|
||||
{{iconSVG}}
|
||||
</g>
|
||||
<text xml:space="preserve" fill="#ebebf5" fill-opacity=".8" font-family="Inter" font-size="32" letter-spacing="0em"
|
||||
opacity=".79" style="white-space:pre">
|
||||
<tspan x="434" y="293.64">See the icon preview, code examples,
|
||||
</tspan>
|
||||
<tspan x="434" y="341.64">release details and other related icons.</tspan>
|
||||
</text>
|
||||
<text xml:space="preserve" fill="#fff" font-family="Inter" font-size="44" font-weight="600" letter-spacing=".05em"
|
||||
style="white-space:pre">
|
||||
<tspan x="438" y="130.5">{{name}}</tspan>
|
||||
</text>
|
||||
<text xml:space="preserve" fill="#98989f" font-family="Inter" font-size="32" letter-spacing=".05em"
|
||||
style="white-space:pre">
|
||||
<tspan x="438" y="208.14">{{tags}}</tspan>
|
||||
</text>
|
||||
<path fill="#161618" d="M0 438h1200v192H0z" />
|
||||
<path fill="#e7e7e7"
|
||||
d="M395.33 562.28q-5.97 0-10.31-2.5a17 17 0 0 1-6.64-7.13q-2.32-4.64-2.32-10.92 0-6.17 2.32-10.83a17.7 17.7 0 0 1 6.56-7.3q4.2-2.62 9.9-2.62 3.66 0 6.92 1.2 3.28 1.17 5.78 3.64a17 17 0 0 1 3.98 6.3 26 26 0 0 1 1.45 9.07v2.89h-32.52v-6.36H404q-.03-2.7-1.17-4.81a8.5 8.5 0 0 0-3.17-3.36 9 9 0 0 0-4.7-1.23q-2.88 0-5.04 1.4a9.7 9.7 0 0 0-3.39 3.65q-1.19 2.25-1.21 4.93v5.54q0 3.5 1.27 6a9 9 0 0 0 3.53 3.8q2.29 1.3 5.32 1.3 2.05 0 3.7-.58a8 8 0 0 0 2.87-1.74 7 7 0 0 0 1.83-2.84l8.73 1a14 14 0 0 1-3.15 6.09 15.4 15.4 0 0 1-5.89 4q-3.6 1.41-8.21 1.41M346.67 562.2q-4.67 0-8.37-2.42a16.4 16.4 0 0 1-5.84-7.03q-2.14-4.6-2.14-11.2 0-6.67 2.17-11.25a16 16 0 0 1 5.91-6.95 15 15 0 0 1 8.3-2.37q3.48 0 5.73 1.2 2.25 1.17 3.56 2.84 1.32 1.63 2.05 3.1h.38v-19.95h9.38v53.33h-9.2v-6.3h-.56q-.74 1.45-2.1 3.1-1.37 1.61-3.61 2.76a12.4 12.4 0 0 1-5.66 1.14m2.6-7.73q2.99 0 5.07-1.62a10 10 0 0 0 3.18-4.55 19 19 0 0 0 1.08-6.8 19 19 0 0 0-1.08-6.74 9.6 9.6 0 0 0-3.15-4.46 8 8 0 0 0-5.1-1.59 8.2 8.2 0 0 0-5.2 1.64 10 10 0 0 0-3.16 4.54 19 19 0 0 0-1.06 6.61q0 3.75 1.06 6.7a10.5 10.5 0 0 0 3.18 4.6 8 8 0 0 0 5.19 1.67M312.98 561.5v-40h9.35v40zm4.7-45.68q-2.22 0-3.82-1.48a4.8 4.8 0 0 1-1.6-3.62q0-2.13 1.6-3.62a5.4 5.4 0 0 1 3.82-1.51q2.25 0 3.82 1.51 1.6 1.49 1.6 3.62a4.8 4.8 0 0 1-1.6 3.62 5.4 5.4 0 0 1-3.82 1.48M288.3 562.28q-5.93 0-10.2-2.63-4.23-2.62-6.53-7.26a24 24 0 0 1-2.27-10.73q0-6.1 2.32-10.76a17.4 17.4 0 0 1 6.56-7.29 19 19 0 0 1 10.07-2.63q4.83 0 8.56 1.8a14.6 14.6 0 0 1 5.96 5.02 15 15 0 0 1 2.53 7.55h-8.93a8.6 8.6 0 0 0-2.59-4.81q-2.01-1.95-5.4-1.96a8.4 8.4 0 0 0-5.03 1.57 10 10 0 0 0-3.39 4.42 18 18 0 0 0-1.19 6.93q0 4.1 1.2 7.03 1.19 2.93 3.33 4.5a8.5 8.5 0 0 0 5.08 1.57q2.07 0 3.7-.78a7.5 7.5 0 0 0 2.76-2.32q1.11-1.5 1.53-3.67h8.93a16 16 0 0 1-2.48 7.52 14.7 14.7 0 0 1-5.83 5.1 19.4 19.4 0 0 1-8.68 1.83M252.04 544.68V521.5h9.34v40h-9.06v-7.11h-.41a12 12 0 0 1-4.42 5.5q-3.05 2.13-7.52 2.13-3.9 0-6.9-1.74a12 12 0 0 1-4.64-5.13 18 18 0 0 1-1.68-8.18V521.5h9.35v24.01q0 3.8 2.06 6.04a7 7 0 0 0 5.43 2.24q2.07 0 4-1.01a8 8 0 0 0 3.18-3.02 9.4 9.4 0 0 0 1.26-5.08M185.96 561.5v-53.33h9.58v45.23h23.3v8.1zM117.1 534c0-7.6-6.1-13.75-13.64-13.75A13.7 13.7 0 0 0 89.82 534c0 17.72 14.25 32.08 31.83 32.08s31.82-14.36 31.82-32.08a50.5 50.5 0 0 0-16.67-37.58 4.6 4.6 0 0 1-.36-6.47 4.5 4.5 0 0 1 6.42-.36 59.7 59.7 0 0 1 19.7 44.4l-.05 2.13c-1.1 21.8-18.97 39.13-40.86 39.13-22.6 0-40.92-18.47-40.92-41.25a22.8 22.8 0 0 1 22.73-22.92A22.8 22.8 0 0 1 126.2 534a4.56 4.56 0 0 1-4.55 4.58 4.56 4.56 0 0 1-4.54-4.58" />
|
||||
<path fill="#f56565"
|
||||
d="M98.91 534a4.56 4.56 0 0 1 4.55-4.58 4.57 4.57 0 0 1 4.55 4.58c0 7.6 6.1 13.75 13.63 13.75 7.3 0 13.26-5.78 13.62-13.04l.02-.71c0-17.72-14.24-32.08-31.82-32.08S71.64 516.28 71.64 534a50.5 50.5 0 0 0 16.93 37.81 4.6 4.6 0 0 1 .4 6.48 4.53 4.53 0 0 1-6.42.4 59.7 59.7 0 0 1-19.98-43.03l-.02-1.66c0-22.78 18.31-41.25 40.91-41.25s40.92 18.47 40.92 41.25l-.04 1.18a22.8 22.8 0 0 1-22.7 21.74A22.8 22.8 0 0 1 98.92 534" />
|
||||
<text xml:space="preserve" fill="#ebebf5" fill-opacity=".8" font-family="Inter" font-size="39" font-weight="500"
|
||||
letter-spacing="0em" style="white-space:pre">
|
||||
<tspan x="808.98" y="548.18">https://lucide.dev</tspan>
|
||||
</text>
|
||||
<path fill="url(#c)" d="M1116 0h84v435h-84z" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="a">
|
||||
<path fill="#fff" d="M0 0h1200v630H0z" />
|
||||
</clipPath>
|
||||
<clipPath id="b">
|
||||
<rect width="320" height="320" x="62" y="66" fill="#fff" rx="24" />
|
||||
</clipPath>
|
||||
<linearGradient id="c" x1="1200" x2="1116" y1="218" y2="218" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#1b1b1f" />
|
||||
<stop offset="1" stop-color="#1b1b1f" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
@@ -0,0 +1,138 @@
|
||||
import type MarkdownIt from 'markdown-it';
|
||||
import type { RenderRule } from 'markdown-it/lib/renderer.mjs';
|
||||
import container from 'markdown-it-container';
|
||||
import sandpackTheme from '../theme/sandpackTheme.json';
|
||||
|
||||
type SnackParams = {
|
||||
defaultFiles?: Record<
|
||||
string,
|
||||
{
|
||||
code: string;
|
||||
active?: boolean;
|
||||
hidden?: boolean;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
type ContainerArgs = [typeof container, string, { render: RenderRule }];
|
||||
|
||||
export default function sandpackPlugin(md: MarkdownIt, pluginOptions: SnackParams = {}) {
|
||||
if (md == null) {
|
||||
throw new Error('MarkdownIt instance is required for sandpackPlugin');
|
||||
}
|
||||
const escapeHtml = md?.utils?.escapeHtml;
|
||||
const defaultFence =
|
||||
md.renderer.rules.fence ||
|
||||
((tokens, idx, options, env, self) => self.renderToken(tokens, idx, options));
|
||||
|
||||
const renderSandbox = (tokenList: any[], index: number) => {
|
||||
const renderFunc = (tokens: any[], idx: number) => {
|
||||
if (tokens[idx].nesting === 1) {
|
||||
const fileAttr: string[] = [];
|
||||
const attrs = Object.fromEntries(tokens[idx].attrs || []);
|
||||
|
||||
const files: Record<
|
||||
string,
|
||||
{
|
||||
code: string;
|
||||
active?: boolean;
|
||||
hidden?: boolean;
|
||||
}
|
||||
> = {};
|
||||
|
||||
for (
|
||||
let i = idx + 1;
|
||||
!(tokens[i].nesting === -1 && tokens[i].type === 'container_sandpack_close');
|
||||
++i
|
||||
) {
|
||||
if (tokens[i].type === 'fence' && tokens[i].tag === 'code') {
|
||||
const info = tokens[i].info ?? '';
|
||||
const [lang, fileName, params = ''] = info.split(' ');
|
||||
|
||||
const active = params.includes('[active]');
|
||||
const hidden = params.includes('[hidden]');
|
||||
|
||||
const code = tokens[i].content;
|
||||
|
||||
if (fileName && code) {
|
||||
files[fileName] = {
|
||||
code,
|
||||
};
|
||||
|
||||
if (active) {
|
||||
(files[fileName] as any).active = true;
|
||||
}
|
||||
|
||||
if (hidden) {
|
||||
(files[fileName] as any).hidden = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { dependencies, showTabs, externalResources, editorWidthPercentage, ...options } =
|
||||
attrs;
|
||||
|
||||
const dependencyList = dependencies?.split(',')?.map((dep: string) => dep.trim()) ?? [];
|
||||
|
||||
const dependencyObject = dependencyList.reduce(
|
||||
(acc: Record<string, string>, name: string) => {
|
||||
acc[name] = 'latest';
|
||||
return acc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
const externalResourcesList = externalResources
|
||||
?.split(',')
|
||||
?.map((res: string) => res.trim())
|
||||
?.filter((res: string) => res.length > 0);
|
||||
|
||||
const filesWithDefaultStyles = {
|
||||
...pluginOptions.defaultFiles,
|
||||
...files,
|
||||
};
|
||||
|
||||
return `\
|
||||
<Sandpack\
|
||||
template="${escapeHtml(attrs.template || 'vanilla')}"\
|
||||
:theme="${escapeHtml(JSON.stringify(sandpackTheme))}"\
|
||||
:customSetup="${escapeHtml(
|
||||
dependencyList
|
||||
? JSON.stringify({
|
||||
dependencies: dependencyList.length ? dependencyObject : {},
|
||||
})
|
||||
: undefined,
|
||||
)}"
|
||||
:files="${escapeHtml(JSON.stringify(filesWithDefaultStyles))}"\
|
||||
:options="${escapeHtml(
|
||||
JSON.stringify({
|
||||
...(showTabs ? { showTabs: JSON.parse(showTabs) } : {}),
|
||||
externalResources: externalResourcesList,
|
||||
editorWidthPercentage: editorWidthPercentage
|
||||
? Number(editorWidthPercentage)
|
||||
: undefined,
|
||||
...options,
|
||||
}),
|
||||
)}"\
|
||||
>`;
|
||||
}
|
||||
return `</Sandpack>`;
|
||||
};
|
||||
return renderFunc(tokenList, index);
|
||||
};
|
||||
|
||||
function createCodeGroup(md: MarkdownIt): ContainerArgs {
|
||||
return [
|
||||
container,
|
||||
'sandpack',
|
||||
{
|
||||
render(tokens, idx) {
|
||||
return renderSandbox(tokens, idx);
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
md.use(...createCodeGroup(md));
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* SnackPlayer markdown-it plugin
|
||||
*
|
||||
* Converts fenced code blocks like:
|
||||
*
|
||||
* ```SnackPlayer name=My%20Example&description=Nice%20demo&ext=tsx
|
||||
* // code here
|
||||
* ```
|
||||
*
|
||||
* into:
|
||||
*
|
||||
* <div
|
||||
* class="snack-player"
|
||||
* data-snack-name="My Example"
|
||||
* ...
|
||||
* />
|
||||
*/
|
||||
|
||||
import type MarkdownIt from 'markdown-it';
|
||||
|
||||
type SnackParams = Record<string, string>;
|
||||
|
||||
function parseParams(paramString = ''): SnackParams {
|
||||
const params = Object.fromEntries(new URLSearchParams(paramString)) as SnackParams;
|
||||
|
||||
if (!params.platform) {
|
||||
params.platform = 'web';
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
export default function snackPlayerPlugin(md: MarkdownIt) {
|
||||
const escapeHtml = md.utils.escapeHtml;
|
||||
const defaultFence =
|
||||
md.renderer.rules.fence ||
|
||||
((tokens, idx, options, env, self) => self.renderToken(tokens, idx, options));
|
||||
|
||||
md.renderer.rules.fence = (tokens, idx, options, env, self) => {
|
||||
const token = tokens[idx];
|
||||
const info = (token.info || '').trim();
|
||||
|
||||
if (!info) {
|
||||
return defaultFence(tokens, idx, options, env, self);
|
||||
}
|
||||
|
||||
const [lang, ...rest] = info.split(/\s+/);
|
||||
if (lang !== 'SnackPlayer') {
|
||||
return defaultFence(tokens, idx, options, env, self);
|
||||
}
|
||||
|
||||
const paramString = rest.join(' ');
|
||||
const params = parseParams(paramString);
|
||||
|
||||
// Gather necessary params
|
||||
const name = params.name ? decodeURIComponent(params.name) : 'Example';
|
||||
const description = params.description
|
||||
? decodeURIComponent(params.description)
|
||||
: 'Example usage';
|
||||
const ext = params.ext ? decodeURIComponent(params.ext) : 'tsx';
|
||||
const filename = `App.${ext}`;
|
||||
|
||||
const files = encodeURIComponent(
|
||||
JSON.stringify({
|
||||
[filename]: {
|
||||
type: 'CODE',
|
||||
contents: token.content,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const dependencies =
|
||||
'react-native-safe-area-context' + (params.dependencies ? `,${params.dependencies}` : '');
|
||||
const platform = params.platform ?? 'web';
|
||||
const supportedPlatforms = params.supportedPlatforms ?? 'ios,android,web';
|
||||
const theme = params.theme ?? 'light';
|
||||
const preview = params.preview ?? 'true';
|
||||
const loading = params.loading ?? 'lazy';
|
||||
const deviceAppearance = params.deviceAppearance ?? 'dark';
|
||||
|
||||
// Build the HTML output (escaping where appropriate)
|
||||
return (
|
||||
`<SnackPlayer` +
|
||||
` class="snack-player"` +
|
||||
` data-snack-name="${escapeHtml(name)}"` +
|
||||
` data-snack-description="${escapeHtml(description)}"` +
|
||||
` data-snack-files="${files}"` +
|
||||
` data-snack-dependencies="${escapeHtml(dependencies)}"` +
|
||||
` data-snack-platform="${escapeHtml(platform)}"` +
|
||||
` data-snack-supported-platforms="${escapeHtml(supportedPlatforms)}"` +
|
||||
// ` data-snack-theme="${escapeHtml(theme)}"` +
|
||||
` data-snack-preview="${escapeHtml(preview)}"` +
|
||||
` data-snack-loading="${escapeHtml(loading)}"` +
|
||||
` data-snack-device-appearance="${escapeHtml(deviceAppearance)}"` +
|
||||
` data-snack-device-frame="false"` +
|
||||
`></SnackPlayer>`
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
import { DefaultTheme, UserConfig } from 'vitepress';
|
||||
|
||||
const sidebar: UserConfig<DefaultTheme.Config>['themeConfig']['sidebar'] = {
|
||||
guide: [
|
||||
{
|
||||
text: 'Introduction',
|
||||
items: [
|
||||
{ text: 'What is lucide?', link: '/guide/' },
|
||||
{ text: 'Installation', link: '/guide/installation' },
|
||||
{ text: 'Comparison', link: '/guide/comparison' },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Basics',
|
||||
items: [
|
||||
{
|
||||
text: 'Color',
|
||||
link: '/guide/basics/color',
|
||||
},
|
||||
{
|
||||
text: 'Sizing',
|
||||
link: '/guide/basics/sizing',
|
||||
},
|
||||
{
|
||||
text: 'Stroke width',
|
||||
link: '/guide/basics/stroke-width',
|
||||
},
|
||||
],
|
||||
},
|
||||
// TODO: Add this section
|
||||
{
|
||||
text: 'Advanced',
|
||||
items: [
|
||||
{
|
||||
text: 'Accessibility',
|
||||
link: '/guide/advanced/accessibility',
|
||||
},
|
||||
{
|
||||
text: 'Global styling',
|
||||
link: '/guide/advanced/global-styling',
|
||||
},
|
||||
// {
|
||||
// text: 'Animations',
|
||||
// },
|
||||
{
|
||||
text: 'Filled icons',
|
||||
link: '/guide/advanced/filled-icons',
|
||||
},
|
||||
{
|
||||
text: 'Aliased Names',
|
||||
link: '/guide/advanced/aliased-names',
|
||||
},
|
||||
// {
|
||||
// text: 'Combining icons',
|
||||
// },
|
||||
// {
|
||||
// text: 'Dynamic imports'
|
||||
// },
|
||||
// {
|
||||
// text: 'Auto importing'
|
||||
// },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Packages',
|
||||
items: [
|
||||
{
|
||||
text: 'Lucide',
|
||||
link: '/guide/packages/lucide',
|
||||
},
|
||||
{
|
||||
text: 'React',
|
||||
link: '/guide/packages/lucide-react',
|
||||
},
|
||||
{
|
||||
text: 'Vue',
|
||||
link: '/guide/packages/lucide-vue',
|
||||
},
|
||||
{
|
||||
text: 'Svelte',
|
||||
link: '/guide/packages/lucide-svelte',
|
||||
},
|
||||
{
|
||||
text: 'Solid',
|
||||
link: '/guide/packages/lucide-solid',
|
||||
},
|
||||
{
|
||||
text: 'React Native',
|
||||
link: '/guide/packages/lucide-react-native',
|
||||
},
|
||||
{
|
||||
text: 'Angular',
|
||||
link: '/guide/packages/angular',
|
||||
},
|
||||
{
|
||||
text: 'Preact',
|
||||
link: '/guide/packages/lucide-preact',
|
||||
},
|
||||
{
|
||||
text: 'Astro',
|
||||
link: '/guide/packages/lucide-astro',
|
||||
},
|
||||
{
|
||||
text: 'Static',
|
||||
link: '/guide/packages/lucide-static',
|
||||
},
|
||||
{
|
||||
text: 'Icon data & helpers',
|
||||
link: '/guide/packages/icons',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Contributing',
|
||||
items: [
|
||||
{
|
||||
text: 'Icon Design Principles',
|
||||
link: '/guide/design/icon-design-guide',
|
||||
},
|
||||
{
|
||||
text: 'Designing in Illustrator',
|
||||
link: '/guide/design/illustrator-guide',
|
||||
},
|
||||
{
|
||||
text: 'Designing in InkScape',
|
||||
link: '/guide/design/inkscape-guide',
|
||||
},
|
||||
{
|
||||
text: 'Designing in Figma',
|
||||
link: '/guide/design/figma-guide',
|
||||
},
|
||||
{
|
||||
text: 'Designing in Affinity Designer',
|
||||
link: '/guide/design/affinity-designer-guide',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
// This should be here to keep the sidebar shown on the icons page
|
||||
icons: [{ text: '', link: '/' }],
|
||||
};
|
||||
|
||||
export default sidebar;
|
||||
@@ -0,0 +1,107 @@
|
||||
import { DefaultTheme } from 'vitepress';
|
||||
|
||||
export const angularSidebar = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
text: 'Overview',
|
||||
link: '/guide/angular/',
|
||||
},
|
||||
{
|
||||
text: 'Getting started',
|
||||
link: '/guide/angular/getting-started',
|
||||
desc: 'Learn how to get started with Lucide for Angular.',
|
||||
},
|
||||
{
|
||||
text: 'Migration from v0',
|
||||
link: '/guide/angular/migration',
|
||||
desc: 'Learn how to migrate from v0 to v1 of Lucide.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Basics',
|
||||
items: [
|
||||
{
|
||||
text: 'Color',
|
||||
desc: 'Adjust the color of your icons',
|
||||
link: '/guide/angular/basics/color',
|
||||
},
|
||||
{
|
||||
text: 'Sizing',
|
||||
desc: 'Adjust the size of your icons',
|
||||
link: '/guide/angular/basics/sizing',
|
||||
},
|
||||
{
|
||||
text: 'Stroke width',
|
||||
desc: 'Adjust the stroke width of your icons',
|
||||
link: '/guide/angular/basics/stroke-width',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Advanced',
|
||||
items: [
|
||||
{
|
||||
text: 'Typescript',
|
||||
link: '/guide/angular/advanced/typescript',
|
||||
desc: 'All exported types and how to use them',
|
||||
},
|
||||
{
|
||||
text: 'Accessibility',
|
||||
link: '/guide/angular/advanced/accessibility',
|
||||
desc: 'Making your icons accessible',
|
||||
},
|
||||
{
|
||||
text: 'Global styling',
|
||||
link: '/guide/angular/advanced/global-styling',
|
||||
desc: 'Apply global styles to all icons',
|
||||
},
|
||||
{
|
||||
text: 'With Lucide Lab',
|
||||
link: '/guide/angular/advanced/with-lucide-lab',
|
||||
desc: 'Using lucide-lab with @lucide/angular',
|
||||
},
|
||||
// {
|
||||
// text: 'Animations',
|
||||
// link: '/guide/angular/advanced/animations',
|
||||
// desc: 'Add animations to your icons',
|
||||
// },
|
||||
{
|
||||
text: 'Filled icons',
|
||||
link: '/guide/angular/advanced/filled-icons',
|
||||
desc: 'Using filled icons in @lucide/angular',
|
||||
},
|
||||
{
|
||||
text: 'Combining icons',
|
||||
link: '/guide/angular/advanced/combining-icons',
|
||||
desc: 'Combine multiple icons into one',
|
||||
},
|
||||
// {
|
||||
// text: 'Dynamic icon component',
|
||||
// link: '/guide/angular/advanced/dynamic-icon-component.md',
|
||||
// desc: 'Dynamically import icons as needed',
|
||||
// },
|
||||
{
|
||||
text: 'Icon provider',
|
||||
link: '/guide/angular/advanced/icon-provider',
|
||||
desc: 'Provide icons at app level and use them by name',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Resources',
|
||||
items: [
|
||||
{
|
||||
text: 'Accessibility in depth',
|
||||
link: '/guide/accessibility',
|
||||
desc: 'Accessibility best practices',
|
||||
},
|
||||
{
|
||||
text: 'VSCode',
|
||||
link: '/guide/vscode',
|
||||
desc: 'VSCode and Lucide',
|
||||
},
|
||||
],
|
||||
},
|
||||
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];
|
||||
@@ -0,0 +1,82 @@
|
||||
import { DefaultTheme } from 'vitepress';
|
||||
|
||||
export const astroSidebar = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
text: 'Overview',
|
||||
link: '/guide/astro/',
|
||||
},
|
||||
{
|
||||
text: 'Getting started',
|
||||
link: '/guide/astro/getting-started',
|
||||
desc: 'Learn how to get started with Lucide for Astro.',
|
||||
},
|
||||
{
|
||||
text: 'Migration from v0',
|
||||
link: '/guide/astro/migration',
|
||||
desc: 'Learn how to migrate from v0 to v1 of Lucide Astro.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Basics',
|
||||
items: [
|
||||
{
|
||||
text: 'Color',
|
||||
desc: 'Adjust the color of your icons',
|
||||
link: '/guide/astro/basics/color',
|
||||
},
|
||||
{
|
||||
text: 'Sizing',
|
||||
desc: 'Adjust the size of your icons',
|
||||
link: '/guide/astro/basics/sizing',
|
||||
},
|
||||
{
|
||||
text: 'Stroke width',
|
||||
desc: 'Adjust the stroke width of your icons',
|
||||
link: '/guide/astro/basics/stroke-width',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Advanced',
|
||||
items: [
|
||||
{
|
||||
text: 'Typescript',
|
||||
link: '/guide/astro/advanced/typescript',
|
||||
desc: 'All exported types and how to use them',
|
||||
},
|
||||
{
|
||||
text: 'Accessibility',
|
||||
link: '/guide/astro/advanced/accessibility',
|
||||
desc: 'Making your icons accessible',
|
||||
},
|
||||
{
|
||||
text: 'Global styling',
|
||||
link: '/guide/astro/advanced/global-styling',
|
||||
desc: 'Apply global styles to all icons',
|
||||
},
|
||||
{
|
||||
text: 'With Lucide Lab',
|
||||
link: '/guide/astro/advanced/with-lucide-lab',
|
||||
desc: 'Using lucide-lab with @lucide/astro',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Resources',
|
||||
items: [
|
||||
{
|
||||
text: 'Accessibility in depth',
|
||||
link: '/guide/accessibility',
|
||||
desc: 'Accessibility best practices',
|
||||
},
|
||||
{
|
||||
text: 'VSCode',
|
||||
link: '/guide/vscode',
|
||||
desc: 'VSCode and Lucide',
|
||||
},
|
||||
],
|
||||
},
|
||||
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];
|
||||
@@ -0,0 +1,49 @@
|
||||
import { DefaultTheme, UserConfig } from 'vitepress';
|
||||
import { reactSidebar } from './react';
|
||||
import { vueSidebar } from './vue';
|
||||
import { svelteSidebar } from './svelte';
|
||||
import { lucideSidebar } from './lucide';
|
||||
import { solidSidebar } from './solid';
|
||||
import { preactSidebar } from './preact';
|
||||
import { reactNativeSidebar } from './react-native';
|
||||
import { astroSidebar } from './astro';
|
||||
import { resourcesSidebar } from './resources';
|
||||
import { angularSidebar } from './angular';
|
||||
import { lucideStaticSidebar } from './static';
|
||||
|
||||
type Sidebar = UserConfig<DefaultTheme.Config>['themeConfig']['sidebar'];
|
||||
|
||||
export const guideSidebarTop: DefaultTheme.SidebarItem[] = [
|
||||
{
|
||||
text: 'Introduction',
|
||||
items: [
|
||||
{ text: 'What is Lucide?', link: '/guide/' },
|
||||
{ text: 'Version 1', link: '/guide/version-1' },
|
||||
{ text: 'Installation', link: '/guide/installation' },
|
||||
{ text: 'Comparison', link: '/guide/comparison' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const sidebar: Sidebar = {
|
||||
'/guide': [{ text: '', link: '/' }],
|
||||
'/guide/lucide/': lucideSidebar,
|
||||
'/guide/react/': reactSidebar,
|
||||
'/guide/vue/': vueSidebar,
|
||||
'/guide/svelte/': svelteSidebar,
|
||||
'/guide/solid/': solidSidebar,
|
||||
'/guide/astro/': astroSidebar,
|
||||
'/guide/preact/': preactSidebar,
|
||||
'/guide/react-native/': reactNativeSidebar,
|
||||
'/guide/angular/': angularSidebar,
|
||||
'/guide/static/': lucideStaticSidebar,
|
||||
'/community': resourcesSidebar,
|
||||
'/contribute': resourcesSidebar,
|
||||
'/license': resourcesSidebar,
|
||||
'/code-of-conduct': resourcesSidebar,
|
||||
'/brand-logo-statement': resourcesSidebar,
|
||||
// This should be here to keep the sidebar shown on the icons page
|
||||
icons: [{ text: '', link: '/' }],
|
||||
};
|
||||
|
||||
export default sidebar;
|
||||
@@ -0,0 +1,87 @@
|
||||
import { DefaultTheme } from 'vitepress';
|
||||
|
||||
export const lucideSidebar = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
text: 'Overview',
|
||||
link: '/guide/lucide/',
|
||||
},
|
||||
{
|
||||
text: 'Getting started',
|
||||
link: '/guide/lucide/getting-started',
|
||||
desc: 'Learn how to get started with Lucide.',
|
||||
},
|
||||
{
|
||||
text: 'Migration from v0',
|
||||
link: '/guide/lucide/migration',
|
||||
desc: 'Learn how to migrate from v0 to v1 of Lucide.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Basics',
|
||||
items: [
|
||||
{
|
||||
text: 'Color',
|
||||
desc: 'Adjust the color of your icons',
|
||||
link: '/guide/lucide/basics/color',
|
||||
},
|
||||
{
|
||||
text: 'Sizing',
|
||||
desc: 'Adjust the size of your icons',
|
||||
link: '/guide/lucide/basics/sizing',
|
||||
},
|
||||
{
|
||||
text: 'Stroke width',
|
||||
desc: 'Adjust the stroke width of your icons',
|
||||
link: '/guide/lucide/basics/stroke-width',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Advanced',
|
||||
items: [
|
||||
{
|
||||
text: 'Global styling',
|
||||
link: '/guide/lucide/advanced/global-styling',
|
||||
desc: 'Apply options and styles globally',
|
||||
},
|
||||
{
|
||||
text: 'Shadow DOM',
|
||||
link: '/guide/lucide/advanced/shadow-dom',
|
||||
desc: 'All exported types and how to use them',
|
||||
},
|
||||
{
|
||||
text: 'Template element',
|
||||
link: '/guide/lucide/advanced/content-template-element',
|
||||
desc: 'Using content template element with lucide',
|
||||
},
|
||||
{
|
||||
text: 'Accessibility',
|
||||
link: '/guide/lucide/advanced/accessibility',
|
||||
desc: 'Making your icons accessible',
|
||||
},
|
||||
{
|
||||
text: 'With Lucide Lab',
|
||||
link: '/guide/lucide/advanced/with-lucide-lab',
|
||||
desc: 'Using lucide-lab with lucide',
|
||||
},
|
||||
{
|
||||
text: 'Filled icons',
|
||||
link: '/guide/lucide/advanced/filled-icons',
|
||||
desc: 'Using filled icons in lucide',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Resources',
|
||||
items: [
|
||||
{
|
||||
text: 'Accessibility in depth',
|
||||
link: '/guide/accessibility',
|
||||
desc: 'Accessibility best practices',
|
||||
},
|
||||
],
|
||||
},
|
||||
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];
|
||||
@@ -0,0 +1,102 @@
|
||||
import { DefaultTheme } from 'vitepress';
|
||||
|
||||
export const preactSidebar = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
text: 'Overview',
|
||||
link: '/guide/preact/',
|
||||
},
|
||||
{
|
||||
text: 'Getting started',
|
||||
link: '/guide/preact/getting-started',
|
||||
desc: 'Learn how to get started with Lucide for PReact.',
|
||||
},
|
||||
{
|
||||
text: 'Migration from v0',
|
||||
link: '/guide/preact/migration',
|
||||
desc: 'Learn how to migrate from v0 to v1 of Lucide.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Basics',
|
||||
items: [
|
||||
{
|
||||
text: 'Color',
|
||||
desc: 'Adjust the color of your icons',
|
||||
link: '/guide/preact/basics/color',
|
||||
},
|
||||
{
|
||||
text: 'Sizing',
|
||||
desc: 'Adjust the size of your icons',
|
||||
link: '/guide/preact/basics/sizing',
|
||||
},
|
||||
{
|
||||
text: 'Stroke width',
|
||||
desc: 'Adjust the stroke width of your icons',
|
||||
link: '/guide/preact/basics/stroke-width',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Advanced',
|
||||
items: [
|
||||
{
|
||||
text: 'Typescript',
|
||||
link: '/guide/preact/advanced/typescript',
|
||||
desc: 'All exported types and how to use them',
|
||||
},
|
||||
{
|
||||
text: 'Accessibility',
|
||||
link: '/guide/preact/advanced/accessibility',
|
||||
desc: 'Making your icons accessible',
|
||||
},
|
||||
{
|
||||
text: 'Global styling',
|
||||
link: '/guide/preact/advanced/global-styling',
|
||||
desc: 'Apply global styles to all icons',
|
||||
},
|
||||
{
|
||||
text: 'With Lucide Lab',
|
||||
link: '/guide/preact/advanced/with-lucide-lab',
|
||||
desc: 'Using lucide-lab with lucide-preact',
|
||||
},
|
||||
// {
|
||||
// text: 'Animations',
|
||||
// link: '/guide/preact/advanced/animations',
|
||||
// desc: 'Add animations to your icons',
|
||||
// },
|
||||
{
|
||||
text: 'Filled icons',
|
||||
link: '/guide/preact/advanced/filled-icons',
|
||||
desc: 'Using filled icons in lucide-preact',
|
||||
},
|
||||
{
|
||||
text: 'Aliased Names',
|
||||
link: '/guide/preact/advanced/aliased-names',
|
||||
desc: 'Using aliased icon names',
|
||||
},
|
||||
{
|
||||
text: 'Combining icons',
|
||||
link: '/guide/preact/advanced/combining-icons',
|
||||
desc: 'Combine multiple icons into one',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Resources',
|
||||
items: [
|
||||
{
|
||||
text: 'Accessibility in depth',
|
||||
link: '/guide/accessibility',
|
||||
desc: 'Accessibility best practices',
|
||||
},
|
||||
{
|
||||
text: 'VSCode',
|
||||
link: '/guide/vscode',
|
||||
desc: 'VSCode and Lucide',
|
||||
},
|
||||
],
|
||||
},
|
||||
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];
|
||||
@@ -0,0 +1,87 @@
|
||||
import { DefaultTheme } from 'vitepress';
|
||||
|
||||
export const reactNativeSidebar = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
text: 'Overview',
|
||||
link: '/guide/react-native/',
|
||||
},
|
||||
{
|
||||
text: 'Getting started',
|
||||
link: '/guide/react-native/getting-started',
|
||||
desc: 'Learn how to get started with Lucide React Native',
|
||||
},
|
||||
{
|
||||
text: 'Migration from v0',
|
||||
link: '/guide/react-native/migration',
|
||||
desc: 'Learn how to migrate from v0 to v1 of Lucide.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Basics',
|
||||
items: [
|
||||
{
|
||||
text: 'Color',
|
||||
desc: 'Adjust the color of your icons',
|
||||
link: '/guide/react-native/basics/color',
|
||||
},
|
||||
{
|
||||
text: 'Sizing',
|
||||
desc: 'Adjust the size of your icons',
|
||||
link: '/guide/react-native/basics/sizing',
|
||||
},
|
||||
{
|
||||
text: 'Stroke width',
|
||||
desc: 'Adjust the stroke width of your icons',
|
||||
link: '/guide/react-native/basics/stroke-width',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Advanced',
|
||||
items: [
|
||||
{
|
||||
text: 'Typescript',
|
||||
link: '/guide/react-native/advanced/typescript',
|
||||
desc: 'All exported types and how to use them',
|
||||
},
|
||||
{
|
||||
text: 'Global styling',
|
||||
link: '/guide/react-native/advanced/global-styling',
|
||||
desc: 'Apply global styles to all icons',
|
||||
},
|
||||
{
|
||||
text: 'With Lucide Lab',
|
||||
link: '/guide/react-native/advanced/with-lucide-lab',
|
||||
desc: 'Using lucide-lab with lucide-react-native',
|
||||
},
|
||||
{
|
||||
text: 'Filled icons',
|
||||
link: '/guide/react-native/advanced/filled-icons',
|
||||
desc: 'Using filled icons in lucide-react-native',
|
||||
},
|
||||
{
|
||||
text: 'Aliased Names',
|
||||
link: '/guide/react-native/advanced/aliased-names',
|
||||
desc: 'Using aliased icon names',
|
||||
},
|
||||
{
|
||||
text: 'Combining icons',
|
||||
link: '/guide/react-native/advanced/combining-icons',
|
||||
desc: 'Combine multiple icons into one',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Resources',
|
||||
items: [
|
||||
{
|
||||
text: 'VSCode',
|
||||
link: '/guide/vscode',
|
||||
desc: 'VSCode and Lucide',
|
||||
},
|
||||
],
|
||||
},
|
||||
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];
|
||||
@@ -0,0 +1,113 @@
|
||||
import { DefaultTheme } from 'vitepress';
|
||||
|
||||
export const reactSidebar = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
text: 'Overview',
|
||||
link: '/guide/react/',
|
||||
},
|
||||
{
|
||||
text: 'Getting started',
|
||||
link: '/guide/react/getting-started',
|
||||
desc: 'Learn how to get started with Lucide for React.',
|
||||
},
|
||||
{
|
||||
text: 'Migration from v0',
|
||||
link: '/guide/react/migration',
|
||||
desc: 'Learn how to migrate from v0 to v1 of Lucide.',
|
||||
},
|
||||
{
|
||||
text: 'Migration from React Feather',
|
||||
link: '/guide/react/migration-from-feather',
|
||||
desc: 'Learn how to migrate from `react-feather` to `lucide-react`',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Basics',
|
||||
items: [
|
||||
{
|
||||
text: 'Color',
|
||||
desc: 'Adjust the color of your icons',
|
||||
link: '/guide/react/basics/color',
|
||||
},
|
||||
{
|
||||
text: 'Sizing',
|
||||
desc: 'Adjust the size of your icons',
|
||||
link: '/guide/react/basics/sizing',
|
||||
},
|
||||
{
|
||||
text: 'Stroke width',
|
||||
desc: 'Adjust the stroke width of your icons',
|
||||
link: '/guide/react/basics/stroke-width',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Advanced',
|
||||
items: [
|
||||
{
|
||||
text: 'Typescript',
|
||||
link: '/guide/react/advanced/typescript',
|
||||
desc: 'All exported types and how to use them',
|
||||
},
|
||||
{
|
||||
text: 'Accessibility',
|
||||
link: '/guide/react/advanced/accessibility',
|
||||
desc: 'Making your icons accessible',
|
||||
},
|
||||
{
|
||||
text: 'Global styling',
|
||||
link: '/guide/react/advanced/global-styling',
|
||||
desc: 'Apply global styles to all icons',
|
||||
},
|
||||
{
|
||||
text: 'With Lucide Lab',
|
||||
link: '/guide/react/advanced/with-lucide-lab',
|
||||
desc: 'Using lucide-lab with lucide-react',
|
||||
},
|
||||
// {
|
||||
// text: 'Animations',
|
||||
// link: '/guide/react/advanced/animations',
|
||||
// desc: 'Add animations to your icons',
|
||||
// },
|
||||
{
|
||||
text: 'Filled icons',
|
||||
link: '/guide/react/advanced/filled-icons',
|
||||
desc: 'Using filled icons in lucide-react',
|
||||
},
|
||||
{
|
||||
text: 'Aliased Names',
|
||||
link: '/guide/react/advanced/aliased-names',
|
||||
desc: 'Using aliased icon names',
|
||||
},
|
||||
|
||||
{
|
||||
text: 'Combining icons',
|
||||
link: '/guide/react/advanced/combining-icons',
|
||||
desc: 'Combine multiple icons into one',
|
||||
},
|
||||
{
|
||||
text: 'Dynamic icon component',
|
||||
link: '/guide/react/advanced/dynamic-icon-component.md',
|
||||
desc: 'Dynamically import icons as needed',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Resources',
|
||||
items: [
|
||||
{
|
||||
text: 'Accessibility in depth',
|
||||
link: '/guide/accessibility',
|
||||
desc: 'Accessibility best practices',
|
||||
},
|
||||
{
|
||||
text: 'VSCode',
|
||||
link: '/guide/vscode',
|
||||
desc: 'VSCode and Lucide',
|
||||
},
|
||||
],
|
||||
},
|
||||
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];
|
||||
@@ -0,0 +1,52 @@
|
||||
export const resourcesSidebar = [
|
||||
{
|
||||
text: 'About',
|
||||
items: [
|
||||
{
|
||||
text: 'License',
|
||||
link: '/license',
|
||||
},
|
||||
{
|
||||
text: 'Community',
|
||||
link: '/community',
|
||||
},
|
||||
{
|
||||
text: 'Code of Conduct',
|
||||
link: '/code-of-conduct',
|
||||
},
|
||||
{
|
||||
text: 'Brand logo statement',
|
||||
link: '/brand-logo-statement',
|
||||
},
|
||||
{
|
||||
text: 'Contributing',
|
||||
link: '/contribute',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Designing icons',
|
||||
items: [
|
||||
{
|
||||
text: 'Icon Design Principles',
|
||||
link: '/contribute/icon-design-guide',
|
||||
},
|
||||
{
|
||||
text: 'Designing in Illustrator',
|
||||
link: '/contribute/illustrator-guide',
|
||||
},
|
||||
{
|
||||
text: 'Designing in Inkscape',
|
||||
link: '/contribute/inkscape-guide',
|
||||
},
|
||||
{
|
||||
text: 'Designing in Figma',
|
||||
link: '/contribute/figma-guide',
|
||||
},
|
||||
{
|
||||
text: 'Designing in Affinity Designer',
|
||||
link: '/contribute/affinity-designer-guide',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,103 @@
|
||||
import { DefaultTheme } from 'vitepress';
|
||||
|
||||
export const solidSidebar = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
text: 'Overview',
|
||||
link: '/guide/solid/',
|
||||
},
|
||||
{
|
||||
text: 'Getting started',
|
||||
link: '/guide/solid/getting-started',
|
||||
desc: 'Learn how to get started with Lucide.',
|
||||
},
|
||||
{
|
||||
text: 'Migration from v0',
|
||||
link: '/guide/solid/migration',
|
||||
desc: 'Learn how to migrate from v0 to v1 of Lucide.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Basics',
|
||||
items: [
|
||||
{
|
||||
text: 'Color',
|
||||
desc: 'Adjust the color of your icons',
|
||||
link: '/guide/solid/basics/color',
|
||||
},
|
||||
{
|
||||
text: 'Sizing',
|
||||
desc: 'Adjust the size of your icons',
|
||||
link: '/guide/solid/basics/sizing',
|
||||
},
|
||||
{
|
||||
text: 'Stroke width',
|
||||
desc: 'Adjust the stroke width of your icons',
|
||||
link: '/guide/solid/basics/stroke-width',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Advanced',
|
||||
items: [
|
||||
{
|
||||
text: 'Typescript',
|
||||
link: '/guide/solid/advanced/typescript',
|
||||
desc: 'All exported types and how to use them',
|
||||
},
|
||||
{
|
||||
text: 'Accessibility',
|
||||
link: '/guide/solid/advanced/accessibility',
|
||||
desc: 'Making your icons accessible',
|
||||
},
|
||||
{
|
||||
text: 'Global styling',
|
||||
link: '/guide/solid/advanced/global-styling',
|
||||
desc: 'Apply global styles to all icons',
|
||||
},
|
||||
{
|
||||
text: 'With Lucide Lab',
|
||||
link: '/guide/solid/advanced/with-lucide-lab',
|
||||
desc: 'Using lucide-lab with lucide-solid',
|
||||
},
|
||||
// {
|
||||
// text: 'Animations',
|
||||
// link: '/guide/solid/advanced/animations',
|
||||
// desc: 'Add animations to your icons',
|
||||
// },
|
||||
{
|
||||
text: 'Filled icons',
|
||||
link: '/guide/solid/advanced/filled-icons',
|
||||
desc: 'Using filled icons in lucide-solid',
|
||||
},
|
||||
{
|
||||
text: 'Aliased Names',
|
||||
link: '/guide/solid/advanced/aliased-names',
|
||||
desc: 'Using aliased icon names',
|
||||
},
|
||||
// Add support for this later
|
||||
// {
|
||||
// text: 'Combining icons',
|
||||
// link: '/guide/solid/advanced/combining-icons',
|
||||
// desc: 'Combine multiple icons into one',
|
||||
// },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Resources',
|
||||
items: [
|
||||
{
|
||||
text: 'Accessibility in depth',
|
||||
link: '/guide/accessibility',
|
||||
desc: 'Accessibility best practices',
|
||||
},
|
||||
{
|
||||
text: 'VSCode',
|
||||
link: '/guide/vscode',
|
||||
desc: 'VSCode and Lucide',
|
||||
},
|
||||
],
|
||||
},
|
||||
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];
|
||||
@@ -0,0 +1,88 @@
|
||||
import { DefaultTheme } from 'vitepress';
|
||||
|
||||
export const lucideStaticSidebar = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
text: 'Overview',
|
||||
desc: 'Overview of using Lucide icons as static assets in your projects',
|
||||
link: '/guide/static/',
|
||||
},
|
||||
{
|
||||
text: 'Getting started',
|
||||
link: '/guide/static/getting-started',
|
||||
desc: 'Learn how to get started with Lucide static.',
|
||||
},
|
||||
{
|
||||
text: 'Migration from v0',
|
||||
link: '/guide/static/migration',
|
||||
desc: 'Learn how to migrate from v0 to v1 of Lucide static.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'SVG Files & Sprite',
|
||||
items: [
|
||||
{
|
||||
text: 'Import SVG files as images',
|
||||
desc: 'Use icons as images in your project',
|
||||
link: '/guide/static/link-as-image',
|
||||
},
|
||||
{
|
||||
text: 'SVG Sprite',
|
||||
desc: 'Use SVG sprites in your project',
|
||||
link: '/guide/static/svg-sprite',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Icon Font',
|
||||
items: [
|
||||
{
|
||||
text: 'Import as font',
|
||||
desc: 'Use icons as a web font in your project',
|
||||
link: '/guide/static/font/',
|
||||
},
|
||||
{
|
||||
text: 'Color',
|
||||
desc: 'Adjust the color of your icons',
|
||||
link: '/guide/static/font/color',
|
||||
},
|
||||
{
|
||||
text: 'Sizing',
|
||||
desc: 'Adjust the size of your icons',
|
||||
link: '/guide/static/font/sizing',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'SVG String JS modules',
|
||||
items: [
|
||||
{
|
||||
text: 'Use in Node.js',
|
||||
desc: 'Use Lucide in your Node.js projects',
|
||||
link: '/guide/static/js-modules/node',
|
||||
},
|
||||
{
|
||||
text: 'Use in JS projects',
|
||||
desc: 'Use Lucide in your JavaScript projects',
|
||||
link: '/guide/static/js-modules/web',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Resources',
|
||||
items: [
|
||||
{
|
||||
text: 'Accessibility in depth',
|
||||
link: '/guide/accessibility',
|
||||
desc: 'Accessibility best practices',
|
||||
},
|
||||
{
|
||||
text: 'VSCode',
|
||||
link: '/guide/vscode',
|
||||
desc: 'VSCode and Lucide',
|
||||
},
|
||||
],
|
||||
},
|
||||
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];
|
||||
@@ -0,0 +1,92 @@
|
||||
import { DefaultTheme } from 'vitepress';
|
||||
|
||||
export const svelteSidebar = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
text: 'Overview',
|
||||
link: '/guide/svelte/',
|
||||
},
|
||||
{
|
||||
text: 'Getting started',
|
||||
link: '/guide/svelte/getting-started',
|
||||
desc: 'Learn how to get started with Lucide for Svelte.',
|
||||
},
|
||||
{
|
||||
text: 'Migration from v0',
|
||||
link: '/guide/svelte/migration',
|
||||
desc: 'Learn how to migrate from v0 to v1 of Lucide.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Basics',
|
||||
items: [
|
||||
{
|
||||
text: 'Color',
|
||||
desc: 'Adjust the color of your icons',
|
||||
link: '/guide/svelte/basics/color',
|
||||
},
|
||||
{
|
||||
text: 'Sizing',
|
||||
desc: 'Adjust the size of your icons',
|
||||
link: '/guide/svelte/basics/sizing',
|
||||
},
|
||||
{
|
||||
text: 'Stroke width',
|
||||
desc: 'Adjust the stroke width of your icons',
|
||||
link: '/guide/svelte/basics/stroke-width',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Advanced',
|
||||
items: [
|
||||
{
|
||||
text: 'Typescript',
|
||||
link: '/guide/svelte/advanced/typescript',
|
||||
desc: 'All exported types and how to use them',
|
||||
},
|
||||
{
|
||||
text: 'Accessibility',
|
||||
link: '/guide/svelte/advanced/accessibility',
|
||||
desc: 'Making your icons accessible',
|
||||
},
|
||||
{
|
||||
text: 'Global styling',
|
||||
link: '/guide/svelte/advanced/global-styling',
|
||||
desc: 'Apply global styles to all icons',
|
||||
},
|
||||
{
|
||||
text: 'With Lucide Lab',
|
||||
link: '/guide/svelte/advanced/with-lucide-lab',
|
||||
desc: 'Using lucide-lab with @lucide/svelte',
|
||||
},
|
||||
{
|
||||
text: 'Filled icons',
|
||||
link: '/guide/svelte/advanced/filled-icons',
|
||||
desc: 'Using filled icons in @lucide/svelte',
|
||||
},
|
||||
{
|
||||
text: 'Combining icons',
|
||||
link: '/guide/svelte/advanced/combining-icons',
|
||||
desc: 'Combine multiple icons into one',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Resources',
|
||||
items: [
|
||||
{
|
||||
text: 'Accessibility in depth',
|
||||
link: '/guide/accessibility',
|
||||
desc: 'Accessibility best practices',
|
||||
},
|
||||
{
|
||||
text: 'VSCode',
|
||||
link: '/guide/vscode',
|
||||
desc: 'VSCode and Lucide',
|
||||
},
|
||||
],
|
||||
},
|
||||
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];
|
||||
@@ -0,0 +1,98 @@
|
||||
import { DefaultTheme } from 'vitepress';
|
||||
|
||||
export const vueSidebar = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
text: 'Overview',
|
||||
link: '/guide/vue/',
|
||||
},
|
||||
{
|
||||
text: 'Getting started',
|
||||
link: '/guide/vue/getting-started',
|
||||
desc: 'Learn how to get started with Lucide for Vue.',
|
||||
},
|
||||
{
|
||||
text: 'Migration from v0',
|
||||
link: '/guide/vue/migration',
|
||||
desc: 'Learn how to migrate from v0 to v1 of Lucide.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Basics',
|
||||
items: [
|
||||
{
|
||||
text: 'Color',
|
||||
desc: 'Adjust the color of your icons',
|
||||
link: '/guide/vue/basics/color',
|
||||
},
|
||||
{
|
||||
text: 'Sizing',
|
||||
desc: 'Adjust the size of your icons',
|
||||
link: '/guide/vue/basics/sizing',
|
||||
},
|
||||
{
|
||||
text: 'Stroke width',
|
||||
desc: 'Adjust the stroke width of your icons',
|
||||
link: '/guide/vue/basics/stroke-width',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Advanced',
|
||||
items: [
|
||||
{
|
||||
text: 'Typescript',
|
||||
link: '/guide/vue/advanced/typescript',
|
||||
desc: 'All exported types and how to use them',
|
||||
},
|
||||
{
|
||||
text: 'Accessibility',
|
||||
link: '/guide/vue/advanced/accessibility',
|
||||
desc: 'Making your icons accessible',
|
||||
},
|
||||
{
|
||||
text: 'Global styling',
|
||||
link: '/guide/vue/advanced/global-styling',
|
||||
desc: 'Apply global styles to all icons',
|
||||
},
|
||||
{
|
||||
text: 'With Lucide Lab',
|
||||
link: '/guide/vue/advanced/with-lucide-lab',
|
||||
desc: 'Using lucide-lab with @lucide/vue',
|
||||
},
|
||||
{
|
||||
text: 'Filled icons',
|
||||
link: '/guide/vue/advanced/filled-icons',
|
||||
desc: 'Using filled icons in @lucide/vue',
|
||||
},
|
||||
{
|
||||
text: 'Aliased Names',
|
||||
link: '/guide/vue/advanced/aliased-names',
|
||||
desc: 'Using aliased icon names',
|
||||
},
|
||||
|
||||
{
|
||||
text: 'Combining icons',
|
||||
link: '/guide/vue/advanced/combining-icons',
|
||||
desc: 'Combine multiple icons into one',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Resources',
|
||||
items: [
|
||||
{
|
||||
text: 'Accessibility in depth',
|
||||
link: '/guide/accessibility',
|
||||
desc: 'Accessibility best practices',
|
||||
},
|
||||
{
|
||||
text: 'VSCode',
|
||||
link: '/guide/vscode',
|
||||
desc: 'VSCode and Lucide',
|
||||
},
|
||||
],
|
||||
},
|
||||
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];
|
||||
@@ -1,62 +1,58 @@
|
||||
<script setup lang="ts">
|
||||
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import {
|
||||
Listbox,
|
||||
ListboxButton,
|
||||
ListboxOptions,
|
||||
ListboxOption,
|
||||
} from '@headlessui/vue'
|
||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
|
||||
import { chevronUp } from '../../../data/iconNodes'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { Listbox, ListboxButton, ListboxOptions, ListboxOption } from '@headlessui/vue';
|
||||
import createLucideIcon from '@lucide/vue/src/createLucideIcon';
|
||||
import { chevronUp } from '../../../data/iconNodes';
|
||||
import { useStorage } from '@vueuse/core';
|
||||
|
||||
interface Props {
|
||||
options: {
|
||||
text: string
|
||||
onClick?: () => void
|
||||
}[],
|
||||
callOptionOnClick?: boolean
|
||||
buttonClass?: string
|
||||
id: string
|
||||
popoverPosition?: 'top' | 'bottom'
|
||||
text: string;
|
||||
onClick?: () => void;
|
||||
}[];
|
||||
callOptionOnClick?: boolean;
|
||||
buttonClass?: string;
|
||||
id: string;
|
||||
popoverPosition?: 'top' | 'bottom';
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
callOptionOnClick: false,
|
||||
popoverPosition: 'bottom'
|
||||
})
|
||||
popoverPosition: 'bottom',
|
||||
});
|
||||
|
||||
const emit = defineEmits(['click', 'optionClick'])
|
||||
const emit = defineEmits(['click', 'optionClick']);
|
||||
|
||||
const buttonRef = ref(null)
|
||||
const buttonRef = ref(null);
|
||||
|
||||
const selectedOption = useStorage(props.id, props.options[0].text)
|
||||
const selectionOptionAction = computed(() => props.options.find(option => option.text === selectedOption.value).onClick)
|
||||
const selectedOption = useStorage(props.id, props.options[0].text);
|
||||
const selectionOptionAction = computed(
|
||||
() => props.options.find((option) => option.text === selectedOption.value).onClick,
|
||||
);
|
||||
|
||||
function onClick(event) {
|
||||
selectionOptionAction.value()
|
||||
selectionOptionAction.value();
|
||||
|
||||
|
||||
emit('click', event)
|
||||
emit('click', event);
|
||||
}
|
||||
|
||||
function onOptionClick(event, option) {
|
||||
if(!props.callOptionOnClick) {
|
||||
return
|
||||
if (!props.callOptionOnClick) {
|
||||
return;
|
||||
}
|
||||
|
||||
option.onClick()
|
||||
option.onClick();
|
||||
|
||||
emit('optionClick', event)
|
||||
emit('optionClick', event);
|
||||
}
|
||||
|
||||
const ChevronUp = createLucideIcon('ChevronUp', chevronUp)
|
||||
const ChevronUp = createLucideIcon('ChevronUp', chevronUp);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Listbox v-model="selectedOption">
|
||||
<div class="menu" >
|
||||
<div class="menu">
|
||||
<div class="button-wrapper">
|
||||
<VPButton
|
||||
v-bind="$attrs"
|
||||
@@ -75,7 +71,10 @@ const ChevronUp = createLucideIcon('ChevronUp', chevronUp)
|
||||
:class="popoverPosition"
|
||||
/>
|
||||
</div>
|
||||
<ListboxOptions class="menu-items" :class="popoverPosition">
|
||||
<ListboxOptions
|
||||
class="menu-items"
|
||||
:class="popoverPosition"
|
||||
>
|
||||
<ListboxOption
|
||||
as="button"
|
||||
class="menu-item"
|
||||
@@ -122,7 +121,9 @@ ul.menu-items {
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-text-1);
|
||||
white-space: nowrap;
|
||||
transition: background-color .25s,color .25s;
|
||||
transition:
|
||||
background-color 0.25s,
|
||||
color 0.25s;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import Icon from 'lucide-vue-next/src/Icon';
|
||||
import Icon from '@lucide/vue/src/Icon';
|
||||
import { search } from '../../../data/iconNodes';
|
||||
|
||||
defineProps({
|
||||
|
||||
@@ -12,7 +12,7 @@ export interface InputProps {
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, nextTick, watch } from 'vue';
|
||||
import Icon from 'lucide-vue-next/src/Icon';
|
||||
import Icon from '@lucide/vue/src/Icon';
|
||||
import { x } from '../../../data/iconNodes';
|
||||
import IconButton from './IconButton.vue';
|
||||
|
||||
@@ -132,7 +132,7 @@ defineExpose({
|
||||
right: 68px;
|
||||
top: 9px;
|
||||
padding: 4px;
|
||||
transition: background-color .25s;
|
||||
transition: background-color 0.25s;
|
||||
}
|
||||
|
||||
.shortcut {
|
||||
|
||||
@@ -7,7 +7,7 @@ export default {
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import Input from './Input.vue';
|
||||
import Icon from 'lucide-vue-next/src/Icon';
|
||||
import Icon from '@lucide/vue/src/Icon';
|
||||
import { search } from '../../../data/iconNodes';
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<script setup lang="ts">
|
||||
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="top-notification">
|
||||
<p>
|
||||
Release candidate for
|
||||
<VPLink
|
||||
class="notification-link"
|
||||
href="/guide/version-1"
|
||||
>Lucide v1 is out!</VPLink
|
||||
>🚀<br />
|
||||
You're looking at the site for v1, for v0 go to
|
||||
<VPLink
|
||||
class="notification-link"
|
||||
href="https://v0.lucide.dev"
|
||||
>v0 site</VPLink
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--vp-layout-top-height: 64px;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
:root {
|
||||
--vp-layout-top-height: 32px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
.top-notification {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
height: var(--vp-layout-top-height);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: var(--vp-c-brand-dark);
|
||||
color: var(--vp-c-bg-alt-down);
|
||||
font-size: 14px;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.notification-link {
|
||||
font-weight: 500;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.notification-link::after {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.top-notification br {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,17 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
|
||||
import createLucideIcon from '@lucide/vue/src/createLucideIcon';
|
||||
|
||||
export type IconNode = [elementName: string, attrs: Record<string, string>][]
|
||||
export type IconNode = [elementName: string, attrs: Record<string, string>][];
|
||||
|
||||
const props = defineProps<{
|
||||
name: string;
|
||||
tags: string[];
|
||||
categories: string[];
|
||||
tags?: string[];
|
||||
categories?: string[];
|
||||
// contributors: Contributor[];
|
||||
iconNode: IconNode;
|
||||
}>()
|
||||
}>();
|
||||
|
||||
const icon = createLucideIcon(props.name, props.iconNode)
|
||||
const icon = createLucideIcon(props.name, props.iconNode);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<script setup lang="ts">
|
||||
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue';
|
||||
|
||||
defineProps<{
|
||||
href?: string;
|
||||
title?: string;
|
||||
desc?: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VPLink
|
||||
class="overview-link"
|
||||
:href="href"
|
||||
>
|
||||
<span class="title">
|
||||
{{ title }}
|
||||
</span>
|
||||
<span
|
||||
v-if="desc"
|
||||
class="desc"
|
||||
>
|
||||
{{ desc }}
|
||||
</span>
|
||||
</VPLink>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.overview-link {
|
||||
display: block;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 11px 16px 13px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transition: border-color 0.25s;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.overview-link:hover {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.desc {
|
||||
display: block;
|
||||
line-height: 20px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.title {
|
||||
display: block;
|
||||
line-height: 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-brand-1);
|
||||
transition: color 0.25s;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div class="overview-link-grid">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.overview-link-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.overview-link-grid > * {
|
||||
box-sizing: border-box;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.overview-link-grid {
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.overview-link-grid {
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { rotateCw } from '../../../data/iconNodes';
|
||||
import Icon from 'lucide-vue-next/src/Icon';
|
||||
import Icon from '@lucide/vue/src/Icon';
|
||||
import IconButton from './IconButton.vue';
|
||||
</script>
|
||||
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import {
|
||||
Listbox,
|
||||
ListboxLabel,
|
||||
ListboxButton,
|
||||
ListboxOptions,
|
||||
ListboxOption,
|
||||
} from '@headlessui/vue';
|
||||
import Icon from '@lucide/vue/src/Icon';
|
||||
import { chevronsUpDown, check } from '../../../data/iconNodes';
|
||||
|
||||
defineProps<{
|
||||
id?: string;
|
||||
items?: { name: string; icon: string }[];
|
||||
}>();
|
||||
|
||||
const selected = defineModel<{ name: string; icon: string }>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Listbox v-model="selected">
|
||||
<div class="select-wrapper">
|
||||
<ListboxButton
|
||||
class="select-button"
|
||||
:id="id"
|
||||
>
|
||||
<img
|
||||
:src="selected.icon"
|
||||
:class="{ 'select-item-icon': true }"
|
||||
:alt="`${selected.name} logo`"
|
||||
loading="lazy"
|
||||
/>
|
||||
<span class="select-text">{{ selected.name }}</span>
|
||||
<span class="select-icon">
|
||||
<Icon
|
||||
:iconNode="chevronsUpDown"
|
||||
class="chevron-icon"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
</ListboxButton>
|
||||
|
||||
<transition
|
||||
leave-active-class="transition-leave"
|
||||
leave-from-class="transition-leave-from"
|
||||
leave-to-class="transition-leave-to"
|
||||
>
|
||||
<ListboxOptions class="select-options">
|
||||
<ListboxOption
|
||||
v-slot="{ active, selected }"
|
||||
v-for="item in items"
|
||||
:key="item.name"
|
||||
:value="item"
|
||||
as="template"
|
||||
>
|
||||
<li :class="['select-option', { active, selected }]">
|
||||
<img
|
||||
:src="item.icon"
|
||||
:class="{ 'select-item-icon': true }"
|
||||
:alt="`${item.name} logo`"
|
||||
loading="lazy"
|
||||
/>
|
||||
<span :class="['option-text', { selected }]">{{ item.name }}</span>
|
||||
<span
|
||||
v-if="selected"
|
||||
class="check-icon"
|
||||
>
|
||||
<Icon
|
||||
:iconNode="check"
|
||||
class="check"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
</li>
|
||||
</ListboxOption>
|
||||
</ListboxOptions>
|
||||
</transition>
|
||||
</div>
|
||||
</Listbox>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.select-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.select-button {
|
||||
background: var(--vp-sidebar-input);
|
||||
border-radius: 8px;
|
||||
color: var(--vp-c-text-1);
|
||||
padding: 7px 14px;
|
||||
height: auto;
|
||||
font-size: 14px;
|
||||
border: 1px solid transparent;
|
||||
cursor: text;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
transition:
|
||||
color 0.25s,
|
||||
border-color 0.25s,
|
||||
background-color 0.25s;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.select-button:focus {
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.select-text {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.select-icon {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.select-item-icon {
|
||||
object-fit: contain;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.chevron-icon {
|
||||
height: 1.25rem;
|
||||
width: 1.25rem;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.transition-leave {
|
||||
transition: opacity 100ms ease-in;
|
||||
}
|
||||
|
||||
.transition-leave-from {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.transition-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.select-options {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 12px;
|
||||
padding: 12px;
|
||||
min-width: 128px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background-color: var(--vp-c-bg-elv);
|
||||
box-shadow: var(--vp-shadow-3);
|
||||
transition: background-color 0.5s;
|
||||
max-height: calc(100vh - var(--vp-nav-height));
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
z-index: 90;
|
||||
right: 0;
|
||||
top: 44px;
|
||||
}
|
||||
|
||||
.select-option {
|
||||
position: relative;
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
padding: 0px 4px;
|
||||
text-align: left;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
border-radius: 6px;
|
||||
line-height: 32px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-text-1);
|
||||
white-space: nowrap;
|
||||
transition:
|
||||
background-color 0.25s,
|
||||
color 0.25s;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.select-option:hover,
|
||||
.select-option.active {
|
||||
color: var(--vp-c-brand);
|
||||
background-color: var(--vp-c-default-soft);
|
||||
}
|
||||
|
||||
.select-option:active {
|
||||
color: var(--vp-c-brand);
|
||||
background-color: var(--vp-c-bg-elv);
|
||||
}
|
||||
|
||||
.option-text {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.option-text.selected {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.check-icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 0.75rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.check {
|
||||
height: 1.25rem;
|
||||
width: 1.25rem;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.select-button,
|
||||
.select-options {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,70 @@
|
||||
<script lang="ts" setup>
|
||||
import { type FunctionalComponent } from 'vue';
|
||||
import { type LucideProps } from '@lucide/vue';
|
||||
import { Sandpack } from 'sandpack-vue3';
|
||||
|
||||
defineProps<FunctionalComponent<LucideProps>>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Sandpack />
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.sp-wrapper + * {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-layout {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-tabs-scrollable-container {
|
||||
border-radius: 8px 8px 0 0;
|
||||
position: relative;
|
||||
|
||||
box-shadow: inset 0 -1px var(--vp-code-tab-divider);
|
||||
margin-bottom: 0px;
|
||||
margin-top: -1px;
|
||||
height: 48px;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-preview-container {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-tabs .sp-tab-button {
|
||||
padding: 0 12px;
|
||||
line-height: 48px;
|
||||
height: 48px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-tabs .sp-tab-button:after {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
left: 8px;
|
||||
bottom: 0px;
|
||||
z-index: 1;
|
||||
height: 1px;
|
||||
content: '';
|
||||
background-color: transparent;
|
||||
transition: background-color 0.25s;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-tabs .sp-tab-button[data-active='true'] {
|
||||
color: var(--vp-code-tab-active-text-color);
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-tabs .sp-tab-button[data-active='true']:after {
|
||||
background-color: var(--vp-code-tab-active-bar-color);
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-button {
|
||||
color: var(--sp-colors-clickable);
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,92 @@
|
||||
<script lang="ts" setup>
|
||||
import { type SandpackFiles } from 'sandpack-vue3';
|
||||
import styledCSS from '../../sandpack-default.css?raw';
|
||||
import Sandpack from './Sandpack.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
files: SandpackFiles;
|
||||
}>();
|
||||
|
||||
const angularFiles = {
|
||||
'/src/index.html': {
|
||||
code: `<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Angular</title>
|
||||
<base href="/">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<app></app>
|
||||
</body>
|
||||
|
||||
</html>`,
|
||||
hidden: true,
|
||||
},
|
||||
'/src/app/app.component.css': {
|
||||
code: styledCSS,
|
||||
hidden: true,
|
||||
},
|
||||
'/src/polyfills.ts': {
|
||||
code: '',
|
||||
hidden: true,
|
||||
},
|
||||
'/src/app/app.config.ts': {
|
||||
code: `import { ApplicationConfig } from '@angular/core';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: []
|
||||
};
|
||||
`,
|
||||
hidden: true,
|
||||
},
|
||||
'/src/main.ts': {
|
||||
code: `import "@angular/compiler";
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { App } from './app/app.component';
|
||||
import { appConfig } from './app/app.config';
|
||||
|
||||
void bootstrapApplication(App, appConfig)
|
||||
.catch((err) => console.error(err));`,
|
||||
hidden: true,
|
||||
},
|
||||
'/package.json': {
|
||||
code: JSON.stringify({
|
||||
dependencies: {
|
||||
'@angular/common': '^21.0.0',
|
||||
'@angular/compiler': '^21.0.0',
|
||||
'@angular/core': '^21.0.0',
|
||||
'@angular/forms': '^21.0.0',
|
||||
'@angular/platform-browser': '^21.0.0',
|
||||
'@angular/router': '^21.0.0',
|
||||
'zone.js': '0.16.1',
|
||||
'core-js': '3.48.0',
|
||||
rxjs: '7.4.0',
|
||||
},
|
||||
main: '/src/main.ts',
|
||||
devDependencies: {
|
||||
'@angular/build': '^21.0.3',
|
||||
'@angular/cli': '^21.0.3',
|
||||
'@angular/compiler-cli': '^21.0.0',
|
||||
},
|
||||
}),
|
||||
hidden: true,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Sandpack
|
||||
template="angular"
|
||||
:files="{
|
||||
...angularFiles,
|
||||
...props.files,
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,22 @@
|
||||
<script lang="ts" setup>
|
||||
import { type SandpackFiles } from 'sandpack-vue3';
|
||||
import styledCSS from '../../sandpack-default.css?raw';
|
||||
import Sandpack from './Sandpack.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
files: SandpackFiles;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Sandpack
|
||||
template="astro"
|
||||
:files="{
|
||||
...props.files,
|
||||
'/src/styles.css': {
|
||||
code: styledCSS,
|
||||
hidden: true,
|
||||
},
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,19 @@
|
||||
<script lang="ts" setup>
|
||||
import { SandpackFiles } from 'sandpack-vue3';
|
||||
import { preactFiles } from './preact/files';
|
||||
import Sandpack from './Sandpack.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
files: SandpackFiles;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Sandpack
|
||||
template="vanilla"
|
||||
:files="{
|
||||
...props.files,
|
||||
...preactFiles,
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,43 @@
|
||||
<script lang="ts" setup>
|
||||
import { SandpackFiles, SANDBOX_TEMPLATES } from 'sandpack-vue3';
|
||||
import Sandpack from './Sandpack.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
files: SandpackFiles;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Sandpack
|
||||
template="vite-solid"
|
||||
:files="{
|
||||
...props.files,
|
||||
...SANDBOX_TEMPLATES['vite-solid'].files,
|
||||
'/package.json': {
|
||||
code: JSON.stringify(
|
||||
{
|
||||
type: 'module',
|
||||
scripts: {
|
||||
dev: 'vite',
|
||||
build: 'tsc && vite build',
|
||||
serve: 'vite preview',
|
||||
},
|
||||
dependencies: {
|
||||
'solid-js': '^1.7.5',
|
||||
'lucide-solid': 'latest',
|
||||
},
|
||||
devDependencies: {
|
||||
vite: '4.2.2',
|
||||
typescript: '^5.0.4',
|
||||
'vite-plugin-solid': '^2.7.0',
|
||||
'esbuild-wasm': '^0.17.19',
|
||||
},
|
||||
main: '/index.tsx',
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
},
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,59 @@
|
||||
<script lang="ts" setup>
|
||||
import { type SandpackFiles } from 'sandpack-vue3';
|
||||
import styledCSS from '../../sandpack-default.css?raw';
|
||||
import Sandpack from './Sandpack.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
files: SandpackFiles;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Sandpack
|
||||
template="vite-svelte"
|
||||
:files="{
|
||||
...props.files,
|
||||
'/src/styles.css': {
|
||||
code: styledCSS,
|
||||
hidden: true,
|
||||
},
|
||||
'/package.json': {
|
||||
hidden: true,
|
||||
code: JSON.stringify(
|
||||
{
|
||||
type: 'module',
|
||||
scripts: {
|
||||
dev: 'vite',
|
||||
},
|
||||
dependencies: {
|
||||
svelte: '^4.2.19',
|
||||
'lucide-svelte': 'latest',
|
||||
},
|
||||
devDependencies: {
|
||||
'@sveltejs/vite-plugin-svelte': '2.1.1',
|
||||
vite: '4.2.2',
|
||||
'esbuild-wasm': '^0.17.19',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
},
|
||||
'/vite.config.js': {
|
||||
hidden: true,
|
||||
code: `import { defineConfig } from 'vite'
|
||||
import { svelte } from '@sveltejs/vite-plugin-svelte'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [svelte()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@lucide/svelte': 'lucide-svelte',
|
||||
},
|
||||
},
|
||||
})`,
|
||||
},
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,23 @@
|
||||
<script lang="ts" setup>
|
||||
import { ComponentPropsOptions, type FunctionalComponent } from 'vue';
|
||||
import { SandpackFiles, SANDBOX_TEMPLATES } from 'sandpack-vue3';
|
||||
import Sandpack from './Sandpack.vue';
|
||||
import styledCSS from '../../sandpack-default.css?raw';
|
||||
|
||||
const props = defineProps<{
|
||||
files: SandpackFiles;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Sandpack
|
||||
template="vue"
|
||||
:files="{
|
||||
...props.files,
|
||||
'/src/styles.css': {
|
||||
code: styledCSS,
|
||||
hidden: true,
|
||||
},
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,53 @@
|
||||
<script setup lang="ts">
|
||||
import { useMutationObserver, useScriptTag } from '@vueuse/core';
|
||||
import { useData } from 'vitepress';
|
||||
import { onMounted, useTemplateRef } from 'vue';
|
||||
|
||||
const { isDark } = useData();
|
||||
const el = useTemplateRef('el');
|
||||
|
||||
useScriptTag('https://snack.expo.dev/embed.js');
|
||||
|
||||
useMutationObserver(
|
||||
el,
|
||||
(mutations) => {
|
||||
const container = el.value;
|
||||
if (mutations[0]) {
|
||||
if ('ExpoSnack' in window) {
|
||||
window?.ExpoSnack?.remove(container);
|
||||
window?.ExpoSnack?.append(container);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
attributes: true,
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
const container = el.value;
|
||||
if ('ExpoSnack' in window) {
|
||||
window?.ExpoSnack?.append(container);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-bind="$attrs"
|
||||
class="snack-player"
|
||||
ref="el"
|
||||
:data-snack-theme="isDark ? 'dark' : 'light'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.snack-player {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
width: 100%;
|
||||
height: 635px;
|
||||
margin-bottom: 24px;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,23 @@
|
||||
import index from './index.js?raw';
|
||||
import packageJson from './package.json?raw';
|
||||
import indexHtml from './index.html?raw';
|
||||
import styles from './styles.css?raw';
|
||||
|
||||
export const preactFiles = {
|
||||
'index.js': {
|
||||
code: index,
|
||||
hidden: true,
|
||||
},
|
||||
'package.json': {
|
||||
code: packageJson,
|
||||
hidden: true,
|
||||
},
|
||||
'index.html': {
|
||||
code: indexHtml,
|
||||
hidden: true,
|
||||
},
|
||||
'styles.css': {
|
||||
code: styles,
|
||||
hidden: true,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Parcel Sandbox</title>
|
||||
<meta charset="UTF-8" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
<script src="index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,7 @@
|
||||
import { render, h } from 'preact';
|
||||
import App from './App';
|
||||
import './styles.css';
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
render(<App />, document.getElementById('root'));
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "npm run -s dev",
|
||||
"build": "preact build",
|
||||
"serve": "preact build && preact serve",
|
||||
"dev": "preact watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"preact-cli": "^1.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"preact": "10.11.3",
|
||||
"preact-compat": "3.19.0",
|
||||
"lucide-preact": "latest"
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -38,7 +38,7 @@ button {
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #F56565;
|
||||
background: #f56565;
|
||||
}
|
||||
|
||||
.app {
|
||||
@@ -0,0 +1,119 @@
|
||||
<script setup lang="tsx">
|
||||
import VPSidebarGroup from 'vitepress/dist/client/theme-default/components/VPSidebarGroup.vue';
|
||||
import sidebar, { guideSidebarTop } from '../../../sidebar';
|
||||
import { useData, useRouter } from 'vitepress';
|
||||
import Select from '../base/Select.vue';
|
||||
import { computed, ref, watch, watchEffect } from 'vue';
|
||||
import { link, route } from '~/.vitepress/data/iconNodes';
|
||||
import { useLocalStorage } from '@vueuse/core';
|
||||
|
||||
const { page } = useData();
|
||||
const router = useRouter();
|
||||
|
||||
const frameworks = [
|
||||
{ name: 'Vanilla', icon: '/framework-logos/js.svg', route: '/guide/lucide/' },
|
||||
{ name: 'React', icon: '/framework-logos/react.svg', route: '/guide/react/' },
|
||||
{ name: 'Vue', icon: '/framework-logos/vue.svg', route: '/guide/vue/' },
|
||||
{ name: 'Svelte', icon: '/framework-logos/svelte.svg', route: '/guide/svelte/' },
|
||||
{ name: 'Solid', icon: '/framework-logos/solid.svg', route: '/guide/solid/' },
|
||||
{ name: 'Angular', icon: '/framework-logos/angular.svg', route: '/guide/angular/' },
|
||||
{ name: 'Preact', icon: '/framework-logos/preact.svg', route: '/guide/preact/' },
|
||||
{
|
||||
name: 'React Native',
|
||||
icon: '/framework-logos/react-native.svg',
|
||||
route: '/guide/react-native/',
|
||||
},
|
||||
{ name: 'Astro', icon: '/framework-logos/astro-dark.svg', route: '/guide/astro/' },
|
||||
{ name: 'Static', icon: '/framework-logos/svg.svg', route: '/guide/static/' },
|
||||
];
|
||||
|
||||
const fallbackFramework = useLocalStorage('lucide-docs-fallback-framework', frameworks[1]);
|
||||
|
||||
const selected = computed(() => {
|
||||
const current = frameworks.find(({ route }) => {
|
||||
if (router.route.path?.startsWith?.('/guide')) {
|
||||
const [, , framework] = router.route.path.split('/');
|
||||
const [, frameWorkRoute] = route.split('/').filter(Boolean);
|
||||
|
||||
return framework === frameWorkRoute;
|
||||
}
|
||||
return router.route.path.split('/').slice(0, 3).join('/') === route;
|
||||
});
|
||||
|
||||
return current || fallbackFramework.value;
|
||||
});
|
||||
|
||||
function onSelectFramework(item: { name: string; icon: string; route: string }) {
|
||||
fallbackFramework.value = item;
|
||||
if (item.route !== router.route.path) {
|
||||
const likeRoute = router.route.path.replace(selected.value.route, item.route);
|
||||
|
||||
console.log(likeRoute);
|
||||
|
||||
const hasRoute = sidebar[item.route]?.some((section) =>
|
||||
section?.items?.some(({ link }) => link === likeRoute),
|
||||
);
|
||||
|
||||
if (hasRoute) {
|
||||
router.go(likeRoute);
|
||||
return;
|
||||
}
|
||||
|
||||
router.go(item.route);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VPSidebarGroup
|
||||
:items="guideSidebarTop"
|
||||
v-if="page?.relativePath?.startsWith?.('guide')"
|
||||
/>
|
||||
<div
|
||||
class="framework-select"
|
||||
v-if="page?.relativePath?.startsWith?.('guide')"
|
||||
>
|
||||
<label for="framework-select">Framework</label>
|
||||
<Select
|
||||
id="framework-select"
|
||||
:items="frameworks"
|
||||
@update:model-value="onSelectFramework"
|
||||
v-model="selected"
|
||||
/>
|
||||
</div>
|
||||
<VPSidebarGroup
|
||||
:key="selected.route"
|
||||
v-if="
|
||||
page?.relativePath?.startsWith?.('guide') &&
|
||||
!page?.relativePath?.startsWith?.(selected.route.substring(1))
|
||||
"
|
||||
:items="sidebar[selected.route]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.framework-select {
|
||||
font-size: 12px;
|
||||
transition:
|
||||
border-color 0.5s,
|
||||
background-color 0.5s ease;
|
||||
margin-bottom: 10px;
|
||||
position: sticky;
|
||||
top: -0.5px;
|
||||
z-index: 10;
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
padding-top: 10px;
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
label {
|
||||
color: var(--vp-c-text-1);
|
||||
padding: 4px 0;
|
||||
line-height: 24px;
|
||||
font-size: 14px;
|
||||
transition: color 0.25s;
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,51 @@
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vitepress';
|
||||
import Badge from '../base/Badge.vue';
|
||||
import HomeContainer from './HomeContainer.vue';
|
||||
import { data } from './HomeHeroIconsCard.data'
|
||||
import FakeInput from '../base/FakeInput.vue'
|
||||
import { nextTick, provide } from 'vue'
|
||||
import useSearchShortcut from '../../utils/useSearchShortcut';
|
||||
|
||||
const { go } = useRouter()
|
||||
|
||||
const { shortcutText: kbdSearchShortcut } = useSearchShortcut(() => {
|
||||
go('/icons/?focus')
|
||||
})
|
||||
|
||||
const enableTransitions = () =>
|
||||
'startViewTransition' in document &&
|
||||
window.matchMedia('(prefers-reduced-motion: no-preference)').matches
|
||||
|
||||
|
||||
async function handleClick(event: MouseEvent) {
|
||||
if (!enableTransitions()) {
|
||||
go('/icons/?focus')
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
await document.startViewTransition(async () => {
|
||||
await go('/icons/?focus');
|
||||
await nextTick()
|
||||
}).ready
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<FakeInput
|
||||
@click="go('/icons/?focus')"
|
||||
:shortcut="kbdSearchShortcut"
|
||||
class="search-box"
|
||||
>
|
||||
Search {{ data.iconsCount }} icons...
|
||||
</FakeInput>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.search-box {
|
||||
view-transition-name: icons-search-box;
|
||||
width: 100%;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,42 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import Badge from '../base/Badge.vue';
|
||||
import HomeContainer from './HomeContainer.vue';
|
||||
import { data } from './HomeHeroBefore.data'
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<HomeContainer class="container">
|
||||
<Badge
|
||||
:href="`https://github.com/lucide-icons/lucide/releases/tag/${data.version}`"
|
||||
>v{{ data.version }}</Badge>
|
||||
</HomeContainer>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
margin-block: 0;;
|
||||
margin-top: 37px;
|
||||
margin-bottom: -96px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.badge {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.container {
|
||||
margin-bottom: -131px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.container {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.badge {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,678 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, shallowRef } from 'vue';
|
||||
import { data } from './HomeHeroIconsCard.data';
|
||||
import { useRouter } from 'vitepress';
|
||||
import { motion, Variants, useScroll, useTransform } from 'motion-v';
|
||||
import LucideIcon from '../base/LucideIcon.vue';
|
||||
|
||||
const emit = defineEmits(['animation-complete']);
|
||||
|
||||
const MotionLucideIcon = motion.create(LucideIcon);
|
||||
|
||||
const preventFocusProps = {
|
||||
tabindex: '-1',
|
||||
focusable: 'false',
|
||||
};
|
||||
|
||||
const COLUMNS = 8;
|
||||
const SIZE = 2;
|
||||
const GAP = 1;
|
||||
|
||||
const { scrollYProgress } = useScroll();
|
||||
const opacity = useTransform(() => 1 - scrollYProgress.get() * 8);
|
||||
|
||||
const icons = ref(
|
||||
data.icons.slice(0, 64).map((icon, index) => {
|
||||
const x = index % COLUMNS;
|
||||
const y = Math.floor(index / COLUMNS);
|
||||
|
||||
if (index === 0) {
|
||||
return {
|
||||
...icon,
|
||||
x: 9999,
|
||||
y: 9999,
|
||||
opacity: 0,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...icon,
|
||||
x: x * (SIZE + GAP) + 0.5,
|
||||
y: y * (SIZE + GAP) + 0.5,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
const { go } = useRouter();
|
||||
const intervalTime = shallowRef();
|
||||
const showHandles = ref(true);
|
||||
const scaleDownVariants: Variants = {
|
||||
fullSize: {
|
||||
scale: 1,
|
||||
},
|
||||
riseUp: {
|
||||
x: 0.5,
|
||||
y: -0.5,
|
||||
animationName: 'riseUp',
|
||||
scale: 1,
|
||||
transition: {
|
||||
delay: 0.5,
|
||||
duration: 1.5,
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
},
|
||||
},
|
||||
small: {
|
||||
x: -10.5,
|
||||
y: -10.5,
|
||||
scale: 0.1,
|
||||
animationName: 'small',
|
||||
transition: {
|
||||
delay: 1,
|
||||
duration: 1,
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
},
|
||||
},
|
||||
};
|
||||
const scaleDownAnimation = ref('fullSize');
|
||||
|
||||
const iconGridAnimation = ref('initial');
|
||||
|
||||
const drawAnimation = ref('visible');
|
||||
const draw: Variants = {
|
||||
hidden: { pathLength: 0, opacity: 0 },
|
||||
visible: {
|
||||
animationName: 'visible',
|
||||
pathLength: 1.02,
|
||||
opacity: 1,
|
||||
transition: {
|
||||
pathLength: { delay: 2.4, type: 'spring', duration: 2.8, bounce: 0 },
|
||||
opacity: { delay: 2.4, duration: 0.1 },
|
||||
},
|
||||
},
|
||||
exit: (path) => ({
|
||||
animationName: 'exit',
|
||||
stroke: path ? 'var(--vp-c-text-1)' : 'var(--vp-c-brand)',
|
||||
pathLength: 1.02,
|
||||
opacity: 1,
|
||||
transition: {
|
||||
duration: 0.8,
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
const onAnimationComplete = (item) => {
|
||||
if (item.animationName === 'visible') {
|
||||
drawAnimation.value = 'exit';
|
||||
return;
|
||||
}
|
||||
if (item.animationName === 'exit') {
|
||||
showHandles.value = false;
|
||||
scaleDownAnimation.value = 'small';
|
||||
}
|
||||
if (item.animationName === 'small') {
|
||||
iconGridAnimation.value = 'showIcons';
|
||||
}
|
||||
if (item.animationName === 'riseUp') {
|
||||
scaleDownAnimation.value = 'small';
|
||||
}
|
||||
if (item.animationName === 'showIcons') {
|
||||
shrinkIconAnimation.value = 'shrinkIcons';
|
||||
}
|
||||
if (item.animationName === 'shrinkIcons') {
|
||||
iconGridAnimation.value = 'initial';
|
||||
|
||||
setTimeout(() => {
|
||||
emit('animation-complete');
|
||||
}, 2800);
|
||||
}
|
||||
};
|
||||
|
||||
const randomIndex = ref(Math.floor(Math.random() * 64));
|
||||
|
||||
const iconAnimationVariants = {
|
||||
initial: {
|
||||
animationName: 'end',
|
||||
opacity: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
transition: { duration: 1, delay: 1, ease: 'easeInOut' },
|
||||
},
|
||||
showIcons: (index) => ({
|
||||
animationName: 'showIcons',
|
||||
opacity: [0, 1, 1],
|
||||
x: [0.5, 0, 0],
|
||||
y: [-0.5, 0, 0],
|
||||
strokeWidth: randomIndex.value === index ? [0, 2, 2] : undefined,
|
||||
transition: { delay: index * 0.023, duration: 1.6, ease: 'easeInOut' },
|
||||
}),
|
||||
};
|
||||
|
||||
const shrinkIconAnimation = ref('initial');
|
||||
|
||||
const shrinkIconVariants = {
|
||||
initial: { strokeWidth: 2 },
|
||||
shrinkIcons: (index) => ({
|
||||
animationName: 'shrinkIcons',
|
||||
opacity: 1,
|
||||
strokeWidth: 0,
|
||||
transition: { delay: 1.8, duration: 1.5, ease: 'easeInOut' },
|
||||
}),
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="home-hero-animation-container"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<div
|
||||
class="home-hero-animation"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<motion.svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="-12 -12 48 48"
|
||||
fill="none"
|
||||
overflow="auto"
|
||||
stroke="currentColor"
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="hero-background"
|
||||
:style="{ opacity }"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<g
|
||||
class="svg-preview-grid-group"
|
||||
stroke-linecap="butt"
|
||||
stroke-width="0.1"
|
||||
stroke="#777"
|
||||
mask="url(#svg-preview-bounding-box-mask)"
|
||||
stroke-opacity="0.3"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<path
|
||||
stroke-dasharray="0 0.1 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0 0.15"
|
||||
stroke-width="0.1"
|
||||
d="M1 0.1v23.8M2 0.1v23.8M4 0.1v23.8M5 0.1v23.8M7 0.1v23.8M8 0.1v23.8M10 0.1v23.8M11 0.1v23.8M13 0.1v23.8M14 0.1v23.8M16 0.1v23.8M17 0.1v23.8M19 0.1v23.8M20 0.1v23.8M22 0.1v23.8M23 0.1v23.8M0.1 1h23.8M0.1 2h23.8M0.1 4h23.8M0.1 5h23.8M0.1 7h23.8M0.1 8h23.8M0.1 10h23.8M0.1 11h23.8M0.1 13h23.8M0.1 14h23.8M0.1 16h23.8M0.1 17h23.8M0.1 19h23.8M0.1 20h23.8M0.1 22h23.8M0.1 23h23.8"
|
||||
/>
|
||||
|
||||
<path
|
||||
d="M3 0.1v23.8M6 0.1v23.8M9 0.1v23.8M12 0.1v23.8M15 0.1v23.8M18 0.1v23.8M21 0.1v23.8M0.1 3h23.8M0.1 6h23.8M0.1 9h23.8M0.1 12h23.8M0.1 15h23.8M0.1 18h23.8M0.1 21h23.8"
|
||||
/>
|
||||
</g>
|
||||
|
||||
<motion.g
|
||||
initial="initial"
|
||||
:variants="shrinkIconVariants"
|
||||
:animate="shrinkIconAnimation"
|
||||
@animation-complete="onAnimationComplete"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<MotionLucideIcon
|
||||
v-for="(icon, index) in icons"
|
||||
size="2"
|
||||
initial="initial"
|
||||
:key="icon.name"
|
||||
:variants="iconAnimationVariants"
|
||||
:animate="iconGridAnimation"
|
||||
:custom="index"
|
||||
strokeWidth="inherit"
|
||||
v-bind="icon"
|
||||
@animation-complete="onAnimationComplete"
|
||||
tabindex="-1"
|
||||
focusable="false"
|
||||
/>
|
||||
<motion.g
|
||||
:variants="scaleDownVariants"
|
||||
:animate="scaleDownAnimation"
|
||||
initial="hidden"
|
||||
@animation-complete="onAnimationComplete"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<motion.path
|
||||
d="M14 12C14 9.79086 12.2091 8 10 8C7.79086 8 6 9.79086 6 12C6 16.4183 9.58172 20 14 20C18.4183 20 22 16.4183 22 12C22 8.446 20.455 5.25285 18 3.05557"
|
||||
:style="{ stroke: 'var(--vp-c-gray-1)' }"
|
||||
:animate="drawAnimation"
|
||||
initial="hidden"
|
||||
:variants="draw"
|
||||
:custom="1"
|
||||
@animation-complete="onAnimationComplete"
|
||||
v-bind="preventFocusProps"
|
||||
/>
|
||||
|
||||
<motion.path
|
||||
d="M10 12C10 14.2091 11.7909 16 14 16C16.2091 16 18 14.2091 18 12C18 7.58172 14.4183 4 10 4C5.58172 4 2 7.58172 2 12C2 15.5841 3.57127 18.8012 6.06253 21"
|
||||
:style="{ stroke: 'var(--vp-c-gray-1)' }"
|
||||
:animate="drawAnimation"
|
||||
initial="hidden"
|
||||
:variants="draw"
|
||||
:custom="0"
|
||||
v-bind="preventFocusProps"
|
||||
/>
|
||||
</motion.g>
|
||||
</motion.g>
|
||||
<motion.g
|
||||
class="svg-preview-control-path-marker-group"
|
||||
stroke="#fff"
|
||||
stroke-width="0.125"
|
||||
:initial="{ opacity: 1 }"
|
||||
:animate="showHandles ? { opacity: 1 } : { opacity: 0 }"
|
||||
:transition="{ delay: 0, duration: 0.2 }"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<motion.path
|
||||
d="M14 12C14 9.79086 12.2091 8 10 8C7.79086 8 6 9.79086 6 12C6 16.4183 9.58172 20 14 20C18.4183 20 22 16.4183 22 12C22 8.446 20.455 5.25285 18 3.05557"
|
||||
:initial="{ opacity: 0 }"
|
||||
:animate="{ opacity: 1 }"
|
||||
:transition="{ delay: 1.6, duration: 1.5 }"
|
||||
v-bind="preventFocusProps"
|
||||
/>
|
||||
<motion.path
|
||||
d="M10 12C10 14.2091 11.7909 16 14 16C16.2091 16 18 14.2091 18 12C18 7.58172 14.4183 4 10 4C5.58172 4 2 7.58172 2 12C2 15.5841 3.57127 18.8012 6.06253 21"
|
||||
:initial="{ opacity: 0 }"
|
||||
:animate="{ opacity: 1 }"
|
||||
:transition="{ delay: 1.6, duration: 1.5 }"
|
||||
v-bind="preventFocusProps"
|
||||
/>
|
||||
<motion.g
|
||||
:initial="{ opacity: 0 }"
|
||||
:animate="{ opacity: 1 }"
|
||||
:transition="{ delay: 0.2, duration: 0.3 }"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<path
|
||||
d="M14 12h.01M10 8h.01M10 8h.01M6 12h.01M6 12h.01M14 20h.01M14 20h.01M22 12h.01M22 12h.01M18 3.05557h.01M10 12h.01M14 16h.01M14 16h.01M18 12h.01M18 12h.01M10 4h.01M10 4h.01M2 12h.01M2 12h.01M6.06253 21h.01"
|
||||
>
|
||||
</path>
|
||||
</motion.g>
|
||||
|
||||
<motion.circle
|
||||
:initial="{ opacity: 0, scale: 0.2 }"
|
||||
:animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0, duration: 0.8 }"
|
||||
cx="14"
|
||||
cy="12"
|
||||
r="0.5"
|
||||
v-bind="preventFocusProps"
|
||||
/>
|
||||
<motion.circle
|
||||
:initial="{ opacity: 0, scale: 0.2 }"
|
||||
:animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0, duration: 0.8 }"
|
||||
cx="14"
|
||||
cy="12"
|
||||
r="0.5"
|
||||
v-bind="preventFocusProps"
|
||||
/>
|
||||
<motion.circle
|
||||
:initial="{ opacity: 0, scale: 0.2 }"
|
||||
:animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0, duration: 0.8 }"
|
||||
cx="18"
|
||||
cy="3.05557"
|
||||
r="0.5"
|
||||
v-bind="preventFocusProps"
|
||||
/>
|
||||
<motion.circle
|
||||
:initial="{ opacity: 0, scale: 0.2 }"
|
||||
:animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0, duration: 0.8 }"
|
||||
cx="10"
|
||||
cy="12"
|
||||
r="0.5"
|
||||
v-bind="preventFocusProps"
|
||||
/>
|
||||
<motion.circle
|
||||
:initial="{ opacity: 0, scale: 0.2 }"
|
||||
:animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0, duration: 0.8 }"
|
||||
cx="6.06253"
|
||||
cy="21"
|
||||
r="0.5"
|
||||
v-bind="preventFocusProps"
|
||||
/>
|
||||
</motion.g>
|
||||
<motion.g
|
||||
class="svg-preview-handles-group"
|
||||
stroke-width="0.12"
|
||||
stroke="#FFF"
|
||||
stroke-opacity="0.3"
|
||||
:initial="{ opacity: 1 }"
|
||||
:animate="showHandles ? { opacity: 1 } : { opacity: 0 }"
|
||||
:transition="{ delay: 0, duration: 0.6 }"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<motion.g
|
||||
:initial="{ opacity: 0, scale: 0.2 }"
|
||||
:animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0.2, duration: 0.3 }"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<path d="M14 12 14 9.79086"></path>
|
||||
<circle
|
||||
cy="9.79086"
|
||||
cx="14"
|
||||
r="0.25"
|
||||
></circle>
|
||||
</motion.g>
|
||||
|
||||
<motion.g
|
||||
:initial="{ opacity: 0, scale: 0.2 }"
|
||||
:animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0.4, duration: 0.3 }"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<path d="M10 8 12.2091 8"></path>
|
||||
<circle
|
||||
cy="8"
|
||||
cx="12.2091"
|
||||
r="0.25"
|
||||
></circle>
|
||||
<path d="M10 8 7.79086 8"></path>
|
||||
<circle
|
||||
cy="8"
|
||||
cx="7.79086"
|
||||
r="0.25"
|
||||
></circle>
|
||||
</motion.g>
|
||||
|
||||
<motion.g
|
||||
:initial="{ opacity: 0, scale: 0.2 }"
|
||||
:animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0.6, duration: 0.3 }"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<path d="M6 12 6 9.79086"></path>
|
||||
<circle
|
||||
cy="9.79086"
|
||||
cx="6"
|
||||
r="0.25"
|
||||
></circle>
|
||||
<path d="M6 12 6 16.4183"></path>
|
||||
<circle
|
||||
cy="16.4183"
|
||||
cx="6"
|
||||
r="0.25"
|
||||
></circle>
|
||||
</motion.g>
|
||||
|
||||
<motion.g
|
||||
:initial="{ opacity: 0, scale: 0.2 }"
|
||||
:animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0.8, duration: 0.3 }"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<path d="M14 20 9.58172 20"></path>
|
||||
<circle
|
||||
cy="20"
|
||||
cx="9.58172"
|
||||
r="0.25"
|
||||
></circle>
|
||||
<path d="M14 20 18.4183 20"></path>
|
||||
<circle
|
||||
cy="20"
|
||||
cx="18.4183"
|
||||
r="0.25"
|
||||
></circle>
|
||||
</motion.g>
|
||||
|
||||
<motion.g
|
||||
:initial="{ opacity: 0, scale: 0.2 }"
|
||||
:animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 1, duration: 0.3 }"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<path d="M22 12 22 16.4183"></path>
|
||||
<circle
|
||||
cy="16.4183"
|
||||
cx="22"
|
||||
r="0.25"
|
||||
></circle>
|
||||
<path d="M22 12 22 8.446"></path>
|
||||
<circle
|
||||
cy="8.446"
|
||||
cx="22"
|
||||
r="0.25"
|
||||
></circle>
|
||||
</motion.g>
|
||||
<motion.g
|
||||
:initial="{ opacity: 0, scale: 0.2 }"
|
||||
:animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 1.2, duration: 0.3 }"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<path d="M18 3.05557 20.455 5.25285"></path>
|
||||
<circle
|
||||
cy="5.25285"
|
||||
cx="20.455"
|
||||
r="0.25"
|
||||
></circle>
|
||||
</motion.g>
|
||||
|
||||
<motion.g
|
||||
:initial="{ opacity: 0, scale: 0.2 }"
|
||||
:animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0.2, duration: 0.3 }"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<path d="M10 12 10 14.2091"></path>
|
||||
<circle
|
||||
cy="14.2091"
|
||||
cx="10"
|
||||
r="0.25"
|
||||
></circle>
|
||||
</motion.g>
|
||||
<motion.g
|
||||
:initial="{ opacity: 0, scale: 0.2 }"
|
||||
:animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0.4, duration: 0.3 }"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<path d="M14 16 11.7909 16"></path>
|
||||
<circle
|
||||
cy="16"
|
||||
cx="11.7909"
|
||||
r="0.25"
|
||||
></circle>
|
||||
<path d="M14 16 16.2091 16"></path>
|
||||
<circle
|
||||
cy="16"
|
||||
cx="16.2091"
|
||||
r="0.25"
|
||||
></circle>
|
||||
</motion.g>
|
||||
|
||||
<motion.g
|
||||
:initial="{ opacity: 0, scale: 0.2 }"
|
||||
:animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0.6, duration: 0.3 }"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<path d="M18 12 18 14.2091"></path>
|
||||
<circle
|
||||
cy="14.2091"
|
||||
cx="18"
|
||||
r="0.25"
|
||||
></circle>
|
||||
<path d="M18 12 18 7.58172"></path>
|
||||
<circle
|
||||
cy="7.58172"
|
||||
cx="18"
|
||||
r="0.25"
|
||||
></circle>
|
||||
</motion.g>
|
||||
|
||||
<motion.g
|
||||
:initial="{ opacity: 0, scale: 0.2 }"
|
||||
:animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0.8, duration: 0.3 }"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<path d="M10 4 14.4183 4"></path>
|
||||
<circle
|
||||
cy="4"
|
||||
cx="14.4183"
|
||||
r="0.25"
|
||||
></circle>
|
||||
<path d="M10 4 5.58172 4"></path>
|
||||
<circle
|
||||
cy="4"
|
||||
cx="5.58172"
|
||||
r="0.25"
|
||||
></circle>
|
||||
</motion.g>
|
||||
<motion.g
|
||||
:initial="{ opacity: 0, scale: 0.2 }"
|
||||
:animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 1, duration: 0.3 }"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<path d="M2 12 2 7.58172"></path>
|
||||
<circle
|
||||
cy="7.58172"
|
||||
cx="2"
|
||||
r="0.25"
|
||||
></circle>
|
||||
<path d="M2 12 2 15.5841"></path>
|
||||
<circle
|
||||
cy="15.5841"
|
||||
cx="2"
|
||||
r="0.25"
|
||||
></circle>
|
||||
</motion.g>
|
||||
<motion.g
|
||||
:initial="{ opacity: 0, scale: 0.2 }"
|
||||
:animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 1.2, duration: 0.3 }"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<path d="M6.06253 21 3.57127 18.8012"></path>
|
||||
<circle
|
||||
cy="18.8012"
|
||||
cx="3.57127"
|
||||
r="0.25"
|
||||
></circle>
|
||||
</motion.g>
|
||||
</motion.g>
|
||||
</motion.svg>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.home-hero-animation-container {
|
||||
margin: -48px -24px 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.home-hero-animation {
|
||||
height: 250px;
|
||||
width: 396px;
|
||||
overflow: hidden;
|
||||
margin: auto;
|
||||
margin-left: calc(((396px - 100vw) / 2) * -1);
|
||||
}
|
||||
|
||||
@media (min-width: 396px) {
|
||||
.home-hero-animation {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-background {
|
||||
transform: rotateX(-51deg) rotateZ(-43deg);
|
||||
transform-style: preserve-3d;
|
||||
will-change: transform, opacity;
|
||||
position: relative;
|
||||
top: -155px;
|
||||
left: -82px;
|
||||
width: 560px;
|
||||
height: 560px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.hero-background {
|
||||
width: 680px;
|
||||
height: 680px;
|
||||
|
||||
left: -100px;
|
||||
top: -188px;
|
||||
}
|
||||
|
||||
.home-hero-animation {
|
||||
height: 305px;
|
||||
width: 480px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.hero-background {
|
||||
width: 760px;
|
||||
height: 760px;
|
||||
|
||||
left: -110px;
|
||||
top: -200px;
|
||||
}
|
||||
|
||||
.home-hero-animation {
|
||||
height: 360px;
|
||||
width: 540px;
|
||||
}
|
||||
|
||||
.home-hero-animation-container {
|
||||
margin-top: -60px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.hero-background {
|
||||
top: -20vw;
|
||||
right: 20vw;
|
||||
width: 80vw;
|
||||
height: 80vw;
|
||||
}
|
||||
|
||||
.home-hero-animation {
|
||||
height: 415px;
|
||||
width: 620px;
|
||||
}
|
||||
|
||||
.home-hero-animation-container {
|
||||
margin: -48px -48px 0 -64px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1160px) {
|
||||
.home-hero-animation-container {
|
||||
margin-right: -64px;
|
||||
margin-bottom: -180px;
|
||||
}
|
||||
|
||||
.home-hero-animation {
|
||||
width: auto;
|
||||
height: calc(((1152px / 2)));
|
||||
top: -20px;
|
||||
}
|
||||
|
||||
.hero-background {
|
||||
top: -20vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.home-hero-animation-container {
|
||||
margin-right: -420px;
|
||||
margin-top: -64px;
|
||||
/* margin-right: calc(((((100vw - 1152px) / 2)) * -1) + 24px); */
|
||||
margin-left: -128px;
|
||||
}
|
||||
|
||||
.hero-background {
|
||||
width: 1024px;
|
||||
height: 1024px;
|
||||
top: -280px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,16 +1,97 @@
|
||||
import iconNodes from '../../../data/iconNodes';
|
||||
|
||||
const getRandomItem = <Item>(items: Item[]): Item =>
|
||||
items[Math.floor(Math.random() * items.length)];
|
||||
const pickIcons = [
|
||||
'bot',
|
||||
'camera',
|
||||
'car',
|
||||
'cloud',
|
||||
'code',
|
||||
'coffee',
|
||||
'compass',
|
||||
'cpu',
|
||||
'disc',
|
||||
'droplet',
|
||||
'eye',
|
||||
'bug',
|
||||
'house',
|
||||
'heart-pulse',
|
||||
'clapperboard',
|
||||
'tool-case',
|
||||
'printer',
|
||||
'album',
|
||||
'pencil-ruler',
|
||||
'calendar',
|
||||
'brain',
|
||||
'audio-waveform',
|
||||
'bandage',
|
||||
'plane-takeoff',
|
||||
'ferris-wheel',
|
||||
'router',
|
||||
'file-box',
|
||||
'folder-tree',
|
||||
'banknote',
|
||||
'cake',
|
||||
'rocket',
|
||||
'picture-in-picture-2',
|
||||
'database',
|
||||
'messages-square',
|
||||
'map-pinned',
|
||||
'megaphone',
|
||||
'moon',
|
||||
'music',
|
||||
'phone',
|
||||
'power-off',
|
||||
'radio',
|
||||
'refresh-ccw',
|
||||
'repeat',
|
||||
'rss',
|
||||
'shield',
|
||||
'shopping-cart',
|
||||
'speakerphone',
|
||||
'star',
|
||||
'sun',
|
||||
'terminal',
|
||||
'thermometer',
|
||||
'thumbs-up',
|
||||
'trash-2',
|
||||
'umbrella',
|
||||
'watch',
|
||||
'sparkles',
|
||||
'wifi',
|
||||
'tent-tree',
|
||||
'traffic-cone',
|
||||
'tree-deciduous',
|
||||
'trophy',
|
||||
'tv',
|
||||
'windmill',
|
||||
'wrench',
|
||||
'satellite',
|
||||
'stethoscope',
|
||||
];
|
||||
|
||||
export default {
|
||||
async load() {
|
||||
const icons = Object.entries(iconNodes).map(([name, iconNode]) => ({ name, iconNode }));
|
||||
|
||||
const randomIcons = Array.from({ length: 200 }, () => getRandomItem(icons));
|
||||
const selectedIcons = icons.filter(({ name }) => pickIcons.includes(name)).reverse();
|
||||
|
||||
const hasDuplicates =
|
||||
new Set(selectedIcons.map(({ name }) => name)).size !== selectedIcons.length;
|
||||
|
||||
if (hasDuplicates) {
|
||||
throw new Error(
|
||||
`Duplicate icons found in the selected icons. Please check the pickIcons array and the iconNodes data for duplicates.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (selectedIcons.length !== 64) {
|
||||
throw new Error(
|
||||
`Expected to find 64 icons, but found ${selectedIcons.length}. Please check the pickIcons array and the iconNodes data.`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
icons: randomIcons,
|
||||
icons: selectedIcons,
|
||||
iconsCount: icons.length,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -1,162 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, shallowRef, onBeforeUnmount} from 'vue';
|
||||
import { data } from './HomeHeroIconsCard.data'
|
||||
import LucideIcon from '../base/LucideIcon.vue'
|
||||
import { useRouter } from 'vitepress';
|
||||
import { random } from 'lodash-es'
|
||||
import FakeInput from '../base/FakeInput.vue'
|
||||
import useSearchShortcut from '../../utils/useSearchShortcut'
|
||||
|
||||
const { go } = useRouter()
|
||||
const intervalTime = shallowRef()
|
||||
|
||||
const { shortcutText: kbdSearchShortcut } = useSearchShortcut(() => {
|
||||
go('/icons/?focus')
|
||||
})
|
||||
|
||||
const getInitialItems = () => data.icons.slice(0, 48)
|
||||
const items = ref(getInitialItems())
|
||||
let id = items.value.length + 1
|
||||
|
||||
function getRandomNewIcon() {
|
||||
const randomIndex = random(0, 200)
|
||||
const newRandomIcon = data.icons[randomIndex]
|
||||
|
||||
if (items.value.some((item) => item.name === newRandomIcon.name)) {
|
||||
return getRandomNewIcon()
|
||||
}
|
||||
|
||||
return newRandomIcon
|
||||
}
|
||||
|
||||
function insert() {
|
||||
const replaceIndex = random(0, 48)
|
||||
const newIcon = getRandomNewIcon()
|
||||
|
||||
items.value[replaceIndex] = newIcon
|
||||
}
|
||||
|
||||
function startInterval() {
|
||||
intervalTime.value = setInterval(() => {
|
||||
insert()
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
// TODO: Try maybe something else for better pref performance
|
||||
onMounted(() => {
|
||||
window.addEventListener('mousemove', startInterval, { once: true })
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(intervalTime.value)
|
||||
})
|
||||
import { ref } from 'vue';
|
||||
import HomeHeroIconsAnimation from './HomeHeroIconsAnimation.vue'
|
||||
|
||||
const animationRun = ref(1)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card-wrapper">
|
||||
<div class="icons-card">
|
||||
<div class="card-grid">
|
||||
<TransitionGroup name="list" mode="out-in">
|
||||
<div
|
||||
v-for="icon in items"
|
||||
:key="icon.name"
|
||||
class="random-icon"
|
||||
>
|
||||
<LucideIcon
|
||||
v-bind="icon"
|
||||
/>
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
<FakeInput
|
||||
@click="go('/icons/?focus')"
|
||||
:shortcut="kbdSearchShortcut"
|
||||
class="search-box"
|
||||
>
|
||||
Search {{ data.iconsCount }} icons...
|
||||
</FakeInput>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<HomeHeroIconsAnimation
|
||||
:key="animationRun"
|
||||
@animation-complete="animationRun++"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card-wrapper {
|
||||
margin-left: auto;
|
||||
margin-bottom: auto;
|
||||
margin-top: 48px;
|
||||
}
|
||||
.icons-card {
|
||||
background: var(--vp-c-bg-alt);
|
||||
padding: 24px;
|
||||
border-radius: 8px;
|
||||
width: 100%;
|
||||
height:100%;
|
||||
max-height: 220px;
|
||||
max-width: 560px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.card-grid {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(36px, 1fr));
|
||||
grid-template-rows: repeat(auto-fill, minmax(36px, 1fr));
|
||||
width: 100%;
|
||||
height:100%;
|
||||
max-height: 168px;
|
||||
max-width: 512px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.list-enter-active {
|
||||
transition: all 0.5s cubic-bezier(.85,.85,.25,1.1);
|
||||
}
|
||||
|
||||
.list-enter-from,
|
||||
.list-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0.01);
|
||||
}
|
||||
|
||||
.list-leave-active {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: -64px;
|
||||
}
|
||||
|
||||
.random-icon {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.search-box {
|
||||
top: unset;
|
||||
bottom: -24px;
|
||||
left: -24px;
|
||||
|
||||
box-shadow: var(--vp-shadow-3);
|
||||
background: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
.dark .search-box {
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
.card-wrapper {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
<script setup lang="ts">
|
||||
import Badge from '../base/Badge.vue';
|
||||
import { moveRight } from '../../../data/iconNodes';
|
||||
import Icon from '@lucide/vue/src/Icon';
|
||||
import { data } from './HomeHeroInfoBefore.data';
|
||||
const rocketDuoTone = [
|
||||
[
|
||||
'path',
|
||||
{
|
||||
d: 'M8 18a2 2 0 0 0-2-2c-2.53-.078-3.247 3.605-3.5 5.5 1.895-.253 5.578-.97 5.5-3.5',
|
||||
fill: 'currentColor',
|
||||
opacity: 0.5,
|
||||
},
|
||||
],
|
||||
[
|
||||
'path',
|
||||
{
|
||||
d: 'M22 2c0 2.72-.78 7.5-6 11 0 0 1.08 3.38 0 5-.969 1.448-4 2-4 2v-5l-3-3H4s.553-3.033 2-4c1.616-1.077 4.982.043 5 .05A12.88 12.88 0 0 1 22 2m-6 3a3 3 0 1 0 0 6 3 3 0 0 0 0-6',
|
||||
fill: 'currentColor',
|
||||
'fill-rule': 'evenodd',
|
||||
'clip-rule': 'evenodd',
|
||||
},
|
||||
],
|
||||
] as const;
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="info">
|
||||
<Badge :href="`https://github.com/lucide-icons/lucide/releases/tag/${data.version}`">
|
||||
v{{ data.version }}
|
||||
</Badge>
|
||||
<Badge
|
||||
class="badge-special confetti-button animate"
|
||||
href="/guide/version-1"
|
||||
>
|
||||
<Icon :iconNode="rocketDuoTone" />
|
||||
Version 1
|
||||
<Icon
|
||||
:iconNode="moveRight"
|
||||
class=""
|
||||
/>
|
||||
</Badge>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.info {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.info {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
gap: 8px;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.badge-special {
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-brand-1);
|
||||
text-underline-offset: 2px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.badge-special::before,
|
||||
.badge-special::after {
|
||||
animation-delay: 0.5s !important;
|
||||
animation-duration: 5s !important;
|
||||
animation-iteration-count: infinite !important;
|
||||
animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1) !important;
|
||||
transform: translateX(32px);
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
@import '../icons/confetti.css';
|
||||
</style>
|
||||
@@ -6,47 +6,56 @@ export default {
|
||||
name: 'lucide',
|
||||
logo: '/framework-logos/js.svg',
|
||||
label: 'Lucide documentation for JavaScript',
|
||||
path: '/guide/lucide/',
|
||||
},
|
||||
{
|
||||
name: 'lucide-react',
|
||||
logo: '/framework-logos/react.svg',
|
||||
label: 'Lucide documentation for React',
|
||||
path: '/guide/react/',
|
||||
},
|
||||
{
|
||||
name: 'lucide-vue',
|
||||
logo: '/framework-logos/vue.svg',
|
||||
label: 'Lucide documentation for Vue',
|
||||
path: '/guide/vue/',
|
||||
},
|
||||
{
|
||||
name: 'lucide-svelte',
|
||||
logo: '/framework-logos/svelte.svg',
|
||||
label: 'Lucide documentation for Svelte',
|
||||
path: '/guide/svelte/',
|
||||
},
|
||||
{
|
||||
name: 'lucide-solid',
|
||||
logo: '/framework-logos/solid.svg',
|
||||
label: 'Lucide documentation for Solid',
|
||||
path: '/guide/solid/',
|
||||
},
|
||||
{
|
||||
name: 'lucide-preact',
|
||||
logo: '/framework-logos/preact.svg',
|
||||
label: 'Lucide documentation for Preact',
|
||||
path: '/guide/preact/',
|
||||
},
|
||||
{
|
||||
name: 'angular',
|
||||
logo: '/framework-logos/angular.svg',
|
||||
label: 'Lucide documentation for Angular',
|
||||
path: '/guide/angular/',
|
||||
},
|
||||
{
|
||||
name: 'lucide-astro',
|
||||
logo: '/framework-logos/astro.svg',
|
||||
logoDark: '/framework-logos/astro-dark.svg',
|
||||
label: 'Lucide documentation for Astro',
|
||||
path: '/guide/astro/',
|
||||
},
|
||||
{
|
||||
name: 'lucide-react-native',
|
||||
logo: '/framework-logos/react-native.svg',
|
||||
label: 'Lucide documentation for React Native',
|
||||
path: '/guide/react-native/',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useSessionStorage } from '@vueuse/core';
|
||||
import IconButton from '../base/IconButton.vue';
|
||||
import VPDocAsideCarbonAds from 'vitepress/dist/client/theme-default/components/VPDocAsideCarbonAds.vue';
|
||||
import { x } from '../../../data/iconNodes';
|
||||
import Icon from 'lucide-vue-next/src/Icon';
|
||||
import Icon from '@lucide/vue/src/Icon';
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
const { theme } = useData();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, useSlots } from 'vue';
|
||||
import { copy } from '../../../data/iconNodes';
|
||||
import Icon from '@lucide/vue/src/Icon';
|
||||
import useConfetti from '../../composables/useConfetti';
|
||||
import Icon from 'lucide-vue-next/src/Icon';
|
||||
const { animate, confetti } = useConfetti();
|
||||
const slots = useSlots();
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import type { IconEntity } from '../../types'
|
||||
import { computed } from 'vue'
|
||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
|
||||
import type { IconEntity } from '../../types';
|
||||
import { computed } from 'vue';
|
||||
import createLucideIcon from '@lucide/vue/src/createLucideIcon';
|
||||
import IconButton from '../base/IconButton.vue';
|
||||
import IconContributors from './IconContributors.vue';
|
||||
import IconPreview from './IconPreview.vue';
|
||||
import { x, expand } from '../../../data/iconNodes'
|
||||
import { x, expand } from '../../../data/iconNodes';
|
||||
import { useRouter } from 'vitepress';
|
||||
import IconInfo from './IconInfo.vue';
|
||||
import Badge from '../base/Badge.vue';
|
||||
@@ -14,63 +14,79 @@ import { satisfies } from 'semver';
|
||||
import { useExternalLibs } from '../../composables/useExternalLibs';
|
||||
|
||||
const props = defineProps<{
|
||||
iconName: string | null
|
||||
}>()
|
||||
iconName: string | null;
|
||||
}>();
|
||||
|
||||
const { externalIconNodes } = useExternalLibs()
|
||||
const { externalIconNodes } = useExternalLibs();
|
||||
|
||||
const { go } = useRouter()
|
||||
const { go } = useRouter();
|
||||
|
||||
const icon = computedAsync<IconEntity | null>(async () => {
|
||||
if (props.iconName) {
|
||||
try {
|
||||
if (props.iconName.includes(':')) {
|
||||
const [library, name] = props.iconName.split(':')
|
||||
const [library, name] = props.iconName.split(':');
|
||||
|
||||
return externalIconNodes.value[library].find((icon) => icon.name === name)
|
||||
return externalIconNodes.value[library].find((icon) => icon.name === name);
|
||||
} else {
|
||||
return (await import(`../../../data/iconDetails/${props.iconName}.ts`)).default as IconEntity
|
||||
return (await import(`../../../data/iconDetails/${props.iconName}.ts`))
|
||||
.default as IconEntity;
|
||||
}
|
||||
} catch (err) {
|
||||
if (!props.iconName.includes(':')) {
|
||||
go(`/icons/${props.iconName}`)
|
||||
go(`/icons/${props.iconName}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}, null)
|
||||
return null;
|
||||
}, null);
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
const isOpen = computed(() => !!icon.value)
|
||||
const emit = defineEmits(['close']);
|
||||
const isOpen = computed(() => !!icon.value);
|
||||
|
||||
function releaseTagLink(version) {
|
||||
const shouldAddV = satisfies(version, `<0.266.0`)
|
||||
const shouldAddV = satisfies(version, `<0.266.0`);
|
||||
|
||||
return `https://github.com/lucide-icons/lucide/releases/tag/${shouldAddV ? 'v' : ''}${version}`
|
||||
return `https://github.com/lucide-icons/lucide/releases/tag/${shouldAddV ? 'v' : ''}${version}`;
|
||||
}
|
||||
|
||||
function onClose() {
|
||||
emit('close')
|
||||
emit('close');
|
||||
}
|
||||
|
||||
const CloseIcon = createLucideIcon('Close', x)
|
||||
const Expand = createLucideIcon('Expand', expand)
|
||||
const CloseIcon = createLucideIcon('Close', x);
|
||||
const Expand = createLucideIcon('Expand', expand);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Transition name="drawer" appear>
|
||||
<div class="overlay-container" v-if="icon">
|
||||
<Transition
|
||||
name="drawer"
|
||||
appear
|
||||
>
|
||||
<div
|
||||
class="overlay-container"
|
||||
v-if="icon"
|
||||
>
|
||||
<div class="overlay-panel">
|
||||
<nav class="overlay-menu">
|
||||
<Badge
|
||||
v-if="icon.createdRelease"
|
||||
class="version"
|
||||
:href="releaseTagLink(icon.createdRelease.version)"
|
||||
>v{{ icon.createdRelease.version }}</Badge>
|
||||
<IconButton @click="go(icon.externalLibrary ? `/icons/${icon.externalLibrary}/${icon.name}` : `/icons/${icon.name}`)">
|
||||
>v{{ icon.createdRelease.version }}</Badge
|
||||
>
|
||||
<IconButton
|
||||
@click="
|
||||
go(
|
||||
icon.externalLibrary
|
||||
? `/icons/${icon.externalLibrary}/${icon.name}`
|
||||
: `/icons/${icon.name}`,
|
||||
)
|
||||
"
|
||||
>
|
||||
<component :is="Expand" />
|
||||
</IconButton>
|
||||
<IconButton @click="onClose">
|
||||
<IconButton @click="onClose">
|
||||
<component :is="CloseIcon" />
|
||||
</IconButton>
|
||||
</nav>
|
||||
@@ -80,9 +96,15 @@ const Expand = createLucideIcon('Expand', expand)
|
||||
:iconNode="icon.iconNode"
|
||||
customizable
|
||||
/>
|
||||
<IconInfo :icon="icon" popoverPosition="top">
|
||||
<IconInfo
|
||||
:icon="icon"
|
||||
popoverPosition="top"
|
||||
>
|
||||
<template v-slot:footer>
|
||||
<IconContributors :icon="icon" class="contributors" />
|
||||
<IconContributors
|
||||
:icon="icon"
|
||||
class="contributors"
|
||||
/>
|
||||
</template>
|
||||
</IconInfo>
|
||||
</div>
|
||||
@@ -157,11 +179,15 @@ const Expand = createLucideIcon('Expand', expand)
|
||||
}
|
||||
|
||||
.drawer-enter-active {
|
||||
transition: opacity 0.5s, transform 0.25s ease;
|
||||
transition:
|
||||
opacity 0.5s,
|
||||
transform 0.25s ease;
|
||||
}
|
||||
|
||||
.drawer-leave-active {
|
||||
transition: opacity 0.25s ease, transform 1.6s ease-out;
|
||||
transition:
|
||||
opacity 0.25s ease,
|
||||
transform 1.6s ease-out;
|
||||
}
|
||||
|
||||
.drawer-enter-from,
|
||||
@@ -177,5 +203,4 @@ const Expand = createLucideIcon('Expand', expand)
|
||||
.contributors {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -5,27 +5,26 @@ import Badge from '../base/Badge.vue';
|
||||
import CopySVGButton from './CopySVGButton.vue';
|
||||
import CopyCodeButton from './CopyCodeButton.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 createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
|
||||
import { diamond } from '../../../data/iconNodes'
|
||||
import createLucideIcon from '@lucide/vue/src/createLucideIcon';
|
||||
import { diamond } from '../../../data/iconNodes';
|
||||
import deprecationReasonTemplate from '../../../../../tools/build-icons/utils/deprecationReasonTemplate.ts';
|
||||
|
||||
|
||||
const props = defineProps<{
|
||||
icon: IconEntity
|
||||
popoverPosition?: 'top' | 'bottom'
|
||||
}>()
|
||||
icon: IconEntity;
|
||||
popoverPosition?: 'top' | 'bottom';
|
||||
}>();
|
||||
|
||||
const { go } = useRouter()
|
||||
const { page } = useData()
|
||||
const { go } = useRouter();
|
||||
const { page } = useData();
|
||||
|
||||
const tags = computed(() => {
|
||||
if (!props.icon || !props?.icon?.tags) return []
|
||||
return props.icon.tags.join(' • ')
|
||||
})
|
||||
if (!props.icon || !props?.icon?.tags) return [];
|
||||
return props.icon.tags.join(' • ');
|
||||
});
|
||||
|
||||
const DiamondIcon = createLucideIcon('Diamond', diamond)
|
||||
const DiamondIcon = createLucideIcon('Diamond', diamond);
|
||||
|
||||
const deprecatedTitle = computed(() => {
|
||||
if (!props.icon.deprecationReason) return '';
|
||||
@@ -43,8 +42,14 @@ const deprecatedTitle = computed(() => {
|
||||
<IconDetailName class="icon-name">
|
||||
{{ icon.name }}
|
||||
</IconDetailName>
|
||||
<div v-if="icon.externalLibrary" class="icon-external-lib">
|
||||
<DiamondIcon fill="currentColor" :size="12"/>
|
||||
<div
|
||||
v-if="icon.externalLibrary"
|
||||
class="icon-external-lib"
|
||||
>
|
||||
<DiamondIcon
|
||||
fill="currentColor"
|
||||
:size="12"
|
||||
/>
|
||||
{{ icon.externalLibrary }}
|
||||
</div>
|
||||
<Badge
|
||||
@@ -55,7 +60,10 @@ const deprecatedTitle = computed(() => {
|
||||
Deprecated
|
||||
</Badge>
|
||||
</div>
|
||||
<div class="tags-scroller" v-if="tags.length">
|
||||
<div
|
||||
class="tags-scroller"
|
||||
v-if="tags.length"
|
||||
>
|
||||
<p class="icon-tags horizontal-scroller">
|
||||
{{ tags }}
|
||||
</p>
|
||||
@@ -72,13 +80,35 @@ const deprecatedTitle = computed(() => {
|
||||
|
||||
<div class="group buttons">
|
||||
<VPButton
|
||||
v-if="!page?.relativePath?.startsWith?.(icon.externalLibrary ? `icons/${icon.externalLibrary}/${icon.name}`: `icons/${icon.name}`)"
|
||||
:href="icon.externalLibrary ? `/icons/${icon.externalLibrary}/${icon.name}`: `/icons/${icon.name}`"
|
||||
v-if="
|
||||
!page?.relativePath?.startsWith?.(
|
||||
icon.externalLibrary
|
||||
? `icons/${icon.externalLibrary}/${icon.name}`
|
||||
: `icons/${icon.name}`,
|
||||
)
|
||||
"
|
||||
:href="
|
||||
icon.externalLibrary
|
||||
? `/icons/${icon.externalLibrary}/${icon.name}`
|
||||
: `/icons/${icon.name}`
|
||||
"
|
||||
text="See in action"
|
||||
@click="go(icon.externalLibrary ? `/icons/${icon.externalLibrary}/${icon.name}`: `/icons/${icon.name}`)"
|
||||
@click="
|
||||
go(
|
||||
icon.externalLibrary
|
||||
? `/icons/${icon.externalLibrary}/${icon.name}`
|
||||
: `/icons/${icon.name}`,
|
||||
)
|
||||
"
|
||||
/>
|
||||
<CopySVGButton
|
||||
:name="icon.name"
|
||||
:popoverPosition="popoverPosition"
|
||||
/>
|
||||
<CopyCodeButton
|
||||
:name="icon.name"
|
||||
:popoverPosition="popoverPosition"
|
||||
/>
|
||||
<CopySVGButton :name="icon.name" :popoverPosition="popoverPosition"/>
|
||||
<CopyCodeButton :name="icon.name" :popoverPosition="popoverPosition"/>
|
||||
</div>
|
||||
<slot name="footer" />
|
||||
</div>
|
||||
@@ -119,7 +149,7 @@ const deprecatedTitle = computed(() => {
|
||||
.deprecated-badge {
|
||||
background-color: var(--vp-c-brand-5);
|
||||
margin-left: 40px;
|
||||
opacity: .8;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.deprecated-badge:hover {
|
||||
@@ -150,7 +180,7 @@ const deprecatedTitle = computed(() => {
|
||||
margin-top: 8px;
|
||||
align-items: center;
|
||||
|
||||
--gradient-background: var(--tags-gradient-background, var(--vp-c-bg-elv))
|
||||
--gradient-background: var(--tags-gradient-background, var(--vp-c-bg-elv));
|
||||
}
|
||||
.horizontal-scroller {
|
||||
overflow-x: scroll;
|
||||
@@ -162,19 +192,18 @@ const deprecatedTitle = computed(() => {
|
||||
}
|
||||
.horizontal-scroller::-webkit-scrollbar {
|
||||
width: 0;
|
||||
display: none
|
||||
display: none;
|
||||
}
|
||||
|
||||
.horizontal-scroller::-webkit-scrollbar-track {
|
||||
background: transparent
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.horizontal-scroller::-webkit-scrollbar-thumb {
|
||||
background: transparent;
|
||||
border: none
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
||||
.tags-scroller::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
@@ -182,7 +211,7 @@ const deprecatedTitle = computed(() => {
|
||||
width: 32px;
|
||||
height: 100%;
|
||||
/* Background Gradient left to right */
|
||||
background: linear-gradient(to right, rgba(255,255,255,0) 0%,var(--gradient-background) 100%);
|
||||
background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, var(--gradient-background) 100%);
|
||||
right: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
|
||||
import createLucideIcon from '@lucide/vue/src/createLucideIcon';
|
||||
import { useMediaQuery } from '@vueuse/core';
|
||||
import { useRouter } from 'vitepress';
|
||||
import getSVGIcon from '../../utils/getSVGIcon';
|
||||
import useConfetti from '../../composables/useConfetti';
|
||||
import Tooltip from '../base/Tooltip.vue';
|
||||
import { diamond } from '../../../data/iconNodes'
|
||||
import { diamond } from '../../../data/iconNodes';
|
||||
|
||||
const downloadText = 'Download!'
|
||||
const copiedText = 'Copied!'
|
||||
const downloadText = 'Download!';
|
||||
const copiedText = 'Copied!';
|
||||
|
||||
export type IconNode = [elementName: string, attrs: Record<string, string>][]
|
||||
export type IconNode = [elementName: string, attrs: Record<string, string>][];
|
||||
|
||||
const props = defineProps<{
|
||||
name: string;
|
||||
@@ -19,50 +19,64 @@ const props = defineProps<{
|
||||
active: boolean;
|
||||
externalLibrary?: string;
|
||||
customizable?: boolean;
|
||||
overlayMode?: boolean
|
||||
hideIcon?: boolean
|
||||
}>()
|
||||
overlayMode?: boolean;
|
||||
hideIcon?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['setActiveIcon'])
|
||||
const emit = defineEmits(['setActiveIcon']);
|
||||
|
||||
const { go } = useRouter()
|
||||
const { go } = useRouter();
|
||||
const showOverlay = useMediaQuery('(min-width: 860px)');
|
||||
const { animate, confetti, confettiText } = useConfetti()
|
||||
|
||||
const { animate, confetti, confettiText } = useConfetti();
|
||||
|
||||
const icon = computed(() => {
|
||||
if (!props.name || !props.iconNode) return null
|
||||
return createLucideIcon(props.name, props.iconNode)
|
||||
})
|
||||
if (!props.name || !props.iconNode) return null;
|
||||
return createLucideIcon(props.name, props.iconNode);
|
||||
});
|
||||
|
||||
const href = computed(() => props.externalLibrary ? `/icons/${props.externalLibrary}/${props.name}` : `/icons/${props.name}`)
|
||||
const href = computed(() =>
|
||||
props.externalLibrary ? `/icons/${props.externalLibrary}/${props.name}` : `/icons/${props.name}`,
|
||||
);
|
||||
|
||||
async function navigateToIcon(event) {
|
||||
if (event.shiftKey) {
|
||||
event.preventDefault()
|
||||
event.preventDefault();
|
||||
const svgString = getSVGIcon(event.target.firstChild, {
|
||||
class: `lucide lucide-${props.name}`,
|
||||
})
|
||||
});
|
||||
|
||||
await navigator.clipboard.writeText(svgString)
|
||||
await navigator.clipboard.writeText(svgString);
|
||||
|
||||
confettiText.value = copiedText
|
||||
confetti()
|
||||
return
|
||||
confettiText.value = copiedText;
|
||||
confetti();
|
||||
return;
|
||||
}
|
||||
|
||||
if(props.overlayMode && showOverlay.value) {
|
||||
event.preventDefault()
|
||||
if (props.overlayMode && showOverlay.value) {
|
||||
event.preventDefault();
|
||||
|
||||
window.history.pushState({}, '', props.externalLibrary ? `/icons/${props.externalLibrary}/${props.name}` : `/icons/${props.name}`)
|
||||
emit('setActiveIcon', props.externalLibrary ? `${props.externalLibrary}:${props.name}`: 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 {
|
||||
event.preventDefault()
|
||||
go(props.externalLibrary ? `/icons/${props.externalLibrary}/${props.name}` : `/icons/${props.name}`)
|
||||
event.preventDefault();
|
||||
go(
|
||||
props.externalLibrary
|
||||
? `/icons/${props.externalLibrary}/${props.name}`
|
||||
: `/icons/${props.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const DiamondIcon = createLucideIcon('Diamond', diamond)
|
||||
const DiamondIcon = createLucideIcon('Diamond', diamond);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -72,7 +86,6 @@ const DiamondIcon = createLucideIcon('Diamond', diamond)
|
||||
@click="navigateToIcon"
|
||||
:class="{ active, animate }"
|
||||
:aria-label="name"
|
||||
|
||||
:data-confetti-text="confettiText"
|
||||
ref="ref"
|
||||
>
|
||||
@@ -91,7 +104,10 @@ const DiamondIcon = createLucideIcon('Diamond', diamond)
|
||||
class="floating-diamond"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<DiamondIcon fill="currentColor" :size="8"/>
|
||||
<DiamondIcon
|
||||
fill="currentColor"
|
||||
:size="8"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
</Tooltip>
|
||||
@@ -108,7 +124,10 @@ const DiamondIcon = createLucideIcon('Diamond', diamond)
|
||||
font-weight: 600;
|
||||
border-radius: 4px;
|
||||
white-space: nowrap;
|
||||
transition: color 0.25s, border-color 0.25s, background-color 0.25s;
|
||||
transition:
|
||||
color 0.25s,
|
||||
border-color 0.25s,
|
||||
background-color 0.25s;
|
||||
border-radius: 6px;
|
||||
background-color: var(--vp-c-bg-alt);
|
||||
display: inline-flex;
|
||||
@@ -135,7 +154,10 @@ const DiamondIcon = createLucideIcon('Diamond', diamond)
|
||||
}
|
||||
|
||||
.icon-button:active {
|
||||
transition: color 0.1s, border-color 0.1s, background-color 0.1s;
|
||||
transition:
|
||||
color 0.1s,
|
||||
border-color 0.1s,
|
||||
background-color 0.1s;
|
||||
}
|
||||
|
||||
.icon-button.medium {
|
||||
@@ -159,9 +181,9 @@ const DiamondIcon = createLucideIcon('Diamond', diamond)
|
||||
}
|
||||
|
||||
.icon-button:active {
|
||||
border-color: var(--vp-button-alt-active-border);
|
||||
color: var(--vp-button-alt-active-text);
|
||||
background-color: var(--vp-button-alt-active-bg);
|
||||
border-color: var(--vp-button-alt-active-border);
|
||||
color: var(--vp-button-alt-active-text);
|
||||
background-color: var(--vp-button-alt-active-bg);
|
||||
}
|
||||
|
||||
.icon-button.active {
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import type { IconEntity } from '../../types'
|
||||
import { computed, ref } from 'vue'
|
||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
|
||||
import type { IconEntity } from '../../types';
|
||||
import { computed, ref } from 'vue';
|
||||
import createLucideIcon from '@lucide/vue/src/createLucideIcon';
|
||||
import { useIconStyleContext } from '../../composables/useIconStyle';
|
||||
|
||||
const props = defineProps<{
|
||||
name: IconEntity['name']
|
||||
iconNode: IconEntity['iconNode']
|
||||
customizable?: boolean
|
||||
}>()
|
||||
name: IconEntity['name'];
|
||||
iconNode: IconEntity['iconNode'];
|
||||
customizable?: boolean;
|
||||
}>();
|
||||
|
||||
const { size, color, strokeWidth, absoluteStrokeWidth } = useIconStyleContext()
|
||||
const previewIcon = ref()
|
||||
const { size, color, strokeWidth, absoluteStrokeWidth } = useIconStyleContext();
|
||||
const previewIcon = ref();
|
||||
|
||||
const gridLines = computed(() => Array.from({ length:(size.value - 1) }))
|
||||
const gridLines = computed(() => Array.from({ length: size.value - 1 }));
|
||||
|
||||
const iconComponent = computed(() => {
|
||||
if (!props.name || !props.iconNode) return null
|
||||
return createLucideIcon(props.name, props.iconNode)
|
||||
})
|
||||
if (!props.name || !props.iconNode) return null;
|
||||
return createLucideIcon(props.name, props.iconNode);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -28,12 +28,33 @@ const iconComponent = computed(() => {
|
||||
:is="iconComponent"
|
||||
:size="size"
|
||||
:color="color"
|
||||
:strokeWidth="absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth"
|
||||
:strokeWidth="absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth"
|
||||
/>
|
||||
<svg class="icon-grid" :viewBox="`0 0 ${size} ${size}`" fill="none" stroke-width="0.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<g :key="`grid-${i}`" v-for="(_, i) in gridLines">
|
||||
<line :key="`horizontal-${i}`" :x1="0" :y1="i + 1" :x2="size" :y2="i + 1" />
|
||||
<line :key="`vertical-${i}`" :x1="i + 1" y1="0" :x2="i + 1" :y2="size" />
|
||||
<svg
|
||||
class="icon-grid"
|
||||
:viewBox="`0 0 ${size} ${size}`"
|
||||
fill="none"
|
||||
stroke-width="0.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g
|
||||
:key="`grid-${i}`"
|
||||
v-for="(_, i) in gridLines"
|
||||
>
|
||||
<line
|
||||
:key="`horizontal-${i}`"
|
||||
:x1="0"
|
||||
:y1="i + 1"
|
||||
:x2="size"
|
||||
:y2="i + 1"
|
||||
/>
|
||||
<line
|
||||
:key="`vertical-${i}`"
|
||||
:x1="i + 1"
|
||||
y1="0"
|
||||
:x2="i + 1"
|
||||
:y2="size"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
@@ -1,27 +1,32 @@
|
||||
<script setup lang="ts">
|
||||
import type { IconEntity } from '../../types'
|
||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
|
||||
import type { IconEntity } from '../../types';
|
||||
import createLucideIcon from '@lucide/vue/src/createLucideIcon';
|
||||
|
||||
const props = defineProps<{
|
||||
name: IconEntity['name']
|
||||
iconNode: IconEntity['iconNode']
|
||||
customizable?: boolean
|
||||
}>()
|
||||
|
||||
const Icon = createLucideIcon(props.name, props.iconNode)
|
||||
name: IconEntity['name'];
|
||||
iconNode: IconEntity['iconNode'];
|
||||
customizable?: boolean;
|
||||
}>();
|
||||
|
||||
const Icon = createLucideIcon(props.name, props.iconNode);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="icons-small-preview">
|
||||
<div class="icon-wrapper">
|
||||
<Icon :size="48" class="lucide-icon"/>
|
||||
<Icon
|
||||
:size="48"
|
||||
class="lucide-icon"
|
||||
/>
|
||||
</div>
|
||||
<div class="icon-wrapper">
|
||||
<Icon :size="32" class="lucide-icon"/>
|
||||
<Icon
|
||||
:size="32"
|
||||
class="lucide-icon"
|
||||
/>
|
||||
</div>
|
||||
<div class="icon-wrapper">
|
||||
<Icon class="lucide-icon"/>
|
||||
<Icon class="lucide-icon" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -42,7 +47,10 @@ const Icon = createLucideIcon(props.name, props.iconNode)
|
||||
font-weight: 600;
|
||||
border-radius: 4px;
|
||||
white-space: nowrap;
|
||||
transition: color 0.25s, border-color 0.25s, background-color 0.25s;
|
||||
transition:
|
||||
color 0.25s,
|
||||
border-color 0.25s,
|
||||
background-color 0.25s;
|
||||
border-radius: 6px;
|
||||
background-color: var(--vp-c-bg-alt);
|
||||
display: inline-flex;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { IconEntity } from '../../types';
|
||||
import { computed } from 'vue';
|
||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon.ts';
|
||||
import createLucideIcon from '@lucide/vue/src/createLucideIcon.ts';
|
||||
import Calendar from '../../../data/iconDetails/calendar.ts';
|
||||
import Clock from '../../../data/iconDetails/clock.ts';
|
||||
import Bug from '../../../data/iconDetails/bug.ts';
|
||||
@@ -66,7 +66,10 @@ const prettyName = props.name
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="showcase" v-if="iconComponent">
|
||||
<section
|
||||
class="showcase"
|
||||
v-if="iconComponent"
|
||||
>
|
||||
<h2 class="title">See this icon in action</h2>
|
||||
<div class="showcase-grid">
|
||||
<div class="showcase-item column">
|
||||
|
||||
@@ -185,5 +185,6 @@ function handleCloseDrawer() {
|
||||
|
||||
.input-wrapper {
|
||||
width: 100%;
|
||||
/* view-transition-name: icons-search-box; */
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
testTubeDiagonal,
|
||||
sword,
|
||||
} from '../../../data/iconNodes';
|
||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
|
||||
import createLucideIcon from '@lucide/vue/src/createLucideIcon';
|
||||
import { useEventListener } from '@vueuse/core';
|
||||
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue';
|
||||
import { IconNode } from '../../types';
|
||||
|
||||
@@ -0,0 +1,379 @@
|
||||
<script setup lang="ts">
|
||||
import VPHero from 'vitepress/dist/client/theme-default/components/VPHero.vue'
|
||||
import { useData } from 'vitepress/dist/client/theme-default/composables/data'
|
||||
import FakeInput from '../base/FakeInput.vue';
|
||||
import { useRouter } from 'vitepress';
|
||||
import { data } from '../home/HomeHeroIconsCard.data'
|
||||
import { useScroll } from '@vueuse/core';
|
||||
import { computed } from 'vue';
|
||||
const { go } = useRouter()
|
||||
|
||||
const { frontmatter: fm } = useData()
|
||||
const { x, y, isScrolling, arrivedState, directions } = useScroll(window)
|
||||
|
||||
const opacity = computed(() => {
|
||||
if (y.value < 0) return 1
|
||||
if (y.value > 300) return 0
|
||||
return 1 - (y.value / 300)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VPHero v-if="fm.hero" class="VPHomeHero" :name="fm.hero.name" :text="fm.hero.text" :tagline="fm.hero.tagline"
|
||||
:image="undefined" :actions="fm.hero.actions">
|
||||
<template #home-hero-image></template>
|
||||
<template #home-hero-actions-after>
|
||||
<FakeInput @click="go('/icons/?focus')" class="search-box">
|
||||
Search {{ data.iconsCount }} icons...
|
||||
</FakeInput>
|
||||
</template>
|
||||
|
||||
</VPHero>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-12 -12 48 48" fill="none" overflow="auto"
|
||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="hero-background" :style="{ opacity: opacity }">
|
||||
<g class="svg-preview-grid-group" stroke-linecap="butt" stroke-width="0.1" stroke="#777"
|
||||
stroke-opacity="0.3">
|
||||
<!-- <rect class="svg-preview-grid-rect" width="23.9" height="23.9" x="0.05" y="0.05" rx="1"></rect> -->
|
||||
<path
|
||||
stroke-dasharray="0 0.1 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0 0.15"
|
||||
stroke-width="0.1"
|
||||
d="M1 0.1v23.8M2 0.1v23.8M4 0.1v23.8M5 0.1v23.8M7 0.1v23.8M8 0.1v23.8M10 0.1v23.8M11 0.1v23.8M13 0.1v23.8M14 0.1v23.8M16 0.1v23.8M17 0.1v23.8M19 0.1v23.8M20 0.1v23.8M22 0.1v23.8M23 0.1v23.8M0.1 1h23.8M0.1 2h23.8M0.1 4h23.8M0.1 5h23.8M0.1 7h23.8M0.1 8h23.8M0.1 10h23.8M0.1 11h23.8M0.1 13h23.8M0.1 14h23.8M0.1 16h23.8M0.1 17h23.8M0.1 19h23.8M0.1 20h23.8M0.1 22h23.8M0.1 23h23.8">
|
||||
</path>
|
||||
<path
|
||||
d="M3 0.1v23.8M6 0.1v23.8M9 0.1v23.8M12 0.1v23.8M15 0.1v23.8M18 0.1v23.8M21 0.1v23.8M0.1 3h23.8M0.1 6h23.8M0.1 9h23.8M0.1 12h23.8M0.1 15h23.8M0.1 18h23.8M0.1 21h23.8">
|
||||
</path>
|
||||
</g>
|
||||
<g class="svg-preview-shadow-mask-group" stroke-width="4" stroke="#777" stroke-opacity="0.15">
|
||||
<mask id="svg-preview-shadow-mask-0" maskUnits="userSpaceOnUse" stroke-opacity="1" stroke-width="4" stroke="#000">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#fff" stroke="none" rx="1"></rect>
|
||||
<path d="M4.9 16.1h.01M4.9 1.9h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-shadow-mask-1" maskUnits="userSpaceOnUse" stroke-opacity="1" stroke-width="4" stroke="#000">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#fff" stroke="none" rx="1"></rect>
|
||||
<path d="M7.8 4.7h.01M7 12.2h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-shadow-mask-2" maskUnits="userSpaceOnUse" stroke-opacity="1" stroke-width="4" stroke="#000">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#fff" stroke="none" rx="1"></rect>
|
||||
<path d="M12 7h.01M14 9h.01M12 11h.01M10 9h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-shadow-mask-3" maskUnits="userSpaceOnUse" stroke-opacity="1" stroke-width="4" stroke="#000">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#fff" stroke="none" rx="1"></rect>
|
||||
<path d="M16.2 4.8h.01M17 12.27h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-shadow-mask-4" maskUnits="userSpaceOnUse" stroke-opacity="1" stroke-width="4" stroke="#000">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#fff" stroke="none" rx="1"></rect>
|
||||
<path d="M19.1 1.9h.01M19.1 16h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-shadow-mask-5" maskUnits="userSpaceOnUse" stroke-opacity="1" stroke-width="4" stroke="#000">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#fff" stroke="none" rx="1"></rect>
|
||||
<path d="M9.5 18h.01M14.5 18h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-shadow-mask-6" maskUnits="userSpaceOnUse" stroke-opacity="1" stroke-width="4" stroke="#000">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#fff" stroke="none" rx="1"></rect>
|
||||
<path d="M8 22h.01M12 11h.01M16 22h.01"></path>
|
||||
</mask>
|
||||
</g>
|
||||
<!-- <g class="svg-preview-shadow-group" stroke-width="4" stroke="#777" stroke-opacity="0.15">
|
||||
<path mask="url(#svg-preview-shadow-mask-0)" d="M 4.9 16.1 C1 12.2 1 5.8 4.9 1.9"></path>
|
||||
<path mask="url(#svg-preview-shadow-mask-1)" d="M 7.8 4.7 A6.14 6.14 0 0 0 7 12.2"></path>
|
||||
<path mask="url(#svg-preview-shadow-mask-2)" d="M 12 7 A2 2 0 0 1 14 9"></path>
|
||||
<path mask="url(#svg-preview-shadow-mask-2)" d="M 14 9 A2 2 0 0 1 12 11"></path>
|
||||
<path mask="url(#svg-preview-shadow-mask-2)" d="M 12 11 A2 2 0 0 1 10 9"></path>
|
||||
<path mask="url(#svg-preview-shadow-mask-2)" d="M 10 9 A2 2 0 0 1 12 7"></path>
|
||||
<path mask="url(#svg-preview-shadow-mask-3)" d="M 16.2 4.8 C18.2 6.8 18.46 9.91 17 12.27"></path>
|
||||
<path mask="url(#svg-preview-shadow-mask-4)" d="M 19.1 1.9 A9.96 9.96 0 0 1 19.1 16"></path>
|
||||
<path mask="url(#svg-preview-shadow-mask-5)" d="M 9.5 18 L 14.5 18"></path>
|
||||
<path mask="url(#svg-preview-shadow-mask-6)" d="M 8 22 L 12 11"></path>
|
||||
<path mask="url(#svg-preview-shadow-mask-6)" d="M 12 11 L 16 22"></path>
|
||||
<path
|
||||
d="M4.9 16.1h.01M4.9 1.9h.01M7.8 4.7h.01M7 12.2h.01M12 7h.01M14 9h.01M12 11h.01M10 9h.01M16.2 4.8h.01M17 12.27h.01M19.1 1.9h.01M19.1 16h.01M9.5 18h.01M14.5 18h.01M8 22h.01M16 22h.01">
|
||||
</path>
|
||||
</g> -->
|
||||
<g>
|
||||
<defs xmlns="http://www.w3.org/2000/svg">
|
||||
<pattern id="backdrop-pattern-:R4:" width=".1" height=".1" patternUnits="userSpaceOnUse"
|
||||
patternTransform="rotate(45 50 50)">
|
||||
<line stroke="red" stroke-width="0.1" y2="1"></line>
|
||||
<line stroke="red" stroke-width="0.1" y2="1"></line>
|
||||
</pattern>
|
||||
</defs>
|
||||
<g stroke-width="4">
|
||||
<mask id="svg-preview-backdrop-mask-:R4:-0" maskUnits="userSpaceOnUse">
|
||||
<path stroke="white" d="M 4.9 16.1 C1 12.2 1 5.8 4.9 1.9"></path>
|
||||
</mask>
|
||||
<path d="M 7.8 4.7 A6.14 6.14 0 0 0 7 12.2" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
|
||||
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-0)"></path>
|
||||
</g>
|
||||
<g stroke-width="4">
|
||||
<mask id="svg-preview-backdrop-mask-:R4:-0" maskUnits="userSpaceOnUse">
|
||||
<path stroke="white" d="M 4.9 16.1 C1 12.2 1 5.8 4.9 1.9"></path>
|
||||
</mask>
|
||||
<path
|
||||
d="M 12 7 A2 2 0 0 1 14 9 M 14 9 A2 2 0 0 1 12 11 M 12 11 A2 2 0 0 1 10 9 M 10 9 A2 2 0 0 1 12 7 M 8 22 L 12 11 M 12 11 L 16 22 M 9.5 18 L 14.5 18"
|
||||
stroke="url(#backdrop-pattern-:R4:)" stroke-width="4" stroke-opacity="0.75"
|
||||
mask="url(#svg-preview-backdrop-mask-:R4:-0)"></path>
|
||||
</g>
|
||||
<g stroke-width="4">
|
||||
<mask id="svg-preview-backdrop-mask-:R4:-1" maskUnits="userSpaceOnUse">
|
||||
<path stroke="white" d="M 7.8 4.7 A6.14 6.14 0 0 0 7 12.2"></path>
|
||||
</mask>
|
||||
<path
|
||||
d="M 12 7 A2 2 0 0 1 14 9 M 14 9 A2 2 0 0 1 12 11 M 12 11 A2 2 0 0 1 10 9 M 10 9 A2 2 0 0 1 12 7 M 8 22 L 12 11 M 12 11 L 16 22 M 9.5 18 L 14.5 18"
|
||||
stroke="url(#backdrop-pattern-:R4:)" stroke-width="4" stroke-opacity="0.75"
|
||||
mask="url(#svg-preview-backdrop-mask-:R4:-1)"></path>
|
||||
</g>
|
||||
<g stroke-width="4">
|
||||
<mask id="svg-preview-backdrop-mask-:R4:-0" maskUnits="userSpaceOnUse">
|
||||
<path stroke="white" d="M 4.9 16.1 C1 12.2 1 5.8 4.9 1.9"></path>
|
||||
</mask>
|
||||
<path d="M 16.2 4.8 C18.2 6.8 18.46 9.91 17 12.27" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
|
||||
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-0)"></path>
|
||||
</g>
|
||||
<g stroke-width="4">
|
||||
<mask id="svg-preview-backdrop-mask-:R4:-1" maskUnits="userSpaceOnUse">
|
||||
<path stroke="white" d="M 7.8 4.7 A6.14 6.14 0 0 0 7 12.2"></path>
|
||||
</mask>
|
||||
<path d="M 16.2 4.8 C18.2 6.8 18.46 9.91 17 12.27" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
|
||||
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-1)"></path>
|
||||
</g>
|
||||
<g stroke-width="4">
|
||||
<mask id="svg-preview-backdrop-mask-:R4:-2" maskUnits="userSpaceOnUse">
|
||||
<path stroke="white"
|
||||
d="M 12 7 A2 2 0 0 1 14 9 M 14 9 A2 2 0 0 1 12 11 M 12 11 A2 2 0 0 1 10 9 M 10 9 A2 2 0 0 1 12 7 M 8 22 L 12 11 M 12 11 L 16 22 M 9.5 18 L 14.5 18">
|
||||
</path>
|
||||
</mask>
|
||||
<path d="M 16.2 4.8 C18.2 6.8 18.46 9.91 17 12.27" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
|
||||
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-2)"></path>
|
||||
</g>
|
||||
<g stroke-width="4">
|
||||
<mask id="svg-preview-backdrop-mask-:R4:-0" maskUnits="userSpaceOnUse">
|
||||
<path stroke="white" d="M 4.9 16.1 C1 12.2 1 5.8 4.9 1.9"></path>
|
||||
</mask>
|
||||
<path d="M 19.1 1.9 A9.96 9.96 0 0 1 19.1 16" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
|
||||
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-0)"></path>
|
||||
</g>
|
||||
<g stroke-width="4">
|
||||
<mask id="svg-preview-backdrop-mask-:R4:-1" maskUnits="userSpaceOnUse">
|
||||
<path stroke="white" d="M 7.8 4.7 A6.14 6.14 0 0 0 7 12.2"></path>
|
||||
</mask>
|
||||
<path d="M 19.1 1.9 A9.96 9.96 0 0 1 19.1 16" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
|
||||
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-1)"></path>
|
||||
</g>
|
||||
<g stroke-width="4">
|
||||
<mask id="svg-preview-backdrop-mask-:R4:-2" maskUnits="userSpaceOnUse">
|
||||
<path stroke="white"
|
||||
d="M 12 7 A2 2 0 0 1 14 9 M 14 9 A2 2 0 0 1 12 11 M 12 11 A2 2 0 0 1 10 9 M 10 9 A2 2 0 0 1 12 7 M 8 22 L 12 11 M 12 11 L 16 22 M 9.5 18 L 14.5 18">
|
||||
</path>
|
||||
</mask>
|
||||
<path d="M 19.1 1.9 A9.96 9.96 0 0 1 19.1 16" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
|
||||
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-2)"></path>
|
||||
</g>
|
||||
<g stroke-width="4">
|
||||
<mask id="svg-preview-backdrop-mask-:R4:-3" maskUnits="userSpaceOnUse">
|
||||
<path stroke="white" d="M 16.2 4.8 C18.2 6.8 18.46 9.91 17 12.27"></path>
|
||||
</mask>
|
||||
<path d="M 19.1 1.9 A9.96 9.96 0 0 1 19.1 16" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
|
||||
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-3)"></path>
|
||||
</g>
|
||||
</g>
|
||||
<g class="svg-preview-handles-group" stroke-width="0.12" stroke="#777" stroke-opacity="0.6">
|
||||
<path d="M4.9 16.1 1 12.2"></path>
|
||||
<circle cy="12.2" cx="1" r="0.25"></circle>
|
||||
<path d="M4.9 1.9 1 5.8"></path>
|
||||
<circle cy="5.8" cx="1" r="0.25"></circle>
|
||||
<path d="M16.2 4.8 18.2 6.8"></path>
|
||||
<circle cy="6.8" cx="18.2" r="0.25"></circle>
|
||||
<path d="M17 12.27 18.46 9.91"></path>
|
||||
<circle cy="9.91" cx="18.46" r="0.25"></circle>
|
||||
</g>
|
||||
<g class="svg-preview-colored-path-group">
|
||||
<path d="M 4.9 16.1 C1 12.2 1 5.8 4.9 1.9" stroke="##dfdfd6"></path>
|
||||
<path d="M 7.8 4.7 A6.14 6.14 0 0 0 7 12.2" stroke="##dfdfd6"></path>
|
||||
<path d="M 12 7 A2 2 0 0 1 14 9" stroke="##dfdfd6"></path>
|
||||
<path d="M 14 9 A2 2 0 0 1 12 11" stroke="##dfdfd6"></path>
|
||||
<path d="M 12 11 A2 2 0 0 1 10 9" stroke="##dfdfd6"></path>
|
||||
<path d="M 10 9 A2 2 0 0 1 12 7" stroke="##dfdfd6"></path>
|
||||
<path d="M 16.2 4.8 C18.2 6.8 18.46 9.91 17 12.27" stroke="##dfdfd6"></path>
|
||||
<path d="M 19.1 1.9 A9.96 9.96 0 0 1 19.1 16" stroke="##dfdfd6"></path>
|
||||
<path d="M 9.5 18 L 14.5 18" stroke="##dfdfd6"></path>
|
||||
<path d="M 8 22 L 12 11" stroke="##dfdfd6"></path>
|
||||
<path d="M 12 11 L 16 22" stroke="##dfdfd6"></path>
|
||||
</g>
|
||||
<g class="svg-preview-radii-group" stroke-width="0.12" stroke-dasharray="0 0.25 0.25" stroke="#777"
|
||||
stroke-opacity="0.3">
|
||||
<circle cx="9.518750780437157" cy="16.261333416579962" r="0.25"></circle>
|
||||
<circle cx="11.118750780437157" cy="1.261333416579964" r="0.25"></circle>
|
||||
<path
|
||||
d="M9.518750780437157 16.261333416579962L4.481249219562843 8.138666583420036L11.118750780437157 1.261333416579964">
|
||||
</path>
|
||||
<circle cx="4.481249219562843" cy="8.138666583420036" r="0.25"></circle>
|
||||
<path d="M7 12.2L12.217985863765243 8.963918492134958L7.8 4.7"></path>
|
||||
<circle cy="8.963918492134958" cx="12.217985863765243" r="0.25" stroke-dasharray="0" stroke="red"></circle>
|
||||
<circle cy="8.963918492134958" cx="12.217985863765243" r="6.14" stroke="red"></circle>
|
||||
<circle cy="9" cx="12" r="0.25" stroke-dasharray="0"></circle>
|
||||
<circle cy="9" cx="12" r="2"></circle>
|
||||
<circle cy="9" cx="12" r="0.25" stroke-dasharray="0"></circle>
|
||||
<circle cy="9" cx="12" r="2"></circle>
|
||||
<circle cy="9" cx="12" r="0.25" stroke-dasharray="0"></circle>
|
||||
<circle cy="9" cx="12" r="2"></circle>
|
||||
<circle cy="9" cx="12" r="0.25" stroke-dasharray="0"></circle>
|
||||
<circle cy="9" cx="12" r="2"></circle>
|
||||
<circle cx="12.035530040810755" cy="23.05" r="0.25"></circle>
|
||||
<circle cx="12.035530040810755" cy="-5.1499999999999995" r="0.25"></circle>
|
||||
<path d="M12.035530040810755 23.05L26.164469959189248 8.95L12.035530040810755 -5.1499999999999995"></path>
|
||||
<circle cx="26.164469959189248" cy="8.95" r="0.25"></circle>
|
||||
<path d="M19.1 16L12.064440320770494 8.95L19.1 1.9"></path>
|
||||
<circle cy="8.95" cx="12.064440320770494" r="0.25" stroke-dasharray="0" stroke="red"></circle>
|
||||
<circle cy="8.95" cx="12.064440320770494" r="9.96" stroke="red"></circle>
|
||||
</g>
|
||||
<g class="svg-preview-control-path-marker-mask-group" stroke-width="1" stroke="#000">
|
||||
<mask id="svg-preview-control-path-marker-mask-0" maskUnits="userSpaceOnUse">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#ccc" stroke="none" rx="1"></rect>
|
||||
<path d="M4.9 16.1h.01"></path>
|
||||
<path d="M4.9 1.9h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-control-path-marker-mask-1" maskUnits="userSpaceOnUse">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#ccc" stroke="none" rx="1"></rect>
|
||||
<path d="M7.8 4.7h.01"></path>
|
||||
<path d="M7 12.2h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-control-path-marker-mask-6" maskUnits="userSpaceOnUse">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#ccc" stroke="none" rx="1"></rect>
|
||||
<path d="M16.2 4.8h.01"></path>
|
||||
<path d="M17 12.27h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-control-path-marker-mask-7" maskUnits="userSpaceOnUse">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#ccc" stroke="none" rx="1"></rect>
|
||||
<path d="M19.1 1.9h.01"></path>
|
||||
<path d="M19.1 16h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-control-path-marker-mask-8" maskUnits="userSpaceOnUse">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#ccc" stroke="none" rx="1"></rect>
|
||||
<path d="M9.5 18h.01"></path>
|
||||
<path d="M14.5 18h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-control-path-marker-mask-9" maskUnits="userSpaceOnUse">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#ccc" stroke="none" rx="1"></rect>
|
||||
<path d="M8 22h.01"></path>
|
||||
<path d="M12 11h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-control-path-marker-mask-10" maskUnits="userSpaceOnUse">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#ccc" stroke="none" rx="1"></rect>
|
||||
<path d="M12 11h.01"></path>
|
||||
<path d="M16 22h.01"></path>
|
||||
</mask>
|
||||
</g>
|
||||
<g class="svg-preview-control-path-group" stroke="#fff" stroke-width="0.125">
|
||||
<path mask="url(#svg-preview-control-path-marker-mask-0)" d="M 4.9 16.1 C1 12.2 1 5.8 4.9 1.9"></path>
|
||||
<path mask="url(#svg-preview-control-path-marker-mask-1)" d="M 7.8 4.7 A6.14 6.14 0 0 0 7 12.2"></path>
|
||||
<path d="M 12 7 A2 2 0 0 1 14 9"></path>
|
||||
<path d="M 14 9 A2 2 0 0 1 12 11"></path>
|
||||
<path d="M 12 11 A2 2 0 0 1 10 9"></path>
|
||||
<path d="M 10 9 A2 2 0 0 1 12 7"></path>
|
||||
<path mask="url(#svg-preview-control-path-marker-mask-6)" d="M 16.2 4.8 C18.2 6.8 18.46 9.91 17 12.27"></path>
|
||||
<path mask="url(#svg-preview-control-path-marker-mask-7)" d="M 19.1 1.9 A9.96 9.96 0 0 1 19.1 16"></path>
|
||||
<path mask="url(#svg-preview-control-path-marker-mask-8)" d="M 9.5 18 L 14.5 18"></path>
|
||||
<path mask="url(#svg-preview-control-path-marker-mask-9)" d="M 8 22 L 12 11"></path>
|
||||
<path mask="url(#svg-preview-control-path-marker-mask-10)" d="M 12 11 L 16 22"></path>
|
||||
</g>
|
||||
<g class="svg-preview-control-path-marker-group" stroke="#fff" stroke-width="0.125">
|
||||
<path
|
||||
d="M4.9 16.1h.01M4.9 1.9h.01M7.8 4.7h.01M7 12.2h.01M16.2 4.8h.01M17 12.27h.01M19.1 1.9h.01M19.1 16h.01M9.5 18h.01M14.5 18h.01M8 22h.01M12 11h.01M12 11h.01M16 22h.01">
|
||||
</path>
|
||||
<circle cx="4.9" cy="16.1" r="0.5"></circle>
|
||||
<circle cx="4.9" cy="1.9" r="0.5"></circle>
|
||||
<circle cx="7.8" cy="4.7" r="0.5"></circle>
|
||||
<circle cx="7" cy="12.2" r="0.5"></circle>
|
||||
<circle cx="16.2" cy="4.8" r="0.5"></circle>
|
||||
<circle cx="17" cy="12.27" r="0.5"></circle>
|
||||
<circle cx="19.1" cy="1.9" r="0.5"></circle>
|
||||
<circle cx="19.1" cy="16" r="0.5"></circle>
|
||||
<circle cx="9.5" cy="18" r="0.5"></circle>
|
||||
<circle cx="14.5" cy="18" r="0.5"></circle>
|
||||
<circle cx="8" cy="22" r="0.5"></circle>
|
||||
<circle cx="16" cy="22" r="0.5"></circle>
|
||||
</g>
|
||||
<g class="svg-preview-handles-group" stroke-width="0.12" style="stroke: var(--vp-c-brand)" stroke-opacity="0.3">
|
||||
<path d="M4.9 16.1 1 12.2"></path>
|
||||
<circle cy="12.2" cx="1" r="0.25"></circle>
|
||||
<path d="M4.9 1.9 1 5.8"></path>
|
||||
<circle cy="5.8" cx="1" r="0.25"></circle>
|
||||
<path d="M16.2 4.8 18.2 6.8"></path>
|
||||
<circle cy="6.8" cx="18.2" r="0.25"></circle>
|
||||
<path d="M17 12.27 18.46 9.91"></path>
|
||||
<circle cy="9.91" cx="18.46" r="0.25"></circle>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.hero {
|
||||
overflow: hidden;
|
||||
height: 60vh;
|
||||
display: flex;
|
||||
position: relative;
|
||||
/* align-items: center;
|
||||
justify-content: center; */
|
||||
}
|
||||
|
||||
.hero-background {
|
||||
transform: rotateX(-51deg) rotateZ(-43deg);
|
||||
transform-style: preserve-3d;
|
||||
position: fixed;
|
||||
top: -240px;
|
||||
right: -320px;
|
||||
width: 112vw;
|
||||
height: 112vh;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-size: 3.2rem;
|
||||
line-height: 1.2;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
/* .VPHomeHero .image {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.VPHomeHero .container {
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.VPHomeHero .main {
|
||||
text-align: center;
|
||||
} */
|
||||
|
||||
/* .VPHomeHero .container .actions {
|
||||
justify-content: center;
|
||||
} */
|
||||
/*
|
||||
@media (min-width: 960px) {
|
||||
.VPHomeHero :deep(.actions) {
|
||||
justify-content: center;
|
||||
}
|
||||
} */
|
||||
|
||||
@media screen and (prefers-color-scheme: light) {
|
||||
.svg-preview-grid-rect { fill: none }
|
||||
}
|
||||
@media screen and (prefers-color-scheme: dark) {
|
||||
.svg-preview-grid-rect { fill: none }
|
||||
.svg
|
||||
.svg-preview-grid-group,
|
||||
.svg-preview-radii-group,
|
||||
.svg-preview-shadow-mask-group,
|
||||
.svg-preview-shadow-group {
|
||||
stroke: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.search-box {
|
||||
/* width: calc(100vw - 272px); */
|
||||
width: 100%;
|
||||
margin-top: 24px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,248 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUnmount, type Component } from 'vue';
|
||||
import {
|
||||
heart,
|
||||
star,
|
||||
zap,
|
||||
code,
|
||||
feather,
|
||||
cloud,
|
||||
sun,
|
||||
moon,
|
||||
camera,
|
||||
music,
|
||||
video,
|
||||
globe,
|
||||
layers,
|
||||
package as packageIcon,
|
||||
compass,
|
||||
command,
|
||||
terminal,
|
||||
database,
|
||||
server,
|
||||
cpu,
|
||||
lock,
|
||||
key,
|
||||
shield,
|
||||
wifi,
|
||||
download,
|
||||
upload,
|
||||
search,
|
||||
settings,
|
||||
users,
|
||||
mail,
|
||||
bell,
|
||||
calendar,
|
||||
clock,
|
||||
gitBranch,
|
||||
funnel,
|
||||
bookmark,
|
||||
tag,
|
||||
sparkles,
|
||||
} from '../../../data/iconNodes';
|
||||
import createElement from 'lucide/src/createElement';
|
||||
import { IconNode } from 'lucide';
|
||||
|
||||
const icons: Component[] = [
|
||||
heart, star, zap, code, feather, cloud, sun, moon, camera, music,
|
||||
video, globe, layers, packageIcon, compass, command, terminal, database,
|
||||
server, cpu, lock, key, shield, wifi, download, upload, search,
|
||||
settings, users, mail, bell, calendar, clock, gitBranch, funnel,
|
||||
bookmark, tag, sparkles,
|
||||
];
|
||||
|
||||
const svgs = icons.map(icon => {
|
||||
const element = createElement(icon as IconNode, {
|
||||
stroke: 'white',
|
||||
opacity: '0.2',
|
||||
})
|
||||
|
||||
return element.outerHTML;
|
||||
});
|
||||
|
||||
const highlightedSvgs = icons.map(icon => {
|
||||
const element = createElement(icon as IconNode, {
|
||||
stroke: 'white',
|
||||
})
|
||||
|
||||
return element.outerHTML;
|
||||
});
|
||||
|
||||
const images = svgs.map(svg => {
|
||||
const img = new Image();
|
||||
img.src = `data:image/svg+xml;base64,${btoa(svg)}`;
|
||||
return img;
|
||||
});
|
||||
|
||||
const highlightedImages = highlightedSvgs.map(svg => {
|
||||
const img = new Image();
|
||||
img.src = `data:image/svg+xml;base64,${btoa(svg)}`;
|
||||
return img;
|
||||
});
|
||||
|
||||
const canvas = ref<HTMLCanvasElement | null>(null);
|
||||
|
||||
// Setting up the columns
|
||||
const fontSize = 16;
|
||||
const gap = 40;
|
||||
const gapY = 8;
|
||||
const intervalTime = ref<number | null>(null);
|
||||
|
||||
// Store individual drops with their positions
|
||||
let individualDrops: Array<{x: number, y: number, active: boolean}> = [];
|
||||
|
||||
// Random integer between min and max
|
||||
function randInt(min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!canvas.value) return;
|
||||
|
||||
const ctx = canvas.value.getContext('2d');
|
||||
if (!ctx) return;
|
||||
|
||||
const width = window.innerWidth;
|
||||
const height = window.innerHeight * 0.6;
|
||||
canvas.value.width = width;
|
||||
canvas.value.height = height;
|
||||
|
||||
let columns = Math.floor(width / 120);
|
||||
|
||||
var rows = Math.floor(canvas.value.height / fontSize);
|
||||
var midStart = Math.floor(rows * 0.25);
|
||||
var midEnd = Math.floor(rows * 0.85);
|
||||
|
||||
let drops = Array.from({ length: columns }, () => randInt(midStart, midEnd));
|
||||
let fps, fpsInterval, startTime, now, then, elapsed;
|
||||
|
||||
// Add click event listener
|
||||
function handleCanvasClick(event: MouseEvent) {
|
||||
const rect = canvas.value?.getBoundingClientRect();
|
||||
if (!rect) return;
|
||||
|
||||
const x = event.clientX - rect.left;
|
||||
const y = event.clientY - rect.top;
|
||||
|
||||
individualDrops.push({
|
||||
x: x,
|
||||
y: y,
|
||||
active: true
|
||||
});
|
||||
}
|
||||
|
||||
canvas.value.addEventListener('click', handleCanvasClick);
|
||||
|
||||
function draw() {
|
||||
if (!ctx) return;
|
||||
|
||||
ctx.fillStyle = 'rgba(27, 27, 31, 0.50)';
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, 1)';
|
||||
|
||||
// Draw regular drops
|
||||
for (var i = drops.length; i > 0; i--) {
|
||||
var img = images[Math.floor(Math.random() * images.length)];
|
||||
ctx.drawImage(img, i * fontSize * 2 + gap, drops[i] * fontSize, (fontSize / 2) + gapY, (fontSize / 2) + gapY);
|
||||
|
||||
drops[i]++;
|
||||
if (Math.random() > .98) {
|
||||
drops[i] = randInt(midStart, midEnd);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw and update individual drops
|
||||
individualDrops = individualDrops.filter(drop => {
|
||||
if (!drop.active) return false;
|
||||
|
||||
var img = highlightedImages[Math.floor(Math.random() * images.length)];
|
||||
ctx.drawImage(img, drop.x - fontSize/2, drop.y, fontSize, fontSize);
|
||||
|
||||
drop.y += fontSize
|
||||
|
||||
// Remove if off screen
|
||||
if (drop.y > height) {
|
||||
drop.active = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function startAnimating(fps) {
|
||||
fpsInterval = 1000 / fps;
|
||||
then = Date.now();
|
||||
startTime = then;
|
||||
animate();
|
||||
}
|
||||
function animate() {
|
||||
|
||||
// request another frame
|
||||
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
// calc elapsed time since last loop
|
||||
|
||||
now = Date.now();
|
||||
elapsed = now - then;
|
||||
|
||||
// if enough time has elapsed, draw the next frame
|
||||
|
||||
if (elapsed > fpsInterval) {
|
||||
|
||||
// Get ready for next frame by setting then=now, but also adjust for your
|
||||
// specified fpsInterval not being a multiple of RAF's interval (16.7ms)
|
||||
then = now - (elapsed % fpsInterval);
|
||||
|
||||
// Put your drawing code here
|
||||
draw();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
startAnimating(12);
|
||||
|
||||
window.addEventListener('resize', function() {
|
||||
canvas.value.width = window.innerWidth;
|
||||
canvas.value.height = window.innerHeight;
|
||||
rows = Math.floor(canvas.value.height / fontSize);
|
||||
columns = Math.floor(canvas.value.width / fontSize);
|
||||
|
||||
var oldDrops = drops.slice();
|
||||
drops = [];
|
||||
for (var i = 0; i < columns; i++) {
|
||||
drops[i] = oldDrops[i % oldDrops.length] ?? randInt(midStart, midEnd);
|
||||
}
|
||||
});
|
||||
|
||||
// Cleanup event listener
|
||||
onBeforeUnmount(() => {
|
||||
canvas.value?.removeEventListener('click', handleCanvasClick);
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="hero">
|
||||
<canvas ref="canvas" class="hero-canvas" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.hero {
|
||||
overflow: hidden;
|
||||
height: 60vh;
|
||||
}
|
||||
|
||||
.hero-canvas {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 60vh;
|
||||
filter: blur(0px);
|
||||
}
|
||||
</style>
|
||||
@@ -1,57 +1,90 @@
|
||||
<script setup lang="ts">
|
||||
import { useData } from 'vitepress'
|
||||
import { useSidebar } from 'vitepress/dist/client/theme-default/composables/sidebar'
|
||||
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue'
|
||||
import { computed } from 'vue'
|
||||
import { useData } from 'vitepress';
|
||||
import { useSidebar } from 'vitepress/dist/client/theme-default/composables/sidebar';
|
||||
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const { theme } = useData()
|
||||
const { hasSidebar } = useSidebar()
|
||||
const { theme } = useData();
|
||||
const { hasSidebar } = useSidebar();
|
||||
|
||||
const githubLink = computed(() => theme.value.socialLinks.find(({icon}) => icon === 'github').link)
|
||||
const githubLink = computed(
|
||||
() => theme.value.socialLinks.find(({ icon }) => icon === 'github').link,
|
||||
);
|
||||
|
||||
const links = computed(() => [
|
||||
{
|
||||
text: 'License',
|
||||
href: '/license'
|
||||
href: '/license',
|
||||
},
|
||||
{
|
||||
text: 'Contribute',
|
||||
href: '/contributing'
|
||||
href: '/contribute/',
|
||||
},
|
||||
{
|
||||
text: 'Changelog',
|
||||
href: `${githubLink.value}/releases`
|
||||
href: `${githubLink.value}/releases`,
|
||||
},
|
||||
{
|
||||
text: 'GitHub',
|
||||
href: `${githubLink.value}`
|
||||
href: `${githubLink.value}`,
|
||||
},
|
||||
{
|
||||
text: 'Issues',
|
||||
href: `${githubLink.value}/issues`
|
||||
}
|
||||
])
|
||||
href: `${githubLink.value}/issues`,
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<footer v-if="theme.footer" class="VPFooter" :class="{ 'has-sidebar': hasSidebar }">
|
||||
<footer
|
||||
v-if="theme.footer"
|
||||
class="VPFooter"
|
||||
:class="{ 'has-sidebar': hasSidebar }"
|
||||
>
|
||||
<div class="container">
|
||||
<div class="sponsors">
|
||||
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss" rel="noreferrer noopener">
|
||||
<img src="/vercel.svg" alt="Powered by Vercel" width="200" />
|
||||
<a
|
||||
href="https://vercel.com?utm_source=lucide&utm_campaign=oss"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
<img
|
||||
src="/vercel.svg"
|
||||
alt="Powered by Vercel"
|
||||
width="200"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge" rel="noreferrer noopener">
|
||||
<img src="/digitalocean.svg" alt="Digital Ocean" width="200" />
|
||||
<a
|
||||
href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
<img
|
||||
src="/digitalocean.svg"
|
||||
alt="Digital Ocean"
|
||||
width="200"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="links">
|
||||
<VPLink v-for="link in links" :href="link.href" :key="link.text" :rel="link.href.startsWith('http') ? 'noreferrer noopener': undefined">
|
||||
<VPLink
|
||||
v-for="link in links"
|
||||
:href="link.href"
|
||||
:key="link.text"
|
||||
:rel="link.href.startsWith('http') ? 'noreferrer noopener' : undefined"
|
||||
>
|
||||
{{ link.text }}
|
||||
</VPLink>
|
||||
</div>
|
||||
<div>
|
||||
<p v-if="theme.footer.message" class="message" v-html="theme.footer.message"></p>
|
||||
<p v-if="theme.footer.copyright" class="copyright" v-html="theme.footer.copyright"></p>
|
||||
<p
|
||||
v-if="theme.footer.message"
|
||||
class="message"
|
||||
v-html="theme.footer.message"
|
||||
></p>
|
||||
<p
|
||||
v-if="theme.footer.copyright"
|
||||
class="copyright"
|
||||
v-html="theme.footer.copyright"
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -70,8 +103,6 @@ const links = computed(() => [
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.container {
|
||||
margin: 0 auto;
|
||||
max-width: calc(var(--vp-layout-max-width) - 64px);
|
||||
@@ -89,10 +120,15 @@ const links = computed(() => [
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.message { order: 2; }
|
||||
.copyright { order: 1; }
|
||||
.message {
|
||||
order: 2;
|
||||
}
|
||||
.copyright {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
.links, .sponsors {
|
||||
.links,
|
||||
.sponsors {
|
||||
display: flex;
|
||||
gap: 32px;
|
||||
align-items: center;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { menu } from '../../../data/iconNodes'
|
||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
|
||||
import { menu } from '../../../data/iconNodes';
|
||||
import createLucideIcon from '@lucide/vue/src/createLucideIcon';
|
||||
|
||||
const Menu = createLucideIcon('menu', menu)
|
||||
const Menu = createLucideIcon('menu', menu);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -14,7 +14,8 @@ export default {
|
||||
return {
|
||||
...pJson,
|
||||
...packageData,
|
||||
documentation: `/guide/packages/${packageData.docsAlias ?? pJson.name}`,
|
||||
documentation:
|
||||
packageData.documentation ?? `/guide/${packageData.docsAlias ?? pJson.name}`,
|
||||
source: `https://github.com/lucide-icons/lucide/tree/main/packages/${packageData.packageDirname ?? pJson.name}`,
|
||||
icon: `/framework-logos/${packageData.icon}.svg`,
|
||||
iconDark: Boolean(packageData.iconDark)
|
||||
@@ -22,6 +23,7 @@ export default {
|
||||
: null,
|
||||
};
|
||||
})
|
||||
.filter((pData) => !pData.hide)
|
||||
.sort((a, b) => a.order - b.order),
|
||||
thirdPartyPackages,
|
||||
};
|
||||
|
||||
@@ -1,27 +1,36 @@
|
||||
import { h } from 'vue';
|
||||
import DefaultTheme from 'vitepress/theme';
|
||||
import './style.css';
|
||||
import 'virtual:group-icons.css';
|
||||
import { Theme } from 'vitepress';
|
||||
import IconsSidebarNavAfter from './layouts/IconsSidebarNavAfter.vue';
|
||||
import HomeHeroIconsCard from './components/home/HomeHeroIconsCard.vue';
|
||||
import HomeHeroBefore from './components/home/HomeHeroBefore.vue';
|
||||
import HomeHeroAfter from './components/home/HomeHeroAfter.vue';
|
||||
import HomeHeroInfoBefore from './components/home/HomeHeroInfoBefore.vue';
|
||||
import LayoutTop from './components/base/LayoutTop.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';
|
||||
import FrameworkSelect from './components/guide/FrameworkSelect.vue';
|
||||
import SnackPlayer from './components/editors/SnackPlayer.vue';
|
||||
|
||||
const theme: Partial<Theme> = {
|
||||
extends: DefaultTheme,
|
||||
Layout() {
|
||||
return h(DefaultTheme.Layout, null, {
|
||||
'home-hero-before': () => h(HomeHeroBefore),
|
||||
'sidebar-nav-before': () => h(FrameworkSelect),
|
||||
'home-hero-info-before': () => h(HomeHeroInfoBefore),
|
||||
'sidebar-nav-after': () => h(IconsSidebarNavAfter),
|
||||
'home-hero-image': () => h(HomeHeroIconsCard),
|
||||
'home-hero-actions-after': () => h(HomeHeroAfter),
|
||||
'layout-top': () => h(LayoutTop),
|
||||
});
|
||||
},
|
||||
enhanceApp({ app }) {
|
||||
app.provide(ICON_STYLE_CONTEXT, iconStyleContext);
|
||||
app.provide(CATEGORY_VIEW_CONTEXT, categoryViewContext);
|
||||
app.provide(EXTERNAL_LIBS_CONTEXT, externalLibContext);
|
||||
app.component('SnackPlayer', SnackPlayer);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
-webkit-font-smoothing: auto;
|
||||
-moz-font-smoothing: auto;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-smoothing: auto;
|
||||
text-rendering: optimizeLegibility;
|
||||
font-smooth: always;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
-webkit-touch-callout: none;
|
||||
background: #202127;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 18px;
|
||||
padding: 10px 20px;
|
||||
line-height: 24px;
|
||||
gap: 8px;
|
||||
border-radius: 24px;
|
||||
outline: none;
|
||||
border: none;
|
||||
background: #111;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #f56565;
|
||||
}
|
||||
|
||||
.app,
|
||||
#app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
svg {
|
||||
display: block;
|
||||
}
|
||||
@@ -26,6 +26,12 @@
|
||||
|
||||
--vp-c-text-4: rgba(60, 60, 67, 0.32);
|
||||
--vp-home-hero-name-color: var(--vp-c-text);
|
||||
|
||||
--vp-a11y-danger: var(--vp-c-danger-3);
|
||||
--vp-a11y-success: var(--vp-c-success-3);
|
||||
--vp-a11y-warning: #da9200;
|
||||
|
||||
--vp-sidebar-input: var(--vp-c-gray-3);
|
||||
}
|
||||
|
||||
.dark {
|
||||
@@ -42,7 +48,27 @@
|
||||
--vp-code-editor-static: #f78c6c;
|
||||
--vp-code-editor-string: #9ecbff;
|
||||
|
||||
--vp-c-text-1: #dfdfd6;
|
||||
--vp-c-text-4: rgba(235, 235, 245, 0.16);
|
||||
|
||||
--vp-a11y-danger: var(--vp-c-danger-2);
|
||||
--vp-a11y-success: var(--vp-c-success-2);
|
||||
--vp-a11y-warning: var(--vp-c-warning-2);
|
||||
|
||||
--vp-sidebar-input: var(--vp-c-bg-soft);
|
||||
}
|
||||
|
||||
@view-transition {
|
||||
navigation: auto;
|
||||
}
|
||||
|
||||
/* ::view-transition-old(icons-search-box),
|
||||
::view-transition-new(icons-search-box) {
|
||||
animation: .3s transition 0s ease;
|
||||
} */
|
||||
|
||||
.VPHome {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.VPNavBarTitle .logo {
|
||||
@@ -70,7 +96,7 @@
|
||||
.VPHomeHero .container .image {
|
||||
margin: 0;
|
||||
order: 2;
|
||||
margin-top: 32px;
|
||||
/* margin-top: 32px; */
|
||||
}
|
||||
|
||||
.VPHomeHero .container .image-container {
|
||||
@@ -91,6 +117,10 @@
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.VPHomeHero .container {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.VPHomeHero .container .image {
|
||||
order: 1;
|
||||
@@ -110,60 +140,17 @@
|
||||
.VPHomeHero .container .image-container {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.VPHomeHero .container {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
.VPNavBarHamburger .container > span {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.sp-wrapper + * {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-layout {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-tabs-scrollable-container {
|
||||
border-radius: 8px 8px 0 0;
|
||||
position: relative;
|
||||
|
||||
box-shadow: inset 0 -1px var(--vp-code-tab-divider);
|
||||
margin-bottom: 0px;
|
||||
margin-top: -1px;
|
||||
height: 48px;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-preview-container {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-tabs .sp-tab-button {
|
||||
padding: 0 12px;
|
||||
line-height: 48px;
|
||||
height: 48px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-tabs .sp-tab-button:after {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
left: 8px;
|
||||
bottom: 0px;
|
||||
z-index: 1;
|
||||
height: 1px;
|
||||
content: '';
|
||||
background-color: transparent;
|
||||
transition: background-color 0.25s;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-tabs .sp-tab-button[data-active='true'] {
|
||||
color: var(--vp-code-tab-active-text-color);
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-tabs .sp-tab-button[data-active='true']:after {
|
||||
background-color: var(--vp-code-tab-active-bar-color);
|
||||
.package-lucide-logo {
|
||||
margin: 0 auto 48px;
|
||||
max-width: 480px;
|
||||
}
|
||||
|
||||
Vendored
+26
-1
@@ -1,4 +1,4 @@
|
||||
import { type IconNode } from 'lucide-vue-next/src/types';
|
||||
import { type IconNode } from '@lucide/vue/src/types';
|
||||
import Vue from 'vue';
|
||||
|
||||
declare module '*.vue' {
|
||||
@@ -11,6 +11,12 @@ declare module '*.data.ts' {
|
||||
export { data };
|
||||
}
|
||||
|
||||
declare module '*.data' {
|
||||
const data: any;
|
||||
|
||||
export { data };
|
||||
}
|
||||
|
||||
declare module '*.wasm' {}
|
||||
|
||||
declare const resvg_wasm: RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
||||
@@ -23,3 +29,22 @@ declare module '*.node.json' {
|
||||
const value: IconNode;
|
||||
export default value;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
ExpoSnack?: {
|
||||
/**
|
||||
* Initialize all snack players on the page
|
||||
*/
|
||||
initialize(): void;
|
||||
/**
|
||||
* Remove a snack player container
|
||||
*/
|
||||
remove(container: Element): void;
|
||||
/**
|
||||
* Append/add a snack player container
|
||||
*/
|
||||
append(container: Element): void;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
editLink: false
|
||||
title: Brand logo Statement
|
||||
description: Lucide does not accept brand logos, and we do not plan to add them in the future. This is due to a combination of legal restrictions, design consistency concerns, and practical maintenance reasons.
|
||||
---
|
||||
|
||||
<!--@include: ../BRAND_LOGOS_STATEMENT.md -->
|
||||
@@ -1,5 +1,7 @@
|
||||
---
|
||||
aside: false
|
||||
editLink: false
|
||||
title: Code of Conduct
|
||||
description: This Code of Conduct outlines our expectations for participant behavior as well as the consequences for unacceptable behavior within our community. We are committed to providing a welcoming, safe, and inclusive environment for all members of our community.
|
||||
---
|
||||
|
||||
<!--@include: ../CODE_OF_CONDUCT.md -->
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# Community Guide
|
||||
|
||||
Welcome to the Lucide community! This guide provides information on how to get involved, contribute, and connect with other members of the Lucide community.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Please read and adhere to our [Code of Conduct](/code-of-conduct.md) to ensure a welcoming and inclusive environment for all community members.
|
||||
|
||||
## Connecting with the Community
|
||||
|
||||
There are several ways to get involved with the Lucide community:
|
||||
|
||||
- **Join the Discord Server**: Connect with other Lucide users, ask questions, and share your projects on our [Discord server](https://discord.gg/EH6nSts).
|
||||
- **Follow us on X**: Stay updated with the latest news and updates by following us on [X](https://x.com/lucide_icons).
|
||||
- **Join discussions on GitHub**: Participate in discussions, provide feedback, and help others on our [GitHub Discussions page](https://github.com/lucide-icons/lucide/discussions).
|
||||
|
||||
## Getting Involved
|
||||
|
||||
- **Contribute to the Project**: Check out our [contribution guidelines](./contribute/index.md) to learn how you can contribute to the Lucide project.
|
||||
- **Report Issues**: If you encounter any bugs or have feature requests, please report them on our [GitHub Issues page](https://github.com/lucide-icons/lucide/issues).
|
||||
|
||||
## What you can do
|
||||
|
||||
There are many ways to contribute to the Lucide community.
|
||||
|
||||
### Help other people
|
||||
|
||||
Contribution in form of code or new icons is always welcome, but you can also help by answering questions from other community members on Discord or GitHub Discussions.
|
||||
|
||||
### Design New Icons
|
||||
|
||||
If you have design skills, consider contributing new icons to the Lucide library. Check out our [icon design guide](/contribute/icon-design-guide.md) for tips and guidelines on creating icons that fit the Lucide style.
|
||||
|
||||
### Contribute Code
|
||||
|
||||
If you're a developer, you can contribute code to the Lucide project. Whether it's fixing bugs, adding new features, or improving documentation, your contributions are valuable. Check out our [contribution guidelines](/contribute/index.md) for more information.
|
||||
|
||||
### Triage Issues
|
||||
|
||||
You can help by reviewing and triaging issues on our GitHub repository. This includes confirming bugs, providing additional information, and helping to prioritize issues for the maintainers.
|
||||
|
||||
### Improve Documentation
|
||||
|
||||
Lucide's documentation is open for contributions. If you find any areas that could be improved or if you have ideas for new guides or tutorials, feel free to submit a pull request.
|
||||
The documentation can be found in the [repository](https://github.com/lucide-icons/lucide/tree/main/docs)
|
||||
|
||||
### Share your experiences
|
||||
|
||||
Share your experiences using Lucide in your projects. Write blog posts, create tutorials, or share your projects on social media to help others discover and learn about Lucide.
|
||||
+2
-2
@@ -13,6 +13,6 @@ This guide describes how to use the Affinity Designer template for Lucide.
|
||||
1. Download and open the [Affinity Designer template](https://github.com/lucide-icons/lucide/blob/main/docs/public/templates/affinity_designer.aftemplate).
|
||||
2. Follow the [Icon Design Principles](icon-design-guide.md) while you use the template (to ensure integrity with the Lucide icon pack).
|
||||
3. Export the file as SVG (`File > Export`). Make sure that _Rastering_ is set to _Nothing_, _Export text as curves_ is checked (hopefully, you won't need this), _Use hex colors_ is checked, and _Flatten transforms_ is checked.
|
||||
|
||||

|
||||
|
||||

|
||||
4. Optimize the exported SVG file further with [SVGOMG](https://jakearchibald.github.io/svgomg/) or [`svgo`](https://github.com/svg/svgo) (using `svgo --multipass exported_icon.svg`).
|
||||
@@ -28,7 +28,7 @@ Set the following:
|
||||
1. Stroke width: 2px
|
||||
2. Stroke alignment: center
|
||||
|
||||

|
||||

|
||||
|
||||
## Export Or Copy Your Icon
|
||||
Once you have completed your icon, you can export it.
|
||||
@@ -1,6 +1,9 @@
|
||||
---
|
||||
title: Icon Design Guide
|
||||
description: Guidelines and best practices for designing icons for the Lucide icon library.
|
||||
---
|
||||
# Icon Design Guide
|
||||
|
||||
Guidelines and best practices for designing icons for the Lucide icon library.
|
||||
|
||||
## Icon Design Principles
|
||||
|
||||
@@ -8,85 +11,85 @@ Here are rules that should be followed to keep quality and consistency when maki
|
||||
|
||||
### 1. Icons must be designed on a **24 by 24 pixels** canvas.
|
||||
|
||||

|
||||

|
||||
|
||||
### 2. Icons must have at least **1 pixel padding** within the canvas.
|
||||
|
||||

|
||||

|
||||
|
||||
### 3. Icons must have a **stroke width of 2 pixels**.
|
||||
|
||||

|
||||

|
||||
|
||||
### 4. Icons must use **round joins**.
|
||||
|
||||

|
||||

|
||||
|
||||
### 5. Icons must use **round caps**.
|
||||
|
||||

|
||||

|
||||
|
||||
### 6. Icons must use **centered strokes**.
|
||||
|
||||

|
||||

|
||||
|
||||
### 7. Shapes (such as rectangles) must have a **border radius of**
|
||||
|
||||
#### A. **2 pixels** if they are at least 8 pixels in size
|
||||
|
||||

|
||||

|
||||
|
||||
#### B. **1 pixel** if they are smaller than 8 pixels in size
|
||||
|
||||

|
||||

|
||||
|
||||
### 8. Distinct elements must have **2 pixels of spacing between each other**
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
### 9. Icons should have a similar optical volume to `circle` and `square`.
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
**Tip:** place your icon next to the circle or square icon and blur them both; your icon should not feel much darker than the base shape.
|
||||
|
||||
### 10. Icons should be visually centered by their center of gravity.
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
**Tip:** place your icon both above/below and next to the square or circle icon and check if it feels off center. Symmetrical icons should always be aligned to the center.
|
||||
|
||||
### 11. Icons should have similar visual density and level of detail.
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
**Tip:** try to make abstractions to dense elements. Blur your icon, and when blurred it should not feel overly dark.
|
||||
|
||||
### 12. Continuous curves should join smoothly.
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
**Tip:** make sure to use arcs or quadratic curves. When using cubic curves control points should have mirrored angles for smooth curves.
|
||||
|
||||
### 13. Icons should aim to be pixel perfect so that they will be sharp on low DPI displays.
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
**Tip:** whenever possible align elements and arc centers to the grid.
|
||||
|
||||
@@ -24,7 +24,7 @@ The Illustrator template is created following guidelines from the [Icon Design G
|
||||
|
||||
5. Export the file with the export menu under: `Export > Export As..` then save the file as SVG. Select the following options in the SVG Options dialog:
|
||||
|
||||

|
||||

|
||||
|
||||
After that, double check that the [code conventions and SVG global attributes](icon-design-guide.md#code-conventions) are correct.
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
aside: false
|
||||
title: Contributing to Lucide
|
||||
title: Contribution guide
|
||||
description: Guidelines and instructions for contributing to the Lucide project.
|
||||
---
|
||||
|
||||
<!--@include: ../CONTRIBUTING.md -->
|
||||
<!--@include: ../../CONTRIBUTING.md -->
|
||||
@@ -4,7 +4,7 @@ title: Icon Design Guide
|
||||
|
||||
# Inkscape Setup Guide
|
||||
|
||||
This guide shows the steps to setup Inkscape for creating icons that conform to the Featherity design
|
||||
This guide shows the steps to setup Inkscape for creating icons that conform to the Lucide design
|
||||
guidelines.
|
||||
|
||||
## Setting up The Canvas
|
||||
@@ -13,11 +13,11 @@ When opening a new document, Inkscape will create a canvas of a default size. T
|
||||
|
||||
1. Open the Document Properties dialog (File -> Document Properties).
|
||||
2. On the “Page Size” tab, under “Custom Size” set the Units to `px` and set both Height and Width to 24.
|
||||

|
||||

|
||||
3. On the “Grid” tab, select `Rectangular Grid` and click “New Grid”.
|
||||

|
||||

|
||||
4. Set the Grid Units to `px` and set Spacing X and Spacing Y both to 1.
|
||||

|
||||

|
||||
5. Close the Document Properties dialog.
|
||||
6. To center the canvas in the viewport, select View -> Zoom -> Drawing.
|
||||
|
||||
@@ -25,17 +25,17 @@ When opening a new document, Inkscape will create a canvas of a default size. T
|
||||
|
||||
1. Create a path or shape.
|
||||
2. With the path selected, open the Stroke and Fill panel by pressing `Ctrl+Shift+F` on your keyboard.
|
||||

|
||||

|
||||
3. On the “Stroke Style” tab:
|
||||
* Set Stroke Width to `2px`.
|
||||
* Select the rounded join type.
|
||||
* Select the rounded cap type.
|
||||
4. If the shape is a rectangle, select the rectangle and in the top of the screen below the menu bar, set `Rx` and `Ry` to `2px`.
|
||||

|
||||

|
||||
|
||||
## Saving A File
|
||||
|
||||
1. When ready to save the file, click Save As and select “Optimized SVG” as the file type.
|
||||

|
||||

|
||||
2. After clicking Save, to conform with the other icons in the package, set Pretty Printing to use spaces and set the indentation depth to 2.
|
||||

|
||||

|
||||
@@ -1,8 +1,9 @@
|
||||
---
|
||||
title: Accessibility
|
||||
description: Learn how to make your icons accessible to all users, including those with disabilities.
|
||||
---
|
||||
|
||||
# Accessibility
|
||||
# Accessibility in dept
|
||||
|
||||
Icons are pictures that show what something means without using words. They can be very helpful
|
||||
because they can quickly give information.
|
||||
@@ -22,7 +23,7 @@ Icons are a helpful tool to improve perception, but they aren't a replacement fo
|
||||
In most cases, it is probably a good idea to also provide a textual representation of your icon's
|
||||
function.
|
||||
|
||||

|
||||
<!--@include: ../../docs/images/a11y/visible-labels.svg -->
|
||||
|
||||
## Contrast
|
||||
|
||||
@@ -32,21 +33,21 @@ with low vision or color vision deficiencies.
|
||||
We recommend
|
||||
following [WCAG 2.1 SC 1.4.3](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html).
|
||||
|
||||

|
||||
<!--@include: ../../docs/images/a11y/contrast.svg -->
|
||||
|
||||
## Use of color
|
||||
|
||||
Avoid relying solely on color to convey meaning in icons, as some users may have color blindness.
|
||||
Instead, use additional visual cues like shape, shading or text.
|
||||
|
||||

|
||||
<!--@include: ../../docs/images/a11y/use-of-color.svg -->
|
||||
|
||||
## Interactivity
|
||||
|
||||
Ensure that interactive icons are accessible via keyboard navigation and provide clear feedback when
|
||||
activated.
|
||||
|
||||

|
||||
<!--@include: ../../docs/images/a11y/interactivity.svg -->
|
||||
|
||||
In most cases this is easily done by wrapping them in icon buttons.
|
||||
|
||||
@@ -55,7 +56,7 @@ In most cases this is easily done by wrapping them in icon buttons.
|
||||
Small targets can be difficult to click or touch, if your icon is interactive, we recommend that it
|
||||
should have a minimum target size of 44×44 pixels.
|
||||
|
||||

|
||||
<!--@include: ../../docs/images/a11y/target-size.svg -->
|
||||
|
||||
In practice, this doesn't necessarily mean that the icon itself should be this large, only its
|
||||
interactive wrapper element.
|
||||
@@ -65,14 +66,14 @@ interactive wrapper element.
|
||||
Icons should represent concepts or actions in a universally understandable way. Avoid using abstract
|
||||
or ambiguous, or culture-specific symbols that might confuse some users.
|
||||
|
||||

|
||||
<!--@include: ../../docs/images/a11y/meaningfulness.svg -->
|
||||
|
||||
## Consistency
|
||||
|
||||
Maintain consistency in icon design and usage across your interface to help users learn and
|
||||
understand their meanings more easily.
|
||||
|
||||

|
||||
<!--@include: ../../docs/images/a11y/consistency.svg -->
|
||||
|
||||
## Text Alternatives
|
||||
|
||||
@@ -94,7 +95,7 @@ In case some of your icons stand alone, and they serve a non-decorative function
|
||||
provide the appropriate accessible label for them.
|
||||
:::
|
||||
|
||||

|
||||
<!--@include: ../../docs/images/a11y/alttext-standalone.svg -->
|
||||
|
||||
In general try to avoid using functional icons with no interactivity, we recommend that:
|
||||
|
||||
@@ -109,7 +110,8 @@ elements (badges, buttons, nav items etc.) only, _not_ the icons themselves.
|
||||
Do not provide an accessible label to icons when used on a button, as this label will be read out by
|
||||
screen readers, leading to nonsensical text.
|
||||
|
||||

|
||||
<!--@include: ../../docs/images/a11y/alttext-buttons.svg -->
|
||||
|
||||
|
||||
::: details Code examples
|
||||
|
||||
@@ -137,7 +139,7 @@ the close button of a dialog for example).
|
||||
As previously stated, you should provide your accessible label on the icon button itself, not the
|
||||
contained icon.
|
||||
|
||||

|
||||
<!--@include: ../../docs/images/a11y/alttext-iconbuttons.svg -->
|
||||
|
||||
::: details Code examples
|
||||
|
||||
@@ -220,3 +222,16 @@ We also recommend checking out the following resources about accessibility:
|
||||
- [A11yTalks](https://www.a11ytalks.com/)
|
||||
- [A11y automation tracker](https://a11y-automation.dev/)
|
||||
- [The A11Y Project](https://www.a11yproject.com/)
|
||||
|
||||
<style>
|
||||
svg.a11y-example {
|
||||
max-width: calc(100% + 48px);
|
||||
margin: 0 -24px;
|
||||
}
|
||||
@media (min-width: 480px) {
|
||||
svg.a11y-example {
|
||||
margin: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,103 +0,0 @@
|
||||
# Aliased Names
|
||||
|
||||
Icons can have multiple names for the same icon. This is because we choose to rename some icons to make them more consistent with the rest of the icon set, or the name was not generic. For example, the `edit-2` icon is renamed to `pen` to make the name more generic, since it is just a pen icon.
|
||||
|
||||
Beside aliases names lucide also includes prefixed and suffixed names to use within your project. This is to prevent import name collisions with other libraries or your own code.
|
||||
|
||||
```tsx
|
||||
// These are all the same icon
|
||||
import {
|
||||
House,
|
||||
HouseIcon,
|
||||
LucideHouse,
|
||||
} from "lucide-react";
|
||||
```
|
||||
|
||||
## Choosing import name style
|
||||
|
||||
To be consistent in your imports or want to change the autocompletion of Lucide icons in your IDE there an option to able the choose the import name style you want.
|
||||
|
||||
This can be done by creating a custom module declaration file to override the lucide imports and turning off the autocomplete in your IDE.
|
||||
|
||||
### Turn off autocomplete in your IDE
|
||||
|
||||
```json [.vscode/settings.json]
|
||||
{
|
||||
"typescript.preferences.autoImportFileExcludePatterns": [
|
||||
"lucide-react", // or
|
||||
"lucide-preact", // or
|
||||
"lucide-react-native", // or
|
||||
"lucide-vue-next",
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Create a custom module declaration file
|
||||
|
||||
Only available for `lucide-react`, `lucide-preact`, `lucide-react-native`, `lucide-vue-next` package.
|
||||
This will enable you to choose the import name style you want to use in your project.
|
||||
|
||||
::: code-group
|
||||
|
||||
```ts [React]
|
||||
declare module "lucide-react" {
|
||||
// Prefixed import names
|
||||
export * from "lucide-react/dist/lucide-react.prefixed";
|
||||
// or
|
||||
// Suffixed import names
|
||||
export * from "lucide-react/dist/lucide-react.suffixed";
|
||||
}
|
||||
```
|
||||
|
||||
```ts [Vue]
|
||||
declare module "lucide-vue-next" {
|
||||
// Prefixed import names
|
||||
export * from "lucide-vue-next/dist/lucide-vue-next.prefixed";
|
||||
// or
|
||||
// Suffixed import names
|
||||
export * from "lucide-vue-next/dist/lucide-vue-next.suffixed";
|
||||
}
|
||||
```
|
||||
|
||||
```ts [Preact]
|
||||
declare module "lucide-preact" {
|
||||
// Prefixed import names
|
||||
export * from "lucide-preact/dist/lucide-preact.prefixed";
|
||||
// or
|
||||
// Suffixed import names
|
||||
export * from "lucide-preact/dist/lucide-preact.suffixed";
|
||||
}
|
||||
```
|
||||
|
||||
```ts [React Native]
|
||||
declare module "lucide-react-native" {
|
||||
// Prefixed import names
|
||||
export * from "lucide-react-native/dist/lucide-react-native.prefixed";
|
||||
// or
|
||||
// Suffixed import names
|
||||
export * from "lucide-react-native/dist/lucide-react-native.suffixed";
|
||||
}
|
||||
```
|
||||
|
||||
```ts [@lucide-icons]
|
||||
declare module "@lucide/icons" {
|
||||
// Prefixed import names
|
||||
export * from "@lucide/icons/dist/lucide-icons.prefixed";
|
||||
// or
|
||||
// Suffixed import names
|
||||
export * from "@lucide/icons/dist/lucide-icons.suffixed";
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
Place this in your project root or in a folder where your tsconfig.json is located, or locate it in your defined type directory.
|
||||
Easiest way is to create a `@types` folder in your project root and name the file `[package-name].d.ts`.
|
||||
|
||||
### Import name styles
|
||||
|
||||
| Import Style | Available imports | Declaration file import |
|
||||
| ------------- | --------------------------- | ----------------------- |
|
||||
| Default | Home, HomeIcon, LucideHome | |
|
||||
| Prefixed | LucideHome | [package].prefixed |
|
||||
| Suffixed | HomeIcon | [package].suffixed |
|
||||
@@ -1,23 +0,0 @@
|
||||
import { Star, StarHalf } from "lucide-react";
|
||||
import "./icon.css";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div className="app">
|
||||
<div className="star-rating">
|
||||
<div className="stars">
|
||||
{ Array.from({ length: 5 }, () => (
|
||||
<Star fill="#111" strokeWidth={0} />
|
||||
))}
|
||||
</div>
|
||||
<div className="stars rating">
|
||||
<Star fill="yellow" strokeWidth={0} />
|
||||
<Star fill="yellow" strokeWidth={0} />
|
||||
<StarHalf fill="yellow" strokeWidth={0} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -1,20 +0,0 @@
|
||||
import App from './App.js?raw'
|
||||
import styles from '../../../basics/examples/styles.css?raw'
|
||||
import IconCss from './icon.css?raw'
|
||||
|
||||
const files = {
|
||||
'App.js': {
|
||||
code: App,
|
||||
active: true,
|
||||
},
|
||||
'icon.css': {
|
||||
code: IconCss,
|
||||
readOnly: false,
|
||||
},
|
||||
'styles.css': {
|
||||
code: styles,
|
||||
hidden: true
|
||||
},
|
||||
}
|
||||
|
||||
export default files
|
||||
@@ -1,14 +0,0 @@
|
||||
.star-rating {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.stars {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.rating {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import {
|
||||
TentTree,
|
||||
Caravan,
|
||||
FlameKindling,
|
||||
MountainSnow,
|
||||
Trees,
|
||||
Axe,
|
||||
Map,
|
||||
CloudMoon,
|
||||
Sparkles,
|
||||
} from "lucide-react";
|
||||
import "./icon.css";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div className="app">
|
||||
<TentTree />
|
||||
<Caravan />
|
||||
<FlameKindling />
|
||||
<MountainSnow />
|
||||
<Trees />
|
||||
<Axe />
|
||||
<Map />
|
||||
<CloudMoon />
|
||||
<Sparkles />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -1,20 +0,0 @@
|
||||
import App from './App.js?raw'
|
||||
import styles from '../../../basics/examples/styles.css?raw'
|
||||
import IconCss from './icon.css?raw'
|
||||
|
||||
const files = {
|
||||
'icon.css': {
|
||||
code: IconCss,
|
||||
readOnly: false,
|
||||
active: true,
|
||||
},
|
||||
'App.js': {
|
||||
code: App,
|
||||
},
|
||||
'styles.css': {
|
||||
code:styles,
|
||||
hidden: true
|
||||
},
|
||||
}
|
||||
|
||||
export default files
|
||||
@@ -1,16 +0,0 @@
|
||||
.lucide {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
stroke-width: 1.5;
|
||||
}
|
||||
|
||||
.lucide * {
|
||||
vector-effect: non-scaling-stroke;
|
||||
}
|
||||
|
||||
.app {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
grid-template-rows: 1fr 1fr 1fr;
|
||||
gap: 6px;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import {
|
||||
CakeSlice,
|
||||
Candy,
|
||||
Apple,
|
||||
Cookie,
|
||||
Martini,
|
||||
IceCream2,
|
||||
Sandwich,
|
||||
Wine,
|
||||
Dessert,
|
||||
} from "lucide-react";
|
||||
import "./icon.css";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div className="app">
|
||||
<CakeSlice />
|
||||
<Candy />
|
||||
<Apple />
|
||||
<Cookie />
|
||||
<Martini />
|
||||
<IceCream2 />
|
||||
<Sandwich />
|
||||
<Wine />
|
||||
<Dessert />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -1,20 +0,0 @@
|
||||
import App from './App.js?raw'
|
||||
import styles from '../../../basics/examples/styles.css?raw'
|
||||
import IconCss from './icon.css?raw'
|
||||
|
||||
const files = {
|
||||
'icon.css': {
|
||||
code: IconCss,
|
||||
readOnly: false,
|
||||
active: true,
|
||||
},
|
||||
'App.js': {
|
||||
code: App,
|
||||
},
|
||||
'styles.css': {
|
||||
code:styles,
|
||||
hidden: true
|
||||
},
|
||||
}
|
||||
|
||||
export default files
|
||||
@@ -1,14 +0,0 @@
|
||||
.lucide {
|
||||
/* Change this! */
|
||||
color: #ffadff;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
.app {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
grid-template-rows: 1fr 1fr 1fr;
|
||||
gap: 6px;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<script setup>
|
||||
import { Sandpack } from 'sandpack-vue3'
|
||||
import sandpackTheme from '../../.vitepress/theme/sandpackTheme.json'
|
||||
import sizeIconExample from './examples/filled-icon-example/files.ts'
|
||||
</script>
|
||||
|
||||
# Filled Icons
|
||||
|
||||
Fills are officially not supported.
|
||||
However, all SVG properties are available on all icons.
|
||||
Fill can still be used and will work fine on certain icons.
|
||||
|
||||
Example with stars:
|
||||
<Sandpack
|
||||
template="react"
|
||||
:theme="sandpackTheme"
|
||||
:files="sizeIconExample"
|
||||
:customSetup='{
|
||||
dependencies: {
|
||||
"lucide-react": "latest"
|
||||
}
|
||||
}'
|
||||
:options="{
|
||||
editorHeight: 480,
|
||||
editorWidthPercentage: 60,
|
||||
}"
|
||||
/>
|
||||
|
||||
## Will Lucide have fills in the future?
|
||||
|
||||
This feature has been requested several times and discussion is happening at [#458](https://github.com/lucide-icons/lucide/discussions/458).
|
||||
@@ -1,55 +0,0 @@
|
||||
<script setup>
|
||||
import { Sandpack } from 'sandpack-vue3'
|
||||
import sandpackTheme from '../../.vitepress/theme/sandpackTheme.json'
|
||||
import globalIconCssExample from './examples/global-styling-css-example/files.ts'
|
||||
import globalAbsoluteStrokewidthExample from './examples/global-styling-absolute-strokewidth-example/files.ts'
|
||||
|
||||
</script>
|
||||
|
||||
# Global Styling
|
||||
|
||||
Adjusting icons can be done by [color](../basics/color.md), [size](../basics/sizing.md) and [stroke width](../basics/stroke-width.md).
|
||||
|
||||
By default, all icons have a **color** value of `currentColor`, a **size** value of `24px`, and a **stroke width** of `2`. Styling icons individually can be done by passing props to the icon component.
|
||||
|
||||
## Style by using CSS
|
||||
Styling icons is easy to accomplish using CSS.
|
||||
|
||||
Every icon has a class attribute applied called `lucide`. This class name can be used in the CSS file to target all icons that are being used within the app.
|
||||
|
||||
- The **color** of the icons can be changed using the [`color`](https://developer.mozilla.org/en-US/docs/Web/CSS/color) CSS property.
|
||||
- The **size** of the icons can be changed using [`width`](https://developer.mozilla.org/en-US/docs/Web/CSS/width) and [`height`](https://developer.mozilla.org/en-US/docs/Web/CSS/height) CSS properties.
|
||||
- The **stroke width** of the icons can be changed using the [`stroke-width`](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-width) CSS property.
|
||||
|
||||
<Sandpack
|
||||
template="react"
|
||||
:theme="sandpackTheme"
|
||||
:files="globalIconCssExample"
|
||||
:customSetup='{
|
||||
dependencies: {
|
||||
"lucide-react": "latest"
|
||||
}
|
||||
}'
|
||||
:options="{
|
||||
editorHeight: 300,
|
||||
}"
|
||||
/>
|
||||
|
||||
### Absolute stroke width
|
||||
|
||||
For global absolute stroke width styling the `vector-effect: non-scaling-stroke` CSS property can be applied to the children. This will keep the stroke-width the same size no matter the size of the icon. See [absolute-stroke-width](../basics/stroke-width.md#absolute-stroke-width) for more info.
|
||||
|
||||
<Sandpack
|
||||
template="react"
|
||||
:theme="sandpackTheme"
|
||||
:files="globalAbsoluteStrokewidthExample"
|
||||
:customSetup='{
|
||||
dependencies: {
|
||||
"lucide-react": "latest"
|
||||
}
|
||||
}'
|
||||
:options="{
|
||||
editorHeight: 300,
|
||||
}"
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
title: Accessibility - Angular
|
||||
description: Best practices for accessible icons in your Angular application.
|
||||
---
|
||||
|
||||
<script setup>
|
||||
import OverviewLink from '~/.vitepress/theme/components/base/OverviewLink.vue'
|
||||
</script>
|
||||
|
||||
# Accessibility
|
||||
|
||||
Lucide icons ship with `aria-hidden="true"` by default. In almost all cases this is exactly what you want.
|
||||
|
||||
## Should icons be accessible?
|
||||
|
||||
Most of the time, icons are used purely for decoration or visual reinforcement. Exposing decorative icons to assistive technologies can create unnecessary noise for screen reader users.
|
||||
|
||||
For a broader explanation of this, and other best practices on how to use icons accessibly in your application, please refer to our detailed guide on accessibility:
|
||||
|
||||
<OverviewLink href="/guide/accessibility" title="Accessible Icons" desc="Best practices for accessible icons in your application."/>
|
||||
|
||||
Only if an icon **conveys essential meaning on its own** should it be made accessible. The sections below explain how to do that in Angular.
|
||||
|
||||
## Making an Icon Accessible
|
||||
|
||||
To expose an icon to assistive technologies, provide an accessible name by binding the `title` input of the icon component.
|
||||
|
||||
This removes the `aria-hidden` attribute and makes the icon visible to screen readers.
|
||||
|
||||
```angular-html
|
||||
<svg lucideHouse title="This is my house" />
|
||||
```
|
||||
|
||||
Choose a label that clearly describes the meaning of the icon or the action it represents in the context of your application.
|
||||
|
||||
## Accessible Icon Buttons
|
||||
|
||||
When an icon is used inside a button, the accessible label should usually be applied to the button itself, and not the icon.
|
||||
|
||||
```angular-html
|
||||
<button aria-label="Go to home">
|
||||
<svg lucideHouse></svg>
|
||||
</button>
|
||||
```
|
||||
|
||||
This ensures assistive technologies describe the interactive element, rather than the decorative graphic inside it.
|
||||
@@ -0,0 +1,119 @@
|
||||
---
|
||||
title: Combining icons - Angular
|
||||
description: Learn how to combine multiple icons into a single icon nested SVG elements in your Angular application.
|
||||
---
|
||||
<script setup>
|
||||
import Sandpack from '~/.vitepress/theme/components/editors/SandpackAngular.vue'
|
||||
</script>
|
||||
|
||||
# Combining icons
|
||||
|
||||
You can combine multiple icons into a single icon by using SVG in SVG.
|
||||
This is useful for if you want to be creative and make your own custom icons by combining existing icons.
|
||||
|
||||
::: sandpack {template=angular showTabs=false editorHeight=400 editorWidthPercentage=60 dependencies="@lucide/angular"}
|
||||
|
||||
```ts /src/app/app.component.ts [active]
|
||||
import { Component, ViewEncapsulation } from "@angular/core";
|
||||
import { LucideScan, LucideUser } from "@lucide/angular";
|
||||
|
||||
@Component({
|
||||
selector: 'app',
|
||||
imports: [LucideScan, LucideUser],
|
||||
template: `
|
||||
<svg lucideScan [size]="48">
|
||||
<svg lucideUser
|
||||
[size]="12"
|
||||
x="6"
|
||||
y="6"
|
||||
[strokeWidth]="4"
|
||||
[absoluteStrokeWidth]="true"
|
||||
/>
|
||||
</svg>`,
|
||||
styleUrls: ['./app.component.css'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class App {
|
||||
}
|
||||
```
|
||||
:::
|
||||
|
||||
This is valid SVG and all SVG properties are supported on the icons.
|
||||
The `x` and `y` coordinates can be adjusted to position the icons as you like.
|
||||
|
||||
::: info Limitation
|
||||
When combining icons, you need to make sure that the `x` and `y` coordinates are within the `viewBox` of the outer icon (24x24).
|
||||
:::
|
||||
|
||||
## With custom SVG elements
|
||||
|
||||
You can also use SVG elements to create your own icons.
|
||||
|
||||
### Example with notification badge
|
||||
|
||||
For example, you can add a notification badge to an icon by using the `circle` SVG element.
|
||||
|
||||
|
||||
::: sandpack {template=angular showTabs=false editorHeight=400 editorWidthPercentage=60 dependencies="@lucide/angular"}
|
||||
|
||||
```ts /src/app/app.component.ts [active]
|
||||
import { Component, ViewEncapsulation, signal } from "@angular/core";
|
||||
import { LucideMail } from "@lucide/angular";
|
||||
|
||||
@Component({
|
||||
selector: 'app',
|
||||
imports: [LucideMail],
|
||||
template: `
|
||||
<svg lucideMail [size]="48">
|
||||
@if (hasUnreadMessages()) {
|
||||
<circle
|
||||
r="3"
|
||||
cx="21"
|
||||
cy="5"
|
||||
stroke="none"
|
||||
fill="#F56565"
|
||||
/>
|
||||
}
|
||||
</svg>`,
|
||||
styleUrls: ['./app.component.css'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class App {
|
||||
protected readonly hasUnreadMessages = signal<boolean>(true);
|
||||
}
|
||||
```
|
||||
:::
|
||||
|
||||
### Example with text element
|
||||
|
||||
You can also use the `text` SVG element to add text to your icon.
|
||||
|
||||
|
||||
::: sandpack {template=angular showTabs=false editorHeight=400 editorWidthPercentage=60 dependencies="@lucide/angular"}
|
||||
|
||||
```ts /src/app/app.component.ts [active]
|
||||
import { Component, ViewEncapsulation } from "@angular/core";
|
||||
import { LucideFile } from "@lucide/angular";
|
||||
|
||||
@Component({
|
||||
selector: 'app',
|
||||
imports: [LucideFile],
|
||||
template: `
|
||||
<svg lucideFile [size]="48">
|
||||
<text
|
||||
x="7.5"
|
||||
y="19"
|
||||
font-size="8"
|
||||
font-family="Verdana,sans-serif"
|
||||
stroke-width="1"
|
||||
>
|
||||
JS
|
||||
</text>
|
||||
</svg>`,
|
||||
styleUrls: ['./app.component.css'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class App {
|
||||
}
|
||||
```
|
||||
:::
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
title: Filled Icons - Angular
|
||||
description: Fills are not officially supported, but can still be applied using standard SVG attributes. This may in some cases produce acceptable results.
|
||||
---
|
||||
<script setup>
|
||||
import Sandpack from '~/.vitepress/theme/components/editors/SandpackAngular.vue'
|
||||
</script>
|
||||
|
||||
# Filled Icons
|
||||
|
||||
Fills are not officially supported by Lucide.
|
||||
|
||||
However, since the icons are standard SVG elements, SVG attributes such as `fill` can still be applied. Depending on the icon, this may produce acceptable results.
|
||||
|
||||
## Example with stars:
|
||||
|
||||
::: sandpack {template=angular editorHeight=580 editorWidthPercentage=60 dependencies="@lucide/angular"}
|
||||
|
||||
```css /src/app/icon.css
|
||||
.star-rating {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.stars {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.rating {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
```
|
||||
|
||||
```ts /src/app/app.component.ts [active]
|
||||
import { Component, ViewEncapsulation } from "@angular/core";
|
||||
import { LucideStar, LucideStarHalf } from "@lucide/angular";
|
||||
|
||||
@Component({
|
||||
selector: 'app',
|
||||
imports: [LucideStar, LucideStarHalf],
|
||||
template: `
|
||||
<div class="star-rating">
|
||||
<div class="stars">
|
||||
<svg lucideStar fill="#111" strokeWidth="0" />
|
||||
<svg lucideStar fill="#111" strokeWidth="0" />
|
||||
<svg lucideStar fill="#111" strokeWidth="0" />
|
||||
<svg lucideStar fill="#111" strokeWidth="0" />
|
||||
<svg lucideStar fill="#111" strokeWidth="0" />
|
||||
</div>
|
||||
<div class="stars rating">
|
||||
<svg lucideStar fill="yellow" strokeWidth="0" />
|
||||
<svg lucideStar fill="yellow" strokeWidth="0" />
|
||||
<svg lucideStarHalf fill="yellow" strokeWidth="0" />
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
styleUrls: ['./app.component.css', './icon.css'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class App {
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
@@ -0,0 +1,184 @@
|
||||
---
|
||||
title: Global Styling - Angular
|
||||
description: Learn how to style all icons globally in your Angular application using CSS or the provideLucideConfig provider.
|
||||
---
|
||||
<script setup>
|
||||
import Sandpack from '~/.vitepress/theme/components/editors/SandpackAngular.vue'
|
||||
</script>
|
||||
|
||||
# Global Styling
|
||||
|
||||
Lucide icons can be customized using the inputs for [color](../basics/color.md), [size](../basics/sizing.md) and [stroke width](../basics/stroke-width.md).
|
||||
|
||||
To style all icons globally, you can either use CSS or configure global defaults using `provideLucideConfig`.
|
||||
|
||||
We recommend using CSS for global styling, as it is the most straightforward approach. However, CSS rules may override the `size`, `color`, and `strokeWidth` inputs on individual icons. If you need to keep those inputs configurable per icon, use `provideLucideConfig` instead.
|
||||
|
||||
## Configuring global defaults
|
||||
|
||||
Lucide Angular provides the `provideLucideConfig` provider to set default properties for all icons.
|
||||
|
||||
You can define global defaults (such as `size`, `color`, or `strokeWidth`) while still allowing individual icons to override them through inputs.
|
||||
|
||||
Register the provider in your application configuration or in a top-level component:
|
||||
|
||||
```ts
|
||||
import { ApplicationConfig } from '@angular/core';
|
||||
import { provideLucideConfig } from '@lucide/angular';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideLucideConfig({
|
||||
strokeWidth: 1.5
|
||||
}),
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
## Style by using CSS
|
||||
|
||||
Styling icons globally can be done using CSS.
|
||||
|
||||
All Lucide icons include the `lucide` class. You can use this class in your styles to target every icon in your application.
|
||||
|
||||
- The **color** of the icons can be changed using the [`color`](https://developer.mozilla.org/en-US/docs/Web/CSS/color) property.
|
||||
- The **size** of the icons can be changed using [`width`](https://developer.mozilla.org/en-US/docs/Web/CSS/width) and [`height`](https://developer.mozilla.org/en-US/docs/Web/CSS/height).
|
||||
- The **stroke width** of the icons can be changed using [`stroke-width`](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-width).
|
||||
|
||||
::: sandpack {template=angular editorHeight=300 dependencies="@lucide/angular"}
|
||||
|
||||
```css /src/app/icon.css [active]
|
||||
.lucide {
|
||||
/* Change this! */
|
||||
color: #ffadff;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
grid-template-rows: 1fr 1fr 1fr;
|
||||
gap: 6px;
|
||||
}
|
||||
```
|
||||
|
||||
```ts /src/app/app.component.ts
|
||||
import { Component, ViewEncapsulation } from "@angular/core";
|
||||
import {
|
||||
LucideCakeSlice,
|
||||
LucideCandy,
|
||||
LucideApple,
|
||||
LucideCookie,
|
||||
LucideMartini,
|
||||
LucideIceCream2,
|
||||
LucideSandwich,
|
||||
LucideWine,
|
||||
LucideDessert,
|
||||
} from "@lucide/angular";
|
||||
|
||||
@Component({
|
||||
selector: 'app',
|
||||
imports: [
|
||||
LucideCakeSlice,
|
||||
LucideCandy,
|
||||
LucideApple,
|
||||
LucideCookie,
|
||||
LucideMartini,
|
||||
LucideIceCream2,
|
||||
LucideSandwich,
|
||||
LucideWine,
|
||||
LucideDessert,
|
||||
],
|
||||
template: `<div class="grid">
|
||||
<svg lucideCakeSlice />
|
||||
<svg lucideCandy />
|
||||
<svg lucideApple />
|
||||
<svg lucideCookie />
|
||||
<svg lucideMartini />
|
||||
<svg lucideIceCream2 />
|
||||
<svg lucideSandwich />
|
||||
<svg lucideWine />
|
||||
<svg lucideDessert />
|
||||
</div>`,
|
||||
styleUrls: ['./app.component.css', './icon.css'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class App {
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Absolute stroke width
|
||||
|
||||
To keep the stroke width constant regardless of icon size, apply `vector-effect: non-scaling-stroke` to the icon's children. See [absolute-stroke-width](../basics/stroke-width.md#absolute-stroke-width) for more details.
|
||||
|
||||
::: sandpack {template=angular editorHeight=300 dependencies="@lucide/angular"}
|
||||
|
||||
```css /src/app/icon.css [active]
|
||||
.lucide {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
stroke-width: 1.5;
|
||||
}
|
||||
|
||||
.lucide * {
|
||||
vector-effect: non-scaling-stroke;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
grid-template-rows: 1fr 1fr 1fr;
|
||||
gap: 6px;
|
||||
}
|
||||
```
|
||||
|
||||
```ts /src/app/app.component.ts
|
||||
import { Component, ViewEncapsulation } from "@angular/core";
|
||||
import {
|
||||
LucideTentTree,
|
||||
LucideCaravan,
|
||||
LucideFlameKindling,
|
||||
LucideMountainSnow,
|
||||
LucideTrees,
|
||||
LucideAxe,
|
||||
LucideMap,
|
||||
LucideCloudMoon,
|
||||
LucideSparkles,
|
||||
} from "@lucide/angular";
|
||||
|
||||
@Component({
|
||||
selector: 'app',
|
||||
imports: [
|
||||
LucideTentTree,
|
||||
LucideCaravan,
|
||||
LucideFlameKindling,
|
||||
LucideMountainSnow,
|
||||
LucideTrees,
|
||||
LucideAxe,
|
||||
LucideMap,
|
||||
LucideCloudMoon,
|
||||
LucideSparkles,
|
||||
],
|
||||
template: `<div class="grid">
|
||||
<svg lucideTentTree />
|
||||
<svg lucideCaravan />
|
||||
<svg lucideFlameKindling />
|
||||
<svg lucideMountainSnow />
|
||||
<svg lucideTrees />
|
||||
<svg lucideAxe />
|
||||
<svg lucideMap />
|
||||
<svg lucideCloudMoon />
|
||||
<svg lucideSparkles />
|
||||
</div>`,
|
||||
styleUrls: ['./app.component.css', './icon.css'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class App {
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
@@ -0,0 +1,139 @@
|
||||
---
|
||||
title: Icon provider - Angular
|
||||
description: Learn how to register icons globally using provideLucideIcons, including custom and legacy icons.
|
||||
---
|
||||
|
||||
# Icon provider
|
||||
|
||||
You can use the `LucideDynamicIcon` component to render icons by name. To use an icon by name, you must first register it with `provideLucideIcons`.
|
||||
|
||||
Make sure you are well acquainted with [how dependency injection in Angular works](https://angular.dev/guide/di).
|
||||
|
||||
## Registering icons
|
||||
|
||||
Use `provideLucideIcons` in your application providers to register icons.
|
||||
|
||||
```ts [app.config.ts]
|
||||
import { ApplicationConfig } from '@angular/core';
|
||||
import { provideLucideIcons, LucideSquareCheck, LucideCircleAlert } from '@lucide/angular';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideLucideIcons(
|
||||
LucideSquareCheck,
|
||||
LucideCircleAlert,
|
||||
),
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
You can then reference the registered icons by name:
|
||||
|
||||
```html [app.html]
|
||||
<svg lucideIcon="square-check"></svg>
|
||||
<svg lucideIcon="circle-alert"></svg>
|
||||
```
|
||||
|
||||
## How names are resolved
|
||||
|
||||
Each registered icon is stored by its icon name. For built-in Lucide icons, this is typically the kebab-case icon name.
|
||||
|
||||
For example, registering `LucideSquareCheck` makes it available as:
|
||||
|
||||
```html
|
||||
<svg lucideIcon="square-check"></svg>
|
||||
```
|
||||
|
||||
If an icon defines aliases, those aliases are also registered automatically.
|
||||
|
||||
## Registering custom icons
|
||||
|
||||
`provideLucideIcons` can also register custom icon data objects.
|
||||
|
||||
```ts [custom-icon.ts]
|
||||
import { LucideIconData } from '@lucide/angular';
|
||||
|
||||
export const MyCustomIcon: LucideIconData = {
|
||||
name: 'my-custom-icon',
|
||||
node: [
|
||||
['circle', { cx: 12, cy: 12, r: 8 }],
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
```ts [app.config.ts]
|
||||
import { ApplicationConfig } from '@angular/core';
|
||||
import { provideLucideIcons } from '@lucide/angular';
|
||||
import { MyCustomIcon } from './custom-icon';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideLucideIcons(MyCustomIcon),
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
```html [app.html]
|
||||
<svg lucideIcon="my-custom-icon"></svg>
|
||||
```
|
||||
|
||||
## Using legacy icon nodes
|
||||
|
||||
If you have icons in the legacy node format, such as custom icons matching `lucide-angular` or `@lucide/lab`, you can convert them using `lucideLegacyIcon`.
|
||||
|
||||
```ts [app.config.ts]
|
||||
import { ApplicationConfig } from '@angular/core';
|
||||
import { provideLucideIcons, lucideLegacyIcon } from '@lucide/angular';
|
||||
import { CirclePlayIcon } from 'lucide-angular';
|
||||
import { burger } from '@lucide/lab';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideLucideIcons(
|
||||
lucideLegacyIcon('circle-play', CirclePlayIcon, ['play-circle']),
|
||||
lucideLegacyIcon('burger', burger, ['hamburger']),
|
||||
),
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
You can then use both the primary name and any aliases:
|
||||
|
||||
```html [app.html]
|
||||
<svg lucideIcon="circle-play"></svg>
|
||||
<svg lucideIcon="play-circle"></svg>
|
||||
<svg lucideIcon="burger"></svg>
|
||||
<svg lucideIcon="hamburger"></svg>
|
||||
```
|
||||
|
||||
## Converting a map of legacy icons
|
||||
|
||||
If you already have a map of legacy icon nodes, use `lucideLegacyIconMap` to convert them into icon data objects.
|
||||
|
||||
```ts [app.config.ts]
|
||||
import { ApplicationConfig } from '@angular/core';
|
||||
import { provideLucideIcons, lucideLegacyIconMap, Circle } from '@lucide/angular';
|
||||
import { UserRoundX } from 'lucide-angular';
|
||||
import { burger } from '@lucide/lab';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideLucideIcons(
|
||||
Circle,
|
||||
...lucideLegacyIconMap({ UserRoundX, burger }),
|
||||
),
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
This converts the object keys to kebab-case icon names:
|
||||
|
||||
- `UserRoundX` → `user-round-x`
|
||||
- `burger` → `burger`
|
||||
|
||||
The original object key is also added as an alias, so both of these will work:
|
||||
|
||||
```html
|
||||
<svg lucideIcon="UserRoundX"></svg>
|
||||
<svg lucideIcon="user-round-x"></svg>
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user