Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03eae6069e | ||
|
|
0991512434 | ||
|
|
68081128b3 | ||
|
|
c8d0ddd437 | ||
|
|
e9cfd3c6d3 | ||
|
|
a25efe8dba | ||
|
|
6f61f73a91 | ||
|
|
bfa381ef26 | ||
|
|
3fef7aca71 | ||
|
|
45aaec4b72 | ||
|
|
f8932f8eb8 | ||
|
|
1a3f0a85a1 | ||
|
|
adc62f5963 | ||
|
|
b3058bb8cf | ||
|
|
53fab88ff9 | ||
|
|
1a49fd88d8 |
@@ -15,17 +15,22 @@ monstera
|
||||
|
||||
# Search tags and terms used in icon and category metadata
|
||||
checkmark
|
||||
activitypub
|
||||
ageusia
|
||||
artboard
|
||||
barbeque
|
||||
choy
|
||||
cicd
|
||||
ecommerce
|
||||
enby
|
||||
fediverse
|
||||
flexbox
|
||||
futbol
|
||||
gddr
|
||||
glassmorphism
|
||||
gltf
|
||||
halloumi
|
||||
helpdesk
|
||||
interpoint
|
||||
interpunct
|
||||
middot
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup>
|
||||
import Icon from '@lucide/vue/src/Icon';
|
||||
import { search } from '../../../data/iconNodes';
|
||||
import Kbd from './Kbd.vue';
|
||||
|
||||
defineProps({
|
||||
shortcut: {
|
||||
@@ -17,10 +18,10 @@ defineProps({
|
||||
class="search-icon"
|
||||
/>
|
||||
<slot />
|
||||
<kbd
|
||||
<Kbd
|
||||
v-if="shortcut"
|
||||
class="shortcut"
|
||||
>{{ shortcut }}</kbd
|
||||
>{{ shortcut }}</Kbd
|
||||
>
|
||||
</button>
|
||||
</template>
|
||||
@@ -59,7 +60,9 @@ defineProps({
|
||||
|
||||
.search-icon {
|
||||
flex-shrink: 0;
|
||||
transition: color 0.25s, transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
transition:
|
||||
color 0.25s,
|
||||
transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
transition-duration: 0.25s, 0.3s;
|
||||
}
|
||||
|
||||
@@ -70,15 +73,6 @@ defineProps({
|
||||
|
||||
.shortcut {
|
||||
margin-left: auto;
|
||||
padding: 2px 6px;
|
||||
font-size: 12px;
|
||||
font-family: inherit;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
color: var(--vp-c-text-3);
|
||||
background: var(--vp-c-default-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@media (hover: none) {
|
||||
|
||||
@@ -15,6 +15,7 @@ import { ref, onMounted, nextTick, watch } from 'vue';
|
||||
import Icon from '@lucide/vue/src/Icon';
|
||||
import { x } from '../../../data/iconNodes';
|
||||
import Button from './Button.vue';
|
||||
import Kbd from './Kbd.vue';
|
||||
|
||||
const props = withDefaults(defineProps<InputProps>(), {
|
||||
type: 'text',
|
||||
@@ -29,7 +30,7 @@ const emit = defineEmits(['change', 'input', 'update:modelValue']);
|
||||
const updateShortcutSpacing = () => {
|
||||
nextTick(() => {
|
||||
if (shortcutEl.value && wrapperEl.value) {
|
||||
const shortcutWidth = shortcutEl.value.offsetWidth;
|
||||
const shortcutWidth = shortcutEl.value.$el.offsetWidth;
|
||||
wrapperEl.value.style.setProperty('--shortcut-width', `${shortcutWidth}px`);
|
||||
}
|
||||
});
|
||||
@@ -83,11 +84,11 @@ defineExpose({
|
||||
:size="20"
|
||||
/>
|
||||
</Button>
|
||||
<kbd
|
||||
<Kbd
|
||||
v-if="shortcut"
|
||||
class="shortcut"
|
||||
ref="shortcutEl"
|
||||
>{{ shortcut }}</kbd
|
||||
>{{ shortcut }}</Kbd
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
@@ -146,33 +147,24 @@ defineExpose({
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
padding: 2px 6px;
|
||||
font-size: 12px;
|
||||
font-family: inherit;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
color: var(--vp-c-text-3);
|
||||
background: var(--vp-c-default-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 4px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.input[type="search"]::-webkit-search-cancel-button {
|
||||
.input[type='search']::-webkit-search-cancel-button {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.input[type="search"]::-ms-clear {
|
||||
.input[type='search']::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.input[type="search"]::-o-clear {
|
||||
.input[type='search']::-o-clear {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.input[type="search"]::-moz-clear {
|
||||
.input[type='search']::-moz-clear {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -195,7 +187,9 @@ defineExpose({
|
||||
z-index: 1;
|
||||
color: var(--vp-c-text-2);
|
||||
pointer-events: none;
|
||||
transition: color 0.25s, transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
transition:
|
||||
color 0.25s,
|
||||
transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
transition-duration: 0.25s, 0.3s;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
label: string
|
||||
id: string
|
||||
}>()
|
||||
label: string;
|
||||
id: string;
|
||||
class?: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="input-field">
|
||||
<div class="input-label" v-if="label">
|
||||
<label :for="id" class="customize-label">
|
||||
<div
|
||||
class="input-field"
|
||||
:class="class"
|
||||
>
|
||||
<div
|
||||
class="input-label"
|
||||
v-if="label"
|
||||
>
|
||||
<label
|
||||
:for="id"
|
||||
class="customize-label"
|
||||
>
|
||||
{{ label }}
|
||||
</label>
|
||||
<div class="display-value" >
|
||||
<slot name="display"/>
|
||||
<div class="display-value">
|
||||
<slot name="display" />
|
||||
</div>
|
||||
</div>
|
||||
<slot />
|
||||
@@ -25,7 +35,7 @@ defineProps<{
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--vt-c-text-1);
|
||||
transition: color .5s;
|
||||
transition: color 0.5s;
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -43,6 +53,6 @@ defineProps<{
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
gap: 12px;
|
||||
gap: 6px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<kbd class="kbd"><slot /></kbd>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.kbd {
|
||||
display: inline-block;
|
||||
min-width: 24px;
|
||||
padding: 2px 6px;
|
||||
font-family: inherit;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
text-align: center;
|
||||
color: var(--vp-c-text-2);
|
||||
background: var(--vp-c-default-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,59 @@
|
||||
<script setup lang="ts">
|
||||
import { Toaster } from 'vue-sonner';
|
||||
import 'vue-sonner/style.css';
|
||||
import { useData } from 'vitepress';
|
||||
import Icon from '@lucide/vue/src/Icon.ts';
|
||||
import { check } from '../../../data/iconNodes';
|
||||
|
||||
const { isDark } = useData();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ClientOnly>
|
||||
<Toaster
|
||||
position="bottom-center"
|
||||
:theme="isDark ? 'dark' : 'light'"
|
||||
>
|
||||
<template #success-icon>
|
||||
<Icon
|
||||
:iconNode="check"
|
||||
name="check"
|
||||
:size="24"
|
||||
/>
|
||||
</template>
|
||||
</Toaster>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
/* Same surface as base/Card.vue. The extra :root outranks sonner's per-theme rules */
|
||||
:root [data-sonner-toaster][data-sonner-theme] {
|
||||
--normal-bg: var(--vp-c-bg-soft);
|
||||
--normal-border: var(--vp-c-divider);
|
||||
--normal-text: var(--vp-c-text-1);
|
||||
--border-radius: 12px;
|
||||
}
|
||||
|
||||
/* Keep the icon and the text on one centred line; the site's line-height pushes the text down */
|
||||
[data-sonner-toast] {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
[data-sonner-toast] [data-icon] {
|
||||
align-self: center;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Sonner's icon box is 16px, so match the 24px check to stop it overflowing into the text */
|
||||
[data-sonner-toast][data-styled='true'][data-type='success'] [data-icon] {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
color: var(--vp-c-success-1);
|
||||
}
|
||||
|
||||
[data-sonner-toast] [data-content],
|
||||
[data-sonner-toast] [data-title] {
|
||||
line-height: 20px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,8 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, shallowRef } from 'vue';
|
||||
import { onMounted, ref, shallowRef, type DefineComponent } from 'vue';
|
||||
import { data } from './HomeHeroIconsCard.data';
|
||||
import { useRouter } from 'vitepress';
|
||||
import { motion, useScroll, useTransform, type Options as MotionOptions } from 'motion-v';
|
||||
import { useMediaQuery } from '@vueuse/core';
|
||||
import LucideIcon from '../base/LucideIcon.vue';
|
||||
|
||||
// motion-v re-exports framer-motion's `Variants`, which doesn't match the `variants` prop of its components
|
||||
@@ -10,7 +11,8 @@ type Variants = MotionOptions['variants'];
|
||||
|
||||
const emit = defineEmits(['animation-complete']);
|
||||
|
||||
const MotionLucideIcon = motion.create(LucideIcon);
|
||||
// motion.create only accepts a props-less DefineComponent, so the SFC's typed props need widening
|
||||
const MotionLucideIcon = motion.create(LucideIcon as unknown as DefineComponent);
|
||||
|
||||
const preventFocusProps = {
|
||||
tabindex: '-1',
|
||||
@@ -21,6 +23,89 @@ const COLUMNS = 8;
|
||||
const SIZE = 2;
|
||||
const GAP = 1;
|
||||
|
||||
/** The two spirals of the Lucide logo, drawn the way the SVG editor previews them. */
|
||||
const LOGO_PATH_OUTER =
|
||||
'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';
|
||||
|
||||
const LOGO_PATH_INNER =
|
||||
'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';
|
||||
|
||||
const VERTICES =
|
||||
'M14 12h.01M10 8h.01M6 12h.01M14 20h.01M22 12h.01M18 3.05557h.01M10 12h.01M14 16h.01M18 12h.01M10 4h.01M2 12h.01M6.06253 21h.01';
|
||||
|
||||
const MARKERS = [
|
||||
[14, 12],
|
||||
[18, 3.05557],
|
||||
[10, 12],
|
||||
[6.06253, 21],
|
||||
];
|
||||
|
||||
/** Bezier handles as [x1, y1, x2, y2] segments, grouped by when they appear. */
|
||||
const HANDLE_GROUPS = [
|
||||
// Outer spiral
|
||||
{ delay: 0.2, segments: [[14, 12, 14, 9.79086]] },
|
||||
{
|
||||
delay: 0.4,
|
||||
segments: [
|
||||
[10, 8, 12.2091, 8],
|
||||
[10, 8, 7.79086, 8],
|
||||
],
|
||||
},
|
||||
{
|
||||
delay: 0.6,
|
||||
segments: [
|
||||
[6, 12, 6, 9.79086],
|
||||
[6, 12, 6, 16.4183],
|
||||
],
|
||||
},
|
||||
{
|
||||
delay: 0.8,
|
||||
segments: [
|
||||
[14, 20, 9.58172, 20],
|
||||
[14, 20, 18.4183, 20],
|
||||
],
|
||||
},
|
||||
{
|
||||
delay: 1,
|
||||
segments: [
|
||||
[22, 12, 22, 16.4183],
|
||||
[22, 12, 22, 8.446],
|
||||
],
|
||||
},
|
||||
{ delay: 1.2, segments: [[18, 3.05557, 20.455, 5.25285]] },
|
||||
// Inner spiral
|
||||
{ delay: 0.2, segments: [[10, 12, 10, 14.2091]] },
|
||||
{
|
||||
delay: 0.4,
|
||||
segments: [
|
||||
[14, 16, 11.7909, 16],
|
||||
[14, 16, 16.2091, 16],
|
||||
],
|
||||
},
|
||||
{
|
||||
delay: 0.6,
|
||||
segments: [
|
||||
[18, 12, 18, 14.2091],
|
||||
[18, 12, 18, 7.58172],
|
||||
],
|
||||
},
|
||||
{
|
||||
delay: 0.8,
|
||||
segments: [
|
||||
[10, 4, 14.4183, 4],
|
||||
[10, 4, 5.58172, 4],
|
||||
],
|
||||
},
|
||||
{
|
||||
delay: 1,
|
||||
segments: [
|
||||
[2, 12, 2, 7.58172],
|
||||
[2, 12, 2, 15.5841],
|
||||
],
|
||||
},
|
||||
{ delay: 1.2, segments: [[6.06253, 21, 3.57127, 18.8012]] },
|
||||
];
|
||||
|
||||
const { scrollYProgress } = useScroll();
|
||||
const opacity = useTransform(() => 1 - scrollYProgress.get() * 8);
|
||||
|
||||
@@ -101,6 +186,9 @@ const draw: Variants = {
|
||||
|
||||
// motion-v passes the variant label that finished, not the resolved variant
|
||||
const onAnimationComplete = (variant: unknown) => {
|
||||
// Reduced motion stops at the icon grid, so don't shrink the icons or loop
|
||||
if (reducedMotion.value) return;
|
||||
|
||||
if (variant === 'visible') {
|
||||
drawAnimation.value = 'exit';
|
||||
return;
|
||||
@@ -155,6 +243,24 @@ const shrinkIconVariants: Variants = {
|
||||
transition: { delay: 1.8, duration: 1.5, ease: 'easeInOut' },
|
||||
}),
|
||||
};
|
||||
|
||||
// Only rendered on the client, so reduced motion is known before the first frame
|
||||
const mounted = ref(false);
|
||||
const reducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)');
|
||||
|
||||
onMounted(() => {
|
||||
if (reducedMotion.value) {
|
||||
drawAnimation.value = 'exit';
|
||||
showHandles.value = false;
|
||||
scaleDownAnimation.value = 'small';
|
||||
iconGridAnimation.value = 'showIcons';
|
||||
}
|
||||
|
||||
mounted.value = true;
|
||||
});
|
||||
|
||||
/** Skips straight to the end state when the user prefers reduced motion. */
|
||||
const initial = <T,>(value: T) => (reducedMotion.value ? false : value);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -167,6 +273,7 @@ const shrinkIconVariants: Variants = {
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<motion.svg
|
||||
v-if="mounted"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="-12 -12 48 48"
|
||||
fill="none"
|
||||
@@ -209,7 +316,7 @@ const shrinkIconVariants: Variants = {
|
||||
<MotionLucideIcon
|
||||
v-for="(icon, index) in icons"
|
||||
size="2"
|
||||
initial="initial"
|
||||
:initial="initial('initial')"
|
||||
:key="icon.name"
|
||||
:variants="iconAnimationVariants"
|
||||
:animate="iconGridAnimation"
|
||||
@@ -223,15 +330,15 @@ const shrinkIconVariants: Variants = {
|
||||
<motion.g
|
||||
:variants="scaleDownVariants"
|
||||
:animate="scaleDownAnimation"
|
||||
initial="hidden"
|
||||
:initial="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)' }"
|
||||
:d="LOGO_PATH_OUTER"
|
||||
:style="{ stroke: reducedMotion ? 'var(--vp-c-text-1)' : 'var(--vp-c-gray-1)' }"
|
||||
:animate="drawAnimation"
|
||||
initial="hidden"
|
||||
:initial="initial('hidden')"
|
||||
:variants="draw"
|
||||
:custom="1"
|
||||
@animation-complete="onAnimationComplete"
|
||||
@@ -239,10 +346,10 @@ const shrinkIconVariants: Variants = {
|
||||
/>
|
||||
|
||||
<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)' }"
|
||||
:d="LOGO_PATH_INNER"
|
||||
:style="{ stroke: reducedMotion ? 'var(--vp-c-brand)' : 'var(--vp-c-gray-1)' }"
|
||||
:animate="drawAnimation"
|
||||
initial="hidden"
|
||||
:initial="initial('hidden')"
|
||||
:variants="draw"
|
||||
:custom="0"
|
||||
v-bind="preventFocusProps"
|
||||
@@ -253,80 +360,36 @@ const shrinkIconVariants: Variants = {
|
||||
class="svg-preview-control-path-marker-group"
|
||||
:style="{ stroke: 'var(--vp-c-text-1)' }"
|
||||
stroke-width="0.125"
|
||||
:initial="{ opacity: 1 }"
|
||||
:animate="showHandles ? { opacity: 1 } : { opacity: 0 }"
|
||||
:transition="{ delay: 0, duration: 0.2 }"
|
||||
:initial="initial({ opacity: 1 })"
|
||||
:animate="{ opacity: showHandles ? 1 : 0 }"
|
||||
:transition="{ 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 }"
|
||||
v-for="d in [LOGO_PATH_OUTER, LOGO_PATH_INNER]"
|
||||
:key="d"
|
||||
:d="d"
|
||||
:initial="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 }"
|
||||
:d="VERTICES"
|
||||
:initial="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"
|
||||
v-for="[cx, cy] in MARKERS"
|
||||
:key="`${cx},${cy}`"
|
||||
:cx="cx"
|
||||
:cy="cy"
|
||||
r="0.5"
|
||||
v-bind="preventFocusProps"
|
||||
/>
|
||||
<motion.circle
|
||||
:initial="{ opacity: 0, scale: 0.2 }"
|
||||
:initial="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"
|
||||
:transition="{ duration: 0.8 }"
|
||||
v-bind="preventFocusProps"
|
||||
/>
|
||||
</motion.g>
|
||||
@@ -335,221 +398,30 @@ const shrinkIconVariants: Variants = {
|
||||
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 }"
|
||||
:initial="initial({ opacity: 1 })"
|
||||
:animate="{ opacity: showHandles ? 1 : 0 }"
|
||||
:transition="{ duration: 0.6 }"
|
||||
v-bind="preventFocusProps"
|
||||
>
|
||||
<motion.g
|
||||
:initial="{ opacity: 0, scale: 0.2 }"
|
||||
v-for="(group, index) in HANDLE_GROUPS"
|
||||
:key="index"
|
||||
:initial="initial({ opacity: 0, scale: 0.2 })"
|
||||
:animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0.2, duration: 0.3 }"
|
||||
:transition="{ delay: group.delay, 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>
|
||||
<template
|
||||
v-for="[x1, y1, x2, y2] in group.segments"
|
||||
:key="`${x2},${y2}`"
|
||||
>
|
||||
<path :d="`M${x1} ${y1} ${x2} ${y2}`" />
|
||||
<circle
|
||||
:cx="x2"
|
||||
:cy="y2"
|
||||
r="0.25"
|
||||
/>
|
||||
</template>
|
||||
</motion.g>
|
||||
</motion.g>
|
||||
</motion.svg>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { computed } from 'vue';
|
||||
import createLucideIcon from '@lucide/vue/src/createLucideIcon';
|
||||
import { useMediaQuery } from '@vueuse/core';
|
||||
import { useRouter } from 'vitepress';
|
||||
import { toast } from 'vue-sonner';
|
||||
import getSVGIcon from '../../utils/getSVGIcon';
|
||||
import useConfetti from '../../composables/useConfetti';
|
||||
import Tooltip from '../base/Tooltip.vue';
|
||||
@@ -38,17 +39,37 @@ const href = computed(() =>
|
||||
props.externalLibrary ? `/icons/${props.externalLibrary}/${props.name}` : `/icons/${props.name}`,
|
||||
);
|
||||
|
||||
// The toast says what was copied, since the name and the SVG are copied the same way
|
||||
async function copyToClipboard(text: string, confettiMessage: string, toastMessage: string) {
|
||||
await navigator.clipboard.writeText(text);
|
||||
|
||||
confettiText.value = confettiMessage;
|
||||
confetti();
|
||||
toast.success(toastMessage);
|
||||
}
|
||||
|
||||
function copyName() {
|
||||
return copyToClipboard(props.name, `Copied name!`, `Copied the name of ${props.name}`);
|
||||
}
|
||||
|
||||
function copySVG(iconButton: HTMLElement) {
|
||||
const svgString = getSVGIcon(iconButton.querySelector('svg'), {
|
||||
class: `lucide lucide-${props.name}`,
|
||||
});
|
||||
|
||||
return copyToClipboard(svgString, `Copied SVG!`, `Copied the SVG of ${props.name}`);
|
||||
}
|
||||
|
||||
async function navigateToIcon(event) {
|
||||
if (event.shiftKey) {
|
||||
event.preventDefault();
|
||||
const svgString = getSVGIcon(event.target.firstChild, {
|
||||
class: `lucide lucide-${props.name}`,
|
||||
});
|
||||
await copySVG(event.currentTarget);
|
||||
return;
|
||||
}
|
||||
|
||||
await navigator.clipboard.writeText(svgString);
|
||||
|
||||
confettiText.value = copiedText;
|
||||
confetti();
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
event.preventDefault();
|
||||
await copyName();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -76,6 +97,34 @@ async function navigateToIcon(event) {
|
||||
}
|
||||
}
|
||||
|
||||
// On a selected (focused) icon: Enter opens it, Ctrl/Cmd + C copies its name, Shift + C its SVG.
|
||||
// (Ctrl/Cmd + Shift + C is the browser's inspect shortcut, so it can't be used for the SVG.)
|
||||
async function handleKeydown(event: KeyboardEvent) {
|
||||
if (
|
||||
event.key === 'Enter' &&
|
||||
!(event.shiftKey || event.ctrlKey || event.metaKey || event.altKey)
|
||||
) {
|
||||
await navigateToIcon(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key.toLowerCase() !== 'c' || event.altKey) return;
|
||||
|
||||
if (event.shiftKey && !(event.ctrlKey || event.metaKey)) {
|
||||
event.preventDefault();
|
||||
await copySVG(event.currentTarget as HTMLElement);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(event.ctrlKey || event.metaKey) || event.shiftKey) return;
|
||||
|
||||
// Leave a normal copy alone when some text on the page is selected
|
||||
if (window.getSelection()?.toString()) return;
|
||||
|
||||
event.preventDefault();
|
||||
await copyName();
|
||||
}
|
||||
|
||||
const DiamondIcon = createLucideIcon('Diamond', diamond);
|
||||
</script>
|
||||
|
||||
@@ -83,7 +132,9 @@ const DiamondIcon = createLucideIcon('Diamond', diamond);
|
||||
<Tooltip :title="name">
|
||||
<a
|
||||
class="icon-button confetti-button vp-raw"
|
||||
tabindex="0"
|
||||
@click="navigateToIcon"
|
||||
@keydown="handleKeydown"
|
||||
:class="{ active, animate }"
|
||||
:aria-label="name"
|
||||
:data-confetti-text="confettiText"
|
||||
@@ -151,6 +202,12 @@ const DiamondIcon = createLucideIcon('Diamond', diamond);
|
||||
|
||||
.confetti-button:before {
|
||||
line-height: 80px;
|
||||
/* Center the "Copied ..." text over the icon (it started at the icon's left edge and ran off to the right). */
|
||||
width: 160px;
|
||||
max-width: none;
|
||||
left: calc(50% - 80px);
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.icon-button:active {
|
||||
@@ -186,10 +243,16 @@ const DiamondIcon = createLucideIcon('Diamond', diamond);
|
||||
background-color: var(--vp-button-alt-active-bg);
|
||||
}
|
||||
|
||||
.icon-button.active {
|
||||
.icon-button.active,
|
||||
.icon-button.animate,
|
||||
.icon-button:focus-visible {
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.icon-button:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.lucide-icon {
|
||||
margin: auto;
|
||||
pointer-events: none;
|
||||
|
||||
@@ -208,6 +208,43 @@ function handleCloseDrawer() {
|
||||
|
||||
window.history.pushState({}, '', url);
|
||||
}
|
||||
|
||||
const ARROW_STEPS = {
|
||||
ArrowLeft: () => -1,
|
||||
ArrowRight: () => 1,
|
||||
ArrowUp: () => -columnSize.value,
|
||||
ArrowDown: () => columnSize.value,
|
||||
};
|
||||
|
||||
// Arrow keys move through the search results: from the search field down into the grid, then
|
||||
// between the icons. The grid shows `columnSize` icons per row, so up and down skip a whole row.
|
||||
function handleArrowKeys(event: KeyboardEvent) {
|
||||
const step = ARROW_STEPS[event.key]?.();
|
||||
if (step === undefined || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey)
|
||||
return;
|
||||
|
||||
const iconButtons = Array.from(
|
||||
overviewEl.value?.querySelectorAll<HTMLElement>('.icons .icon-button') ?? [],
|
||||
);
|
||||
const target = event.target as HTMLElement;
|
||||
const fromSearch = target.tagName === 'INPUT' && target.closest('.input-wrapper') !== null;
|
||||
|
||||
let next: HTMLElement | undefined;
|
||||
if (fromSearch) {
|
||||
if (event.key !== 'ArrowDown') return;
|
||||
next = iconButtons[0];
|
||||
} else {
|
||||
const index = iconButtons.indexOf(target);
|
||||
if (index === -1) return;
|
||||
next = iconButtons[index + step];
|
||||
}
|
||||
|
||||
if (next) {
|
||||
event.preventDefault();
|
||||
next.focus();
|
||||
next.scrollIntoView({ block: 'nearest' });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -215,6 +252,7 @@ function handleCloseDrawer() {
|
||||
ref="overviewEl"
|
||||
class="overview-container"
|
||||
:class="{ 'icon-drawer-open': activeIconName }"
|
||||
@keydown="handleArrowKeys"
|
||||
>
|
||||
<StickyBar>
|
||||
<InputSearch
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
<script setup lang="ts">
|
||||
import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import Icon from '@lucide/vue/src/Icon';
|
||||
import Kbd from '../base/Kbd.vue';
|
||||
import { x } from '~/.vitepress/data/iconNodes';
|
||||
|
||||
const dialog = ref<HTMLDialogElement | null>(null);
|
||||
const isClosing = ref(false);
|
||||
const modifierKey = ref('Ctrl');
|
||||
|
||||
const shortcuts = [
|
||||
{ keys: ['mod', 'K'], alternative: ['/'], description: 'Search' },
|
||||
{ keys: ['Shift', 'Click'], description: 'Copy the SVG of an icon' },
|
||||
{ keys: ['mod', 'Click'], description: 'Copy the name of an icon' },
|
||||
{ keys: ['Arrow keys'], description: 'Move through the search results' },
|
||||
{ keys: ['Enter'], description: 'Open the selected icon' },
|
||||
{ keys: ['mod', 'C'], description: 'Copy the name of the selected icon' },
|
||||
{ keys: ['Shift', 'C'], description: 'Copy the SVG of the selected icon' },
|
||||
{ keys: ['Shift', '?'], description: 'Show keyboard shortcuts' },
|
||||
];
|
||||
|
||||
function keyLabel(key: string) {
|
||||
return key === 'mod' ? modifierKey.value : key;
|
||||
}
|
||||
|
||||
function open() {
|
||||
dialog.value?.showModal();
|
||||
// Focus the dialog itself rather than its close button, so the button is not shown as selected
|
||||
dialog.value?.focus();
|
||||
}
|
||||
|
||||
// The dialog leaves the top layer as soon as it closes, so play the exit transition first
|
||||
async function close() {
|
||||
const element = dialog.value;
|
||||
|
||||
if (!element?.open || isClosing.value) return;
|
||||
|
||||
isClosing.value = true;
|
||||
await nextTick();
|
||||
await Promise.allSettled(element.getAnimations().map((animation) => animation.finished));
|
||||
element.close();
|
||||
isClosing.value = false;
|
||||
}
|
||||
|
||||
function handleDialogClick(event: MouseEvent) {
|
||||
// A click on the backdrop lands on the dialog element itself
|
||||
if (event.target === dialog.value) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
function isTyping(event: KeyboardEvent) {
|
||||
const target = event.target as HTMLElement | null;
|
||||
|
||||
return (
|
||||
target?.isContentEditable ||
|
||||
target?.tagName === 'INPUT' ||
|
||||
target?.tagName === 'TEXTAREA' ||
|
||||
target?.tagName === 'SELECT'
|
||||
);
|
||||
}
|
||||
|
||||
// The ? key opens the dialog, except while typing in a field
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
if (event.key === '?' && !event.ctrlKey && !event.metaKey && !event.altKey && !isTyping(event)) {
|
||||
event.preventDefault();
|
||||
open();
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
modifierKey.value = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform) ? '⌘' : 'Ctrl';
|
||||
window.addEventListener('keydown', handleKeydown);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('keydown', handleKeydown);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
type="button"
|
||||
class="keyboard-shortcuts-hint"
|
||||
aria-haspopup="dialog"
|
||||
@click="open"
|
||||
>
|
||||
<span class="hint-label">Keyboard shortcuts</span>
|
||||
<span class="hint-keys">
|
||||
<Kbd>Shift</Kbd>
|
||||
<span>+</span>
|
||||
<Kbd>?</Kbd>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<dialog
|
||||
ref="dialog"
|
||||
class="shortcuts-dialog"
|
||||
:class="{ closing: isClosing }"
|
||||
tabindex="-1"
|
||||
aria-labelledby="shortcuts-title"
|
||||
@click="handleDialogClick"
|
||||
@cancel.prevent="close"
|
||||
>
|
||||
<div class="shortcuts-header">
|
||||
<h2 id="shortcuts-title">Keyboard shortcuts</h2>
|
||||
<button
|
||||
type="button"
|
||||
class="shortcuts-close"
|
||||
aria-label="Close"
|
||||
@click="close"
|
||||
>
|
||||
<Icon
|
||||
:iconNode="x"
|
||||
name="x"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<dl class="shortcuts-list">
|
||||
<div
|
||||
v-for="shortcut in shortcuts"
|
||||
:key="shortcut.description"
|
||||
class="shortcut-row"
|
||||
>
|
||||
<dt class="shortcut-keys">
|
||||
<template
|
||||
v-for="(key, index) in shortcut.keys"
|
||||
:key="key"
|
||||
>
|
||||
<span
|
||||
v-if="index > 0"
|
||||
class="shortcut-plus"
|
||||
>+</span
|
||||
>
|
||||
<Kbd>{{ keyLabel(key) }}</Kbd>
|
||||
</template>
|
||||
<template v-if="shortcut.alternative">
|
||||
<span class="shortcut-or">or</span>
|
||||
<Kbd
|
||||
v-for="key in shortcut.alternative"
|
||||
:key="key"
|
||||
>{{ key }}</Kbd
|
||||
>
|
||||
</template>
|
||||
</dt>
|
||||
<dd class="shortcut-description">{{ shortcut.description }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.keyboard-shortcuts-hint {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-top: 12px;
|
||||
padding: 24px 0px;
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
font-size: 12px;
|
||||
color: var(--vp-c-text-1);
|
||||
position: sticky;
|
||||
bottom: -96px;
|
||||
background-color: var(--vp-sidebar-bg-color);
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.hint-keys {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.shortcuts-dialog {
|
||||
width: min(480px, calc(100vw - 32px));
|
||||
padding: 20px 24px 24px;
|
||||
color: var(--vp-c-text-1);
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 12px;
|
||||
box-shadow: var(--vp-shadow-4);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.shortcuts-dialog::backdrop {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.shortcuts-dialog[open] {
|
||||
transition:
|
||||
opacity 150ms ease-out,
|
||||
transform 200ms cubic-bezier(0.34, 1.4, 0.64, 1);
|
||||
}
|
||||
|
||||
.shortcuts-dialog[open]::backdrop {
|
||||
transition: background-color 150ms ease-out;
|
||||
}
|
||||
|
||||
@starting-style {
|
||||
.shortcuts-dialog[open] {
|
||||
opacity: 0;
|
||||
transform: translateY(-6px) scale(0.98);
|
||||
}
|
||||
|
||||
.shortcuts-dialog[open]::backdrop {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.shortcuts-dialog.closing {
|
||||
opacity: 0;
|
||||
transform: translateY(-6px) scale(0.98);
|
||||
transition:
|
||||
opacity 150ms ease-in,
|
||||
transform 150ms ease-in;
|
||||
}
|
||||
|
||||
.shortcuts-dialog.closing::backdrop {
|
||||
background-color: transparent;
|
||||
transition: background-color 150ms ease-in;
|
||||
}
|
||||
|
||||
.shortcuts-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.shortcuts-header h2 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.shortcuts-close {
|
||||
display: flex;
|
||||
padding: 4px;
|
||||
color: var(--vp-c-text-2);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.shortcuts-close:hover,
|
||||
.shortcuts-close:focus-visible {
|
||||
color: var(--vp-c-text-1);
|
||||
background: var(--vp-c-default-soft);
|
||||
}
|
||||
|
||||
.shortcuts-close svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.shortcuts-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.shortcut-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.shortcut-keys {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.shortcut-description {
|
||||
margin: 0;
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.shortcut-plus,
|
||||
.shortcut-or {
|
||||
color: var(--vp-c-text-3);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.shortcut-or {
|
||||
margin: 0 4px;
|
||||
}
|
||||
</style>
|
||||
@@ -89,6 +89,7 @@ function onReset() {
|
||||
<InputField
|
||||
id="non-scaling-stroke"
|
||||
label="Non-scaling stroke"
|
||||
class="non-scaling-stroke-switch"
|
||||
>
|
||||
<Switch
|
||||
id="non-scaling-stroke-switch"
|
||||
|
||||
@@ -173,3 +173,12 @@
|
||||
0% 0%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Keep the copy confetti even with reduced motion turned on (owner request): the site-wide
|
||||
reduced-motion rule in style.css shrinks every animation to 0.01ms, which hid it at once. */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.confetti-button.animate:before,
|
||||
.confetti-button.animate:after {
|
||||
animation-duration: 1s !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { EXTERNAL_LIBS_CONTEXT, externalLibContext } from './composables/useExte
|
||||
import SideBarNavBefore from './layouts/SideBarNavBefore.vue';
|
||||
import NavBarSponsorButton from './layouts/NavBarSponsorButton.vue';
|
||||
import SnackPlayer from './components/editors/SnackPlayer.vue';
|
||||
import Toaster from './components/base/Toaster.vue';
|
||||
import { setupTracking } from './utils/openpanel';
|
||||
import trackUnderlineDirection from './utils/underlineDirection';
|
||||
|
||||
@@ -25,6 +26,7 @@ const theme: Partial<Theme> = {
|
||||
'sidebar-nav-after': () => h(IconsSidebarNavAfter),
|
||||
'home-hero-image': () => h(HomeHeroIconsCard),
|
||||
'home-hero-actions-after': () => h(HomeHeroAfter),
|
||||
'layout-bottom': () => h(Toaster),
|
||||
'nav-bar-content-after': () => h(NavBarSponsorButton),
|
||||
// Keep this here for now, we might want to add more things to the top bar in the future
|
||||
// 'layout-top': () => h(LayoutTop),
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
<script setup>
|
||||
import { useData } from 'vitepress'
|
||||
import { computed } from 'vue';
|
||||
import { useData } from 'vitepress';
|
||||
import SidebarIconCustomizer from '../components/icons/SidebarIconCustomizer.vue';
|
||||
import ExternalLibrarySelect from '../components/icons/SidebarExternalLibrarySelect.vue';
|
||||
import CategoryList from '../components/icons/CategoryList.vue';
|
||||
import KeyboardShortcuts from '../components/icons/KeyboardShortcuts.vue';
|
||||
|
||||
import CategoryList from '../components/icons/CategoryList.vue'
|
||||
import SidebarIconCustomizer from '../components/icons/SidebarIconCustomizer.vue'
|
||||
import ExternalLibrarySelect from '../components/icons/SidebarExternalLibrarySelect.vue'
|
||||
|
||||
const { page } = useData()
|
||||
const { page } = useData();
|
||||
|
||||
const isIconsPage = computed(() => page.value?.relativePath?.startsWith?.('icons'));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<SidebarIconCustomizer v-if="page?.relativePath?.startsWith?.('icons')"/>
|
||||
<ExternalLibrarySelect v-if="page?.relativePath?.startsWith?.('icons')"/>
|
||||
<CategoryList v-if="page?.relativePath?.startsWith?.('icons')"/>
|
||||
<div v-if="isIconsPage">
|
||||
<SidebarIconCustomizer />
|
||||
<ExternalLibrarySelect />
|
||||
<CategoryList />
|
||||
<KeyboardShortcuts />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -385,6 +385,12 @@ html.dark .VPNav .VPSwitchAppearance .icon .moon {
|
||||
transition: transform 0.3s cubic-bezier(0.34, 1.4, 0.64, 1);
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.VPSidebar {
|
||||
padding-right: 24px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Links in the page text: a faint underline shows they are links, and on hover a solid
|
||||
underline draws in from the left. Drawn with backgrounds so it also follows links that
|
||||
wrap onto a second line. */
|
||||
|
||||
@@ -11,7 +11,7 @@ const allowedAttrs = [
|
||||
'class',
|
||||
];
|
||||
|
||||
export default function getSVGIcon(element?: HTMLElement, attrs?: Record<string, string>) {
|
||||
export default function getSVGIcon(element?: Element, attrs?: Record<string, string>) {
|
||||
const svg = element ?? document.querySelector('#previewer svg');
|
||||
if (!svg) return;
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ export default function useSearchShortcut(callback: () => void) {
|
||||
// Check for Cmd+K (Mac), Ctrl+K (Windows/Linux), or forward slash
|
||||
if (((event.metaKey || event.ctrlKey) && event.key === 'k') || event.key === '/') {
|
||||
event.preventDefault();
|
||||
// Algolia listens for the same keys on window, so claim them before it sees them
|
||||
event.stopImmediatePropagation();
|
||||
callback();
|
||||
}
|
||||
}
|
||||
@@ -24,11 +26,11 @@ export default function useSearchShortcut(callback: () => void) {
|
||||
shortcutText.value = isMac ? '⌘K' : 'Ctrl+K';
|
||||
|
||||
// Add keyboard shortcut listener
|
||||
window.addEventListener('keydown', handleKeydown);
|
||||
window.addEventListener('keydown', handleKeydown, { capture: true });
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('keydown', handleKeydown);
|
||||
window.removeEventListener('keydown', handleKeydown, { capture: true });
|
||||
});
|
||||
|
||||
return { shortcutText };
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
title: Comparison
|
||||
description: A comparison between Lucide and Feather Icons, highlighting the differences and benefits of each.
|
||||
---
|
||||
|
||||
# Comparison
|
||||
|
||||
## Lucide vs Feather Icons
|
||||
|
||||
Lucide is a community-driven fork of [Feather Icons](https://github.com/feathericons/feather).
|
||||
|
||||
The decision to create Lucide arose from growing dissatisfaction with the moderation of the Feather Icons project. With more than 300 open issues and over 100 open PRs, the Feather Icons project has been abandoned and is no longer actively maintained. Unfortunately, this means that numerous developers and designers have invested their time in contributing to Feather Icons without the possibility of their PRs being accepted.
|
||||
|
||||
In an effort to expand the icon set while remaining true to the original minimalist design language, Lucide is driven by a community of developers and designers. We strive to grow together and maintain a faithful continuation of the project.
|
||||
|
||||
### Why should I choose Lucide over Feather Icons?
|
||||
|
||||
- Lucide has expanded its icon set by 1600+ in the last few years. Lucide now has over 1800 icons, while Feather has around 287 icons.
|
||||
- Well maintained code base.
|
||||
- Active community.
|
||||
|
||||
### Should I migrate to Lucide?
|
||||
|
||||
That depends on whether you're satisfied with the icons from Feather Icons. If that is the case, it may not be worth the effort.
|
||||
However, if you find yourself struggling and feeling limited by the icons provided by Feather, you can consider migrating.
|
||||
When we forked, we didn't remove any icons, but some icons have been renamed.
|
||||
@@ -2,7 +2,6 @@
|
||||
title: What is Lucide?
|
||||
description: An introduction to Lucide, an open-source icon library that provides 1600+ vector (svg) files for displaying icons and symbols in digital and non-digital projects.
|
||||
nextPage:
|
||||
- comparison
|
||||
- installation
|
||||
---
|
||||
|
||||
|
||||
@@ -73,10 +73,11 @@
|
||||
"lodash-es": "^4.18.1",
|
||||
"lucide": "workspace:*",
|
||||
"lucide-react": "workspace:*",
|
||||
"motion-v": "2.4.0",
|
||||
"motion-v": "2.11.0",
|
||||
"react": "19.3.1",
|
||||
"react-dom": "19.3.1",
|
||||
"sandpack-vue3": "3.1.12",
|
||||
"vue": "^3.5.44"
|
||||
"vue": "^3.5.44",
|
||||
"vue-sonner": "^2.0.9"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
"presentation",
|
||||
"reporting",
|
||||
"data visualisation",
|
||||
"visualisation"
|
||||
"visualisation",
|
||||
"analyze",
|
||||
"kpi",
|
||||
"metrics",
|
||||
"insights"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
"valid",
|
||||
"ticked",
|
||||
"task",
|
||||
"todo"
|
||||
"todo",
|
||||
"selected",
|
||||
"okay",
|
||||
"ok"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
"tags": [
|
||||
"coding",
|
||||
"programming",
|
||||
"development"
|
||||
"development",
|
||||
"developer",
|
||||
"dev"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"$schema": "../group.schema.json",
|
||||
"description": "Style: drawn with a dashed or dotted line",
|
||||
"tags": [
|
||||
"dashed",
|
||||
"dotted",
|
||||
"broken line"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "../group.schema.json",
|
||||
"description": "Subject: a face showing an emotion",
|
||||
"tags": [
|
||||
"emoji",
|
||||
"emotion",
|
||||
"mood",
|
||||
"feeling",
|
||||
"expression",
|
||||
"reaction"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"$schema": "../group.schema.json",
|
||||
"description": "Action: moving into something or bringing something in",
|
||||
"tags": [
|
||||
"in",
|
||||
"inside",
|
||||
"inwards",
|
||||
"input",
|
||||
"incoming",
|
||||
"import",
|
||||
"insert",
|
||||
"entry",
|
||||
"entrance",
|
||||
"ingress"
|
||||
]
|
||||
}
|
||||
@@ -13,6 +13,11 @@
|
||||
"hazard",
|
||||
"exclamation mark",
|
||||
"exclamation point",
|
||||
"!"
|
||||
"!",
|
||||
"critical",
|
||||
"urgent",
|
||||
"severity",
|
||||
"punctuation",
|
||||
"symbol"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "../group.schema.json",
|
||||
"description": "Action: moving out of something or sending something out",
|
||||
"tags": [
|
||||
"out",
|
||||
"outside",
|
||||
"outwards",
|
||||
"output",
|
||||
"outgoing",
|
||||
"export",
|
||||
"leave",
|
||||
"egress",
|
||||
"exit"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "../group.schema.json",
|
||||
"description": "Layout: resizing, splitting or merging panes and grid cells",
|
||||
"tags": [
|
||||
"pane",
|
||||
"grid",
|
||||
"resize",
|
||||
"window",
|
||||
"merge",
|
||||
"resizing"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"$schema": "../group.schema.json",
|
||||
"description": "Geometry: drawn strokes, paths and line segments",
|
||||
"tags": [
|
||||
"stroke",
|
||||
"path",
|
||||
"segment",
|
||||
"vector",
|
||||
"graphics",
|
||||
"design",
|
||||
"shape",
|
||||
"curve"
|
||||
]
|
||||
}
|
||||
@@ -3,19 +3,32 @@
|
||||
"description": "Status: help, or something unknown",
|
||||
"tags": [
|
||||
"help",
|
||||
"assistance",
|
||||
"support",
|
||||
"guide",
|
||||
"tutorial",
|
||||
"manual",
|
||||
"faq",
|
||||
"hint",
|
||||
"query",
|
||||
"unknown",
|
||||
"unidentified",
|
||||
"unspecified",
|
||||
"uncertain",
|
||||
"unsure",
|
||||
"doubt",
|
||||
"undetermined",
|
||||
"missing",
|
||||
"to be determined",
|
||||
"mystery",
|
||||
"question",
|
||||
"question mark",
|
||||
"?"
|
||||
"?",
|
||||
"quiz",
|
||||
"inquiry",
|
||||
"enquiry",
|
||||
"ask",
|
||||
"punctuation",
|
||||
"symbol"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"lens",
|
||||
"zoom",
|
||||
"inspection",
|
||||
"visual search"
|
||||
"visual search",
|
||||
"loupe"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
"member",
|
||||
"client",
|
||||
"participant",
|
||||
"guest"
|
||||
"guest",
|
||||
"customer",
|
||||
"username"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
"cross",
|
||||
"times",
|
||||
"ex",
|
||||
"×"
|
||||
"×",
|
||||
"deny",
|
||||
"decline",
|
||||
"refuse"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -190,6 +190,7 @@
|
||||
"$group:code",
|
||||
"$group:cog",
|
||||
"$group:currency",
|
||||
"$group:dashed",
|
||||
"$group:digit",
|
||||
"$group:direction-down",
|
||||
"$group:direction-down-left",
|
||||
@@ -200,11 +201,16 @@
|
||||
"$group:direction-up-left",
|
||||
"$group:direction-up-right",
|
||||
"$group:dot",
|
||||
"$group:emoticon",
|
||||
"$group:enter",
|
||||
"$group:exclamation-mark",
|
||||
"$group:exit",
|
||||
"$group:graph",
|
||||
"$group:heart",
|
||||
"$group:key",
|
||||
"$group:layout-resize",
|
||||
"$group:letter",
|
||||
"$group:line",
|
||||
"$group:lock",
|
||||
"$group:minus",
|
||||
"$group:navigation-first",
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
"code",
|
||||
"glob pattern",
|
||||
"wildcard",
|
||||
"*"
|
||||
"*",
|
||||
"required",
|
||||
"mandatory"
|
||||
],
|
||||
"categories": [
|
||||
"text",
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"$schema": "../icon.schema.json",
|
||||
"contributors": [
|
||||
"KesleyDavid",
|
||||
"karsa-mistmere"
|
||||
],
|
||||
"use-cases": [
|
||||
"Marking interoperable Fediverse profile links in social clients",
|
||||
"Representing a three-star typographic symbol in publishing tools",
|
||||
"Separating article sections with a decorative typographic mark",
|
||||
"Representing star patterns in astronomy apps"
|
||||
],
|
||||
"tags": [
|
||||
"asterisk",
|
||||
"fediverse",
|
||||
"activitypub",
|
||||
"symbol",
|
||||
"typography",
|
||||
"social",
|
||||
"decentralized",
|
||||
"federation",
|
||||
"stars",
|
||||
"three",
|
||||
"ornament",
|
||||
"punctuation",
|
||||
"divider"
|
||||
],
|
||||
"categories": [
|
||||
"text"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M12 2v8" />
|
||||
<path d="m14.5 15 7 4" />
|
||||
<path d="m14.5 19 7-4" />
|
||||
<path d="M18 13v8" />
|
||||
<path d="m2.5 15 7 4" />
|
||||
<path d="m2.5 19 7-4" />
|
||||
<path d="M6 13v8" />
|
||||
<path d="m8.5 4 7 4" />
|
||||
<path d="m8.5 8 7-4" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 440 B |
@@ -10,7 +10,10 @@
|
||||
"badge",
|
||||
"rosette",
|
||||
"prize",
|
||||
"winner"
|
||||
"winner",
|
||||
"skill",
|
||||
"skills",
|
||||
"expertise"
|
||||
],
|
||||
"categories": [
|
||||
"account",
|
||||
|
||||
@@ -14,7 +14,13 @@
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"$extends:badge",
|
||||
"$group:check"
|
||||
"$group:check",
|
||||
"premium",
|
||||
"vip",
|
||||
"exclusive",
|
||||
"pro",
|
||||
"guarantee",
|
||||
"warranty"
|
||||
],
|
||||
"categories": [
|
||||
"social"
|
||||
|
||||
@@ -7,7 +7,11 @@
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"$extends:badge",
|
||||
"$group:percent"
|
||||
"$group:percent",
|
||||
"sell",
|
||||
"selling",
|
||||
"promotion",
|
||||
"promo"
|
||||
],
|
||||
"categories": [
|
||||
"finance",
|
||||
|
||||
@@ -10,6 +10,6 @@
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" />
|
||||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" />
|
||||
<line x1="12" x2="12.01" y1="17" y2="17" />
|
||||
<path d="M9 9.5a3 2.5 0 016 0c0 2-2 2-3 3.5" />
|
||||
<path d="M12 17h.01" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 481 B After Width: | Height: | Size: 459 B |
@@ -21,7 +21,8 @@
|
||||
"emergency",
|
||||
"aid",
|
||||
"safety",
|
||||
"patch"
|
||||
"patch",
|
||||
"patient"
|
||||
],
|
||||
"categories": [
|
||||
"medical"
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
"cash",
|
||||
"funds",
|
||||
"transaction",
|
||||
"finance"
|
||||
"finance",
|
||||
"salary"
|
||||
],
|
||||
"categories": [
|
||||
"finance"
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M10 17h.01" />
|
||||
<path d="M10 7v6" />
|
||||
<path d="M10 16h.01" />
|
||||
<path d="M10 8v4" />
|
||||
<path d="M14 6h2a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2" />
|
||||
<path d="M22 14v-4" />
|
||||
<path d="M6 18H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h2" />
|
||||
|
||||
|
Before Width: | Height: | Size: 393 B After Width: | Height: | Size: 393 B |
@@ -7,6 +7,7 @@
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"$extends:book",
|
||||
"$group:dashed",
|
||||
"code",
|
||||
"coding",
|
||||
"version control",
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
],
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"$extends:book-open"
|
||||
"$extends:book-open",
|
||||
"docs",
|
||||
"wiki",
|
||||
"knowledge base"
|
||||
],
|
||||
"categories": [
|
||||
"text",
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
],
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"$extends:book"
|
||||
"$extends:book",
|
||||
"docs",
|
||||
"wiki",
|
||||
"knowledge base"
|
||||
],
|
||||
"categories": [
|
||||
"text",
|
||||
|
||||
@@ -24,7 +24,10 @@
|
||||
"smart",
|
||||
"deep learning",
|
||||
"machine learning",
|
||||
"computing"
|
||||
"computing",
|
||||
"skill",
|
||||
"skills",
|
||||
"expertise"
|
||||
],
|
||||
"categories": [
|
||||
"medical",
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
"briefcase",
|
||||
"portfolio",
|
||||
"business",
|
||||
"career",
|
||||
"employment",
|
||||
"professional"
|
||||
],
|
||||
"categories": [
|
||||
|
||||
@@ -10,7 +10,12 @@
|
||||
"work",
|
||||
"bag",
|
||||
"baggage",
|
||||
"folder"
|
||||
"folder",
|
||||
"job",
|
||||
"career",
|
||||
"employment",
|
||||
"hiring",
|
||||
"experience"
|
||||
],
|
||||
"categories": [
|
||||
"travel"
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"count",
|
||||
"calculating machine"
|
||||
"calculating machine",
|
||||
"budget"
|
||||
],
|
||||
"categories": [
|
||||
"math",
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
"singular",
|
||||
"once",
|
||||
"1",
|
||||
"first"
|
||||
"first",
|
||||
"today"
|
||||
],
|
||||
"categories": [
|
||||
"$extends:calendar"
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
"$extends:calendar",
|
||||
"$group:check",
|
||||
"subscribe",
|
||||
"schedule"
|
||||
"schedule",
|
||||
"today"
|
||||
],
|
||||
"categories": [
|
||||
"$extends:calendar"
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
],
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"$extends:calendar"
|
||||
"$extends:calendar",
|
||||
"week",
|
||||
"weekly"
|
||||
],
|
||||
"categories": [
|
||||
"$extends:calendar"
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
"tags": [
|
||||
"$extends:calendar",
|
||||
"range",
|
||||
"period"
|
||||
"period",
|
||||
"week",
|
||||
"weekly"
|
||||
],
|
||||
"categories": [
|
||||
"$extends:calendar"
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
"mineral",
|
||||
"pentagon",
|
||||
"multidimensional",
|
||||
"metrics",
|
||||
"performance",
|
||||
"comparison",
|
||||
"profile",
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
{
|
||||
"$schema": "../icon.schema.json",
|
||||
"contributors": [
|
||||
"danielbayley"
|
||||
"danielbayley",
|
||||
"karsa-mistmere"
|
||||
],
|
||||
"use-cases": [
|
||||
"Categorizing cheeses in grocery shopping apps",
|
||||
"Marking cheese-based dishes on restaurant menus"
|
||||
],
|
||||
"tags": [
|
||||
"swiss",
|
||||
@@ -0,0 +1,16 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M14 11a3 3 0 01-6 0 5 5 0 016 0" />
|
||||
<path d="M14.292 2.436a1.55 1.55 0 011.463-.088l.278.13A10.33 10.33 0 0122 11v8a2 2 0 01-2 2h-1.75a.28.28 0 01-.265-.25 2 2 0 00-3.97 0 .28.28 0 01-.265.25H4a2 2 0 01-2-2v-.75a.28.28 0 01.25-.265 2 2 0 00-.046-3.975.21.21 0 01-.204-.214v-2.678a2 2 0 01.952-1.703l6.062-3.732a.3.3 0 01.27-.002A2.5 2.5 0 0013 3.5l-.002-.104a.3.3 0 01.122-.24z" />
|
||||
<path d="M22 11h-8" />
|
||||
<path d="M8 11H2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 651 B |
@@ -6,7 +6,7 @@
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"$group:direction-down-left",
|
||||
"outwards"
|
||||
"$group:exit"
|
||||
],
|
||||
"categories": [
|
||||
"arrows"
|
||||
@@ -16,6 +16,11 @@
|
||||
"name": "arrow-down-left-from-circle",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true
|
||||
},
|
||||
{
|
||||
"name": "circle-arrow-out-down-left",
|
||||
"deprecated": true,
|
||||
"deprecationReason": "alias.name"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 300 B After Width: | Height: | Size: 300 B |
@@ -6,7 +6,7 @@
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"$group:direction-down-right",
|
||||
"outwards"
|
||||
"$group:exit"
|
||||
],
|
||||
"categories": [
|
||||
"arrows"
|
||||
@@ -16,6 +16,11 @@
|
||||
"name": "arrow-down-right-from-circle",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true
|
||||
},
|
||||
{
|
||||
"name": "circle-arrow-out-down-right",
|
||||
"deprecated": true,
|
||||
"deprecationReason": "alias.name"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 303 B After Width: | Height: | Size: 303 B |
@@ -6,7 +6,7 @@
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"$group:direction-up-left",
|
||||
"outwards",
|
||||
"$group:exit",
|
||||
"keyboard",
|
||||
"button",
|
||||
"escape"
|
||||
@@ -19,6 +19,11 @@
|
||||
"name": "arrow-up-left-from-circle",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true
|
||||
},
|
||||
{
|
||||
"name": "circle-arrow-out-up-left",
|
||||
"deprecated": true,
|
||||
"deprecationReason": "alias.name"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 296 B After Width: | Height: | Size: 296 B |
@@ -6,7 +6,7 @@
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"$group:direction-up-right",
|
||||
"outwards"
|
||||
"$group:exit"
|
||||
],
|
||||
"categories": [
|
||||
"arrows"
|
||||
@@ -16,6 +16,11 @@
|
||||
"name": "arrow-up-right-from-circle",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true
|
||||
},
|
||||
{
|
||||
"name": "circle-arrow-out-up-right",
|
||||
"deprecated": true,
|
||||
"deprecationReason": "alias.name"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 299 B After Width: | Height: | Size: 299 B |
@@ -6,6 +6,7 @@
|
||||
],
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"$group:dashed",
|
||||
"pending",
|
||||
"dot",
|
||||
"progress",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
],
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"$group:dashed",
|
||||
"pending",
|
||||
"progress",
|
||||
"issue",
|
||||
|
||||
@@ -10,6 +10,6 @@
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" />
|
||||
<path d="M9 9.5a3 2.5 0 016 0c0 2-2 2-3 3.5" />
|
||||
<path d="M12 17h.01" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 322 B After Width: | Height: | Size: 320 B |
@@ -10,7 +10,9 @@
|
||||
"$extends:clipboard",
|
||||
"$group:check",
|
||||
"copied",
|
||||
"pasted"
|
||||
"pasted",
|
||||
"result",
|
||||
"checklist"
|
||||
],
|
||||
"categories": [
|
||||
"$extends:clipboard"
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"$extends:clipboard",
|
||||
"tasks"
|
||||
"tasks",
|
||||
"checklist"
|
||||
],
|
||||
"categories": [
|
||||
"$extends:clipboard"
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
],
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"import"
|
||||
"import",
|
||||
"install",
|
||||
"installation"
|
||||
],
|
||||
"categories": [
|
||||
"arrows",
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"$extends:cloud",
|
||||
"bolt"
|
||||
"bolt",
|
||||
"thunder",
|
||||
"thunderstorm",
|
||||
"thunderbolt"
|
||||
],
|
||||
"categories": [
|
||||
"$extends:cloud"
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
],
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"file"
|
||||
"file",
|
||||
"deploy",
|
||||
"deployment"
|
||||
],
|
||||
"categories": [
|
||||
"arrows",
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
"bank",
|
||||
"purchase",
|
||||
"payment",
|
||||
"cc"
|
||||
"cc",
|
||||
"cashier",
|
||||
"merchant"
|
||||
],
|
||||
"categories": [
|
||||
"account",
|
||||
|
||||
@@ -16,7 +16,11 @@
|
||||
"ruler",
|
||||
"winner",
|
||||
"favorite",
|
||||
"favourite"
|
||||
"favourite",
|
||||
"premium",
|
||||
"vip",
|
||||
"exclusive",
|
||||
"pro"
|
||||
],
|
||||
"categories": [
|
||||
"gaming"
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
"egress",
|
||||
"gate",
|
||||
"gateway",
|
||||
"emergency exit"
|
||||
"emergency exit",
|
||||
"quit"
|
||||
],
|
||||
"categories": [
|
||||
"home",
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
"import",
|
||||
"export",
|
||||
"save",
|
||||
"pull"
|
||||
"pull",
|
||||
"install",
|
||||
"installation"
|
||||
],
|
||||
"categories": [
|
||||
"arrows",
|
||||
|
||||
@@ -11,7 +11,11 @@
|
||||
"browser",
|
||||
"language",
|
||||
"translate",
|
||||
"globe"
|
||||
"globe",
|
||||
"www",
|
||||
"http",
|
||||
"https",
|
||||
"website"
|
||||
],
|
||||
"categories": [
|
||||
"navigation"
|
||||
|
||||
@@ -14,7 +14,11 @@
|
||||
"overflow",
|
||||
"dots",
|
||||
"…",
|
||||
"..."
|
||||
"...",
|
||||
"kebab",
|
||||
"kebab menu",
|
||||
"three dots",
|
||||
"3 dots"
|
||||
],
|
||||
"categories": [
|
||||
"layout"
|
||||
|
||||
@@ -25,7 +25,10 @@
|
||||
"overflow",
|
||||
"dots",
|
||||
"…",
|
||||
"..."
|
||||
"...",
|
||||
"meatball menu",
|
||||
"three dots",
|
||||
"3 dots"
|
||||
],
|
||||
"categories": [
|
||||
"layout",
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"$schema": "../icon.schema.json",
|
||||
"contributors": [
|
||||
"karsa-mistmere",
|
||||
"danielbayley",
|
||||
"lt25106"
|
||||
],
|
||||
"use-cases": [
|
||||
"Highlighting warnings or errors in user interfaces",
|
||||
"Marking important notices that need attention"
|
||||
],
|
||||
"tags": [
|
||||
"$group:exclamation-mark",
|
||||
"bang",
|
||||
"surprise",
|
||||
"emphasis",
|
||||
"interjection",
|
||||
"factorial",
|
||||
"negation"
|
||||
],
|
||||
"categories": [
|
||||
"letters-digits",
|
||||
"text",
|
||||
"notifications"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M12 15V4" />
|
||||
<path d="M12 20h.01" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 258 B |
@@ -9,7 +9,8 @@
|
||||
"tags": [
|
||||
"outbound",
|
||||
"open",
|
||||
"share"
|
||||
"share",
|
||||
"redirect"
|
||||
],
|
||||
"categories": [
|
||||
"arrows",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
],
|
||||
"tags": [
|
||||
"$extends:eye",
|
||||
"$group:dashed",
|
||||
"hide",
|
||||
"conceal",
|
||||
"mask",
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
"visibility",
|
||||
"vision",
|
||||
"preview",
|
||||
"read"
|
||||
"read",
|
||||
"eyes"
|
||||
],
|
||||
"categories": [
|
||||
"accessibility",
|
||||
|
||||
@@ -6,14 +6,13 @@
|
||||
],
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"$group:emoticon",
|
||||
"anger",
|
||||
"rage",
|
||||
"fury",
|
||||
"resentment",
|
||||
"irritation",
|
||||
"disgust",
|
||||
"emoji",
|
||||
"emotion"
|
||||
"disgust"
|
||||
],
|
||||
"categories": [
|
||||
"emoji"
|
||||
|
||||
@@ -5,12 +5,11 @@
|
||||
],
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"$group:emoticon",
|
||||
"nuisance",
|
||||
"annoyed",
|
||||
"annoyance",
|
||||
"frustration",
|
||||
"emoji",
|
||||
"emotion"
|
||||
"frustration"
|
||||
],
|
||||
"categories": [
|
||||
"emoji"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
],
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"$group:emoticon",
|
||||
"happy",
|
||||
"happiness",
|
||||
"good",
|
||||
@@ -14,9 +15,7 @@
|
||||
"pleasure",
|
||||
"good cheer",
|
||||
"humour",
|
||||
"funny",
|
||||
"emoji",
|
||||
"emotion"
|
||||
"funny"
|
||||
],
|
||||
"categories": [
|
||||
"emoji"
|
||||
|
||||
@@ -8,9 +8,8 @@
|
||||
],
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"emoji",
|
||||
"$group:emoticon",
|
||||
"neutral",
|
||||
"emotion",
|
||||
"meh",
|
||||
"blank stare",
|
||||
"social masking",
|
||||
|
||||
@@ -8,10 +8,9 @@
|
||||
],
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"emoji",
|
||||
"$group:emoticon",
|
||||
"bad",
|
||||
"sadness",
|
||||
"emotion",
|
||||
"concern",
|
||||
"disappointment"
|
||||
],
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
"$extends:face-slightly-smiling",
|
||||
"$extends:group",
|
||||
"$group:plus",
|
||||
"react",
|
||||
"reaction"
|
||||
"react"
|
||||
],
|
||||
"categories": [
|
||||
"emoji",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
],
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
"$group:emoticon",
|
||||
"smile",
|
||||
"happy",
|
||||
"happiness",
|
||||
@@ -16,9 +17,7 @@
|
||||
"friendly",
|
||||
"patronizing",
|
||||
"passive aggressive",
|
||||
"ironic",
|
||||
"emoji",
|
||||
"emotion"
|
||||
"ironic"
|
||||
],
|
||||
"categories": [
|
||||
"emoji"
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
"tags": [
|
||||
"$extends:file",
|
||||
"$group:question-mark",
|
||||
"readme"
|
||||
"readme",
|
||||
"404",
|
||||
"not found",
|
||||
"page not found"
|
||||
],
|
||||
"categories": [
|
||||
"$extends:file"
|
||||
|
||||
@@ -10,6 +10,6 @@
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" />
|
||||
<path d="M9 9.5a3 2.5 0 016 0c0 2-2 2-3 3.5" />
|
||||
<path d="M12 17h.01" />
|
||||
<path d="M9.1 9a3 3 0 0 1 5.82 1c0 2-3 3-3 3" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 411 B After Width: | Height: | Size: 410 B |
@@ -9,7 +9,11 @@
|
||||
"$extends:file",
|
||||
"spreadsheet",
|
||||
"sheet",
|
||||
"table"
|
||||
"table",
|
||||
"csv",
|
||||
"xls",
|
||||
"xlsx",
|
||||
"excel"
|
||||
],
|
||||
"categories": [
|
||||
"$extends:file"
|
||||
|
||||
@@ -10,7 +10,11 @@
|
||||
"$extends:file",
|
||||
"data",
|
||||
"txt",
|
||||
"pdf"
|
||||
"pdf",
|
||||
"doc",
|
||||
"docx",
|
||||
"word",
|
||||
"docs"
|
||||
],
|
||||
"categories": [
|
||||
"$extends:file",
|
||||
|
||||
@@ -12,7 +12,10 @@
|
||||
"tags": [
|
||||
"$extends:file",
|
||||
"$group:x",
|
||||
"lost"
|
||||
"lost",
|
||||
"404",
|
||||
"not found",
|
||||
"page not found"
|
||||
],
|
||||
"categories": [
|
||||
"$extends:file"
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
"tags": [
|
||||
"send",
|
||||
"share",
|
||||
"email"
|
||||
"email",
|
||||
"redirect"
|
||||
],
|
||||
"categories": [
|
||||
"mail"
|
||||
|
||||