mirror of
https://github.com/lucide-icons/lucide.git
synced 2026-11-14 13:58:21 +00:00
Add icon customizer
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: string
|
||||
id: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const value = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (val) => emit('update:modelValue', val)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="color-picker">
|
||||
<div class="color-input-wrapper">
|
||||
<input
|
||||
type="color"
|
||||
:id="id"
|
||||
:name="id"
|
||||
class="color-input"
|
||||
v-model="value"
|
||||
/>
|
||||
</div>
|
||||
<!-- TODO: This should have an hidden label -->
|
||||
<input
|
||||
type="text"
|
||||
:id="`${id}-input`"
|
||||
:name="`${id}-input`"
|
||||
class="color-input-text"
|
||||
v-model="value"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.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(--color-picker-bg, var(--vp-c-bg-soft));
|
||||
border-radius: 8px;
|
||||
color: var(--vp-c-text-2);
|
||||
padding: 4px 8px;
|
||||
height: auto;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
border: 1px solid transparent;
|
||||
cursor: text;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.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>
|
||||
@@ -7,18 +7,29 @@
|
||||
<style scoped>
|
||||
.home-container {
|
||||
position: relative;
|
||||
padding: 24px;
|
||||
padding-inline: 24px;
|
||||
margin-block: 24px;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.home-container {
|
||||
padding: 48px;
|
||||
padding-inline: 48px;
|
||||
margin-block: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.home-container {
|
||||
padding: 64px;
|
||||
padding-inline: 64px;
|
||||
margin-block: 64px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.home-container {
|
||||
margin: 64px auto;
|
||||
max-width: 1152px;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -32,12 +32,16 @@ function insert() {
|
||||
items.value[replaceIndex] = newIcon
|
||||
}
|
||||
|
||||
function startInterval() {
|
||||
intervalTime.value = setInterval(() => {
|
||||
insert()
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
// TODO: Try maybe something else for better pref performance
|
||||
// onMounted(() => {
|
||||
// intervalTime.value = setInterval(() => {
|
||||
// insert()
|
||||
// }, 2000)
|
||||
// })
|
||||
onMounted(() => {
|
||||
window.addEventListener('mousemove', startInterval, { once: true })
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(intervalTime.value)
|
||||
@@ -76,7 +80,7 @@ onBeforeUnmount(() => {
|
||||
/* padding: 0 24px; */
|
||||
margin-left: auto;
|
||||
margin-bottom: auto;
|
||||
margin-top: 48px;;
|
||||
margin-top: 48px;
|
||||
}
|
||||
.icons-card {
|
||||
background: var(--vp-c-bg-alt);
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
<script setup lang="ts">
|
||||
import { syncRef, useCssVar } from '@vueuse/core'
|
||||
import HomeContainer from './HomeContainer.vue'
|
||||
import { rotateCw } from '../../iconNodes'
|
||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
|
||||
import RangeSlider from './RangeSlider.vue'
|
||||
import IconButton from './IconButton.vue'
|
||||
import InputField from './InputField.vue'
|
||||
import ColorPicker from './ColorPicker.vue'
|
||||
import HomeIconCustomizerIcons from './HomeIconCustomizerIcons.vue'
|
||||
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const iconContainer = ref<HTMLElement | null>()
|
||||
const color = ref('currentColor')
|
||||
const strokeWidth = ref(2)
|
||||
const size = ref(24)
|
||||
|
||||
const colorCssVar = useCssVar(
|
||||
'--customize-color',
|
||||
iconContainer,
|
||||
{
|
||||
initialValue: 'default'
|
||||
}
|
||||
)
|
||||
|
||||
const strokeWidthCssVar = useCssVar(
|
||||
'--customize-strokeWidth',
|
||||
iconContainer,
|
||||
{
|
||||
initialValue: '2'
|
||||
}
|
||||
)
|
||||
|
||||
const sizeCssVar = useCssVar(
|
||||
'--customize-size',
|
||||
iconContainer,
|
||||
{
|
||||
initialValue: '24'
|
||||
}
|
||||
)
|
||||
|
||||
syncRef(color, colorCssVar)
|
||||
syncRef(strokeWidth, strokeWidthCssVar)
|
||||
syncRef(size, sizeCssVar)
|
||||
|
||||
const RotateIcon = createLucideIcon('RotateIcon', rotateCw)
|
||||
|
||||
function resetStyle () {
|
||||
color.value = 'currentColor'
|
||||
strokeWidth.value = 2
|
||||
size.value = 24
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<HomeContainer>
|
||||
<div class="card">
|
||||
<div class="card-column">
|
||||
|
||||
|
||||
<h2 class="title">
|
||||
Style as you please
|
||||
</h2>
|
||||
<p class="copy">
|
||||
Lucide has a lot of customization options to match the icons with you UI.
|
||||
</p>
|
||||
|
||||
<div class="customizer">
|
||||
<InputField
|
||||
id="icon-color"
|
||||
label="Color"
|
||||
class="color-picker-field"
|
||||
>
|
||||
<template #display>
|
||||
<ColorPicker v-model="color" id="icon-color" />
|
||||
</template>
|
||||
</InputField>
|
||||
|
||||
<InputField
|
||||
id="stroke-width"
|
||||
label="Stroke width"
|
||||
>
|
||||
<template #display>
|
||||
<span class="customize-label">{{ strokeWidth }}px</span>
|
||||
</template>
|
||||
<RangeSlider
|
||||
id="stroke-width"
|
||||
name="stroke-width"
|
||||
v-model="strokeWidth"
|
||||
:min="1"
|
||||
:max="4"
|
||||
:steps="0.5"
|
||||
/>
|
||||
</InputField>
|
||||
|
||||
<InputField
|
||||
id="size"
|
||||
label="Size"
|
||||
>
|
||||
<template #display>
|
||||
<span class="customize-label">{{ size }}px</span>
|
||||
</template>
|
||||
<RangeSlider
|
||||
id="size"
|
||||
name="size"
|
||||
v-model="size"
|
||||
:min="16"
|
||||
:max="48"
|
||||
/>
|
||||
</InputField>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="icons-container card-column" ref="iconContainer">
|
||||
<HomeIconCustomizerIcons />
|
||||
</div>
|
||||
</div>
|
||||
</HomeContainer>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card {
|
||||
display: block;
|
||||
border-radius: 12px;
|
||||
height: 100%;
|
||||
background-color: var(--vp-c-bg-soft);
|
||||
padding: 24px;
|
||||
--slider-bar-color: var(--vp-c-bg-soft-down);
|
||||
--color-picker-bg: var(--vp-c-bg-soft-down);
|
||||
}
|
||||
|
||||
.title {
|
||||
line-height: 32px;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.copy {
|
||||
padding-top: 8px;
|
||||
line-height: 24px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.customizer {
|
||||
margin-top: 32px;
|
||||
padding: 0;
|
||||
background: none;
|
||||
max-width: 280px;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
|
||||
.card {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
/*
|
||||
.card-column {
|
||||
flex: 1;
|
||||
} */
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.card {
|
||||
grid-template-columns: 1fr 2fr;
|
||||
}
|
||||
}
|
||||
|
||||
.color-picker-field:deep(.display-value) {
|
||||
width: 130px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,66 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { data } from './HomeHeroIconsCard.data'
|
||||
import IconButton from './IconButton.vue';
|
||||
import LucideIcon from './LucideIcon.vue'
|
||||
|
||||
const getInitialItems = () => data.icons.slice(0, 64)
|
||||
const items = ref(getInitialItems())
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="icon-grid">
|
||||
<div
|
||||
v-for="icon in items"
|
||||
class="icon-grid-item"
|
||||
>
|
||||
<LucideIcon v-bind="icon" class="lucide-icon"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.icon-grid {
|
||||
display: grid;
|
||||
gap: 1px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(56px, 1fr));
|
||||
grid-template-rows: repeat(auto-fill, minmax(56px, 1fr));
|
||||
width: 100%;
|
||||
height:100%;
|
||||
max-height: 300px;
|
||||
gap: 1px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
border-radius: 12px;
|
||||
border: 8px solid var(--vp-c-bg);
|
||||
position: relative;
|
||||
top: 48px;
|
||||
right: 0;
|
||||
box-shadow: var(--vp-shadow-4);
|
||||
}
|
||||
|
||||
.icon-grid-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
/* width: 56px;
|
||||
height: 56px; */
|
||||
background: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.icon-grid {
|
||||
top: 0;
|
||||
right: -48px;
|
||||
}
|
||||
}
|
||||
|
||||
.lucide-icon {
|
||||
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>
|
||||
@@ -1,244 +0,0 @@
|
||||
<script setup>
|
||||
import { useCssVar, syncRef } from '@vueuse/core'
|
||||
import { useIconStyleContext } from '../composables/useIconStyle'
|
||||
import RangeSlider from './RangeSlider.vue'
|
||||
import IconButton from './IconButton.vue'
|
||||
import { rotateCw } from '../../iconNodes'
|
||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
|
||||
|
||||
const { color, strokeWidth, size } = useIconStyleContext()
|
||||
|
||||
const colorCssVar = useCssVar(
|
||||
'--customize-color',
|
||||
typeof document !== 'undefined' ? document?.documentElement : undefined,
|
||||
{
|
||||
initialValue: 'default'
|
||||
}
|
||||
)
|
||||
|
||||
const strokeWidthCssVar = useCssVar(
|
||||
'--customize-strokeWidth',
|
||||
typeof document !== 'undefined' ? document?.documentElement : undefined,
|
||||
{
|
||||
initialValue: '2'
|
||||
}
|
||||
)
|
||||
|
||||
const sizeCssVar = useCssVar(
|
||||
'--customize-size',
|
||||
typeof document !== 'undefined' ? document?.documentElement : undefined,
|
||||
{
|
||||
initialValue: '24'
|
||||
}
|
||||
)
|
||||
|
||||
syncRef(color, colorCssVar)
|
||||
syncRef(strokeWidth, strokeWidthCssVar)
|
||||
syncRef(size, sizeCssVar)
|
||||
|
||||
const RotateIcon = createLucideIcon('RotateIcon', rotateCw)
|
||||
|
||||
function resetStyle () {
|
||||
color.value = 'currentColor'
|
||||
strokeWidth.value = '2'
|
||||
size.value = '24'
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">
|
||||
Customizer
|
||||
</h2>
|
||||
<IconButton class="reset-button" @click="resetStyle">
|
||||
<RotateIcon size="20"/>
|
||||
</IconButton>
|
||||
</div>
|
||||
<div class="input-field">
|
||||
<div class="input-label">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-field">
|
||||
<div class="input-label">
|
||||
<label for="stroke" class="customize-label">
|
||||
Stroke width
|
||||
</label>
|
||||
<div class="display-value">
|
||||
<span class="customize-label">{{ strokeWidth }}px</span>
|
||||
</div>
|
||||
</div>
|
||||
<RangeSlider
|
||||
id="stroke"
|
||||
name="stroke"
|
||||
v-model="strokeWidth"
|
||||
:min="0"
|
||||
:max="8"
|
||||
/>
|
||||
</div>
|
||||
<div class="input-field">
|
||||
<div class="input-label">
|
||||
<label for="size" class="customize-label">
|
||||
Size
|
||||
</label>
|
||||
<div class="display-value">
|
||||
<span class="customize-label">{{ size }}px</span>
|
||||
</div>
|
||||
</div>
|
||||
<RangeSlider
|
||||
id="size"
|
||||
name="size"
|
||||
v-model="size"
|
||||
:min="16"
|
||||
:max="48"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.customize-label {
|
||||
line-height: 20px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--vt-c-text-1);
|
||||
transition: color .5s;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-weight: 700;
|
||||
color: var(--vp-c-text-1);
|
||||
line-height: 32px;
|
||||
font-size: 16px;
|
||||
/* margin-bottom: 12px; */
|
||||
}
|
||||
.card {
|
||||
background: var(--vp-c-bg);
|
||||
padding: 12px 24px 24px;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 24px;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
.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: 4px 8px;
|
||||
height: auto;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
border: 1px solid transparent;
|
||||
cursor: text;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
|
||||
.input-field:not(:last-child) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.display-value {
|
||||
font-size: 13px;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.input-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.reset-button {
|
||||
background: none;
|
||||
padding: 0;
|
||||
transition: ease-in-out 0.1s transform;
|
||||
}
|
||||
|
||||
.reset-button:hover {
|
||||
background: none;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
/* a rotate css animation keyframes */
|
||||
@keyframes rotate {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
|
||||
.reset-button:active {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,48 @@
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
label: string
|
||||
id: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="input-field">
|
||||
<div class="input-label">
|
||||
<label :for="id" class="customize-label">
|
||||
{{ label }}
|
||||
</label>
|
||||
<div class="display-value" >
|
||||
<slot name="display"/>
|
||||
</div>
|
||||
</div>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.customize-label {
|
||||
line-height: 20px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--vt-c-text-1);
|
||||
transition: color .5s;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.input-field:not(:last-child) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.display-value {
|
||||
font-size: 13px;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.input-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
gap: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -36,10 +36,11 @@ const value = computed({
|
||||
|
||||
<template>
|
||||
<Input
|
||||
ref="input"
|
||||
type="search"
|
||||
v-bind="$attrs"
|
||||
v-model="value"
|
||||
class="input-wrapper" ref="searchInput"
|
||||
class="input-wrapper"
|
||||
>
|
||||
<template #icon>
|
||||
<component :is="SearchIcon" class="search-icon" />
|
||||
|
||||
@@ -5,6 +5,7 @@ interface Props {
|
||||
modelValue: number;
|
||||
min?: number;
|
||||
max?: number;
|
||||
steps?: number;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
@@ -15,6 +16,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
defineEmits(['update:modelValue'])
|
||||
|
||||
const percentage = computed<string>(() => `${((props.modelValue - props.min) / (props.max - props.min)) * 100}%`);
|
||||
// TODO: Steps must be implemented
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -37,6 +39,7 @@ const percentage = computed<string>(() => `${((props.modelValue - props.min) / (
|
||||
width: 100%;
|
||||
line-height: 10px;
|
||||
height: 20px;
|
||||
--bar-color: var(--slider-bar-color, var(--vp-c-bg-soft));
|
||||
}
|
||||
|
||||
.slider:hover input{
|
||||
@@ -64,7 +67,7 @@ const percentage = computed<string>(() => `${((props.modelValue - props.min) / (
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
.slider .bar:after {
|
||||
background: var(--vp-c-bg-soft);
|
||||
background: var(--bar-color);
|
||||
width: calc(100% - v-bind(percentage));
|
||||
right: 0;
|
||||
border-top-right-radius: 4px;
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
<script setup lang="ts">
|
||||
import { shallowRef, type Ref } from 'vue'
|
||||
import { useCssVar, syncRef } from '@vueuse/core'
|
||||
import { useIconStyleContext } from '../composables/useIconStyle'
|
||||
import RangeSlider from './RangeSlider.vue'
|
||||
import IconButton from './IconButton.vue'
|
||||
import InputField from './InputField.vue'
|
||||
import ColorPicker from './ColorPicker.vue'
|
||||
import { rotateCw } from '../../iconNodes'
|
||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
|
||||
|
||||
const props = defineProps<{
|
||||
rootEl?: Ref<HTMLElement>
|
||||
}>()
|
||||
|
||||
const { color, strokeWidth, size } = useIconStyleContext()
|
||||
const documentRef = shallowRef<HTMLElement | undefined>(typeof document !== 'undefined' ? document?.documentElement : undefined)
|
||||
|
||||
const colorCssVar = useCssVar(
|
||||
'--customize-color',
|
||||
props.rootEl?.value ?? documentRef.value,
|
||||
{
|
||||
initialValue: 'default'
|
||||
}
|
||||
)
|
||||
|
||||
const strokeWidthCssVar = useCssVar(
|
||||
'--customize-strokeWidth',
|
||||
props.rootEl?.value ?? documentRef.value,
|
||||
{
|
||||
initialValue: '2'
|
||||
}
|
||||
)
|
||||
|
||||
const sizeCssVar = useCssVar(
|
||||
'--customize-size',
|
||||
props.rootEl?.value ?? documentRef.value,
|
||||
{
|
||||
initialValue: '24'
|
||||
}
|
||||
)
|
||||
|
||||
syncRef(color, colorCssVar)
|
||||
syncRef(strokeWidth, strokeWidthCssVar)
|
||||
syncRef(size, sizeCssVar)
|
||||
|
||||
const RotateIcon = createLucideIcon('RotateIcon', rotateCw)
|
||||
|
||||
function resetStyle () {
|
||||
color.value = 'currentColor'
|
||||
strokeWidth.value = 2
|
||||
size.value = 24
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="customizer-card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">
|
||||
Customizer
|
||||
</h2>
|
||||
<IconButton class="reset-button" @click="resetStyle">
|
||||
<RotateIcon :size="20"/>
|
||||
</IconButton>
|
||||
</div>
|
||||
<InputField
|
||||
id="icon-color"
|
||||
label="Color"
|
||||
>
|
||||
<template #display>
|
||||
<ColorPicker v-model="color" id="icon-color" class="color-picker"/>
|
||||
</template>
|
||||
</InputField>
|
||||
|
||||
<InputField
|
||||
id="stroke-width"
|
||||
label="Stroke width"
|
||||
>
|
||||
<template #display>
|
||||
<span class="customize-label">{{ strokeWidth }}px</span>
|
||||
</template>
|
||||
<RangeSlider
|
||||
id="stroke-width"
|
||||
name="stroke-width"
|
||||
v-model="strokeWidth"
|
||||
:min="1"
|
||||
:max="4"
|
||||
:steps="0.5"
|
||||
/>
|
||||
</InputField>
|
||||
|
||||
<InputField
|
||||
id="size"
|
||||
label="Size"
|
||||
>
|
||||
<template #display>
|
||||
<span class="customize-label">{{ size }}px</span>
|
||||
</template>
|
||||
<RangeSlider
|
||||
id="size"
|
||||
name="size"
|
||||
v-model="size"
|
||||
:min="16"
|
||||
:max="48"
|
||||
/>
|
||||
</InputField>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-weight: 700;
|
||||
color: var(--vp-c-text-1);
|
||||
line-height: 32px;
|
||||
font-size: 16px;
|
||||
/* margin-bottom: 12px; */
|
||||
}
|
||||
.customizer-card {
|
||||
background: var(--vp-c-bg);
|
||||
padding: 12px 24px 24px;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 24px;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.color-picker {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.reset-button {
|
||||
background: none;
|
||||
padding: 0;
|
||||
transition: ease-in-out 0.1s transform;
|
||||
}
|
||||
|
||||
.reset-button:hover {
|
||||
background: none;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
/* a rotate css animation keyframes */
|
||||
@keyframes rotate {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
|
||||
.reset-button:active {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -1,5 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
hellow world
|
||||
</div>
|
||||
</template>
|
||||
@@ -42,11 +42,7 @@ const getIconCodes = (name: string): CodeExampleType => {
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<${pascalCase}
|
||||
color="red"
|
||||
size={48}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<${pascalCase}/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -63,7 +59,7 @@ export default App;
|
||||
{
|
||||
language: 'vue',
|
||||
code: `<template>
|
||||
<${pascalCase} color="red" :size="32" />
|
||||
<${pascalCase} />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -84,11 +80,7 @@ import { ${pascalCase} } from 'lucide-vue-next';
|
||||
import { ${pascalCase} } from 'lucide-svelte';
|
||||
</script>
|
||||
|
||||
<${pascalCase}
|
||||
color={red}
|
||||
size={48}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<${pascalCase}/>
|
||||
`,
|
||||
metastring: '{2,5}',
|
||||
},
|
||||
@@ -104,11 +96,7 @@ import { ${pascalCase} } from 'lucide-svelte';
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<${pascalCase}
|
||||
color="red"
|
||||
size={48}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<${pascalCase}/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -128,11 +116,7 @@ export default App;
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<${pascalCase}
|
||||
color="red"
|
||||
size={48}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<${pascalCase}/>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { useData } from 'vitepress'
|
||||
|
||||
import CategoryList from '../../components/CategoryList.vue'
|
||||
import IconCustomizer from '../../components/IconCustomizer.vue'
|
||||
import SidebarIconCustomizer from '../../components/SidebarIconCustomizer.vue'
|
||||
|
||||
const { page } = useData()
|
||||
|
||||
@@ -10,7 +10,7 @@ const { page } = useData()
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<IconCustomizer v-if="page?.relativePath?.startsWith?.('icons')"/>
|
||||
<SidebarIconCustomizer v-if="page?.relativePath?.startsWith?.('icons')"/>
|
||||
<CategoryList v-if="page?.relativePath?.startsWith?.('icons')"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
.VPNavBarTitle .logo {
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
}
|
||||
|
||||
.VPNavBarTitle .title {
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"cleanUrls" : true,
|
||||
"redirects": [
|
||||
{
|
||||
"source": "/icon/:path*",
|
||||
"destination":"/icons/:path*",
|
||||
"permanent": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -54,7 +54,7 @@ const codeExample = computed(() => params.value.codeExamples?.map(
|
||||
max-width: 240px;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
(min-width: 640px) {
|
||||
.layout {
|
||||
align-items: flex-start;
|
||||
display: grid;
|
||||
|
||||
@@ -50,6 +50,8 @@ features:
|
||||
|
||||
<script setup>
|
||||
import HomePackagesSection from './.vitepress/components/HomePackagesSection.vue'
|
||||
import HomeIconCustomizer from './.vitepress/components/HomeIconCustomizer.vue'
|
||||
</script>
|
||||
|
||||
<HomePackagesSection />
|
||||
<HomeIconCustomizer />
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"cleanUrls" : true,
|
||||
"routes": [
|
||||
{
|
||||
"source": "/icon/:path*",
|
||||
"destination": "/icons/:path*",
|
||||
"permanent": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -6,7 +6,7 @@ const currentDir = process.cwd();
|
||||
const vercelRouteConfig = {
|
||||
version: 3,
|
||||
overrides: {},
|
||||
// cleanUrls: true,
|
||||
cleanUrls: true,
|
||||
routes: [
|
||||
{
|
||||
handle: 'filesystem',
|
||||
|
||||
Reference in New Issue
Block a user