mirror of
https://github.com/lucide-icons/lucide.git
synced 2026-11-14 13:58:21 +00:00
Add first cusotmizer
This commit is contained in:
@@ -2,12 +2,26 @@
|
||||
import { ref, computed } from 'vue'
|
||||
import { useData } from 'vitepress'
|
||||
import VPDocOutlineItem from 'vitepress/dist/client/theme-default/components/VPDocOutlineItem.vue'
|
||||
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue'
|
||||
import { isActive } from 'vitepress/dist/client/theme-default/support/utils'
|
||||
import { onContentUpdated } from 'vitepress'
|
||||
import { useActiveAnchor } from '../composables/useActiveAnchor'
|
||||
import { data } from './CategoriesList.data'
|
||||
import { useRouter } from 'vitepress';
|
||||
|
||||
|
||||
const { go } = useRouter()
|
||||
|
||||
const { frontmatter, theme, params, page } = useData()
|
||||
|
||||
const categoriesIsActive = computed(() => {
|
||||
return isActive(page.value.relativePath, '/icons/categories');
|
||||
});
|
||||
|
||||
const overviewIsActive = computed(() => {
|
||||
return isActive(page.value.relativePath, '/icons/');
|
||||
});
|
||||
|
||||
interface Header {
|
||||
level: number
|
||||
title: string
|
||||
@@ -19,16 +33,6 @@ type MenuItem = Omit<Header, 'slug' | 'children'> & {
|
||||
children?: MenuItem[]
|
||||
}
|
||||
|
||||
// const headers = shallowRef<MenuItem[]>([])
|
||||
|
||||
// onContentUpdated(() => {
|
||||
// headers.value = data.categories.map(({ name, title }) => ({
|
||||
// level: 2,
|
||||
// link: `#${name}`,
|
||||
// title,
|
||||
// }))
|
||||
// })
|
||||
|
||||
const headers = computed(() => {
|
||||
const linkPrefix = page.value.relativePath.startsWith('icons/categories')
|
||||
? '' : '/icons/categories'
|
||||
@@ -48,9 +52,12 @@ useActiveAnchor(container, marker)
|
||||
|
||||
<template>
|
||||
<div class="category-list" ref="container">
|
||||
<h2 class="sidebar-title">
|
||||
<VPLink class="sidebar-title" href="/icons/" :class="{ 'active': overviewIsActive } ">
|
||||
All
|
||||
</VPLink>
|
||||
<VPLink class="sidebar-title" href="/icons/categories" :class="{ 'active': categoriesIsActive } ">
|
||||
Categories
|
||||
</h2>
|
||||
</VPLink>
|
||||
<div class="content">
|
||||
<div class="outline-marker" ref="marker" />
|
||||
<nav aria-labelledby="doc-outline-aria-label">
|
||||
@@ -62,16 +69,23 @@ useActiveAnchor(container, marker)
|
||||
|
||||
<style scoped>
|
||||
.category-list {
|
||||
margin-top: -16px;
|
||||
/* margin-top: -24px; */
|
||||
}
|
||||
.sidebar-title {
|
||||
font-weight: 700;
|
||||
color: var(--vp-c-text-1);
|
||||
margin-bottom: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 6px;
|
||||
line-height: 24px;
|
||||
font-size: 14px;
|
||||
display: block;
|
||||
transition: color 0.25s;
|
||||
}
|
||||
|
||||
.sidebar-title:hover, .sidebar-title.active {
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
.content {
|
||||
margin-top: 12px;
|
||||
position: relative;
|
||||
border-left: 1px solid var(--vp-c-divider);
|
||||
padding-left: 16px;
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
<script setup>
|
||||
import { useCssVar } from '@vueuse/core'
|
||||
|
||||
const color = useCssVar(
|
||||
'--customize-color',
|
||||
document.documentElement,
|
||||
{
|
||||
initialValue: 'currentColor'
|
||||
}
|
||||
)
|
||||
|
||||
const strokeWidth = useCssVar(
|
||||
'--customize-strokeWidth',
|
||||
document.documentElement,
|
||||
{
|
||||
initialValue: '2'
|
||||
}
|
||||
)
|
||||
|
||||
const size = useCssVar(
|
||||
'--customize-size',
|
||||
document.documentElement,
|
||||
{
|
||||
initialValue: '24'
|
||||
}
|
||||
)
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card">
|
||||
<label for="icon-color" class="customize-label">
|
||||
Color
|
||||
</label>
|
||||
<div class="color-picker">
|
||||
|
||||
<div class="color-input-wrapper">
|
||||
<input
|
||||
type="color"
|
||||
id="icon-color"
|
||||
name="icon-color"
|
||||
class="color-input"
|
||||
v-model="color"
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
id="icon-color-input"
|
||||
name="icon-color-input"
|
||||
class="color-input-text"
|
||||
v-model="color"
|
||||
/>
|
||||
</div>
|
||||
<label for="stroke" class="customize-label">
|
||||
Stroke width
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
id="stroke"
|
||||
name="stroke"
|
||||
v-model="strokeWidth"
|
||||
/>
|
||||
|
||||
<label for="size" class="customize-label">
|
||||
Size
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
id="size"
|
||||
name="size"
|
||||
v-model="size"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.customize-label {
|
||||
padding-bottom: 8px;
|
||||
line-height: 20px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--vt-c-text-1);
|
||||
transition: color .5s;
|
||||
display: block;
|
||||
}
|
||||
.card {
|
||||
background: var(--vp-c-bg);
|
||||
padding: 24px;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.color-input {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
left: -5px;
|
||||
}
|
||||
.color-input-wrapper {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
border-radius: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.color-picker {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 8px;
|
||||
color: var(--vp-c-text-2);
|
||||
padding: 8px 12px;
|
||||
height: auto;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
border: 1px solid transparent;
|
||||
cursor: text;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.color-input-text {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 8px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
border-radius: 8px;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.color-picker:hover, .color-picker:focus {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-bg-alt);
|
||||
}
|
||||
</style>
|
||||
@@ -19,7 +19,12 @@ const tags = computed(() => {
|
||||
|
||||
<template>
|
||||
<!-- <PageContainer class="overview"> -->
|
||||
<IconPreview :name="$params.name" :iconNode="$params.iconNode" class="preview" />
|
||||
<IconPreview
|
||||
:name="$params.name"
|
||||
:iconNode="$params.iconNode"
|
||||
class="preview"
|
||||
customizable
|
||||
/>
|
||||
<div class="details">
|
||||
<h1 class="title">
|
||||
{{ $params.name }}
|
||||
|
||||
@@ -52,7 +52,11 @@
|
||||
<component :is="CloseIcon" />
|
||||
</IconButton>
|
||||
</nav>
|
||||
<IconPreview :name="props.icon.name" :iconNode="props.icon.iconNode"/>
|
||||
<IconPreview
|
||||
:name="props.icon.name"
|
||||
:iconNode="props.icon.iconNode"
|
||||
customizable
|
||||
/>
|
||||
<div class="icon-info">
|
||||
<IconDetailName class="icon-name">
|
||||
{{ props.icon.name }}
|
||||
|
||||
@@ -22,6 +22,7 @@ function setActiveIcon(name: string) {
|
||||
v-bind="icon"
|
||||
@setActiveIcon="setActiveIcon(icon.name)"
|
||||
:active="activeIcon === icon.name"
|
||||
customizable
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,6 +14,7 @@ const props = defineProps<{
|
||||
// contributors: Contributor[];
|
||||
iconNode: IconNode;
|
||||
active: boolean;
|
||||
customizable?: boolean;
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['setActiveIcon'])
|
||||
@@ -43,7 +44,7 @@ function navigateToIcon() {
|
||||
:aria-label="name"
|
||||
>
|
||||
<KeepAlive>
|
||||
<component :is="icon" />
|
||||
<component :is="icon" class="lucide-icon" :class="{ customizable }" size="auto"/>
|
||||
</KeepAlive>
|
||||
</button>
|
||||
</template>
|
||||
@@ -54,7 +55,6 @@ function navigateToIcon() {
|
||||
border: 1px solid transparent;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
white-space: nowrap;
|
||||
transition: color 0.25s, border-color 0.25s, background-color 0.25s;
|
||||
@@ -76,7 +76,7 @@ function navigateToIcon() {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
margin-left: 12px;
|
||||
margin-left: 24px;
|
||||
transform: translate(-50%, 48px) scale(0.9);
|
||||
font-weight: 400;
|
||||
position: absolute;
|
||||
@@ -135,4 +135,15 @@ function navigateToIcon() {
|
||||
/* color: var(--vp-button-alt-active-text);
|
||||
background-color: var(--vp-button-alt-active-bg); */
|
||||
}
|
||||
|
||||
.lucide-icon {
|
||||
margin: auto;
|
||||
}
|
||||
.lucide-icon.customizable {
|
||||
will-change: width, height, stroke-width, stroke;
|
||||
color: var(--customize-color, currentColor);
|
||||
stroke-width: var(--customize-strokeWidth, 2);
|
||||
width: calc(var(--customize-size, 24) * 1px);
|
||||
height: calc(var(--customize-size, 24) * 1px);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,6 +7,7 @@ import CloseButton from './CloseButton.vue';
|
||||
const props = defineProps<{
|
||||
name: IconEntity['name']
|
||||
iconNode: IconEntity['iconNode']
|
||||
customizable?: boolean
|
||||
}>()
|
||||
|
||||
const size = 24
|
||||
@@ -22,7 +23,7 @@ const iconComponent = computed(() => {
|
||||
|
||||
<template>
|
||||
<div class="icon-container">
|
||||
<component :is="iconComponent" class="icon-component" />
|
||||
<component :is="iconComponent" class="icon-component" :class="{ customizable }"/>
|
||||
<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" />
|
||||
@@ -57,4 +58,12 @@ const iconComponent = computed(() => {
|
||||
color: var(--vp-c-neutral);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.icon-component.customizable {
|
||||
will-change: width, height, stroke-width, stroke;
|
||||
color: var(--customize-color, currentColor);
|
||||
stroke-width: var(--customize-strokeWidth, 2);
|
||||
width: calc(var(--customize-size, 24) * 1px);
|
||||
height: calc(var(--customize-size, 24) * 1px);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -86,7 +86,7 @@ export default defineConfig({
|
||||
}
|
||||
],
|
||||
'icons': [
|
||||
{ text: 'All', link: '/icons/' },
|
||||
{ text: '', link: '/' },
|
||||
// { text: 'Categorized', link: '/icons/categorized' },
|
||||
// {
|
||||
// text: 'Categories',
|
||||
|
||||
@@ -2,14 +2,14 @@ import { h } from 'vue'
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import './style.css'
|
||||
import { Theme } from 'vitepress'
|
||||
import IconsLayout from './layouts/IconsLayout.vue'
|
||||
import IconsSidebarNavAfter from './layouts/IconsSidebarNavAfter.vue'
|
||||
import HomeHeroIconsCard from '../components/HomeHeroIconsCard.vue'
|
||||
|
||||
const theme: Partial<Theme> = {
|
||||
extends: DefaultTheme,
|
||||
Layout() {
|
||||
return h(DefaultTheme.Layout, null, {
|
||||
'sidebar-nav-after': () => h(IconsLayout),
|
||||
'sidebar-nav-after': () => h(IconsSidebarNavAfter),
|
||||
'home-hero-image': () => h(HomeHeroIconsCard)
|
||||
})
|
||||
},
|
||||
|
||||
+2
@@ -4,6 +4,7 @@ import { useData } from 'vitepress'
|
||||
|
||||
import { useSidebar } from 'vitepress/dist/client/theme-default/composables/sidebar.js'
|
||||
import CategoriesList from '../../components/CategoriesList.vue'
|
||||
import IconCustomizer from '../../components/IconCustomizer.vue'
|
||||
|
||||
const { frontmatter, page } = useData()
|
||||
const { isOpen, isSidebarEnabled } = useSidebar()
|
||||
@@ -17,6 +18,7 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<IconCustomizer/>
|
||||
<CategoriesList v-if="page?.relativePath?.startsWith?.('icons')"/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -5,6 +5,7 @@ aside: false
|
||||
editLink: false
|
||||
next: false
|
||||
prev: false
|
||||
sidebar: true
|
||||
---
|
||||
<script setup>
|
||||
import IconPreview from '../.vitepress/components/IconPreview.vue'
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
layout: page
|
||||
outline: 2
|
||||
outlineTitle: Categories
|
||||
sidebar: true
|
||||
---
|
||||
|
||||
<script setup>
|
||||
|
||||
@@ -14,7 +14,6 @@ export function getAllCategoryFiles() {
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
async load() {
|
||||
const icons = await getAllData()
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
layout: page
|
||||
outline: 2
|
||||
outlineTitle: Categories
|
||||
sidebar: true
|
||||
---
|
||||
|
||||
<script setup>
|
||||
|
||||
Reference in New Issue
Block a user