mirror of
https://github.com/lucide-icons/lucide.git
synced 2025-12-16 13:57:41 +01:00
fix(site): Small adjustments color picker and add clear button search bar (#3851)
* Small adjustments * Format code * format code * Remove default value * format code * update yml file * Format code * Update docs/.vitepress/theme/components/base/Input.vue Co-authored-by: Karsa <contact@karsa.org> * Add extra check if null or undefined --------- Co-authored-by: Karsa <contact@karsa.org>
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { createLucideIcon } from 'lucide-react/src/lucide-react';
|
import { createLucideIcon } from 'lucide-react/src/lucide-react';
|
||||||
import { type LucideProps, type IconNode } from 'lucide-react/src/createLucideIcon';
|
import { type LucideProps, type IconNode } from 'lucide-react/src/types';
|
||||||
import { IconEntity } from '../theme/types';
|
import { IconEntity } from '../theme/types';
|
||||||
import { renderToStaticMarkup } from 'react-dom/server';
|
import { renderToStaticMarkup } from 'react-dom/server';
|
||||||
import { IconContent } from './generateZip';
|
import { IconContent } from './generateZip';
|
||||||
|
|||||||
@@ -1,23 +1,31 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { useData } from 'vitepress';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: string
|
modelValue: string;
|
||||||
id: string
|
id: string;
|
||||||
}>()
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const { isDark } = useData();
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
||||||
const value = computed({
|
const value = computed({
|
||||||
get: () => props.modelValue,
|
get: () => {
|
||||||
set: (val) => emit('update:modelValue', val)
|
if (props.modelValue == null || props.modelValue === 'currentColor') {
|
||||||
})
|
return isDark.value ? '#ffffff' : '#000000';
|
||||||
|
}
|
||||||
|
|
||||||
|
return props.modelValue;
|
||||||
|
},
|
||||||
|
set: (val) => emit('update:modelValue', val),
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="color-picker">
|
<div class="color-picker">
|
||||||
<div class="color-input-wrapper">
|
<div class="color-input-wrapper">
|
||||||
<!-- TODO: Add currentColor div if value is currentColor -->
|
|
||||||
<input
|
<input
|
||||||
type="color"
|
type="color"
|
||||||
:id="id"
|
:id="id"
|
||||||
@@ -33,6 +41,7 @@ const value = computed({
|
|||||||
class="color-input-text"
|
class="color-input-text"
|
||||||
aria-label="Color picker input"
|
aria-label="Color picker input"
|
||||||
v-model="value"
|
v-model="value"
|
||||||
|
placeholder="[default]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -45,19 +54,21 @@ const value = computed({
|
|||||||
top: -5px;
|
top: -5px;
|
||||||
left: -5px;
|
left: -5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-input-wrapper {
|
.color-input-wrapper {
|
||||||
height: 24px;
|
height: 24px;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 12px;
|
border-radius: 4px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-picker {
|
.color-picker {
|
||||||
background: var(--color-picker-bg, var(--vp-c-bg-alt));
|
background: var(--color-picker-bg, var(--vp-c-bg-alt));
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
color: var(--vp-c-text-2);
|
color: var(--vp-c-text-2);
|
||||||
padding: 4px 8px;
|
padding: 3px 8px 3px 3px;
|
||||||
height: auto;
|
height: auto;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
@@ -66,6 +77,10 @@ const value = computed({
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
|
transition:
|
||||||
|
color 0.25s,
|
||||||
|
border-color 0.25s,
|
||||||
|
background-color 0.25s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-input-text {
|
.color-input-text {
|
||||||
@@ -79,15 +94,18 @@ const value = computed({
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
cursor: text;
|
cursor: text;
|
||||||
transition: border-color 0.25s, background 0.4s ease;
|
transition:
|
||||||
|
border-color 0.25s,
|
||||||
|
background 0.4s ease;
|
||||||
|
letter-spacing: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-picker:hover, .color-picker:focus {
|
.color-picker:hover,
|
||||||
|
.color-picker:focus {
|
||||||
border-color: var(--vp-c-brand);
|
border-color: var(--vp-c-brand);
|
||||||
background: var(--vp-c-bg-alt);
|
background: var(--vp-c-bg-alt);
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-input[value="currentColor"] {
|
.color-input[value='currentColor'] {
|
||||||
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,22 +1,27 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
|
import Icon from 'lucide-vue-next/src/Icon';
|
||||||
import { search } from '../../../data/iconNodes'
|
import { search } from '../../../data/iconNodes';
|
||||||
|
|
||||||
const SearchIcon = createLucideIcon('search', search)
|
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
shortcut: {
|
shortcut: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false
|
required: false,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<button class="fake-input">
|
<button class="fake-input">
|
||||||
<component :is="SearchIcon" class="search-icon"/>
|
<Icon
|
||||||
<slot/>
|
:iconNode="search"
|
||||||
<kbd v-if="shortcut" class="shortcut">{{ shortcut }}</kbd>
|
class="search-icon"
|
||||||
|
/>
|
||||||
|
<slot />
|
||||||
|
<kbd
|
||||||
|
v-if="shortcut"
|
||||||
|
class="shortcut"
|
||||||
|
>{{ shortcut }}</kbd
|
||||||
|
>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -34,10 +39,14 @@ defineProps({
|
|||||||
cursor: text;
|
cursor: text;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
transition: color 0.25s, border-color 0.25s, background-color 0.25s;
|
transition:
|
||||||
|
color 0.25s,
|
||||||
|
border-color 0.25s,
|
||||||
|
background-color 0.25s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fake-input:hover, .fake-input:focus {
|
.fake-input:hover,
|
||||||
|
.fake-input:focus {
|
||||||
border-color: var(--vp-c-brand);
|
border-color: var(--vp-c-brand);
|
||||||
background: var(--vp-c-bg-alt);
|
background: var(--vp-c-bg-alt);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
.icon-button {
|
.icon-button {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
|
|||||||
@@ -1,60 +1,90 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface InputProps {
|
export interface InputProps {
|
||||||
type: string
|
type: string;
|
||||||
modelValue: string
|
modelValue: string;
|
||||||
shortcut?: string
|
shortcut?: string;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, nextTick, watch } from 'vue'
|
import { ref, onMounted, nextTick, watch } from 'vue';
|
||||||
|
import Icon from 'lucide-vue-next/src/Icon';
|
||||||
|
import { x } from '../../../data/iconNodes';
|
||||||
|
import IconButton from './IconButton.vue';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<InputProps>(), {
|
const props = withDefaults(defineProps<InputProps>(), {
|
||||||
type: 'text'
|
type: 'text',
|
||||||
})
|
});
|
||||||
|
|
||||||
const input = ref()
|
const input = ref();
|
||||||
const wrapperEl = ref()
|
const wrapperEl = ref();
|
||||||
const shortcutEl = ref()
|
const shortcutEl = ref();
|
||||||
|
|
||||||
defineEmits(['change', 'input', 'update:modelValue'])
|
const emit = defineEmits(['change', 'input', 'update:modelValue']);
|
||||||
|
|
||||||
const updateShortcutSpacing = () => {
|
const updateShortcutSpacing = () => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (shortcutEl.value && wrapperEl.value) {
|
if (shortcutEl.value && wrapperEl.value) {
|
||||||
const shortcutWidth = shortcutEl.value.offsetWidth
|
const shortcutWidth = shortcutEl.value.offsetWidth;
|
||||||
wrapperEl.value.style.setProperty('--shortcut-width', `${shortcutWidth}px`)
|
wrapperEl.value.style.setProperty('--shortcut-width', `${shortcutWidth}px`);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
onMounted(updateShortcutSpacing)
|
onMounted(updateShortcutSpacing);
|
||||||
watch(() => props.shortcut, updateShortcutSpacing)
|
watch(() => props.shortcut, updateShortcutSpacing);
|
||||||
|
|
||||||
|
function onClear() {
|
||||||
|
emit('update:modelValue', '');
|
||||||
|
input.value.focus();
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
focus: () => {
|
focus: () => {
|
||||||
input.value.focus()
|
input.value.focus();
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="input-wrapper" ref="wrapperEl">
|
<div
|
||||||
<slot name="icon" class="icon" />
|
class="input-wrapper"
|
||||||
|
ref="wrapperEl"
|
||||||
|
>
|
||||||
|
<slot
|
||||||
|
name="icon"
|
||||||
|
class="icon"
|
||||||
|
/>
|
||||||
<input
|
<input
|
||||||
:type="type"
|
:type="type"
|
||||||
class="input"
|
class="input"
|
||||||
:class="{'has-icon': $slots.icon, 'has-shortcut': shortcut}"
|
:class="{ 'has-icon': $slots.icon, 'has-shortcut': shortcut }"
|
||||||
ref="input"
|
ref="input"
|
||||||
:value="modelValue"
|
:value="modelValue"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
@input="$emit('update:modelValue', $event.target.value)"
|
@input="$emit('update:modelValue', $event.target.value)"
|
||||||
/>
|
/>
|
||||||
<kbd v-if="shortcut" class="shortcut" ref="shortcutEl">{{ shortcut }}</kbd>
|
<IconButton
|
||||||
|
@click="onClear"
|
||||||
|
v-if="type === 'search' && modelValue"
|
||||||
|
class="clear-button"
|
||||||
|
aria-label="Clear input"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
:iconNode="x"
|
||||||
|
:size="20"
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
|
<kbd
|
||||||
|
v-if="shortcut"
|
||||||
|
class="shortcut"
|
||||||
|
ref="shortcutEl"
|
||||||
|
>{{ shortcut }}</kbd
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -62,6 +92,7 @@ defineExpose({
|
|||||||
.input-wrapper {
|
.input-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
@@ -71,13 +102,18 @@ defineExpose({
|
|||||||
height: 40px;
|
height: 40px;
|
||||||
background-color: var(--vp-c-bg-alt);
|
background-color: var(--vp-c-bg-alt);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
transition:
|
||||||
|
color 0.25s,
|
||||||
|
border-color 0.25s,
|
||||||
|
background-color 0.25s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input.has-shortcut {
|
.input.has-shortcut {
|
||||||
padding-right: calc(var(--shortcut-width, 40px) + 22px);
|
padding-right: calc(var(--shortcut-width, 40px) + 22px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.input:hover, .input:focus {
|
.input:hover,
|
||||||
|
.input:focus {
|
||||||
border-color: var(--vp-c-brand);
|
border-color: var(--vp-c-brand);
|
||||||
background: var(--vp-c-bg-alt);
|
background: var(--vp-c-bg-alt);
|
||||||
}
|
}
|
||||||
@@ -86,6 +122,14 @@ defineExpose({
|
|||||||
padding-left: 52px;
|
padding-left: 52px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clear-button {
|
||||||
|
position: absolute;
|
||||||
|
right: 56px;
|
||||||
|
top: 9px;
|
||||||
|
padding: 4px;
|
||||||
|
transition: background-color .25s;
|
||||||
|
}
|
||||||
|
|
||||||
.shortcut {
|
.shortcut {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 12px;
|
right: 12px;
|
||||||
@@ -111,7 +155,7 @@ defineExpose({
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.input-wrapper svg {
|
.input-wrapper > svg {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 16px;
|
left: 16px;
|
||||||
top: 12px;
|
top: 12px;
|
||||||
|
|||||||
@@ -1,38 +1,36 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue';
|
||||||
import Input from './Input.vue'
|
import Input from './Input.vue';
|
||||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
|
import Icon from 'lucide-vue-next/src/Icon';
|
||||||
import { search } from '../../../data/iconNodes'
|
import { search } from '../../../data/iconNodes';
|
||||||
|
|
||||||
const SearchIcon = createLucideIcon('search', search)
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
modelValue: string
|
modelValue: string;
|
||||||
shortcut?: string
|
shortcut?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
const input = ref()
|
const input = ref();
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
focus: () => {
|
focus: () => {
|
||||||
input.value.focus()
|
input.value.focus();
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
const value = computed({
|
const value = computed({
|
||||||
get: () => props.modelValue,
|
get: () => props.modelValue,
|
||||||
set: (val) => emit('update:modelValue', val)
|
set: (val) => emit('update:modelValue', val),
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -46,7 +44,10 @@ const value = computed({
|
|||||||
class="input-wrapper"
|
class="input-wrapper"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<component :is="SearchIcon" class="search-icon" />
|
<Icon
|
||||||
|
:iconNode="search"
|
||||||
|
class="search-icon"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</Input>
|
</Input>
|
||||||
</template>
|
</template>
|
||||||
@@ -62,7 +63,8 @@ const value = computed({
|
|||||||
background-color: var(--vp-c-bg-alt);
|
background-color: var(--vp-c-bg-alt);
|
||||||
}
|
}
|
||||||
|
|
||||||
.input:hover, .input:focus {
|
.input:hover,
|
||||||
|
.input:focus {
|
||||||
border-color: var(--vp-c-brand);
|
border-color: var(--vp-c-brand);
|
||||||
background: var(--vp-c-bg-alt);
|
background: var(--vp-c-bg-alt);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { rotateCw } from '../../../data/iconNodes'
|
import { rotateCw } from '../../../data/iconNodes';
|
||||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
|
import Icon from 'lucide-vue-next/src/Icon';
|
||||||
import IconButton from "./IconButton.vue";
|
import IconButton from './IconButton.vue';
|
||||||
|
|
||||||
const RotateIcon = createLucideIcon('RotateIcon', rotateCw)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<IconButton class="reset-button">
|
<IconButton class="reset-button">
|
||||||
<RotateIcon :size="20"/>
|
<Icon
|
||||||
|
:size="20"
|
||||||
|
:iconNode="rotateCw"
|
||||||
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ const RotateIcon = createLucideIcon('RotateIcon', rotateCw)
|
|||||||
0% {
|
0% {
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
transform: rotate(359deg);
|
transform: rotate(359deg);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,16 +22,16 @@ export default {
|
|||||||
logo: '/framework-logos/svelte.svg',
|
logo: '/framework-logos/svelte.svg',
|
||||||
label: 'Lucide documentation for Svelte',
|
label: 'Lucide documentation for Svelte',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'lucide-preact',
|
|
||||||
logo: '/framework-logos/preact.svg',
|
|
||||||
label: 'Lucide documentation for Preact',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'lucide-solid',
|
name: 'lucide-solid',
|
||||||
logo: '/framework-logos/solid.svg',
|
logo: '/framework-logos/solid.svg',
|
||||||
label: 'Lucide documentation for Solid',
|
label: 'Lucide documentation for Solid',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'lucide-preact',
|
||||||
|
logo: '/framework-logos/preact.svg',
|
||||||
|
label: 'Lucide documentation for Preact',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'lucide-angular',
|
name: 'lucide-angular',
|
||||||
logo: '/framework-logos/angular.svg',
|
logo: '/framework-logos/angular.svg',
|
||||||
@@ -48,11 +48,6 @@ export default {
|
|||||||
logo: '/framework-logos/react-native.svg',
|
logo: '/framework-logos/react-native.svg',
|
||||||
label: 'Lucide documentation for React Native',
|
label: 'Lucide documentation for React Native',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'lucide-flutter',
|
|
||||||
logo: '/framework-logos/flutter.svg',
|
|
||||||
label: 'Lucide documentation for Flutter',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,45 +2,48 @@
|
|||||||
import { useData } from 'vitepress';
|
import { useData } from 'vitepress';
|
||||||
import { useSessionStorage } from '@vueuse/core';
|
import { useSessionStorage } from '@vueuse/core';
|
||||||
import IconButton from '../base/IconButton.vue';
|
import IconButton from '../base/IconButton.vue';
|
||||||
import VPDocAsideCarbonAds from 'vitepress/dist/client/theme-default/components/VPDocAsideCarbonAds.vue'
|
import VPDocAsideCarbonAds from 'vitepress/dist/client/theme-default/components/VPDocAsideCarbonAds.vue';
|
||||||
import { x } from '../../../data/iconNodes'
|
import { x } from '../../../data/iconNodes';
|
||||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
|
import Icon from 'lucide-vue-next/src/Icon';
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
const { theme } = useData()
|
const { theme } = useData();
|
||||||
const showAd = useSessionStorage('show-carbon-ads', true)
|
const showAd = useSessionStorage('show-carbon-ads', true);
|
||||||
const carbonLoaded = ref(true)
|
const carbonLoaded = ref(true);
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
drawerOpen: boolean
|
drawerOpen: boolean;
|
||||||
}>()
|
}>();
|
||||||
|
|
||||||
const CloseIcon = createLucideIcon('Close', x)
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (window?._carbonads == null) {
|
if (window?._carbonads == null) {
|
||||||
carbonLoaded.value = false
|
carbonLoaded.value = false;
|
||||||
}
|
}
|
||||||
}, 5000)
|
}, 5000);
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
:class="{
|
:class="{
|
||||||
'drawer-open': drawerOpen,
|
'drawer-open': drawerOpen,
|
||||||
'hide-ad': !(showAd && carbonLoaded)
|
'hide-ad': !(showAd && carbonLoaded),
|
||||||
}"
|
}"
|
||||||
class="floating-ad"
|
class="floating-ad"
|
||||||
v-if="theme.carbonAds"
|
v-if="theme.carbonAds"
|
||||||
>
|
>
|
||||||
<IconButton @click="showAd = false" class="hide-button">
|
<IconButton
|
||||||
<component :is="CloseIcon" :size="20" absoluteStrokeWidth />
|
@click="showAd = false"
|
||||||
</IconButton>
|
class="hide-button"
|
||||||
<VPDocAsideCarbonAds
|
>
|
||||||
:carbon-ads="theme.carbonAds"
|
<Icon
|
||||||
|
:iconNode="x"
|
||||||
|
:size="20"
|
||||||
|
absoluteStrokeWidth
|
||||||
/>
|
/>
|
||||||
|
</IconButton>
|
||||||
|
<VPDocAsideCarbonAds :carbon-ads="theme.carbonAds" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -51,7 +54,9 @@ onMounted(() => {
|
|||||||
bottom: 32px;
|
bottom: 32px;
|
||||||
width: 224px;
|
width: 224px;
|
||||||
right: 32px;
|
right: 32px;
|
||||||
transition: opacity 0.5s, transform 0.25s ease;
|
transition:
|
||||||
|
opacity 0.5s,
|
||||||
|
transform 0.25s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.floating-ad.drawer-open {
|
.floating-ad.drawer-open {
|
||||||
@@ -67,8 +72,11 @@ onMounted(() => {
|
|||||||
transform: translateY(-288px) translateX(224px);
|
transform: translateY(-288px) translateX(224px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.floating-ad.drawer-open, .floating-ad.hide-ad {
|
.floating-ad.drawer-open,
|
||||||
transition: opacity 0.25s, transform 0.5s cubic-bezier(0.19, 1, 0.22, 1);
|
.floating-ad.hide-ad {
|
||||||
|
transition:
|
||||||
|
opacity 0.25s,
|
||||||
|
transform 0.5s cubic-bezier(0.19, 1, 0.22, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1280px) {
|
@media (min-width: 1280px) {
|
||||||
|
|||||||
@@ -1,70 +1,68 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import ButtonMenu from '../base/ButtonMenu.vue'
|
import ButtonMenu from '../base/ButtonMenu.vue';
|
||||||
import { useIconStyleContext } from '../../composables/useIconStyle';
|
import { useIconStyleContext } from '../../composables/useIconStyle';
|
||||||
import useConfetti from '../../composables/useConfetti';
|
import useConfetti from '../../composables/useConfetti';
|
||||||
import getSVGIcon from '../../utils/getSVGIcon';
|
import getSVGIcon from '../../utils/getSVGIcon';
|
||||||
import downloadData from '../../utils/downloadData';
|
import downloadData from '../../utils/downloadData';
|
||||||
|
|
||||||
const downloadText = 'Download!'
|
const downloadText = 'Download!';
|
||||||
const copiedText = 'Copied!'
|
const copiedText = 'Copied!';
|
||||||
const confettiText = ref(copiedText)
|
const confettiText = ref(copiedText);
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
name: string
|
name: string;
|
||||||
popoverPosition?: 'top' | 'bottom'
|
popoverPosition?: 'top' | 'bottom';
|
||||||
}>()
|
}>();
|
||||||
|
|
||||||
const { size } = useIconStyleContext()
|
const { size } = useIconStyleContext();
|
||||||
|
|
||||||
const { animate, confetti } = useConfetti()
|
const { animate, confetti } = useConfetti();
|
||||||
|
|
||||||
function copySVG() {
|
function copySVG() {
|
||||||
confettiText.value = copiedText
|
confettiText.value = copiedText;
|
||||||
const svgString = getSVGIcon()
|
const svgString = getSVGIcon();
|
||||||
|
|
||||||
navigator.clipboard.writeText(svgString)
|
navigator.clipboard.writeText(svgString);
|
||||||
|
|
||||||
confetti()
|
confetti();
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyDataUrl() {
|
function copyDataUrl() {
|
||||||
confettiText.value = copiedText
|
confettiText.value = copiedText;
|
||||||
const svgString = getSVGIcon()
|
const svgString = getSVGIcon();
|
||||||
|
|
||||||
// Create SVG data url
|
// Create SVG data url
|
||||||
const dataUrl = `data:image/svg+xml;base64,${btoa(svgString)}`
|
const dataUrl = `data:image/svg+xml;base64,${btoa(svgString)}`;
|
||||||
navigator.clipboard.writeText(dataUrl)
|
navigator.clipboard.writeText(dataUrl);
|
||||||
|
|
||||||
confetti()
|
confetti();
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadSVG() {
|
function downloadSVG() {
|
||||||
confettiText.value = downloadText
|
confettiText.value = downloadText;
|
||||||
const svgString = getSVGIcon()
|
const svgString = getSVGIcon();
|
||||||
|
|
||||||
downloadData(`${props.name}.svg`, `data:image/svg+xml;base64,${btoa(svgString)}`)
|
downloadData(`${props.name}.svg`, `data:image/svg+xml;base64,${btoa(svgString)}`);
|
||||||
confetti()
|
confetti();
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadPNG() {
|
function downloadPNG() {
|
||||||
confettiText.value = downloadText
|
confettiText.value = downloadText;
|
||||||
const svgString = getSVGIcon()
|
const svgString = getSVGIcon();
|
||||||
|
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
canvas.width = size.value;
|
canvas.width = size.value;
|
||||||
canvas.height = size.value;
|
canvas.height = size.value;
|
||||||
const ctx = canvas.getContext("2d");
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
const image = new Image();
|
const image = new Image();
|
||||||
image.src = `data:image/svg+xml;base64,${btoa(svgString)}`;
|
image.src = `data:image/svg+xml;base64,${btoa(svgString)}`;
|
||||||
image.onload = function() {
|
image.onload = function () {
|
||||||
ctx.drawImage(image, 0, 0);
|
ctx.drawImage(image, 0, 0);
|
||||||
downloadData(`${props.name}.png`, canvas.toDataURL('image/png'))
|
downloadData(`${props.name}.png`, canvas.toDataURL('image/png'));
|
||||||
confetti()
|
confetti();
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -75,10 +73,10 @@ function downloadPNG() {
|
|||||||
:data-confetti-text="confettiText"
|
:data-confetti-text="confettiText"
|
||||||
:popoverPosition="popoverPosition"
|
:popoverPosition="popoverPosition"
|
||||||
:options="[
|
:options="[
|
||||||
{ text: 'Copy SVG' , onClick: copySVG },
|
{ text: 'Copy SVG', onClick: copySVG },
|
||||||
{ text: 'Copy Data URL' , onClick: copyDataUrl },
|
{ text: 'Copy Data URL', onClick: copyDataUrl },
|
||||||
{ text: 'Download SVG' , onClick: downloadSVG },
|
{ text: 'Download SVG', onClick: downloadSVG },
|
||||||
{ text: 'Download PNG' , onClick: downloadPNG },
|
{ text: 'Download PNG', onClick: downloadPNG },
|
||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,41 +1,44 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, useSlots } from 'vue';
|
import { computed, useSlots } from 'vue';
|
||||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
|
import { copy } from '../../../data/iconNodes';
|
||||||
import { copy } from '../../../data/iconNodes'
|
|
||||||
import useConfetti from '../../composables/useConfetti';
|
import useConfetti from '../../composables/useConfetti';
|
||||||
const { animate, confetti } = useConfetti()
|
import Icon from 'lucide-vue-next/src/Icon';
|
||||||
const slots = useSlots()
|
const { animate, confetti } = useConfetti();
|
||||||
|
const slots = useSlots();
|
||||||
|
|
||||||
const copiedText = computed(() => slots.default?.()[0].children)
|
const copiedText = computed(() => slots.default?.()[0].children);
|
||||||
|
|
||||||
function copyText() {
|
function copyText() {
|
||||||
navigator.clipboard.writeText(copiedText.value)
|
navigator.clipboard.writeText(copiedText.value);
|
||||||
|
|
||||||
confetti()
|
confetti();
|
||||||
}
|
}
|
||||||
|
|
||||||
const Copy = createLucideIcon('ChevronUp', copy)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<h1
|
<h1
|
||||||
class="icon-name confetti-button"
|
class="icon-name confetti-button"
|
||||||
:class="{animate}"
|
:class="{ animate }"
|
||||||
data-confetti-text="Copied!"
|
data-confetti-text="Copied!"
|
||||||
@click="copyText"
|
@click="copyText"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
<Copy :size="20" class="copy-icon"/>
|
<Icon
|
||||||
|
:iconNode="copy"
|
||||||
|
:size="20"
|
||||||
|
class="copy-icon"
|
||||||
|
/>
|
||||||
</h1>
|
</h1>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@import './confetti.css';
|
@import './confetti.css';
|
||||||
|
|
||||||
.icon-name {
|
.icon-name {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: 32px;
|
line-height: 32px;
|
||||||
transition: background ease-in .15s;;
|
transition: background ease-in 0.15s;
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
width: auto;
|
width: auto;
|
||||||
@@ -48,7 +51,7 @@ const Copy = createLucideIcon('ChevronUp', copy)
|
|||||||
}
|
}
|
||||||
|
|
||||||
.icon-name:hover .copy-icon {
|
.icon-name:hover .copy-icon {
|
||||||
opacity: .9;
|
opacity: 0.9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-name:before,
|
.icon-name:before,
|
||||||
@@ -65,10 +68,10 @@ const Copy = createLucideIcon('ChevronUp', copy)
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
transition:ease .3s opacity;
|
transition: ease 0.3s opacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-name:hover .copy-icon:hover {
|
.icon-name:hover .copy-icon:hover {
|
||||||
opacity: .6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,75 +1,72 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { shallowRef, type Ref, watch, computed } from 'vue'
|
import { shallowRef, type Ref, watch, computed } from 'vue';
|
||||||
import { useCssVar, syncRef } from '@vueuse/core'
|
import { useCssVar, syncRef } from '@vueuse/core';
|
||||||
import { STYLE_DEFAULTS, useIconStyleContext } from '../../composables/useIconStyle'
|
import { STYLE_DEFAULTS, useIconStyleContext } from '../../composables/useIconStyle';
|
||||||
import RangeSlider from '../base/RangeSlider.vue'
|
import RangeSlider from '../base/RangeSlider.vue';
|
||||||
import InputField from '../base/InputField.vue'
|
import InputField from '../base/InputField.vue';
|
||||||
import ColorPicker from '../base/ColorPicker.vue'
|
import ColorPicker from '../base/ColorPicker.vue';
|
||||||
import ResetButton from '../base/ResetButton.vue'
|
import ResetButton from '../base/ResetButton.vue';
|
||||||
import Switch from '../base/Switch.vue'
|
import Switch from '../base/Switch.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
rootEl?: Ref<HTMLElement>
|
rootEl?: Ref<HTMLElement>;
|
||||||
}>()
|
}>();
|
||||||
|
|
||||||
const { color, strokeWidth, size, absoluteStrokeWidth } = useIconStyleContext()
|
const { color, strokeWidth, size, absoluteStrokeWidth } = useIconStyleContext();
|
||||||
const documentRef = shallowRef<HTMLElement | undefined>(typeof document !== 'undefined' ? document?.documentElement : undefined)
|
const documentRef = shallowRef<HTMLElement | undefined>(
|
||||||
|
typeof document !== 'undefined' ? document?.documentElement : undefined,
|
||||||
|
);
|
||||||
|
|
||||||
const colorCssVar = useCssVar(
|
const colorCssVar = useCssVar('--customize-color', props.rootEl?.value ?? documentRef.value, {
|
||||||
'--customize-color',
|
initialValue: `${STYLE_DEFAULTS.color}`,
|
||||||
props.rootEl?.value ?? documentRef.value,
|
});
|
||||||
{
|
|
||||||
initialValue: `${STYLE_DEFAULTS.color}`
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const strokeWidthCssVar = useCssVar(
|
const strokeWidthCssVar = useCssVar(
|
||||||
'--customize-strokeWidth',
|
'--customize-strokeWidth',
|
||||||
props.rootEl?.value ?? documentRef.value,
|
props.rootEl?.value ?? documentRef.value,
|
||||||
{
|
{
|
||||||
initialValue: `${STYLE_DEFAULTS.strokeWidth}`
|
initialValue: `${STYLE_DEFAULTS.strokeWidth}`,
|
||||||
}
|
},
|
||||||
)
|
);
|
||||||
|
|
||||||
const sizeCssVar = useCssVar(
|
const sizeCssVar = useCssVar('--customize-size', props.rootEl?.value ?? documentRef.value, {
|
||||||
'--customize-size',
|
initialValue: `${STYLE_DEFAULTS.size}`,
|
||||||
props.rootEl?.value ?? documentRef.value,
|
});
|
||||||
{
|
|
||||||
initialValue: `${STYLE_DEFAULTS.size}`
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
syncRef(color, colorCssVar, { direction: 'ltr' })
|
syncRef(color, colorCssVar, { direction: 'ltr' });
|
||||||
syncRef(strokeWidth, strokeWidthCssVar, { direction: 'ltr' })
|
syncRef(strokeWidth, strokeWidthCssVar, { direction: 'ltr' });
|
||||||
syncRef(size, sizeCssVar, { direction: 'ltr' })
|
syncRef(size, sizeCssVar, { direction: 'ltr' });
|
||||||
|
|
||||||
function resetStyle () {
|
function resetStyle() {
|
||||||
color.value = STYLE_DEFAULTS.color
|
color.value = STYLE_DEFAULTS.color;
|
||||||
strokeWidth.value = STYLE_DEFAULTS.strokeWidth
|
strokeWidth.value = STYLE_DEFAULTS.strokeWidth;
|
||||||
size.value = STYLE_DEFAULTS.size
|
size.value = STYLE_DEFAULTS.size;
|
||||||
absoluteStrokeWidth.value = STYLE_DEFAULTS.absoluteStrokeWidth
|
absoluteStrokeWidth.value = STYLE_DEFAULTS.absoluteStrokeWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(absoluteStrokeWidth, (enabled) => {
|
watch(absoluteStrokeWidth, (enabled) => {
|
||||||
const htmlEl = document.documentElement
|
const htmlEl = document.documentElement;
|
||||||
|
|
||||||
htmlEl.classList.toggle('absolute-stroke-width', enabled)
|
htmlEl.classList.toggle('absolute-stroke-width', enabled);
|
||||||
})
|
});
|
||||||
|
|
||||||
const customizingActive = computed(() => {
|
const customizingActive = computed(() => {
|
||||||
return color.value !== STYLE_DEFAULTS.color
|
return (
|
||||||
|| strokeWidth.value !== STYLE_DEFAULTS.strokeWidth
|
color.value !== STYLE_DEFAULTS.color ||
|
||||||
|| size.value !== STYLE_DEFAULTS.size
|
strokeWidth.value !== STYLE_DEFAULTS.strokeWidth ||
|
||||||
|| absoluteStrokeWidth.value !== STYLE_DEFAULTS.absoluteStrokeWidth
|
size.value !== STYLE_DEFAULTS.size ||
|
||||||
})
|
absoluteStrokeWidth.value !== STYLE_DEFAULTS.absoluteStrokeWidth
|
||||||
|
);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="customizer-card" :class="{ customized: customizingActive }">
|
<div
|
||||||
|
class="customizer-card"
|
||||||
|
:class="{ customized: customizingActive }"
|
||||||
|
>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h2 class="card-title">
|
<h2 class="card-title">Customizer</h2>
|
||||||
Customizer
|
|
||||||
</h2>
|
|
||||||
<ResetButton @click="resetStyle"></ResetButton>
|
<ResetButton @click="resetStyle"></ResetButton>
|
||||||
</div>
|
</div>
|
||||||
<InputField
|
<InputField
|
||||||
@@ -77,7 +74,11 @@ const customizingActive = computed(() => {
|
|||||||
label="Color"
|
label="Color"
|
||||||
>
|
>
|
||||||
<template #display>
|
<template #display>
|
||||||
<ColorPicker v-model="color" id="icon-color" class="color-picker"/>
|
<ColorPicker
|
||||||
|
v-model="color"
|
||||||
|
id="icon-color"
|
||||||
|
class="color-picker"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</InputField>
|
</InputField>
|
||||||
|
|
||||||
@@ -117,7 +118,7 @@ const customizingActive = computed(() => {
|
|||||||
|
|
||||||
<InputField
|
<InputField
|
||||||
id="absolute-stroke-width"
|
id="absolute-stroke-width"
|
||||||
label="Absolute Stroke width"
|
label="Absolute stroke width"
|
||||||
>
|
>
|
||||||
<Switch
|
<Switch
|
||||||
id="absolute-stroke-width"
|
id="absolute-stroke-width"
|
||||||
@@ -143,6 +144,7 @@ const customizingActive = computed(() => {
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
/* margin-bottom: 12px; */
|
/* margin-bottom: 12px; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.customizer-card {
|
.customizer-card {
|
||||||
background: var(--vp-c-bg);
|
background: var(--vp-c-bg);
|
||||||
padding: 12px 24px 24px;
|
padding: 12px 24px 24px;
|
||||||
@@ -151,7 +153,7 @@ const customizingActive = computed(() => {
|
|||||||
position: relative;
|
position: relative;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
transition: border-color .4s ease-in-out;
|
transition: border-color 0.4s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.customizer-card.customized {
|
.customizer-card.customized {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { IconNode } from 'lucide-vue-next/src/createLucideIcon';
|
import { type IconNode } from 'lucide-vue-next/src/types';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
declare module '*.vue' {
|
declare module '*.vue' {
|
||||||
@@ -20,5 +20,6 @@ declare module 'node:module' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare module '*.node.json' {
|
declare module '*.node.json' {
|
||||||
export default IconNode;
|
const value: IconNode;
|
||||||
|
export default value;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user