Compare commits

...

26 Commits

Author SHA1 Message Date
Eric Fennis
571cab88ee v0.11.0 2020-11-19 22:06:13 +01:00
Eric Fennis
68c5ed6097 add files icon (#131)
Co-Authored-By @delnyn
2020-11-17 20:11:40 +01:00
Andreas Törnkvist
85efb8e1b6 Fix: Inconsistent tag spacing (#139) 2020-11-17 20:04:06 +01:00
Andreas Törnkvist
00c3487dff Icons: Beaker, Flasks, Pipette (#135)
* Add: Beaker, Flasks, Pipette

* Tag spacing
2020-11-17 20:03:48 +01:00
Frank Riccobono
cec73c5217 Fix: Bump Chakra-UI and add close button to toast (#138) 2020-11-17 13:26:29 +01:00
Eric Fennis
8f1c7eb737 Fix site search (#133) 2020-11-16 12:05:34 +01:00
Eric Fennis
0dd10483c9 Refactor text-align icons (#109)
* Add new text-align icons

* Stretch icon

Co-authored-by: appmachine <appmachine@appmachines-iMac.local>
2020-11-16 12:05:01 +01:00
Eric Fennis
a0c447cece Add menu (#136) 2020-11-16 12:04:44 +01:00
Andreas Törnkvist
7cf928e94c Icons: Tools and Construction (#118)
* Adding icons: axe,  gavel, hammer, hardhat, shovel

* Update axe, gavel, hammer

* rename tool -> wrench

* Update SVG-structure

* Add ruler

* minify hammer.svg

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>

* minify hard-hat.svg

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>

* minify gavel.svg

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>

* minify shovel.svg

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>

* minify ruler.svg

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2020-11-07 14:23:01 +01:00
Frank Riccobono
8caf6efe72 Fix style issues and performance issues for Color Picker and Search Bar (#123)
* feat: ability to customize icons before downloading PNG or SVG

* feat: change color input to be a graphical color picker

* feat: tweak appearance of new inputs

* fix: switch to correct Chakra-UI@Next slider syntax

* fix: make default color `currentColor` and also add Reset button

* fix: move background color from search bar InputGroup to Input

* fix: add margins and border radius to color picker components

* fix: Downgrade color picker to version (see: https://github.com/casesandberg/react-color/issues/731)
2020-11-05 21:07:04 +01:00
Locness
a9fec942ff Add "photo" tags to image + tags to image-off (#125) 2020-11-05 21:06:45 +01:00
Eric Fennis
00cbc81331 Site: Add Logo (#121)
* bump package

* Add Logo

* remove console

* prettify it

* add favicons and fix issue
2020-11-03 20:58:51 +01:00
Eric Fennis
4e3af5c601 add album icon (#76) 2020-11-02 21:14:38 +01:00
Eric Fennis
632dda526a feat: Add clover icon (#65)
* add clover icon

* add space
2020-11-02 21:14:26 +01:00
Eric Fennis
c858240c01 refactor and add more icons (#106) 2020-11-02 21:13:57 +01:00
Andreas Törnkvist
1fb70e67ee Feather icon requests (#114)
* Create FEATHER_ICON_REQUESTS.md

* Feather requests in CONTRIBUTING.md

* Add unlabeled issues to request-list

* Revert "Add unlabeled issues to request-list"

This reverts commit 18a69038e2.

* Revert "Create FEATHER_ICON_REQUESTS.md"

This reverts commit ec96cbd9fd.

* Update link to Fether Requests in CONTRIBUTING

* Update CONTRIBUTING.md

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2020-10-31 17:11:42 +01:00
Collin Monahan
b533cf8480 doc/spelling (#120) 2020-10-31 16:11:18 +01:00
Frank Riccobono
51fd3af446 Add customization options to the icon display and download (#113)
* feat: ability to customize icons before downloading PNG or SVG

* feat: change color input to be a graphical color picker

* feat: tweak appearance of new inputs

* fix: switch to correct Chakra-UI@Next slider syntax

* fix: make default color `currentColor` and also add Reset button
2020-10-31 12:53:25 +01:00
Locness
f3c3fea228 Docs : Improve the design guide (#111)
- Use modal verbs instead of "has"
- Use "icons" instead of "each icon"
- change some other things as well
2020-10-27 21:48:45 +01:00
Themistoklis
29574a6385 Add indentation icons in/out (#102) 2020-10-27 21:39:23 +01:00
Eric Fennis
5c96b8d848 Feature/site detail page (#99)
* site: pull data from "icons" dir

* site: display icons

* site: remove redundant code

* site: colour mode support

* site: header

* site: order imports

* site: search

* site: add toast when copying icon

* site: styling

* site: hero

* fix: disable theme toggle transitions

* feat: Use Yarn Workspaces

* refactor: Update site deploy scripts

* refactor: Remove dark mode for now

* feat: Add site title

* refactor: Fix warning and format

* feat: Add dark mode back 👀

* feat: Escape key to reset query

* Fix by aelfric

* Add Github link

* Fix #40

* Add site overlay

* sort categories

* Add detail page

* Add first categories

* add box

* move site to root directory

* fix merge issues

* Fix routing issues

* Fix icon overlay

* Add copy and download icon

* Fix style issues

* Add text

* update chakra UI

* remove import

* update dependecies

* add lucide react

* Fix bugs

* delete stats files

* update charkra version

Co-authored-by: John Letey <johnletey@gmail.com>
Co-authored-by: appmachine <appmachine@appmachines-iMac.local>
2020-10-26 08:59:56 +01:00
Eric Fennis
2c38fac9b1 Docs: Design guide (#73)
* Add design guide

* Update ICON_DESIGN_GUIDE.md

* Update ICON_DESIGN_GUIDE.md

* Add conventions section

* Moved to the docs directory

* Update docs/ICON_DESIGN_GUIDE.md

Co-authored-by: Locness <37651007+locness3@users.noreply.github.com>

* Update docs/ICON_DESIGN_GUIDE.md

Co-authored-by: Locness <37651007+locness3@users.noreply.github.com>

* Add raw parameter to images url

* add alt test

* Add alt text

Co-authored-by: Locness <37651007+locness3@users.noreply.github.com>
2020-10-25 22:48:03 +01:00
Eric Fennis
0b88415247 feat: add logo (#92) 2020-10-25 21:16:39 +00:00
delnyn
9b25845258 Update readme's table of content (#104)
* Update (#2)

update

* Update README.md

* Fix links

* Fix typo

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>

Co-authored-by: Locness <37651007+locness3@users.noreply.github.com>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2020-10-20 17:22:22 +02:00
Alexander Barrios
49973ff32b Fix problems with SVGs (#100)
* Update font.yml

* Update font.yml

* Update font.yml

* Update font.yml

* Update font.yml

* Update font.yml

* Create outline_svg.js

* Update font.yml

* Update font.yml

* Update outline_svg.js

* Update font.yml

* FIX: Problems with SVGs

* FIX: Problems with SVGs

Node runs this script to fix SVG files so FontCustom can compile correctly

* Trigger on PR

* Update scripts/outline_svg.js

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2020-10-19 11:26:39 +02:00
Alexander Barrios
714f63d0d3 FIX: 404 when installing dependencies using APT (#98)
I added a step that updates APT repositories before installing dependencies
2020-10-16 08:59:45 +02:00
99 changed files with 4478 additions and 1428 deletions

View File

@@ -1,4 +1,4 @@
{
module.exports = {
"env": {
"browser": true,
"node": true

View File

@@ -2,21 +2,31 @@ name: Build Lucide
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
Build:
runs-on: ubuntu-latest
steps:
- name: Clone 'Lucide'
uses: actions/checkout@v2
- name: Update repos
run: sudo apt-get update
- name: Install FontForge
run: sudo apt-get install zlib1g-dev fontforge woff2
run: sudo apt-get install zlib1g-dev fontforge
- name: Install NodeJS and Yarn
run: sudo apt-get install nodejs yarn
- name: Clone sfnt2woff-zopfli repo
run: git clone https://github.com/bramstein/sfnt2woff-zopfli.git sfnt2woff-zopfli
- name: Install and move sfnt2woff-zopfli
run: |
cd sfnt2woff-zopfli
@@ -26,6 +36,7 @@ jobs:
- name: Clone woff2
run: git clone --recursive https://github.com/google/woff2.git
- name: Install woff2
run: |
cd woff2
@@ -34,12 +45,20 @@ jobs:
- name: Install Font Custom dependency
run: sudo gem install fontcustom
- name: Install "outline-stroke"
run: sudo yarn add svg-outline-stroke svgson
- name: "Outline SVG"
run: mkdir converted_icons && node scripts/outline_svg.js
- name: Build 'Lucide'
run: echo "Building Featherity font" && fontcustom compile ./icons -h -n Featherity -o build -F
run: echo "Building Lucide font" && fontcustom compile ./converted_icons -h -n Lucide -o build -F
- name: Zip 'Lucide'
run: zip -r Featherity.zip build
run: zip -r Lucide.zip build
- name: 'Upload to Artifacts'
uses: actions/upload-artifact@v1.0.0

View File

@@ -2,7 +2,7 @@
:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
The following is a set of guidelines for contributing to Featherity. Feel free to propose changes to this document in a pull request.
The following is a set of guidelines for contributing to Lucide. Feel free to propose changes to this document in a pull request.
## Pull Requests
@@ -22,3 +22,6 @@ Guidelines for pull requests:
Before creating an icon request, please search to see if someone has requested the icon already. If there is an open request, please add a :+1:.
If the icon has not already been requested, [create an issue](https://github.com/lucide-icons/lucide/issues/new?title=Icon%20Request:) with a title of `Icon request: <icon name>` and add as much information as possible.
## Icon Requests from Feather
If you are a designer who wants to contribute to Lucide but you don't know what icons to work on, then have a look at the Requests from Feather. All open, unfinished and valid requests can be found in [Feather Icon Requests](https://github.com/lucide-icons/lucide/issues/119).

View File

@@ -1,3 +1,5 @@
<p align=center><img width="410" src="https://lucide.netlify.app/logo-text.svg" alt="Lucide Logo"></p>
# Lucide
![NPM](https://img.shields.io/npm/l/lucide)
@@ -10,9 +12,18 @@ Lucide is a community-run fork of [Feather Icons](https://github.com/feathericon
## Table of Contents
* [Installation](#installation)
* [Package managers](#package-managers)
* [CDN](#cdn)
* [Usage](#usage)
* [Unpkg](#with-unpkg)
* [ESModules](#with-esmodules)
* [Options](#additional-options)
* [Treeshake library](#treeshake-the-library-only-use-the-icons-you-use)
* [Custom binding](#custom-element-binding)
* [Figma](#figma)
* [Contributing](#contributing)
* [Community](#community)
* [License](#license)
## Installation
@@ -62,7 +73,7 @@ Here is a complete example with unpkg
### With ESModules
To reduce bundle size, lucide is build to be fully threeshakeble.
To reduce bundle size, lucide is built to be fully treeshakable.
The `createIcons` function will search for HTMLElements with the attribute `icon-name` and replace it with the svg from the given icon name.
```html
@@ -73,7 +84,7 @@ The `createIcons` function will search for HTMLElements with the attribute `icon
```js
import { createIcons, icons } from 'lucide';
// Caustion, this will import all the icons and bundle them.
// Caution, this will import all the icons and bundle them.
createIcons({icons});
// Recommended way, to include only the icons you need.
@@ -107,7 +118,7 @@ createIcons({
});
```
#### Threeshake the library, only use the icons you use
#### Treeshake the library, only use the icons you use
```js
import { createIcons, Menu, ArrowRight, Globe } from 'lucide';

19
categories.json Normal file
View File

@@ -0,0 +1,19 @@
{
"arrows": [],
"brands": [],
"code": [],
"connectivity": ["airplay"],
"cursors": [],
"development": [],
"devices": ["alarm-clock"],
"file-system": [],
"layout": [],
"maths": ["activity"],
"multimedia": [],
"notifications": ["alert-circle", "alert-octagon", "alert-triangle"],
"nature": [],
"shopping": [],
"shapes": [],
"sports": [],
"text-edit": ["align-center","align-right","align-left","align-justify" ]
}

78
docs/ICON_DESIGN_GUIDE.md Normal file
View File

@@ -0,0 +1,78 @@
# Icon Design Guide
Here are rules that should be followed to keep quality and consistency when making icons for Lucide.
## Summary of the rules we have
1. Icons must be designed on a **24 by 24 pixels** canvas.
2. Icons must have a **1 pixel padding** within the canvas.
3. Icons must have a **stroke width of 2 pixels**.
4. Icons must use **round joins**.
5. Icons must use **round caps**.
6. Icons must use **centered strokes**.
7. Shapes (such as rectangles) in icons must have **border radius of 2 pixels**.
8. Distinct elements must have **2 pixels of spacing between each other**.
## The Rules Visualized
### 1. Icons must be designed on a 24 by 24 pixels canvas.
![24px-24px](images/24px-24px.png?raw=true "24px-24px")
### 2. Icons must have a 1 pixel padding within the canvas.
![1px-padding](images/1px-padding.png?raw=true "1px-padding")
### 3. Icons must have a stroke width of 2 pixels.
![2px-stroke](images/2px-stroke.png?raw=true "2px-stroke")
### 4. Icons must use round joins.
![round-joints](images/round-joints.png?raw=true "round-joints")
### 5. Icons must use round caps.
![round-caps](images/round-caps.png?raw=true "round-caps")
### 6. Icons must use centered strokes.
![centered-strokes](images/centered-strokes.png?raw=true "centered-strokes")
### 7. Shapes (such as squares) in icons must have border radius of 2 pixels.
![2px-border-radius](images/2px-border-radius.png?raw=true "2px-border-radius")
### 8. Distinct elements must have 2 pixels of spacing between each other.
![2px-element-spacing](images/2px-element-spacing.png?raw=true "2px-element-spacing")
## Code Conventions
Before an icon is added to the library, we like to have readable and optimized svg code.
### Global Attributes
For each icon these attributes are applied, corresponding to the above rules.
```xml
<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"
>
<!-- SVGElements -->
</svg>
```
### Minify paths
Code of paths can get really big.
To reduce file size we like to minify the code.
We recommend to use the [SVGOMG](https://jakearchibald.github.io/svgomg/) to minify paths.

BIN
docs/images/1px-padding.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
docs/images/24px-24px.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
docs/images/2px-stroke.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
docs/images/round-caps.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

14
icons/album.svg Normal file
View File

@@ -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"
>
<rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
<polyline points="11 3 11 11 14 8 17 11 17 3" />
</svg>

After

Width:  |  Height:  |  Size: 319 B

View File

@@ -9,8 +9,7 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="18" y1="10" x2="6" y2="10" />
<line x1="21" y1="6" x2="3" y2="6" />
<line x1="21" y1="14" x2="3" y2="14" />
<line x1="18" y1="18" x2="6" y2="18" />
<line x1="17" y1="12" x2="7" y2="12" />
<line x1="19" y1="18" x2="5" y2="18" />
</svg>

Before

Width:  |  Height:  |  Size: 374 B

After

Width:  |  Height:  |  Size: 332 B

View File

@@ -9,8 +9,7 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="21" y1="10" x2="3" y2="10" />
<line x1="21" y1="6" x2="3" y2="6" />
<line x1="21" y1="14" x2="3" y2="14" />
<line x1="21" y1="18" x2="3" y2="18" />
<line x1="3" y1="6" x2="21" y2="6" />
<line x1="3" y1="12" x2="21" y2="12" />
<line x1="3" y1="18" x2="21" y2="18" />
</svg>

Before

Width:  |  Height:  |  Size: 374 B

After

Width:  |  Height:  |  Size: 332 B

View File

@@ -9,8 +9,7 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="17" y1="10" x2="3" y2="10" />
<line x1="21" y1="6" x2="3" y2="6" />
<line x1="21" y1="14" x2="3" y2="14" />
<line x1="15" y1="12" x2="3" y2="12" />
<line x1="17" y1="18" x2="3" y2="18" />
</svg>

Before

Width:  |  Height:  |  Size: 374 B

After

Width:  |  Height:  |  Size: 332 B

View File

@@ -9,8 +9,7 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="21" y1="10" x2="7" y2="10" />
<line x1="21" y1="6" x2="3" y2="6" />
<line x1="21" y1="14" x2="3" y2="14" />
<line x1="21" y1="12" x2="9" y2="12" />
<line x1="21" y1="18" x2="7" y2="18" />
</svg>

Before

Width:  |  Height:  |  Size: 374 B

After

Width:  |  Height:  |  Size: 332 B

14
icons/axe.svg Normal file
View File

@@ -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="m14 12-8.501 8.501c-0.828 0.828-2.17 0.828-2.998 0-1e-3 -1e-3 -1e-3 -1e-3 -2e-3 -0-0.828-0.828-0.828-2.17 0-2.998l8.501-8.501" />
<path d="m9 7 4-4 6 6h3s-0.051 0.254-0.13 0.648c-0.538 2.691-2.477 4.888-5.081 5.756-1.003 0.334-1.789 0.596-1.789 0.596v-3z" />
</svg>

After

Width:  |  Height:  |  Size: 480 B

15
icons/beaker.svg Normal file
View File

@@ -0,0 +1,15 @@
<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="M4.5 3h15" />
<path d="M6 3v16a2 2 0 002 2h8a2 2 0 002-2V3" />
<path d="M6 14h12" />
</svg>

After

Width:  |  Height:  |  Size: 308 B

View File

@@ -10,6 +10,6 @@
stroke-linejoin="round"
>
<line x1="2" y1="2" x2="22" y2="22" />
<path d="M9.5 4h5L17 7h3a2 2 0 012 2v7.5M7 7H4a2 2 0 00-2 2v9a2 2 0 002 2h16"/>
<path d="M14.121 15.121A3 3 0 119.88 10.88"/>
<path d="M9.5 4h5L17 7h3a2 2 0 012 2v7.5M7 7H4a2 2 0 00-2 2v9a2 2 0 002 2h16" />
<path d="M14.121 15.121A3 3 0 119.88 10.88" />
</svg>

Before

Width:  |  Height:  |  Size: 379 B

After

Width:  |  Height:  |  Size: 381 B

17
icons/clover.svg Normal file
View File

@@ -0,0 +1,17 @@
<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="M16.2 3.8a2.7 2.7 0 00-3.81 0l-.4.38-.4-.4a2.7 2.7 0 00-3.82 0C6.73 4.85 6.67 6.64 8 8l4 4 4-4c1.33-1.36 1.27-3.15.2-4.2z" />
<path d="M8 8c-1.36-1.33-3.15-1.27-4.2-.2a2.7 2.7 0 000 3.81l.38.4-.4.4a2.7 2.7 0 000 3.82C4.85 17.27 6.64 17.33 8 16" />
<path d="M16 16c1.36 1.33 3.15 1.27 4.2.2a2.7 2.7 0 000-3.81l-.38-.4.4-.4a2.7 2.7 0 000-3.82C19.15 6.73 17.36 6.67 16 8" />
<path d="M7.8 20.2a2.7 2.7 0 003.81 0l.4-.38.4.4a2.7 2.7 0 003.82 0c1.06-1.06 1.12-2.85-.21-4.21l-4-4-4 4c-1.33 1.36-1.27 3.15-.2 4.2z" />
<path d="M7 17L2 22" />
</svg>

After

Width:  |  Height:  |  Size: 762 B

15
icons/file-check-2.svg Normal file
View File

@@ -0,0 +1,15 @@
<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="M4 22h14a2 2 0 002-2V7.5L14.5 2H6a2 2 0 00-2 2v4" />
<path d="M14 2v6h6" />
<path d="M3 15l2 2 4-4" />
</svg>

After

Width:  |  Height:  |  Size: 326 B

15
icons/file-check.svg Normal file
View File

@@ -0,0 +1,15 @@
<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.5 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V7.5L14.5 2z" />
<polyline points="14 2 14 8 20 8" />
<path d="M9 15l2 2 4-4" />
</svg>

After

Width:  |  Height:  |  Size: 356 B

16
icons/file-code.svg Normal file
View File

@@ -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="M4 22h14a2 2 0 002-2V7.5L14.5 2H6a2 2 0 00-2 2v4" />
<path d="M14 2v6h6" />
<path d="M9 18l3-3-3-3" />
<path d="M5 12l-3 3 3 3" />
</svg>

After

Width:  |  Height:  |  Size: 356 B

17
icons/file-digit.svg Normal file
View File

@@ -0,0 +1,17 @@
<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="M4 22h14a2 2 0 002-2V7.5L14.5 2H6a2 2 0 00-2 2v4" />
<path d="M14 2v6h6" />
<path d="M10 12h2v6" />
<rect x="2" y="12" width="4" height="6" />
<path d="M10 18h4" />
</svg>

After

Width:  |  Height:  |  Size: 392 B

15
icons/file-minus-2.svg Normal file
View File

@@ -0,0 +1,15 @@
<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="M4 22h14a2 2 0 002-2V7.5L14.5 2H6a2 2 0 00-2 2v4" />
<path d="M14 2v6h6" />
<path d="M3 15h6" />
</svg>

After

Width:  |  Height:  |  Size: 320 B

View File

@@ -9,7 +9,7 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
<path d="M14.5 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V7.5L14.5 2z" />
<polyline points="14 2 14 8 20 8" />
<line x1="9" y1="15" x2="15" y2="15" />
</svg>

Before

Width:  |  Height:  |  Size: 363 B

After

Width:  |  Height:  |  Size: 369 B

16
icons/file-plus-2.svg Normal file
View File

@@ -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="M4 22h14a2 2 0 002-2V7.5L14.5 2H6a2 2 0 00-2 2v4" />
<path d="M14 2v6h6" />
<path d="M3 15h6" />
<path d="M6 12v6" />
</svg>

After

Width:  |  Height:  |  Size: 343 B

View File

@@ -9,7 +9,7 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
<path d="M14.5 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V7.5L14.5 2z" />
<polyline points="14 2 14 8 20 8" />
<line x1="12" y1="18" x2="12" y2="12" />
<line x1="9" y1="15" x2="15" y2="15" />

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 412 B

16
icons/file-search.svg Normal file
View File

@@ -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="M4 22h14a2 2 0 002-2V7.5L14.5 2H6a2 2 0 00-2 2v3" />
<path d="M14 2v6h6" />
<path d="M5 17a3 3 0 100-6 3 3 0 000 6z" />
<path d="M9 18l-1.5-1.5" />
</svg>

After

Width:  |  Height:  |  Size: 373 B

View File

@@ -9,9 +9,9 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
<path d="M14.5 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V7.5L14.5 2z" />
<polyline points="14 2 14 8 20 8" />
<line x1="16" y1="13" x2="8" y2="13" />
<line x1="16" y1="17" x2="8" y2="17" />
<polyline points="10 9 9 9 8 9" />
<line x1="10" y1="9" x2="8" y2="9" />
</svg>

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 451 B

16
icons/file-x-2.svg Normal file
View File

@@ -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="M4 22h14a2 2 0 002-2V7.5L14.5 2H6a2 2 0 00-2 2v4" />
<path d="M14 2v6h6" />
<path d="M3 12.5l5 5" />
<path d="M8 12.5l-5 5" />
</svg>

After

Width:  |  Height:  |  Size: 352 B

16
icons/file-x.svg Normal file
View File

@@ -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.5 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V7.5L14.5 2z" />
<polyline points="14 2 14 8 20 8" />
<line x1="9.5" y1="12.5" x2="14.5" y2="17.5" />
<line x1="14.5" y1="12.5" x2="9.5" y2="17.5" />
</svg>

After

Width:  |  Height:  |  Size: 427 B

View File

@@ -9,6 +9,6 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z" />
<polyline points="13 2 13 9 20 9" />
<path d="M14.5 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V7.5L14.5 2z" />
<polyline points="14 2 14 8 20 8" />
</svg>

Before

Width:  |  Height:  |  Size: 321 B

After

Width:  |  Height:  |  Size: 327 B

15
icons/files.svg Normal file
View File

@@ -0,0 +1,15 @@
<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="M15.5 2H8.6c-.4 0-.8.2-1.1.5-.3.3-.5.7-.5 1.1v12.8c0 .4.2.8.5 1.1.3.3.7.5 1.1.5h9.8c.4 0 .8-.2 1.1-.5.3-.3.5-.7.5-1.1V6.5L15.5 2z" />
<path d="M3 7.6v12.8c0 .4.2.8.5 1.1.3.3.7.5 1.1.5h9.8" />
<path d="M15 2v5h5" />
</svg>

After

Width:  |  Height:  |  Size: 438 B

15
icons/flask-conical.svg Normal file
View File

@@ -0,0 +1,15 @@
<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="M10 2v8L4.72 20.55a1 1 0 00.9 1.45h12.76a1 1 0 00.9-1.45L14 10V2" />
<path d="M8.5 2h7" />
<path d="M7 16h10" />
</svg>

After

Width:  |  Height:  |  Size: 336 B

17
icons/flask-round.svg Normal file
View File

@@ -0,0 +1,17 @@
<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="M10 2v7.31" />
<path d="M14 9.3V1.99" />
<path d="M8.5 2h7" />
<path d="M14 9.3a6.5 6.5 0 11-4 0" />
<path d="M5.58 16.5h12.85" />
</svg>

After

Width:  |  Height:  |  Size: 358 B

View File

@@ -9,6 +9,6 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 15l3.5-3.5"/>
<path d="M20.3 18c.4-1 .7-2.2.7-3.4C21 9.8 17 6 12 6s-9 3.8-9 8.6c0 1.2.3 2.4.7 3.4"/>
<path d="M12 15l3.5-3.5" />
<path d="M20.3 18c.4-1 .7-2.2.7-3.4C21 9.8 17 6 12 6s-9 3.8-9 8.6c0 1.2.3 2.4.7 3.4" />
</svg>

Before

Width:  |  Height:  |  Size: 326 B

After

Width:  |  Height:  |  Size: 328 B

17
icons/gavel.svg Normal file
View File

@@ -0,0 +1,17 @@
<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 13l-7.5 7.5c-.83.83-2.17.83-3 0 0 0 0 0 0 0a2.12 2.12 0 010-3L11 10" />
<path d="M16 16l6-6" />
<path d="M8 8l6-6" />
<path d="M9 7l8 8" />
<path d="M21 11l-8-8" />
</svg>

After

Width:  |  Height:  |  Size: 396 B

15
icons/hammer.svg Normal file
View File

@@ -0,0 +1,15 @@
<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="M15 12l-8.5 8.5c-.83.83-2.17.83-3 0 0 0 0 0 0 0a2.12 2.12 0 010-3L12 9" />
<path d="M17.64 15L22 10.64" />
<path d="M20.91 11.7l-1.25-1.25c-.6-.6-.93-1.4-.93-2.25v-.86L16.01 4.6a5.56 5.56 0 00-3.94-1.64H9l.92.82A6.18 6.18 0 0112 8.4v1.56l2 2h2.47l2.26 1.91" />
</svg>

After

Width:  |  Height:  |  Size: 484 B

16
icons/hard-hat.svg Normal file
View File

@@ -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="M2 18a1 1 0 001 1h18a1 1 0 001-1v-2a1 1 0 00-1-1H3a1 1 0 00-1 1v2z" />
<path d="M10 10V5a1 1 0 011-1h2a1 1 0 011 1v5" />
<path d="M4 15v-3a6 6 0 016-6h0" />
<path d="M14 6h0a6 6 0 016 6v3" />
</svg>

After

Width:  |  Height:  |  Size: 417 B

View File

@@ -9,6 +9,6 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M9 11l-6 6v3h9l3-3"/>
<path d="M22 12l-4.6 4.6a2 2 0 01-2.8 0l-5.2-5.2a2 2 0 010-2.8L14 4"/>
<path d="M9 11l-6 6v3h9l3-3" />
<path d="M22 12l-4.6 4.6a2 2 0 01-2.8 0l-5.2-5.2a2 2 0 010-2.8L14 4" />
</svg>

Before

Width:  |  Height:  |  Size: 314 B

After

Width:  |  Height:  |  Size: 316 B

16
icons/indent.svg Normal file
View File

@@ -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"
>
<polyline points="3 8 7 12 3 16" />
<line x1="21" y1="12" x2="11" y2="12" />
<line x1="21" y1="6" x2="11" y2="6" />
<line x1="21" y1="18" x2="11" y2="18" />
</svg>

After

Width:  |  Height:  |  Size: 373 B

View File

@@ -9,7 +9,7 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="3" y1="12" x2="21" y2="12" />
<line x1="3" y1="6" x2="21" y2="6" />
<line x1="3" y1="18" x2="21" y2="18" />
<line x1="4" y1="12" x2="20" y2="12" />
<line x1="4" y1="6" x2="20" y2="6" />
<line x1="4" y1="18" x2="20" y2="18" />
</svg>

Before

Width:  |  Height:  |  Size: 332 B

After

Width:  |  Height:  |  Size: 332 B

16
icons/outdent.svg Normal file
View File

@@ -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"
>
<polyline points="7 8 3 12 7 16" />
<line x1="21" y1="12" x2="11" y2="12" />
<line x1="21" y1="6" x2="11" y2="6" />
<line x1="21" y1="18" x2="11" y2="18" />
</svg>

After

Width:  |  Height:  |  Size: 373 B

15
icons/pipette.svg Normal file
View File

@@ -0,0 +1,15 @@
<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="M2 22l2-2h3l7-7" />
<path d="M4 20v-3l7-7" />
<path d="M14.29 13.3a1 1 0 001.41 0l.8-.8c.27-.27.27-.72 0-1s-.28-.72 0-1l4.08-4.08a2 2 0 000-2.83l-.17-.17a2 2 0 00-2.83 0L13.5 7.51c-.28.27-.73.27-1 0s-.73-.28-1 0l-.8.79a1 1 0 000 1.41l3.59 3.59z" />
</svg>

After

Width:  |  Height:  |  Size: 472 B

17
icons/ruler.svg Normal file
View File

@@ -0,0 +1,17 @@
<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="m16.0 2.0 6 6-14 14-6-6 14-14" />
<path d="m7.5 10.5 2 2" />
<path d="m10.5 7.5 2 2" />
<path d="m13.5 4.5 2 2" />
<path d="m4.5 13.5 2 2" />
</svg>

After

Width:  |  Height:  |  Size: 379 B

15
icons/shovel.svg Normal file
View File

@@ -0,0 +1,15 @@
<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="M2 22v-5l5-5 5 5-5 5z" />
<path d="M9.5 14.5L16 8" />
<path d="M17 2l5 5-.5.5a3.53 3.53 0 01-5 0s0 0 0 0a3.53 3.53 0 010-5L17 2" />
</svg>

After

Width:  |  Height:  |  Size: 355 B

View File

Before

Width:  |  Height:  |  Size: 376 B

After

Width:  |  Height:  |  Size: 376 B

View File

@@ -1,7 +1,7 @@
{
"name": "lucide",
"description": "Lucide is a community-run fork of Feather Icons, open for anyone to contribute icons.",
"version": "0.1.2",
"version": "0.11.0",
"license": "ISC",
"amdName": "lucide",
"homepage": "https://lucide.netlify.app",

55
scripts/outline_svg.js Normal file
View File

@@ -0,0 +1,55 @@
const { promises: fs } = require("fs");
const outlineStroke = require("svg-outline-stroke");
const { parse, stringify } = require("svgson");
const inputDir = `./icons/`;
const outputDir = `./converted_icons/`;
async function init() {
try {
const files = await fs.readdir(inputDir);
for (let file of files) {
const icon = await fs.readFile(`${inputDir}${file}`);
const scaled = await parse(icon.toString(), {
transformNode: transformForward
});
const outlined = await outlineStroke(stringify(scaled));
const outlinedWithoutAttrs = await parse(outlined, {
transformNode: transformBackwards
});
await fs.writeFile(
`${outputDir}${file}`,
stringify(outlinedWithoutAttrs)
);
}
} catch (err) {
console.log(err);
}
}
init();
function transformForward(node) {
if (node.name === "svg") {
return {
...node,
attributes: {
...node.attributes,
width: 960,
height: 960
}
};
}
return node;
}
function transformBackwards(node) {
if (node.name === "svg") {
const { width, height, ...attributes } = node.attributes;
return {
...node,
attributes
};
}
return node;
}

View File

@@ -1,3 +0,0 @@
{
"presets": ["next/babel"]
}

4
site/.eslintrc.js Normal file
View File

@@ -0,0 +1,4 @@
const { builtinModules } = require('module')
const rootConfig = require('../.eslintrc.js')
module.exports = rootConfig;

3
site/babel.config.js Normal file
View File

@@ -0,0 +1,3 @@
module.exports = {
presets: ['next/babel'],
};

View File

@@ -2,6 +2,6 @@ module.exports = {
testPathIgnorePatterns: ['<rootDir>/.next/', '<rootDir>/node_modules/'],
setupFilesAfterEnv: ['<rootDir>/setupTests.js'],
transform: {
'^.+\\.(js|jsx|ts|tsx)$': '<rootDir>/../../node_modules/babel-jest',
'^.+\\.(js|jsx|ts|tsx)$': '<rootDir>/node_modules/babel-jest',
},
};

5
site/next-env.d.ts vendored
View File

@@ -1,2 +1,7 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
declare module "*.svg" {
const content: any;
export default content;
}

View File

@@ -11,16 +11,19 @@
"test": "jest"
},
"dependencies": {
"@chakra-ui/core": "^0.8.0",
"@emotion/core": "^10.0.28",
"@emotion/styled": "^10.0.27",
"@chakra-ui/core": "^1.0.0-rc.8",
"downloadjs": "^1.4.7",
"emotion-theming": "^10.0.27",
"framer-motion": "^2.9.4",
"fuse.js": "^6.0.4",
"jszip": "^3.4.0",
"lodash": "^4.17.20",
"lucide-react": "^0.1.2-beta.4",
"next": "^9.5.4",
"react": "^16.13.1",
"react-dom": "^16.13.1"
"react-color": "2.17.3",
"react-dom": "^16.13.1",
"react-spring": "^8.0.27",
"react-svg-loader": "^3.0.3"
},
"devDependencies": {
"@testing-library/dom": "^7.24.4",
@@ -31,6 +34,8 @@
"@types/react": "^16.9.35",
"@types/react-dom": "^16.9.8",
"babel-jest": "^26.5.2",
"babel-loader": "^8.1.0",
"cheerio": "^1.0.0-rc.3",
"jest": "^26.5.2",
"react-test-renderer": "^16.13.1",
"typescript": "^3.9.5"

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<TileColor>#F56565</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
site/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

24
site/public/favicon.svg Normal file
View File

@@ -0,0 +1,24 @@
<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"
>
<style>
.path {
stroke: #2D3748;
}
@media (prefers-color-scheme: dark) {
.path {
stroke: #fff;
}
}
</style>
<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" class="path"/>
<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" stroke="#F56565" />
</svg>

After

Width:  |  Height:  |  Size: 720 B

View File

@@ -0,0 +1,5 @@
<svg width="82" height="24" viewBox="0 0 82 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M28.52 5.4h2.14v10.84h5.28V18h-7.42V5.4zm15.24 3.16h2.06V18h-1.58l-.26-1.14h-.08a5.26 5.26 0 01-1.26.94c-.507.267-1.12.4-1.84.4-1 0-1.8-.28-2.4-.84-.6-.56-.9-1.4-.9-2.52V8.56h2.06v6.06c0 .733.166 1.247.5 1.54.346.293.76.44 1.24.44.52 0 .98-.107 1.38-.32.4-.227.76-.52 1.08-.88V8.56zm9.297 8.08c.427 0 .8-.047 1.12-.14.334-.107.633-.227.9-.36l.42 1.48a4.7 4.7 0 01-1.16.42 6.55 6.55 0 01-1.5.16c-.693 0-1.347-.1-1.96-.3a4.782 4.782 0 01-1.58-.92 4.593 4.593 0 01-1.06-1.54c-.253-.613-.38-1.327-.38-2.14 0-.747.12-1.42.36-2.02.24-.613.58-1.133 1.02-1.56.44-.427.967-.753 1.58-.98a5.786 5.786 0 012.02-.34c.413 0 .827.033 1.24.1.427.067.894.22 1.4.46l-.46 1.5a3.995 3.995 0 00-1.96-.52c-.92 0-1.666.287-2.24.86-.573.573-.86 1.387-.86 2.44 0 .64.087 1.18.26 1.62.187.427.427.773.72 1.04.294.267.62.46.98.58.373.107.753.16 1.14.16zM57.211 18V8.56h2.06V18h-2.06zm1.04-11.78c-.36 0-.646-.107-.86-.32a1.18 1.18 0 01-.32-.84c0-.333.107-.607.32-.82.214-.227.5-.34.86-.34.347 0 .627.113.84.34.227.213.34.487.34.82 0 .347-.113.627-.34.84-.213.213-.493.32-.84.32zM68.33 3.78h2.06V18h-1.62l-.24-1.04h-.08c-.347.4-.734.707-1.16.92-.427.213-.98.32-1.66.32-.534 0-1.047-.1-1.54-.3a3.997 3.997 0 01-1.3-.94c-.374-.413-.674-.927-.9-1.54-.227-.613-.34-1.32-.34-2.12 0-.747.093-1.42.28-2.02.2-.6.48-1.113.84-1.54a3.71 3.71 0 011.36-.98c.533-.24 1.14-.36 1.82-.36.466 0 .92.073 1.36.22.44.147.813.353 1.12.62V3.78zm0 7.12c-.56-.64-1.234-.96-2.02-.96-.347 0-.68.06-1 .18-.32.12-.607.313-.86.58a2.834 2.834 0 00-.6 1.02c-.147.413-.22.92-.22 1.52s.066 1.113.2 1.54c.146.427.333.78.56 1.06.226.28.486.487.78.62.306.12.62.18.94.18.48 0 .9-.107 1.26-.32a2.93 2.93 0 00.96-.9V10.9zm12.858 2.74h-6.62v.08c0 .96.28 1.687.84 2.18.56.493 1.3.74 2.22.74.507 0 .96-.04 1.36-.12a6.47 6.47 0 001.24-.42l.4 1.5a6.51 6.51 0 01-1.38.42c-.533.12-1.12.18-1.76.18-.68 0-1.327-.093-1.94-.28a4.449 4.449 0 01-1.62-.88 4.253 4.253 0 01-1.08-1.52c-.267-.613-.4-1.347-.4-2.2 0-.733.107-1.4.32-2a4.56 4.56 0 01.94-1.56c.413-.44.907-.773 1.48-1a5.077 5.077 0 011.94-.36c.613 0 1.167.1 1.66.3.507.2.933.487 1.28.86.36.36.633.807.82 1.34.2.52.3 1.1.3 1.74v1zm-2.12-1.44c0-.32-.04-.62-.12-.9-.08-.28-.2-.52-.36-.72a1.605 1.605 0 00-.64-.5c-.253-.133-.56-.2-.92-.2-.667 0-1.213.213-1.64.64-.413.427-.667 1-.76 1.72l4.44-.04z" fill="#2D3748"/>
<path d="M14 12a4 4 0 00-8 0 8 8 0 1016 0 11.97 11.97 0 00-4-8.944" stroke="#2D3748" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10 12a4 4 0 008 0 8 8 0 10-16 0c0 3.584 1.571 6.801 4.063 9" stroke="#F56565" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

14
site/public/logo.svg Normal file
View File

@@ -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="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" />
<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" stroke="#F56565" />
</svg>

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M5152 6381 c-123 -43 -199 -158 -189 -291 6 -96 34 -139 193 -297 77
-76 165 -167 195 -202 304 -358 520 -760 649 -1206 37 -129 34 -113 59 -240
27 -135 30 -152 37 -215 3 -30 8 -63 9 -72 7 -35 18 -247 19 -343 0 -163 -15
-305 -48 -463 -112 -529 -490 -1043 -971 -1320 -209 -120 -503 -220 -755 -257
-95 -14 -434 -13 -540 2 -371 51 -728 207 -1015 442 -127 104 -227 207 -356
368 -40 51 -140 218 -189 318 -174 352 -252 834 -181 1110 87 332 363 590 692
644 259 43 488 -13 691 -169 150 -115 278 -312 314 -481 18 -89 23 -122 25
-184 4 -123 45 -208 129 -263 60 -40 108 -54 177 -51 127 5 235 93 268 217 15
58 10 207 -10 313 -42 217 -113 397 -222 562 -48 73 -61 89 -123 161 -233 267
-569 443 -930 485 -81 9 -285 7 -365 -4 -142 -19 -317 -75 -458 -146 -250
-126 -488 -355 -620 -599 -69 -128 -139 -322 -153 -430 -3 -19 -9 -57 -14 -85
-11 -62 -10 -370 2 -456 4 -35 11 -82 14 -104 25 -190 86 -416 163 -609 38
-94 137 -294 157 -317 8 -8 14 -19 14 -24 0 -13 75 -128 153 -234 195 -265
452 -500 747 -683 119 -73 272 -149 405 -201 83 -32 266 -92 311 -102 10 -2
53 -11 94 -20 280 -60 629 -76 873 -40 18 3 48 7 67 9 19 2 46 7 60 10 14 3
38 8 54 11 70 11 144 27 181 40 22 7 81 26 130 41 296 90 656 292 905 508 97
85 272 267 340 356 19 25 37 47 40 50 19 19 125 180 180 274 150 261 266 585
310 866 6 41 13 86 16 100 25 156 25 635 -1 769 -1 9 -6 39 -9 66 -11 85 -36
227 -52 300 -9 39 -18 79 -20 90 -8 43 -77 283 -96 335 -33 93 -60 164 -73
195 -7 17 -25 59 -40 95 -187 448 -511 905 -903 1272 -122 114 -216 142 -340
99z"/>
<path d="M2827 6125 c-1 -2 -48 -5 -104 -9 -424 -25 -871 -176 -1243 -420
-313 -205 -583 -481 -787 -801 -56 -88 -159 -284 -187 -355 -84 -214 -152
-434 -170 -555 -9 -63 -18 -121 -22 -138 -19 -102 -25 -468 -11 -642 39 -489
174 -973 393 -1408 35 -70 75 -145 89 -167 14 -22 25 -42 25 -45 0 -12 175
-273 247 -367 153 -203 408 -476 533 -572 115 -88 269 -80 378 18 58 53 86
123 86 216 1 108 -18 137 -204 320 -267 264 -444 500 -613 820 -135 257 -250
584 -302 865 -10 50 -19 97 -20 106 -2 9 -6 38 -9 65 -4 27 -9 65 -11 84 -23
182 -24 552 0 640 2 8 6 36 10 63 18 148 101 399 190 577 50 99 171 290 225
354 134 160 252 272 395 376 263 190 558 314 870 364 22 4 56 9 75 13 76 13
435 11 520 -3 47 -7 101 -16 120 -19 19 -3 42 -7 50 -10 8 -2 26 -6 40 -9 115
-22 309 -94 460 -172 612 -313 1029 -926 1098 -1614 9 -91 8 -306 -2 -355 -77
-373 -352 -642 -726 -711 -65 -11 -254 -5 -323 11 -132 30 -233 78 -342 161
-160 121 -283 307 -321 485 -15 74 -23 133 -27 205 -3 61 -11 94 -30 132 -122
243 -482 202 -542 -62 -13 -60 -8 -215 10 -306 2 -8 6 -31 10 -50 23 -129 75
-268 160 -430 20 -38 89 -140 120 -179 247 -304 569 -489 950 -545 84 -12 335
-13 395 -2 14 3 40 8 59 10 133 20 324 90 461 168 384 219 646 592 720 1023
20 118 25 385 10 500 -5 33 -11 85 -15 115 -15 121 -58 307 -105 449 -286 874
-1026 1545 -1920 1742 -126 28 -119 26 -181 34 -30 4 -65 9 -79 11 -90 12
-393 26 -403 19z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -0,0 +1,19 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

View File

@@ -0,0 +1,19 @@
.icon-large svg {
width: 100%;
height: 100%;
position: relative;
z-index: 1;
}
.icon-grid {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
body {
min-height: 100%;
padding-bottom: 80px;
}

View File

@@ -0,0 +1,77 @@
import { SyntheticEvent, useEffect, useRef, useState } from 'react';
import { FormLabel, Icon, Input, InputGroup, InputLeftElement } from '@chakra-ui/core';
import { CustomPicker } from 'react-color';
const { Saturation, Hue } = require('react-color/lib/components/common');
type ColorPickerProps = {
value: string;
hex: string;
hsl: string;
hsv: string;
onChange: (s: string, e: SyntheticEvent) => void;
};
function ColorPicker({ hsv, hsl, onChange, value: color }: ColorPickerProps) {
const [value, setValue] = useState(color);
const input = useRef<HTMLInputElement>(null);
useEffect(() => {
if (color !== value && input.current !== document.activeElement) {
setValue(color === 'currentColor' ? color : String(color).toUpperCase());
}
}, [color]);
const handleChange = (e) => {
let value = e.target.value;
setValue(value);
onChange(value, e);
};
return (
<div>
<FormLabel htmlFor="color" fontWeight={'bold'}>
Color
</FormLabel>
<InputGroup>
<InputLeftElement
children={
<Icon>
<rect x={0} width={24} y={0} height={24} fill={value} rx={2} />
</Icon>
}
/>
<Input value={value} name="color" onChange={handleChange} ref={input} />
</InputGroup>
<div
style={{
width: '100%',
paddingBottom: '75%',
position: 'relative',
overflow: 'hidden',
marginTop: '0.5rem',
borderRadius: '0.375rem',
border: '1px solid',
borderColor: 'inherit',
}}
>
<Saturation hsl={hsl} hsv={hsv} onChange={onChange} />
</div>
<div
style={{
minHeight: '2em',
position: 'relative',
margin: '0.5rem 0 0 0',
borderRadius: '0.375rem',
border: '1px solid',
borderColor: 'inherit',
overflow: 'hidden',
}}
>
<Hue hsl={hsl} onChange={onChange} direction={'horizontal'} />
</div>
</div>
);
}
export default CustomPicker(ColorPicker);

View File

@@ -0,0 +1,47 @@
import { createContext, useState } from 'react';
interface ICustomIconStyle {
color: string;
setColor: (s: string) => void;
strokeWidth: number;
setStroke: (n: number) => void;
size: number;
setSize: (n: number) => void;
resetStyle: () => void;
}
const DEFAULT_STYLE = {
color: 'currentColor',
strokeWidth: 2,
size: 24,
}
export const IconStyleContext = createContext<ICustomIconStyle>({
color: 'currentColor',
setColor: (s: string) => null,
strokeWidth: 2,
setStroke: (n: number) => null,
size: 24,
setSize: (n: number) => null,
resetStyle: ()=>null
});
export function CustomizeIconContext({ children }) {
const [color, setColor] = useState(DEFAULT_STYLE.color);
const [stroke, setStroke] = useState(DEFAULT_STYLE.strokeWidth);
const [size, setSize] = useState(DEFAULT_STYLE.size);
function resetStyle(){
setColor(DEFAULT_STYLE.color);
setStroke(DEFAULT_STYLE.strokeWidth);
setSize(DEFAULT_STYLE.size);
}
return (
<IconStyleContext.Provider
value={{ color, setColor, strokeWidth: stroke, setStroke, size, setSize, resetStyle }}
>
{children}
</IconStyleContext.Provider>
);
}

View File

@@ -0,0 +1,57 @@
import {Button, Flex, Link, Stack, Text,} from "@chakra-ui/core";
import download from "downloadjs";
import JSZip from "jszip";
import { Download, Github } from 'lucide-react';
import {IconCustomizerDrawer} from "./IconCustomizerDrawer";
function generateZip(icons) {
const zip = new JSZip();
Object.values(icons).forEach((icon) =>
// @ts-ignore
zip.file(`${icon.name}.svg`, icon.src)
);
return zip.generateAsync({ type: 'blob' });
}
const Header = ({ data }) => {
const downloadAllIcons = async () => {
const zip = await generateZip(data);
download(zip, 'feather.zip');
};
const repositoryUrl = 'https://github.com/lucide-icons/lucide';
return (
<Flex direction="column" align="center" justify="center">
<Text fontSize="4xl" as="b" mb="4" textAlign="center">
Simply beautiful open source icons, community-sourced
</Text>
<Text fontSize="lg" as="p" textAlign="center" mb="8">
An open-source icon library, a fork of <Link href="https://github.com/feathericons/feather" isExternal>Feather Icons</Link>. <br/>We're expanding the icon set as much as possible while keeping it nice-looking - <Link href={repositoryUrl} isExternal>join us</Link>!
</Text>
<Stack isInline marginTop={3} marginBottom={10}>
<Button
leftIcon={<Download/>}
size="lg"
onClick={downloadAllIcons}
>
Download all
</Button>
<IconCustomizerDrawer/>
<Button
as="a"
leftIcon={<Github/>}
size="lg"
href={repositoryUrl}
target="__blank"
onClick={downloadAllIcons}
>
Github
</Button>
</Stack>
</Flex>
)
};
export default Header;

View File

@@ -0,0 +1,96 @@
import { useContext, useState } from 'react';
import { IconStyleContext } from './CustomizeIconContext';
import { Edit } from 'lucide-react';
import {
Button,
Drawer,
DrawerBody,
DrawerCloseButton,
DrawerContent,
DrawerHeader,
DrawerOverlay,
FormControl,
FormLabel,
Grid,
Slider,
SliderFilledTrack,
SliderThumb,
SliderTrack,
Flex,
Text,
} from '@chakra-ui/core';
import ColorPicker from './ColorPicker';
export function IconCustomizerDrawer() {
const [showCustomize, setShowCustomize] = useState(false);
const { color, setColor, size, setSize, strokeWidth, setStroke, resetStyle } = useContext(IconStyleContext);
return (
<>
<Button as="a" leftIcon={<Edit />} size="lg" onClick={() => setShowCustomize(true)}>
Customize
</Button>
<Drawer isOpen={showCustomize} placement="right" onClose={() => setShowCustomize(false)}>
<DrawerOverlay />
<DrawerContent>
<DrawerCloseButton />
<DrawerHeader>Customize Icons</DrawerHeader>
<DrawerBody>
<Grid gridGap={'1em'}>
<FormControl>
<ColorPicker
color={color}
value={color}
onChangeComplete={(col) => setColor(col.hex)}
/>
</FormControl>
<FormControl>
<FormLabel htmlFor="stroke">
<Flex>
<Text flexGrow={1} fontWeight={'bold'}>
Stroke
</Text>
<Text>{strokeWidth}px</Text>
</Flex>
</FormLabel>
<Slider
value={strokeWidth}
onChange={setStroke}
min={0.5}
max={3}
step={0.5}
name={'stroke'}
>
<SliderTrack>
<SliderFilledTrack bg={color} />
</SliderTrack>
<SliderThumb />
</Slider>
</FormControl>
<FormControl>
<FormLabel htmlFor="size">
<Flex>
<Text flexGrow={1} fontWeight={'bold'}>
Size
</Text>
<Text>{size}px</Text>
</Flex>
</FormLabel>
<Slider value={size} onChange={setSize} min={12} max={64} step={1} name={'size'}>
<SliderTrack>
<SliderFilledTrack bg={color} />
</SliderTrack>
<SliderThumb />
</Slider>
</FormControl>
<FormControl>
<Button onClick={resetStyle}>Reset</Button>
</FormControl>
</Grid>
</DrawerBody>
</DrawerContent>
</Drawer>
</>
);
}

View File

@@ -0,0 +1,211 @@
import { useSpring, animated } from "react-spring";
import { Box, Text, IconButton, useColorMode, Flex, ButtonGroup, Button, useToast } from "@chakra-ui/core";
import theme from "../lib/theme";
import download from 'downloadjs';
import copy from "copy-to-clipboard";
import { X as Close } from 'lucide-react';
import {useContext, useRef} from "react";
import {IconStyleContext} from "./CustomizeIconContext";
import {IconWrapper} from "./IconWrapper";
type IconDownload = {
src: string;
name: string;
};
const IconDetailOverlay = ({ isOpen = true, onClose, icon }) => {
const toast = useToast();
const { colorMode } = useColorMode();
const { tags = [], name } = icon;
const {color, strokeWidth, size} = useContext(IconStyleContext);
const iconRef = useRef<SVGSVGElement>(null);
const { transform, opacity } = useSpring({
opacity: isOpen ? 1 : 0,
transform: `translateY(${isOpen ? -120 : 0}%)`,
config: { mass: 5, tension: 500, friction: 80 },
});
const handleClose = () => {
onClose();
};
const panelStyling = {
transform: transform.interpolate(t => t),
opacity: opacity.interpolate(o => o),
width: "100%",
willChange: "transform"
}
const iconStyling = (isLight) => ({
height: "25vw",
width: "25vw",
minHeight: "160px",
minWidth: "160px",
maxHeight: "240px",
maxWidth: "240px",
color: color,
});
const downloadIcon = ({src, name} : IconDownload) => download(src, `${name}.svg`, 'image/svg+xml');
const copyIcon = ({src, name} : IconDownload) => {
copy(src);
toast({
title: "Copied!",
description: `Icon "${name}" copied to clipboard.`,
status: "success",
duration: 1500,
isClosable: true
});
}
const downloadPNG = ({src, name}: IconDownload) => {
const canvas = document.createElement('canvas');
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext("2d");
const image = new Image();
image.src = `data:image/svg+xml;base64,${btoa(src)}`;
image.onload = function() {
ctx.drawImage(image, 0, 0);
const link = document.createElement('a');
link.download = `${name}.png`;
link.href = canvas.toDataURL('image/png')
link.click();
}
}
return (
<Box
position="fixed"
bottom={0}
zIndex={2}
width="100%"
left={0}
height={0}
key={name}
>
<Flex
alignItems="center"
justifyContent="space-between"
pt={4}
pb={4}
maxW="850px"
margin="0 auto"
w="full"
px={8}
>
<animated.div
style={panelStyling}
>
<Box
borderWidth="1px"
rounded="lg"
width="full"
boxShadow={theme.shadows.xl}
position="relative"
bg={
colorMode == "light"
? theme.colors.white
: theme.colors.gray[700]
}
padding={8}
>
<IconButton
size="sm"
aria-label="Close overlay"
variant="ghost"
color="current"
ml="3"
position="absolute"
top={4}
right={4}
onClick={handleClose}
icon={<Close />}
/>
<Flex direction={['column', 'row']} alignItems={['center', 'flex-start']}>
<Flex>
<Box
borderWidth="1px"
rounded="md"
position="relative"
bg={
colorMode == "light"
? theme.colors.whiteAlpha[800]
: theme.colors.blackAlpha[500]
}
padding={0}
>
<div
style={iconStyling(colorMode == "light")}
className="icon-large"
>
<IconWrapper
content={icon.content}
stroke={color}
strokeWidth={strokeWidth}
height={size}
width={size}
ref={iconRef}
/>
</div>
<svg className="icon-grid" width="24" height="24" viewBox={`0 0 ${size} ${size}`} fill="none" stroke={colorMode == "light" ? '#E2E8F0' : theme.colors.gray[600]} strokeWidth="0.1" xmlns="http://www.w3.org/2000/svg">
{ Array.from({ length:(size - 1) }, (_, i) => (
<g key={`grid-${i}`}>
<line key={`horizontal-${i}`} x1={0} y1={i + 1} x2={size} y2={i + 1} />
<line key={`vertical-${i}`} x1={i + 1} y1={0} x2={i + 1} y2={size} />
</g>
)) }
</svg>
</Box>
</Flex>
<Flex marginLeft={[0, 8]}>
<Box>
<Text fontSize="3xl" style={{ cursor: "pointer" }} mb={1}>
{icon.name}
</Text>
<Box mb={4}>
{ tags?.length ? (
<Text
fontSize="xl"
fontWeight="bold"
color={
colorMode === "light"
? 'gray.600'
: 'gray.500'
}
>
{ tags.join(' • ') }
</Text>
) : ''}
{/* <Button size="sm" fontSize="md" variant="ghost" onClick={() => downloadIcon(icon)}>
Edit Tags
</Button> */}
</Box>
<ButtonGroup spacing={4}>
<Button variant="solid" onClick={() => downloadIcon({src: iconRef.current.outerHTML, name: icon.name})} mb={1}>
Download SVG
</Button>
<Button variant="solid" onClick={() => copyIcon({src: iconRef.current.outerHTML, name: icon.name})} mb={1}>
Copy SVG
</Button>
<Button variant="solid" onClick={() => downloadPNG({src: iconRef.current.outerHTML, name: icon.name})} mb={1}>
Download PNG
</Button>
</ButtonGroup>
</Box>
</Flex>
</Flex>
</Box>
</animated.div>
</Flex>
</Box>
);
};
export default IconDetailOverlay;

View File

@@ -0,0 +1,84 @@
import { Button, Flex, Grid, Text, useToast } from "@chakra-ui/core";
import download from 'downloadjs';
import Link from 'next/link'
import copy from "copy-to-clipboard";
import {useContext, useMemo} from "react";
import {IconStyleContext} from "./CustomizeIconContext";
import {IconWrapper} from "./IconWrapper";
import { useRouter } from "next/router";
const IconList = ({icons}) => {
const router = useRouter()
const toast = useToast();
const {color, size, strokeWidth} = useContext(IconStyleContext);
const { search } = router.query;
const query = useMemo(()=> search !== undefined ? { search } : {},[search])
return (
<Grid
templateColumns={`repeat(auto-fill, minmax(160px, 1fr))`}
gap={5}
marginBottom="320px"
>
{ icons.map((icon) => {
const actualIcon = icon.item ? icon.item : icon;
const { name, content } = actualIcon;
return (
<Link
key={name}
scroll={false}
href={{
pathname: '/icon/[iconName]',
query: {
...query,
iconName: name,
},
}}
>
<Button
variant="ghost"
borderWidth="1px"
rounded="lg"
padding={16}
onClick={(event) => {
if (event.shiftKey) {
copy(actualIcon.src);
toast({
title: "Copied!",
description: `Icon "${name}" copied to clipboard.`,
status: "success",
duration: 1500,
});
}
if (event.metaKey) {
download(
actualIcon.src,
`${name}.svg`,
"image/svg+xml"
);
}
}}
key={name}
alignItems="center"
>
<Flex direction="column" align="center" justify="center">
<IconWrapper
content={content}
stroke={color}
strokeWidth={strokeWidth}
height={size}
width={size}
/>
<Text marginTop={5}>{name}</Text>
</Flex>
</Button>
</Link>
);
})}
</Grid>
);
}
export default IconList;

View File

@@ -0,0 +1,119 @@
import {
Box,
Input,
InputGroup,
InputLeftElement,
Text,
useColorMode,
Icon,
} from '@chakra-ui/core';
import IconList from './IconList';
import { useEffect, useMemo, useRef, useState } from 'react';
import useSearch from '../lib/useSearch';
import { useRouter } from 'next/router';
import theme from '../lib/theme';
import { Search as SearchIcon } from 'lucide-react';
import debounce from 'lodash/debounce';
const isFilledString = (string) => string !== undefined && string !== null && string !== '';
const IconOverview = ({ data }) => {
const router = useRouter();
const { search } = router.query;
const [queryText, setQueryText] = useState(search);
const { colorMode } = useColorMode();
const inputElement = useRef(null);
function handleKeyDown(event) {
if (event.key === '/' && inputElement.current !== document.activeElement) {
event.preventDefault();
inputElement.current.focus();
}
}
const setQueryParam = (searchString) => {
const { query, asPath } = router;
if(isFilledString(searchString)) {
let route = {
pathname: '',
query
}
if(query.iconName) {
route.query.iconName = query.iconName;
route.pathname = '/icon/[iconName]';
}
route.query.search = searchString;
router.replace(route);
}
else {
if (query?.search) {
delete query.search;
router.replace({
query
})
}
}
}
// @ts-ignore
const searchResults = useMemo(() => useSearch(data, queryText), [data, queryText])
const handleSearchInput = debounce((event) => {
event.persist();
const { value = '' } = inputElement?.current;
setQueryText(value)
setQueryParam(value)
}, 400)
useEffect(() => {
setQueryText(search)
}, [search]);
useEffect(() => {
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, []);
return (
<>
<InputGroup position="sticky" top={4} zIndex={1}>
<InputLeftElement
children={
<Icon>
<SearchIcon />
</Icon>
}
/>
<Input
ref={inputElement}
placeholder={`Search ${Object.keys(data).length} icons (Press "/" to focus)`}
onChange={handleSearchInput}
defaultValue={queryText}
bg={colorMode == 'light' ? theme.colors.white : theme.colors.gray[700]}
/>
</InputGroup>
<Box marginTop={5}>
{searchResults.length > 0 ? (
<IconList icons={searchResults} />
) : (
<Text
fontSize="2xl"
fontWeight="bold"
textAlign="center"
style={{ wordBreak: 'break-word' }}
>
No results found for "{queryText}"
</Text>
)}
</Box>
</>
);
};
export default IconOverview;

View File

@@ -0,0 +1,27 @@
import { forwardRef, SVGProps } from 'react';
interface IconWrapperProps extends SVGProps<SVGSVGElement> {
content: string;
}
export const IconWrapper = forwardRef<SVGSVGElement, IconWrapperProps>((props, ref) => {
const defaultAttrs : SVGProps<SVGSVGElement>= {
xmlns: 'http://www.w3.org/2000/svg',
width: '24px',
height: '24px',
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
strokeWidth: '2px',
strokeLinecap: 'round',
strokeLinejoin: 'round',
};
const { content, ...rest } = props;
const attrs = {
...defaultAttrs,
...rest,
};
return <svg ref={ref} {...attrs} dangerouslySetInnerHTML={{ __html: content }} />;
});

View File

@@ -1,10 +1,15 @@
import { Box, Divider, Flex, Text, Link, Icon, useColorMode } from "@chakra-ui/core";
import { Box, Divider, Flex, Text, Link, Icon, useColorMode, useColorModeValue, IconButton } from "@chakra-ui/core";
import { useKeyBindings } from "../lib/key";
import {useRouter} from "next/router";
import { useRouter } from "next/router";
import NextLink from "next/link"
import { Moon, Sun } from 'lucide-react';
import Logo from 'babel-loader!react-svg-loader?jsx=true!../../public/logo.svg';
const Layout = ({ children }) => {
const router = useRouter();
const { colorMode, toggleColorMode } = useColorMode();
const text = useColorModeValue('dark', 'light')
const ColorModeToggle = useColorModeValue(Moon, Sun);
function setQuery(query){
router.push({
@@ -12,6 +17,7 @@ const Layout = ({ children }) => {
query: { query: query }
}).then();
}
useKeyBindings({
Escape: {
fn: () => setQuery(""),
@@ -35,27 +41,45 @@ const Layout = ({ children }) => {
px={8}
>
<Flex justifyContent="center" alignItems="center">
<Text
fontSize="4xl"
onClick={() => setQuery("")}
style={{ cursor: "pointer" }}
>
Lucide
</Text>
<NextLink href="/" passHref>
<Link display="flex" _hover={{textDecoration: 'none'}}>
<Icon boxSize={12} marginRight="8px">
<Logo/>
</Icon>
<Text
fontSize="40px"
lineHeight="48px"
>
Lucide
</Text>
</Link>
</NextLink>
</Flex>
<Flex justifyContent="center" alignItems="center">
<Link href="https://github.com/lucide-icons/lucide" isExternal style={{ fontSize: "18px", marginRight: '24px' }}>
<Link
href="https://github.com/lucide-icons/lucide"
isExternal
marginRight={6}
fontSize="xl"
>
Github
</Link>
<div onClick={toggleColorMode} style={{ cursor: "pointer" }}>
<Icon name={colorMode == "light" ? "moon" : "sun"} size="24px" />
</div>
<IconButton
size="md"
fontSize="lg"
aria-label={`Switch to ${text} mode`}
variant="ghost"
color="current"
ml="3"
onClick={toggleColorMode}
icon={<ColorModeToggle />}
/>
</Flex>
</Flex>
</Flex>
<Flex margin="0 auto" direction="column" maxW="1250px" px={8}>
{children}
<Divider marginTop={10} marginBottom={10} />
<Divider marginTop={4} marginBottom={8} />
</Flex>
</Box>
);

View File

@@ -1,5 +1,6 @@
import fs from "fs";
import path from "path";
import cheerio from 'cheerio';
import tags from '../../../tags.json';
const directory = path.join(process.cwd(), "../icons");
@@ -16,10 +17,14 @@ export function getData(name) {
const fullPath = path.join(directory, `${name}.svg`);
const fileContents = fs.readFileSync(fullPath, "utf8");
const $ = cheerio.load(fileContents);
const content = $("svg").html();
return {
name,
tags: tags[name] || [],
src: fileContents,
content: content
};
}

View File

@@ -1,33 +0,0 @@
import { useEffect, useMemo, useState } from 'react';
import { useDebounce } from './useDebounce';
function useSearch(icons: Object, query: string | string[]) {
let iconList = useMemo(() => Object.values(icons), [icons]);
const [results, setResults] = useState(iconList);
// query can be an array because this is a valid query string ?query=xyz&query=abc
const debouncedQuery = useDebounce(
typeof query === 'string' ? query.trim() : typeof query === 'undefined' ? '' : query[0].trim(),
300
);
async function doSearch() {
if (debouncedQuery) {
const Fuse = (await import('fuse.js')).default;
const fuse = new Fuse(iconList, {
threshold: 0.2,
keys: ['name', 'tags'],
});
return fuse.search(debouncedQuery);
} else {
return iconList;
}
}
useEffect(() => {
doSearch().then(setResults);
}, [debouncedQuery]);
return results;
}
export default useSearch;

View File

@@ -4,70 +4,7 @@ const theme = {
...chakraTheme,
fonts: {
...chakraTheme.fonts,
body: `Jost,-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"`,
},
icons: {
...chakraTheme.icons,
sun: {
path: (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="currentColor"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="12" cy="12" r="5" />
<line x1="12" y1="1" x2="12" y2="3" />
<line x1="12" y1="21" x2="12" y2="23" />
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
<line x1="1" y1="12" x2="3" y2="12" />
<line x1="21" y1="12" x2="23" y2="12" />
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
</svg>
),
},
moon: {
path: (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="currentColor"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
</svg>
),
},
search: {
path: (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="11" cy="11" r="8" />
<line x1="21" y1="21" x2="16.65" y2="16.65" />
</svg>
),
},
body: `'Mukta', sans-serif`,
},
};

View File

@@ -0,0 +1,20 @@
import { useEffect, useMemo, useState } from 'react';
import { useDebounce } from './useDebounce';
function useSearch(icons: Array<any>, query:string) {
if(!query) return icons;
const searchString = query.toLowerCase()
return icons.filter(({ name, tags }) => {
const icon = { name, tags };
return Object.keys(icon).some(
key => String(icon[key])
.toLowerCase()
.includes(searchString)
)
});
}
export default useSearch;

View File

@@ -1,5 +1,6 @@
import { CSSReset, ThemeProvider, ColorModeProvider } from '@chakra-ui/core';
import { ChakraProvider } from '@chakra-ui/core';
import customTheme from '../lib/theme';
import '../assets/styling.css';
import Head from 'next/head';
const App = ({ Component, pageProps }) => {
@@ -8,12 +9,9 @@ const App = ({ Component, pageProps }) => {
<Head>
<title>Lucide</title>
</Head>
<ThemeProvider theme={customTheme}>
<ColorModeProvider>
<CSSReset />
<Component {...pageProps} />
</ColorModeProvider>
</ThemeProvider>
<ChakraProvider theme={customTheme}>
<Component {...pageProps} />
</ChakraProvider>
</>
);
};

View File

@@ -1,4 +1,5 @@
import Document, { Head, Html, Main, NextScript } from "next/document";
import { ColorModeScript } from "@chakra-ui/core"
class MyDocument extends Document {
render() {
@@ -6,9 +7,17 @@ class MyDocument extends Document {
<Html>
<Head>
<link
href="https://indestructibletype.com/fonts/Jost.css"
href="https://fonts.googleapis.com/css2?family=Mukta:wght@400;600;700&display=swap"
rel="stylesheet"
/>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"/>
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"/>
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"/>
<link rel="manifest" href="/site.webmanifest"/>
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#F56565"/>
<link rel="icon" type="image/svg+xml" href="/favicon.svg"/>
<meta name="msapplication-TileColor" content="#F56565"/>
<meta name="theme-color" content="#F56565"></meta>
</Head>
<style jsx global>{`
* {
@@ -20,6 +29,7 @@ class MyDocument extends Document {
}
`}</style>
<body>
<ColorModeScript />
<Main />
<NextScript />
</body>

View File

@@ -0,0 +1,55 @@
import { useEffect } from 'react'
import { useRouter } from 'next/router'
import IconDetailOverlay from '../../components/IconDetailOverlay'
import { getAllData, getData } from '../../lib/icons';
import IconOverview from '../../components/IconOverview';
import Layout from '../../components/Layout';
import Header from '../../components/Header';
const IconPage = ({ icon, data }) => {
const router = useRouter()
const onClose = () => {
let query = {};
if(router.query.search) {
query = {
search: router.query.search
};
}
router.push({
pathname: '/',
query,
})
}
return (
<Layout>
<IconDetailOverlay
key={icon.name}
icon={icon}
onClose={onClose}
/>
<Header {...{data}}/>
<IconOverview {...{data}}/>
</Layout>
)
}
export default IconPage
export function getStaticProps({ params: { iconName } }) {
const data = getAllData();
const icon = getData(iconName);
return { props: { icon, data } }
}
export function getStaticPaths() {
return {
paths: getAllData().map(({name: iconName }) => ({
params: { iconName },
})),
fallback: false,
}
}

View File

@@ -1,148 +1,34 @@
import {
Button,
Flex,
Grid,
Link,
Icon,
Input,
InputGroup,
InputLeftElement,
Stack,
Text,
useToast,
} from '@chakra-ui/core';
import copy from 'copy-to-clipboard';
import download from 'downloadjs';
import JSZip from 'jszip';
import { useEffect, useRef, useState } from 'react';
import Layout from '../components/Layout';
import { getAllData } from '../lib/icons';
import useSearch from '../lib/search';
import { useRouter } from 'next/router';
import { useDebounce } from '../lib/useDebounce';
import Layout from "../components/Layout";
import { getAllData } from "../lib/icons";
function generateZip(icons) {
const zip = new JSZip();
Object.values(icons).forEach((icon) =>
// @ts-ignore
zip.file(`${icon.name}.svg`, icon.src)
);
return zip.generateAsync({ type: 'blob' });
}
import IconOverview from "../components/IconOverview";
import IconDetailOverlay from "../components/IconDetailOverlay";
import { useRouter } from "next/router";
import Header from "../components/Header";
import {CustomizeIconContext} from "../components/CustomizeIconContext";
const IndexPage = ({ data }) => {
const router = useRouter();
const { query } = router.query;
const [queryText, setQueryText] = useState(query || '');
const toast = useToast();
const debouncedQuery = useDebounce(queryText, 1000);
const results = useSearch(data, queryText);
useEffect(() => {
setQueryText(query);
}, [query]);
useEffect(() => {
router.push({
pathname: '/',
query: { query: debouncedQuery },
});
}, [debouncedQuery]);
const inputElement = useRef(null);
function handleKeyDown(event) {
if (event.key === '/' && inputElement.current !== document.activeElement) {
event.preventDefault();
inputElement.current.focus();
}
}
useEffect(() => {
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, []);
const getIcon = (iconName) => data.find(({name}) => name === iconName) || {};
return (
<Layout>
<Flex direction="column" align="center" justify="center">
<Text fontSize="3xl" as="b" mb="4">
Simply beautiful open source icons, community-sourced
</Text>
<Text fontSize="lg" as="p" textAlign="center" mb="8">
An open-source icon library, a fork of Feather Icons. <br/>We're expanding the icon set as much as possible while keeping it nice-looking - <Link href="https://github.com/lucide-icons/lucide" isExternal>join us</Link>!
</Text>
<Stack isInline marginTop={3} marginBottom={10}>
<Button
onClick={async () => {
const zip = await generateZip(data);
download(zip, 'feather.zip');
}}
>
Download all
</Button>
</Stack>
</Flex>
<InputGroup position="sticky" top={2} zIndex={1}>
<InputLeftElement children={<Icon name="search" />} />
<Input
ref={inputElement}
placeholder={`Search ${Object.keys(data).length} icons (Press "/" to focus)`}
value={queryText}
onChange={(event) => setQueryText(event.target.value)}
marginBottom={5}
<CustomizeIconContext>
<IconDetailOverlay
isOpen={!!router.query.iconName}
icon={getIcon(router.query.iconName)}
onClose={() => router.push('/')}
/>
</InputGroup>
{results.length > 0 ? (
<Grid templateColumns={`repeat(auto-fill, minmax(160px, 1fr))`} gap={5}>
{results.map((icon) => {
// @ts-ignore
const actualIcon = icon.item ? icon.item : icon;
return (
<Button
variant="ghost"
borderWidth="1px"
rounded="lg"
padding={16}
onClick={(event) => {
if (event.shiftKey) {
copy(actualIcon.src);
toast({
title: 'Copied!',
description: `Icon "${actualIcon.name}" copied to clipboard.`,
status: 'success',
duration: 1500,
});
} else {
download(actualIcon.src, `${actualIcon.name}.svg`, 'image/svg+xml');
}
}}
key={actualIcon.name}
alignItems="center"
>
<Flex direction="column" align="center" justify="center">
<div dangerouslySetInnerHTML={{ __html: actualIcon.src }} />
<Text marginTop={5}>{actualIcon.name}</Text>
</Flex>
</Button>
);
})}
</Grid>
) : (
<Text
fontSize="2xl"
fontWeight="bold"
textAlign="center"
style={{ wordBreak: 'break-word' }}
>
No results found for "{query}"
</Text>
)}
<Header {...{data}}/>
<IconOverview {...{data}}/>
</CustomizeIconContext>
</Layout>
);
};
export async function getStaticProps() {
let data = getAllData();
return {
props: {
data,

View File

@@ -0,0 +1,18 @@
import { getAllData } from '../lib/icons';
import { renderHook } from '@testing-library/react-hooks';
import useSearch from '../lib/useSearch';
describe('Icon Overview', () => {
it('can search filter icons', async () => {
let allData = getAllData();
const { result: result1, waitForNextUpdate: wait1 } = renderHook(() => useSearch(allData, ''));
expect(result1.current).toHaveLength(allData.length);
const { result: result2, waitForNextUpdate: wait2 } = renderHook(() =>
useSearch(allData, 'airplay')
);
await wait2();
expect(result2.current).toHaveLength(2);
});
});

View File

@@ -3,9 +3,8 @@ import Index from '../pages/index';
import React from 'react';
import { render } from './test-utils';
import { getAllData } from '../lib/icons';
import App from '../pages/_app';
import { renderHook } from '@testing-library/react-hooks';
import useSearch from '../lib/search';
describe('App', () => {
it('renders without crashing', () => {
@@ -15,16 +14,4 @@ describe('App', () => {
screen.getByText('Simply beautiful open source icons, community-sourced')
).toBeInTheDocument();
});
it('can search filter icons', async () => {
let allData = getAllData();
const { result: result1, waitForNextUpdate: wait1 } = renderHook(() => useSearch(allData, ''));
expect(result1.current).toHaveLength(allData.length);
const { result: result2, waitForNextUpdate: wait2 } = renderHook(() =>
useSearch(allData, 'airplay')
);
await wait2();
expect(result2.current).toHaveLength(2);
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -12,10 +12,12 @@
"archive": ["index", "box"],
"at-sign": ["mention", "at", "email", "message"],
"award": ["achievement", "badge"],
"axe": ["hatchet"],
"aperture": ["camera", "photo"],
"bar-chart": ["statistics", "diagram", "graph"],
"bar-chart-2": ["statistics", "diagram", "graph"],
"battery": ["power", "electricity"],
"beaker": ["cup"],
"battery-charging": ["power", "electricity"],
"bell": ["alarm", "notification", "sound"],
"bell-off": ["alarm", "notification", "silent"],
@@ -74,14 +76,18 @@
"file-minus": ["delete", "remove", "erase"],
"file-plus": ["add", "create", "new"],
"file-text": ["data", "txt", "pdf"],
"files": ["multiple", "copy"],
"film": ["movie", "video"],
"filter": ["funnel", "hopper"],
"flag": ["report"],
"flask-conical": ["beaker", "erlenmeyer"],
"flask-round": ["beaker"],
"folder-minus": ["directory"],
"folder-plus": ["directory"],
"folder": ["directory"],
"framer": ["logo", "design", "tool"],
"frown": ["emoji", "face", "bad", "sad", "emotion"],
"gavel": ["hammer", "mallet"],
"gift": ["present", "box", "birthday", "party"],
"git-branch": ["code", "version control"],
"git-commit": ["code", "version control"],
@@ -90,14 +96,17 @@
"github": ["logo", "version control"],
"gitlab": ["logo", "version control"],
"globe": ["world", "browser", "language", "translate"],
"hammer": ["mallet"],
"hard-drive": ["computer", "server", "memory", "data"],
"hard-hat": ["helmet", "construction"],
"hash": ["hashtag", "number", "pound"],
"headphones": ["music", "audio", "sound"],
"heart": ["like", "love", "emotion"],
"help-circle": ["question mark"],
"hexagon": ["shape", "node.js", "logo"],
"home": ["house", "living"],
"image": ["picture"],
"image": ["picture", "photo"],
"image-off": ["picture", "photo"],
"inbox": ["email"],
"instagram": ["logo", "camera"],
"key": ["password", "login", "authentication", "secure"],
@@ -151,6 +160,7 @@
"phone": ["call"],
"play": ["music", "start"],
"pie-chart": ["statistics", "diagram"],
"pipette": ["eye dropper", "color picker"],
"play-circle": ["music", "start"],
"plus": ["add", "new"],
"plus-circle": ["add", "new"],
@@ -166,6 +176,7 @@
"rotate-ccw": ["arrow"],
"rotate-cw": ["arrow"],
"rss": ["feed", "subscribe"],
"ruler": ["measure"],
"save": ["floppy disk"],
"scissors": ["cut"],
"search": ["find", "magnifier", "magnifying glass"],
@@ -176,6 +187,7 @@
"shield-off": ["security", "insecure"],
"shopping-bag": ["ecommerce", "cart", "purchase", "store"],
"shopping-cart": ["ecommerce", "cart", "purchase", "store"],
"shovel": ["dig", "spade"],
"shuffle": ["music"],
"skip-back": ["music"],
"skip-forward": ["music"],
@@ -199,7 +211,7 @@
"thumbs-up": ["like", "good", "emotion"],
"toggle-left": ["on", "off", "switch"],
"toggle-right": ["on", "off", "switch"],
"tool": ["settings", "spanner"],
"wrench": ["tool", "settings", "spanner"],
"trash": ["garbage", "delete", "remove", "bin"],
"trash-2": ["garbage", "delete", "remove", "bin"],
"triangle": ["delta"],

233
yarn.lock

File diff suppressed because it is too large Load Diff