Compare commits

...

45 Commits

Author SHA1 Message Date
Eric Fennis
95a7d8648f v0.14.1 2021-02-27 11:31:24 +01:00
Eric Fennis
b07eb8c00d Merge branch 'master' of github.com:lucide-icons/lucide into hotfix/fix-react-component 2021-02-27 11:23:36 +01:00
Eric Fennis
40cb396a6c Fix proptypes 2021-02-27 11:21:35 +01:00
Eric Fennis
87ab0bfb62 Figma plugin! (#227)
* initialize figma plugin

* Add icons to view

* Make the plugin work

* Fix search hook

* Finialize figma plugin

* Finish up icons

* remove unused code

* Add cover and icon
2021-02-26 16:35:18 +01:00
Yashu Mittal
c4f50417d5 maintain 1px border spacing in languages icon (#232) 2021-02-26 13:32:07 +01:00
Lucide Bot
0ca1b98689 📦 Bump version to 0.14.0 2021-02-22 19:46:47 +00:00
Eric Fennis
7ce35bac34 Merge branch 'master' of github.com:ericfennis/featherity 2021-02-22 20:36:44 +01:00
Eric Fennis
7bd41d9b48 Add vue to main readme 2021-02-22 20:33:43 +01:00
Eric Fennis
5ec34cb249 Merge branch 'master' of github.com:lucide-icons/lucide 2021-02-22 20:32:02 +01:00
Eric Fennis
4024911219 Lucide Vue Package (#174)
* add configs

* Add vue components

* Add documentation

* add alpha release version

* improve npm ignore files

* add tests

* Make style and class attrs work

* 📦 bump version

* Add Icon suffix for component names

* bump version

* Add icon component example

* remove space

* improvements package.json

* update tests

* update workflow
2021-02-22 20:26:38 +01:00
Eric Fennis
acea1b4116 Add radio receiver (#190) 2021-02-21 19:29:46 +01:00
Eric Fennis
ec3542dab8 Add tv-2 icon (#189) 2021-02-21 19:29:37 +01:00
Eric Fennis
51cf68c11f Add history icon (#188) 2021-02-21 19:29:26 +01:00
Yashu Mittal
0c460bc3dd add image-minus icon (#191)
* add image-minus icon

* refactor the icon
2021-02-18 08:40:21 +01:00
Yashu Mittal
7e91be9e38 add binary icon (#203) 2021-02-17 13:24:11 +01:00
Yashu Mittal
d042899a06 add regex icon (#213) 2021-02-17 13:18:42 +01:00
Eric Fennis
891115f6fd Add pencil icon (#129) 2021-02-17 13:04:50 +01:00
Yashu Mittal
8ce52d834d add inspect icon (#209) 2021-02-15 16:21:00 +01:00
Yashu Mittal
b6ea440d70 add option icon (#225) 2021-02-15 16:20:47 +01:00
Yashu Mittal
a3125d53cb change lucide domain url to lucide.dev (#228) 2021-02-15 16:20:30 +01:00
Eric Fennis
a7e8b3bcb7 Add contributors to icon overlay and add dot (#223)
* add contributers

* Add icon fetcher

* add contributing json

* Fix fetch call

* Add contributers to site

* Add caching for github api

* Fix build

* Move context provider

* Revert packages changes

* Fix mobile layout

* remove react-spring

* remove incorrect type prop
2021-02-12 20:38:47 +01:00
Eric Fennis
c4dfe6b8cb Update tags (#224)
* Add script to add more tags

* add tags

* update tags

* Fix tags
2021-02-12 16:07:57 +01:00
Yashu Mittal
90e86767d8 add bell plus & minus icon (#202)
* add bell plus & minus icon

* adjust 2px spacing in bell-minus icon
2021-02-12 15:52:02 +01:00
Eric Fennis
30f7be3fd4 Update README.md 2021-02-10 21:23:37 +01:00
Eric Fennis
313b46ecc1 update tags 2021-02-08 22:17:56 +01:00
Eric Fennis
367f89ef1f add tags 2021-02-08 21:55:48 +01:00
Eric Fennis
32ba19d591 Add script to add more tags 2021-02-08 21:18:43 +01:00
John Letey
5d945372d4 Vercel Sponsorship (#221)
* feat: add Vercel sponsorship requirements

* chore: fix syntax error
2021-02-08 07:53:49 +01:00
Yashu Mittal
736b888608 add shield alert, check & close icon (#208)
* add shield alert, check & close icon

* Update icons/shield-close.svg

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

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2021-02-07 11:24:37 +01:00
Yashu Mittal
fc03912eef add wallet icon (#193)
* add wallet icon

* Update icons/wallet.svg

Co-authored-by: Alexandr Antonov <alexandr-post@yandex.ru>

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

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2021-02-07 11:23:44 +01:00
Yashu Mittal
0864092670 add sigma icon (#197)
Co-authored-by: johnletey <johnletey@gmail.com>
2021-02-06 22:54:45 +01:00
Aishwarya Sharma
400f7871d2 Add icon podcast (#132)
* Add icon podcast

* update to svg with stroke width

* Update podcast.svg

* Update icons/podcast.svg
2021-02-06 22:46:06 +01:00
Yashu Mittal
0ce207bc2f add asterisk icon (#196) 2021-02-04 19:28:22 +01:00
Yashu Mittal
8c85053002 add alarm check, minus, plus icon (#198) 2021-02-04 19:27:41 +01:00
Yashu Mittal
a453c61aa5 decrease slash size by 1px border (#200) 2021-02-04 19:26:29 +01:00
Yashu Mittal
5ad6f5bba3 add webcam icon (#192)
* add webcam icon

* Update icons/webcam.svg

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

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2021-02-04 19:08:05 +01:00
Eric Fennis
68b91149f2 Add repeat 1 icon (#187) 2021-02-04 19:07:27 +01:00
Yashu Mittal
c50b8181a0 Add icon image-plus (#184)
* add icon image-add

* Rename to "image-plus"
2021-02-04 19:06:58 +01:00
Eric Fennis
1db690953b Merge branch 'master' of github.com:lucide-icons/lucide 2021-02-03 13:14:49 +01:00
Eric Fennis
cef1e1aafa test raw icons 2021-02-03 13:14:03 +01:00
Yashu Mittal
d29667ed99 add globe-2 icon (#178)
* add globe-2 icon

* Refactor & set border at 1px

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

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2021-02-02 21:10:25 +01:00
Yashu Mittal
d31c2a2f2a optimize icons using script (#195) 2021-02-02 21:09:33 +01:00
Yashu Mittal
e4a86687e2 Add git-branch-new icon (#182)
* Add git-branch-new icon

* rename icon to "git-branch-plus"

* Refactor git-branch-new icon

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

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2021-02-02 11:15:10 +01:00
Eric Fennis
2d9eee74c5 📦 Bump versions 2021-01-27 20:58:26 +01:00
Eric Fennis
0668f8aebe Fix auth token setting 2021-01-27 20:45:10 +01:00
103 changed files with 11684 additions and 469 deletions

View File

@@ -24,7 +24,7 @@ jobs:
clean: true clean: true
- name: Set Auth Token - name: Set Auth Token
run: yarn config set 'npmRegistries["//registry.npmjs.org"].npmAuthToken' ${{ secrets.NPM_TOKEN }} run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
# Build lucide # Build lucide
- name: Install Dependencies Lucide - name: Install Dependencies Lucide
@@ -33,6 +33,9 @@ jobs:
- name: Build lucide package - name: Build lucide package
run: yarn build run: yarn build
- name: Test lucide package
run: yarn test
# Build lucide-react # Build lucide-react
- name: Install Dependencies lucide-react - name: Install Dependencies lucide-react
run: yarn --pure-lockfile run: yarn --pure-lockfile
@@ -42,6 +45,23 @@ jobs:
run: yarn build run: yarn build
working-directory: packages/lucide-react working-directory: packages/lucide-react
- name: Test lucide-react
run: yarn test
working-directory: packages/lucide-react
# Build lucide-vue
- name: Install Dependencies lucide-vue
run: yarn --pure-lockfile
working-directory: packages/lucide-vue
- name: Build lucide-vue
run: yarn build
working-directory: packages/lucide-vue
- name: Test lucide-vue
run: yarn test
working-directory: packages/lucide-vue
# Publish lucide # Publish lucide
- name: Set package.json version lucide - name: Set package.json version lucide
run: yarn version --new-version ${{ steps.get_version.outputs.VERSION }} --no-git-tag-version run: yarn version --new-version ${{ steps.get_version.outputs.VERSION }} --no-git-tag-version
@@ -58,10 +78,20 @@ jobs:
run: yarn publish run: yarn publish
working-directory: packages/lucide-react working-directory: packages/lucide-react
# Publish lucide-vue
- name: Set package.json version lucide-vue
run: yarn version --new-version ${{ steps.get_version.outputs.VERSION }} --no-git-tag-version
working-directory: packages/lucide-vue
- name: publish lucide-vue
run: yarn publish
working-directory: packages/lucide-vue
- name: Commit package.json - name: Commit package.json
run: | run: |
git add package.json git add package.json
git add packages/lucide-react/package.json git add packages/lucide-react/package.json
git add packages/lucide-vue/package.json
git -c user.name="Lucide Bot" -c user.email="lucide-bot@users.noreply.github.com" \ git -c user.name="Lucide Bot" -c user.email="lucide-bot@users.noreply.github.com" \
commit -m ":package: Bump version to ${{ steps.get_version.outputs.VERSION }}" --no-verify --quiet commit -m ":package: Bump version to ${{ steps.get_version.outputs.VERSION }}" --no-verify --quiet
git remote set-url --push origin https://lucide-bot:$GITHUB_TOKEN@github.com/$GITHUB_REPOSITORY.git git remote set-url --push origin https://lucide-bot:$GITHUB_TOKEN@github.com/$GITHUB_REPOSITORY.git

View File

@@ -5,3 +5,12 @@ node_modules
tests tests
scripts scripts
site site
src
build
babel.config.js
categories.json
jest.config.js
netlify.toml
rollup.config.js
rollup.plugins.js
tags.json

View File

@@ -1,4 +1,4 @@
<p align=center><img width="410" src="https://lucide.netlify.app/logo-text.svg" alt="Lucide Logo"></p> <p align=center><img width="410" src="https://lucide.dev/logo-text.svg" alt="Lucide Logo"></p>
# Lucide # Lucide
@@ -22,6 +22,7 @@ Lucide is a community-run fork of [Feather Icons](https://github.com/feathericon
* [Treeshake library](#treeshake-the-library-only-use-the-icons-you-use) * [Treeshake library](#treeshake-the-library-only-use-the-icons-you-use)
* [Custom binding](#custom-element-binding) * [Custom binding](#custom-element-binding)
* [React](#with-react) * [React](#with-react)
* [Vue](#with-vue)
* [Figma](#figma) * [Figma](#figma)
* [Contributing](#contributing) * [Contributing](#contributing)
* [Community](#community) * [Community](#community)
@@ -163,6 +164,20 @@ npm install lucide-react
For more details, see the [documentation](https://github.com/lucide-icons/lucide/blob/master/packages/lucide-react/README.md). For more details, see the [documentation](https://github.com/lucide-icons/lucide/blob/master/packages/lucide-react/README.md).
### With Vue
You can also use the Lucide library using the Vue package.
```sh
yarn add lucide-vue
# or
npm install lucide-vue
```
For more details, see the [documentation](https://github.com/lucide-icons/lucide/blob/master/packages/lucide-vue/README.md).
### Figma ### Figma
You can use the components from [this Figma file](https://www.figma.com/file/g0UipfQlRfGrntKPxZknM7/Featherity). You can use the components from [this Figma file](https://www.figma.com/file/g0UipfQlRfGrntKPxZknM7/Featherity).
@@ -180,3 +195,9 @@ Join the community on our [Discord](https://discord.gg/EH6nSts) server!
## License ## License
Lucide is licensed under the [ISC License](https://github.com/lucide-icons/lucide/blob/master/LICENSE). Lucide is licensed under the [ISC License](https://github.com/lucide-icons/lucide/blob/master/LICENSE).
<p align="center">
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
<img src="./site/public/vercel.svg" alt="Powered by Vercel" width="200" />
</a>
</p>

18
icons/alarm-check.svg Normal file
View File

@@ -0,0 +1,18 @@
<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 21a8 8 0 100-16 8 8 0 000 16z" />
<path d="M5 3L2 6" />
<path d="M22 6l-3-3" />
<path d="M6 19l-2 2" />
<path d="M18 19l2 2" />
<path d="M9 13l2 2 4-4" />
</svg>

After

Width:  |  Height:  |  Size: 388 B

18
icons/alarm-minus.svg Normal file
View File

@@ -0,0 +1,18 @@
<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 21a8 8 0 100-16 8 8 0 000 16z" />
<path d="M5 3L2 6" />
<path d="M22 6l-3-3" />
<path d="M6 19l-2 2" />
<path d="M18 19l2 2" />
<path d="M8 13h8" />
</svg>

After

Width:  |  Height:  |  Size: 382 B

19
icons/alarm-plus.svg Normal file
View File

@@ -0,0 +1,19 @@
<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 21a8 8 0 100-16 8 8 0 000 16z" />
<path d="M5 3L2 6" />
<path d="M22 6l-3-3" />
<path d="M6 19l-2 2" />
<path d="M18 19l2 2" />
<path d="M12 9v8" />
<path d="M8 13h8" />
</svg>

After

Width:  |  Height:  |  Size: 405 B

View File

@@ -9,5 +9,5 @@
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
> >
<path d="m9 3h6v11h4l-7 7-7-7h4z" /> <path d="M9 3h6v11h4l-7 7-7-7h4z" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 247 B

After

Width:  |  Height:  |  Size: 247 B

View File

@@ -9,5 +9,5 @@
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
> >
<path d="m3 12 7-7v4h11v6h-11v4z" /> <path d="M3 12l7-7v4h11v6H10v4z" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 247 B

After

Width:  |  Height:  |  Size: 246 B

View File

@@ -9,5 +9,5 @@
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
> >
<path d="m21 12-7-7v4h-11v6h11v4z" /> <path d="M21 12l-7-7v4H3v6h11v4z" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 248 B

After

Width:  |  Height:  |  Size: 247 B

View File

@@ -9,5 +9,5 @@
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
> >
<path d="m9 21v-11h-4l7-7 7 7h-4v11z" /> <path d="M9 21V10H5l7-7 7 7h-4v11z" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 251 B

After

Width:  |  Height:  |  Size: 249 B

15
icons/asterisk.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="M12 6v12" />
<path d="M17.196 9L6.804 15" />
<path d="M6.804 9l10.392 6" />
</svg>

After

Width:  |  Height:  |  Size: 299 B

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

@@ -0,0 +1,15 @@
<svg
width="24"
height="24"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M13.73 21a2 2 0 01-3.46 0" />
<path d="M21 5h-6" />
<path d="M18.021 9C18.29 15.193 21 17 21 17H3s3-2 3-9a6 6 0 017-5.916" />
</svg>

After

Width:  |  Height:  |  Size: 349 B

View File

@@ -10,8 +10,8 @@
stroke-linejoin="round" stroke-linejoin="round"
> >
<path d="M13.73 21a2 2 0 01-3.46 0" /> <path d="M13.73 21a2 2 0 01-3.46 0" />
<path d="M18.63 13A17.89 17.89 0 0118 8" /> <path d="M18.63 13A17.888 17.888 0 0118 8" />
<path d="M6.26 6.26A5.86 5.86 0 006 8c0 7-3 9-3 9h14" /> <path d="M6.26 6.26A5.86 5.86 0 006 8c0 7-3 9-3 9h14" />
<path d="M18 8a6 6 0 00-9.33-5" /> <path d="M18 8a6 6 0 00-9.33-5" />
<line x1="1" y1="1" x2="23" y2="23" /> <path d="M2 2l20 20" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 432 B

After

Width:  |  Height:  |  Size: 419 B

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

@@ -0,0 +1,16 @@
<svg
width="24"
height="24"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M18.387 12C19.198 15.799 21 17 21 17H3s3-2 3-9a6 6 0 017-5.916" />
<path d="M13.73 21a2 2 0 01-3.46 0" />
<path d="M18 2v6" />
<path d="M21 5h-6" />
</svg>

After

Width:  |  Height:  |  Size: 374 B

16
icons/binary.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="M10 4H6v6h4V4z" />
<path d="M18 14h-4v6h4v-6z" />
<path d="M14 4h2v6m-2 0h4" />
<path d="M6 14h2v6m-2 0h4" />
</svg>

After

Width:  |  Height:  |  Size: 335 B

18
icons/git-branch-plus.svg Normal file
View File

@@ -0,0 +1,18 @@
<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="M6 3v12" />
<path d="M18 9a3 3 0 100-6 3 3 0 000 6z" />
<path d="M6 21a3 3 0 100-6 3 3 0 000 6z" />
<path d="M15 6a9 9 0 00-9 9" />
<path d="M18 15v6" />
<path d="M21 18h-6" />
</svg>

After

Width:  |  Height:  |  Size: 406 B

16
icons/globe-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="M15 21v-4a2 2 0 012-2h4" />
<path d="M7 4v2a3 3 0 003 2h0a2 2 0 012 2 2 2 0 004 0 2 2 0 012-2h3" />
<path d="M3 11h2a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v4" />
<circle cx="12" cy="12" r="10" />
</svg>

After

Width:  |  Height:  |  Size: 420 B

15
icons/history.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="M3 3v5h5" />
<path d="M3.05 13A9 9 0 106 5.3L3 8" />
<path d="M12 7v5l4 2" />
</svg>

After

Width:  |  Height:  |  Size: 301 B

16
icons/image-minus.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="M21 11v8a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h8" />
<path d="M8.5 10a1.5 1.5 0 100-3 1.5 1.5 0 000 3z" />
<path d="M21 15l-5-5L5 21" />
<path d="M22 5h-6" />
</svg>

After

Width:  |  Height:  |  Size: 388 B

17
icons/image-plus.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="M21 11v8a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h8" />
<path d="M8.5 10a1.5 1.5 0 100-3 1.5 1.5 0 000 3z" />
<path d="M21 15l-5-5L5 21" />
<path d="M19 2v6" />
<path d="M22 5h-6" />
</svg>

After

Width:  |  Height:  |  Size: 411 B

View File

@@ -9,5 +9,5 @@
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
> >
<path d="M18.1783 8C23.2739 8 23.2739 16 18.1783 16C13.0828 16 11.0446 8 5.43949 8C0.853503 8 0.853504 16 5.43949 16C11.0446 16 13.0828 8 18.1783 8Z" /> <path d="M18.178 8c5.096 0 5.096 8 0 8-5.095 0-7.133-8-12.739-8-4.585 0-4.585 8 0 8 5.606 0 7.644-8 12.74-8z" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 363 B

After

Width:  |  Height:  |  Size: 323 B

15
icons/inspect.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="M19 11V4a2 2 0 00-2-2H4a2 2 0 00-2 2v13a2 2 0 002 2h7" />
<path d="M12 12l4.166 10 1.48-4.355L22 16.166 12 12z" />
<path d="M18 18l3 3" />
</svg>

After

Width:  |  Height:  |  Size: 362 B

View File

@@ -9,10 +9,10 @@
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
> >
<line x1="5" y1="8" x2="11" y2="14" /> <path d="M5 8l6 6" />
<path d="M4 14l6-6 2-3" /> <path d="M4 14l6-6 2-3" />
<line x1="2" y1="5" x2="14" y2="5" /> <path d="M2 5h12" />
<line x1="7" y1="2" x2="8" y2="2" /> <path d="M7 2h1" />
<path d="M22 21l-5-10-5 10" /> <path d="M22 22l-5-10-5 10" />
<line x1="14" y1="17" x2="20" y2="17" /> <path d="M14 18h6" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 433 B

After

Width:  |  Height:  |  Size: 363 B

14
icons/option.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="M3 3h6l6 18h6" />
<path d="M14 3h7" />
</svg>

After

Width:  |  Height:  |  Size: 260 B

14
icons/pencil.svg Normal file
View File

@@ -0,0 +1,14 @@
<svg
width="24"
height="24"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="18" y1="2" x2="22" y2="6" />
<path d="M7.5 20.5L19 9l-4-4L3.5 16.5 2 22l5.5-1.5z" />
</svg>

After

Width:  |  Height:  |  Size: 307 B

16
icons/podcast.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"
>
<circle cx="12" cy="11" r="1" />
<path d="M17.03 18.46a9 9 0 10-10.02.03" />
<path d="M16.06 13.91a5 5 0 10-7.97.2" />
<path d="M11.11 17a.9.9 0 111.78 0l-.52 4.67a.37.37 0 01-.74 0l-.52-4.68z" />
</svg>

After

Width:  |  Height:  |  Size: 413 B

16
icons/radio-receiver.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="M5 16v2" />
<path d="M19 16v2" />
<rect x="2" y="8" width="20" height="8" rx="2" />
<path d="M18 12h0" />
</svg>

After

Width:  |  Height:  |  Size: 331 B

16
icons/regex.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="M17 3v10" />
<path d="M12.67 5.5l8.66 5" />
<path d="M12.67 10.5l8.66-5" />
<path d="M9 17a2 2 0 00-2-2H5a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2v-2z" />
</svg>

After

Width:  |  Height:  |  Size: 380 B

17
icons/repeat-1.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="M17 2l4 4-4 4" />
<path d="M3 11v-1a4 4 0 014-4h14" />
<path d="M7 22l-4-4 4-4" />
<path d="M21 13v1a4 4 0 01-4 4H3" />
<path d="M11 10h1v4" />
</svg>

After

Width:  |  Height:  |  Size: 371 B

View File

@@ -9,9 +9,9 @@
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
> >
<path d="M6 10H4C2.89543 10 2 9.10457 2 8V4C2 2.89543 2.89543 2 4 2H20C21.1046 2 22 2.89543 22 4V8C22 9.10457 21.1046 10 20 10H18" /> <path d="M6 10H4a2 2 0 01-2-2V4a2 2 0 012-2h16a2 2 0 012 2v4a2 2 0 01-2 2h-2" />
<path d="M6 14H4C2.89543 14 2 14.8954 2 16V20C2 21.1046 2.89543 22 4 22H20C21.1046 22 22 21.1046 22 20V16C22 14.8954 21.1046 14 20 14H18" /> <path d="M6 14H4a2 2 0 00-2 2v4a2 2 0 002 2h16a2 2 0 002-2v-4a2 2 0 00-2-2h-2" />
<path d="M6 6H6.01" /> <path d="M6 6h.01" />
<path d="M6 18H6.01" /> <path d="M6 18h.01" />
<path d="M13 6L9 12H15L11 18" /> <path d="M13 6l-4 6h6l-4 6" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 573 B

After

Width:  |  Height:  |  Size: 457 B

15
icons/shield-alert.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="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
<path d="M12 8v4" />
<path d="M12 16h.01" />
</svg>

After

Width:  |  Height:  |  Size: 316 B

14
icons/shield-check.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="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
<path d="M9 12l2 2 4-4" />
</svg>

After

Width:  |  Height:  |  Size: 296 B

15
icons/shield-close.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="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
<line x1="9.5" y1="9" x2="14.5" y2="14" />
<line x1="14.5" y1="9" x2="9.5" y2="14" />
</svg>

After

Width:  |  Height:  |  Size: 357 B

13
icons/sigma.svg Normal file
View File

@@ -0,0 +1,13 @@
<svg
width="24"
height="24"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M18 7V4H6l6 8-6 8h12v-3" />
</svg>

After

Width:  |  Height:  |  Size: 247 B

View File

@@ -1,8 +1,8 @@
<svg <svg
viewBox="0 0 24 24"
width="24" width="24"
height="24" height="24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none" fill="none"
stroke="currentColor" stroke="currentColor"
stroke-width="2" stroke-width="2"
@@ -10,7 +10,7 @@
stroke-linejoin="round" stroke-linejoin="round"
> >
<path d="M7 20h10" /> <path d="M7 20h10" />
<path d="M10 20c5.5-2.5.8-6.4 3-10"/> <path d="M10 20c5.5-2.5.8-6.4 3-10" />
<path d="M9.5 9.4c1.1.8 1.8 2.2 2.3 3.7-2 .4-3.5.4-4.8-.3-1.2-.6-2.3-1.9-3-4.2 2.8-.5 4.4 0 5.5.8z"/> <path d="M9.5 9.4c1.1.8 1.8 2.2 2.3 3.7-2 .4-3.5.4-4.8-.3-1.2-.6-2.3-1.9-3-4.2 2.8-.5 4.4 0 5.5.8z" />
<path d="M14.1 6a7 7 0 00-1.1 4c1.9-.1 3.3-.6 4.3-1.4 1-1 1.6-2.3 1.7-4.6-2.7.1-4 1-4.9 2z"/> <path d="M14.1 6a7 7 0 00-1.1 4c1.9-.1 3.3-.6 4.3-1.4 1-1 1.6-2.3 1.7-4.6-2.7.1-4 1-4.9 2z" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 472 B

After

Width:  |  Height:  |  Size: 475 B

14
icons/tv-2.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="M7 21h10" />
<rect x="2" y="3" width="20" height="14" rx="2" />
</svg>

After

Width:  |  Height:  |  Size: 285 B

15
icons/wallet.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="M20 12V8H6a2 2 0 01-2-2c0-1.1.9-2 2-2h12v4" />
<path d="M4 6v12c0 1.1.9 2 2 2h14v-4" />
<path d="M18 12a2 2 0 00-2 2c0 1.1.9 2 2 2h4v-4h-4z" />
</svg>

After

Width:  |  Height:  |  Size: 367 B

15
icons/webcam.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"
>
<circle cx="12" cy="10" r="8" />
<circle cx="12" cy="10" r="3" />
<path d="M12 22v-4m-5 4h5-5zm10 0h-5 5z" />
</svg>

After

Width:  |  Height:  |  Size: 324 B

View File

@@ -1,12 +1,13 @@
{ {
"name": "lucide", "name": "lucide",
"description": "Lucide is a community-run fork of Feather Icons, open for anyone to contribute icons.", "description": "Lucide is a community-run fork of Feather Icons, open for anyone to contribute icons.",
"version": "0.12.0", "version": "0.14.0",
"license": "ISC", "license": "ISC",
"homepage": "https://lucide.netlify.app", "homepage": "https://lucide.dev",
"repository": "github:lucide-icons/lucide", "bugs": "https://github.com/lucide-icons/lucide/issues",
"bugs": { "repository": {
"url": "https://github.com/lucide-icons/lucide/issues" "type": "git",
"url": "https://github.com/lucide-icons/lucide.git"
}, },
"amdName": "lucide", "amdName": "lucide",
"source": "build/lucide.js", "source": "build/lucide.js",
@@ -24,6 +25,7 @@
"build:es": "babel build -d dist/esm", "build:es": "babel build -d dist/esm",
"build:bundles": "rollup -c rollup.config.js", "build:bundles": "rollup -c rollup.config.js",
"optimize": "npx babel-node ./scripts/optimizeSvgs.js --presets @babel/env", "optimize": "npx babel-node ./scripts/optimizeSvgs.js --presets @babel/env",
"addtags": "npx babel-node ./scripts/addMissingKeysToTags.js --presets @babel/env",
"test": "jest" "test": "jest"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -0,0 +1,25 @@
# Lucide Figma plugin
A Figma plugin for using Lucide Icons
## Local development
1. Install the dependencies
```sh
yarn
```
2. Build the plugin
```sh
yarn watch
```
3. Open the [Figma desktop app](https://www.figma.com/downloads/)
4. Go to `Menu > Plugins > Development > New Plugin...`
5. Choose `lucide/packages/lucide-figma/manifest.json`
6. Run the plugin by going to `Menu > Plugins > Development > Lucide Icons`

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

675
packages/lucide-figma/figma.d.ts vendored Normal file
View File

@@ -0,0 +1,675 @@
// Global variable with Figma's plugin API.
declare const figma: PluginAPI
declare const __html__: string
interface PluginAPI {
readonly currentPage: PageNode
// Root of the current Figma document.
readonly root: DocumentNode
// API for accessing viewport information.
readonly viewport: ViewportAPI
// call this once your plugin is finished executing.
closePlugin(): void
// Command that the user chose through menu when launching the plugin.
readonly command: string
// Finds a node by its id. If not found, returns null.
getNodeById(id: string): BaseNode | null
// Finds a style by its id. If not found, returns null.
getStyleById(id: string): BaseStyle | null
// Access browser APIs and/or show UI to the user.
showUI(html: string, options?: ShowUIOptions): void
readonly ui: UIAPI
// Lets you store persistent data on the user's local machine
readonly clientStorage: ClientStorageAPI
// This value is returned when a property is in a "mixed" state.
// In order to check if a property is in a mixed state, always
// compare directly to this value. I.e.
// `if (node.cornerRadius === figma.mixed) { ... }`
mixed: symbol
// Creates new nodes. Nodes will start off inserted
// into the current page.
// To move them elsewhere use `appendChild` or `insertChild`
createRectangle(): RectangleNode
createLine(): LineNode
createEllipse(): EllipseNode
createPolygon(): PolygonNode
createStar(): StarNode
createVector(): VectorNode
createText(): TextNode
createBooleanOperation(): BooleanOperationNode
createFrame(): FrameNode
createComponent(): ComponentNode
createPage(): PageNode
createSlice(): SliceNode
// Creates styles. A style's id can be assigned to
// node properties like textStyleId, fillStyleId, etc.
createPaintStyle(): PaintStyle
createTextStyle(): TextStyle
createEffectStyle(): EffectStyle
createGridStyle(): GridStyle
// These let you insert stuff from the team library if you have the key
importComponentByKeyAsync(key: string): Promise<ComponentNode>
importStyleByKeyAsync(key: string): Promise<BaseStyle>
// Return all fonts currently supported for use with the "fontName" property
listAvailableFontsAsync(): Promise<Font[]>
// You must await the promise returned here before being able to use "fontName"
loadFontAsync(fontName: FontName): Promise<void>
// Creates node from an SVG string.
createNodeFromSvg(svg: string): FrameNode
// Creates an Image object using the provided file contents.
createImage(data: Uint8Array): Image
// Groups every node in `nodes` under a new group.
group(nodes: ReadonlyArray<BaseNode>, parent: BaseNode & ChildrenMixin, index?: number): FrameNode
// Flattens every node in `nodes` into a single vector network.
flatten(nodes: ReadonlyArray<BaseNode>, parent?: BaseNode & ChildrenMixin, index?: number): VectorNode
}
interface ClientStorageAPI {
// This stores information in the browser, not on the server. It's similar to localStorage, but is
// asynchronous, and allows storing objects, arrays, strings, numbers, booleans, null, undefined and Uint8Arrays.
getAsync(key: string): Promise<any | undefined>
setAsync(key: string, value: any): Promise<void>
}
type ShowUIOptions = {
visible?: boolean, // defaults to true
width?: number, // defaults to 300
height?: number, // defaults to 200
}
interface UIAPI {
show(): void
hide(): void
resize(width: number, height: number): void
close(): void
// Sends a message to the iframe.
postMessage(pluginMessage: any): void
// Registers a callback for messages sent by the iframe.
onmessage: ((pluginMessage: any) => void) | undefined
}
interface ViewportAPI {
center: { x: number, y: number }
// 1.0 means 100% zoom, 0.5 means 50% zoom.
zoom: number
// Adjust the viewport such that it shows the provided nodes.
scrollAndZoomIntoView(nodes: ReadonlyArray<BaseNode>)
}
// manifest.json format
interface ManifestJson {
// Name of the plugin.
name: string
// Version of the runtime that the plugin uses, e.g. '0.5.0'.
version: string
// The file name that contains the plugin code.
script: string
// The file name that contains the html code made available in script.
html?: string
// Shell command to be executed before the contents of the `html` and `script` files are read.
build?: string
// Menu items to show up in UI.
menu?: ManifestMenuItem[]
}
type ManifestMenuItem =
// Clickable menu item.
{ name: string, command: string } |
// Separator
{ separator: true } |
// Submenu
{ name: string, menu: ManifestMenuItem[] }
////////////////////////////////////////////////////////////////////////////////
// Values
// These are the top two rows of a 3x3 matrix. This is enough to represent
// translation, rotation, and skew.
type Transform = [
[number, number, number],
[number, number, number]
]
interface Vector {
readonly x: number
readonly y: number
}
interface RGB {
readonly r: number
readonly g: number
readonly b: number
}
interface RGBA {
readonly r: number
readonly g: number
readonly b: number
readonly a: number
}
interface FontName {
readonly family: string
readonly style: string
}
interface ArcData {
readonly startingAngle: number
readonly endingAngle: number
readonly innerRadius: number
}
interface ShadowEffect {
readonly type: "DROP_SHADOW" | "INNER_SHADOW"
readonly color: RGBA
readonly offset: Vector
readonly radius: number
readonly visible: boolean
readonly blendMode: BlendMode
}
interface BlurEffect {
readonly type: "LAYER_BLUR" | "BACKGROUND_BLUR"
readonly radius: number
readonly visible: boolean
}
type Effect = ShadowEffect | BlurEffect
type ConstraintType = "MIN" | "CENTER" | "MAX" | "STRETCH" | "SCALE"
interface Constraints {
readonly horizontal: ConstraintType
readonly vertical: ConstraintType
}
interface ColorStop {
readonly position: number
readonly color: RGBA
}
interface SolidPaint {
readonly type: "SOLID"
readonly color: RGB
readonly visible?: boolean
readonly opacity?: number
}
interface GradientPaint {
readonly type: "GRADIENT_LINEAR" | "GRADIENT_RADIAL" | "GRADIENT_ANGULAR" | "GRADIENT_DIAMOND"
readonly gradientTransform: Transform
readonly gradientStops: ReadonlyArray<ColorStop>
readonly visible?: boolean
readonly opacity?: number
}
interface ImagePaint {
readonly type: "IMAGE"
readonly scaleMode: "FILL" | "FIT" | "CROP" | "TILE"
readonly image: Image | null
readonly imageTransform?: Transform // setting for "CROP"
readonly scalingFactor?: number // setting for "TILE"
readonly visible?: boolean
readonly opacity?: number
}
type Paint = SolidPaint | GradientPaint | ImagePaint
interface Guide {
readonly axis: "X" | "Y"
readonly offset: number
}
interface RowsColsLayoutGrid {
readonly pattern: "ROWS" | "COLUMNS"
readonly alignment: "MIN" | "STRETCH" | "CENTER"
readonly gutterSize: number
readonly count: number // Infinity when "Auto" is set in the UI
readonly sectionSize?: number // Not set for alignment: "STRETCH"
readonly offset?: number // Not set for alignment: "CENTER"
readonly visible?: boolean
readonly color?: RGBA
}
interface GridLayoutGrid {
readonly pattern: "GRID"
readonly sectionSize: number
readonly visible?: boolean
readonly color?: RGBA
}
type LayoutGrid = RowsColsLayoutGrid | GridLayoutGrid
interface ExportSettingsImage {
format: "JPG" | "PNG"
contentsOnly?: boolean // defaults to true
suffix?: string
constraint?: { // defaults to unscaled ({ type: "SCALE", value: 1 })
type: "SCALE" | "WIDTH" | "HEIGHT"
value: number
}
}
interface ExportSettingsSVG {
format: "SVG"
contentsOnly?: boolean // defaults to true
suffix?: string
svgOutlineText?: boolean // defaults to true
svgIdAttribute?: boolean // defaults to false
svgSimplifyStroke?: boolean // defaults to true
}
interface ExportSettingsPDF {
format: "PDF"
contentsOnly?: boolean // defaults to true
suffix?: string
}
type ExportSettings = ExportSettingsImage | ExportSettingsSVG | ExportSettingsPDF
type WindingRule = "NONZERO" | "EVENODD"
interface VectorVertex {
readonly x: number
readonly y: number
readonly strokeCap?: StrokeCap
readonly strokeJoin?: StrokeJoin
readonly cornerRadius?: number
readonly handleMirroring?: HandleMirroring
}
interface VectorSegment {
readonly start: number
readonly end: number
readonly tangentStart?: Vector // Defaults to { x: 0, y: 0 }
readonly tangentEnd?: Vector // Defaults to { x: 0, y: 0 }
}
interface VectorRegion {
readonly windingRule: WindingRule
readonly loops: ReadonlyArray<ReadonlyArray<number>>
}
interface VectorNetwork {
readonly vertices: ReadonlyArray<VectorVertex>
readonly segments: ReadonlyArray<VectorSegment>
readonly regions?: ReadonlyArray<VectorRegion> // Defaults to []
}
interface VectorPath {
// Similar to the svg fill-rule
// "NONE" means an open path won't have a fill
readonly windingRule: WindingRule | "NONE"
readonly data: string
}
type VectorPaths = ReadonlyArray<VectorPath>
interface NumberWithUnits {
readonly value: number
readonly units: "PIXELS" | "PERCENT"
}
type BlendMode =
"PASS_THROUGH" |
"NORMAL" |
"DARKEN" |
"MULTIPLY" |
"LINEAR_BURN" |
"COLOR_BURN" |
"LIGHTEN" |
"SCREEN" |
"LINEAR_DODGE" |
"COLOR_DODGE" |
"OVERLAY" |
"SOFT_LIGHT" |
"HARD_LIGHT" |
"DIFFERENCE" |
"EXCLUSION" |
"HUE" |
"SATURATION" |
"COLOR" |
"LUMINOSITY"
interface Font {
fontName: FontName
}
////////////////////////////////////////////////////////////////////////////////
// Mixins
interface BaseNodeMixin {
readonly id: string
readonly parent: (BaseNode & ChildrenMixin) | null
name: string
visible: boolean
locked: boolean
removed: boolean
toString(): string
remove(): void
// Attach custom data to a node. Only your plugin will be able to read this.
getPluginData(key: string): string
setPluginData(key: string, value: string): void
// Attach custom data to a node. All plugins will be able to read this.
// Namespace is a string that must be at least 3 alphanumeric characters, and should
// be a name related to your plugin. This is a mandatory argument to avoid multiple
// multiple plugins adding keys like "data" and colliding with each other. Other
// plugins will still be able to read shared plugin data as long as they know the
// namespace you use.
getSharedPluginData(namespace: string, key: string): string
setSharedPluginData(namespace: string, key: string, value: string): void
}
interface ChildrenMixin {
// Sorted back-to-front. I.e. the top-most child is last in this array.
readonly children: ReadonlyArray<BaseNode>
// Adds to the end of the .children array. I.e. visually on top of all other
// children.
appendChild(child: BaseNode): void
insertChild(index: number, child: BaseNode): void
findAll(callback?: (node: BaseNode) => boolean): ReadonlyArray<BaseNode>
findOne(callback: (node: BaseNode) => boolean): BaseNode | null
}
interface LayoutMixin {
readonly absoluteTransform: Transform
relativeTransform: Transform
x: number // The same as "relativeTransform[0][2]"
y: number // The same as "relativeTransform[1][2]"
rotation: number // The angle of the x axis of "relativeTransform" in degrees. Returns values from -180 to 180.
readonly size: Vector
readonly width: number // The same as "size.x"
readonly height: number // The same as "size.y"
// Resizes the node. If children of the node has constraints, it applies those constraints
// width and height must be >= 0.01
resize(width: number, height: number): void
// Resizes the node. Children of the node are never resized, even if those children have
// constraints. width and height must be >= 0.01
resizeWithoutConstraints(width: number, height: number): void
constraints: Constraints
}
interface BlendMixin {
opacity: number
blendMode: BlendMode
isMask: boolean
effects: ReadonlyArray<Effect>
effectStyleId: string
}
interface FrameMixin {
backgrounds: ReadonlyArray<Paint>
layoutGrids: ReadonlyArray<LayoutGrid>
clipsContent: boolean
guides: ReadonlyArray<Guide>
gridStyleId: string
backgroundStyleId: string
}
type StrokeCap = "NONE" | "ROUND" | "SQUARE" | "ARROW_LINES" | "ARROW_EQUILATERAL"
type StrokeJoin = "MITER" | "BEVEL" | "ROUND"
type HandleMirroring = "NONE" | "ANGLE" | "ANGLE_AND_LENGTH"
interface GeometryMixin {
fills: ReadonlyArray<Paint> | symbol // This can return figma.mixed on TEXT nodes
strokes: ReadonlyArray<Paint>
strokeWeight: number
strokeAlign: "CENTER" | "INSIDE" | "OUTSIDE"
strokeCap: StrokeCap | symbol // This can return figma.mixed on VECTOR nodes if vertices have different strokeCap values
strokeJoin: StrokeJoin | symbol // This can return figma.mixed on VECTOR nodes if vertices have different strokeJoin values
dashPattern: ReadonlyArray<number>
fillStyleId: string | symbol // This can return figma.mixed on TEXT nodes
strokeStyleId: string
}
interface CornerMixin {
// This can return figma.mixed on VECTOR nodes if vertices have different cornerRadius values,
// and on RECTANGLE nodes if node.topLeftRadius etc has different values
cornerRadius: number | symbol
cornerSmoothing: number
}
interface ExportMixin {
exportSettings: ExportSettings[]
exportAsync(settings?: ExportSettings): Promise<Uint8Array> // Defaults to PNG format
}
////////////////////////////////////////////////////////////////////////////////
// Nodes
interface DocumentNode extends BaseNodeMixin, ChildrenMixin {
readonly type: "DOCUMENT"
clone(): DocumentNode // Note: this always throws an error
}
interface PageNode extends BaseNodeMixin, ChildrenMixin, ExportMixin {
readonly type: "PAGE"
clone(): PageNode // cloned node starts off inserted into current page
guides: ReadonlyArray<Guide>
selection: ReadonlyArray<BaseNode>
}
interface FrameNode extends BaseNodeMixin, BlendMixin, ChildrenMixin, FrameMixin, LayoutMixin, ExportMixin {
readonly type: "FRAME" | "GROUP"
clone(): FrameNode // cloned node starts off inserted into current page
}
interface SliceNode extends BaseNodeMixin, LayoutMixin, ExportMixin {
readonly type: "SLICE"
clone(): SliceNode // cloned node starts off inserted into current page
}
interface RectangleNode extends BaseNodeMixin, BlendMixin, CornerMixin, GeometryMixin, LayoutMixin, ExportMixin {
readonly type: "RECTANGLE"
clone(): RectangleNode // cloned node starts off inserted into current page
topLeftRadius: number
topRightRadius: number
bottomLeftRadius: number
bottomRightRadius: number
}
interface LineNode extends BaseNodeMixin, BlendMixin, GeometryMixin, LayoutMixin, ExportMixin {
readonly type: "LINE"
clone(): LineNode // cloned node starts off inserted into current page
}
interface EllipseNode extends BaseNodeMixin, BlendMixin, CornerMixin, GeometryMixin, LayoutMixin, ExportMixin {
readonly type: "ELLIPSE"
clone(): EllipseNode // cloned node starts off inserted into current page
arcData: ArcData
}
interface PolygonNode extends BaseNodeMixin, BlendMixin, CornerMixin, GeometryMixin, LayoutMixin, ExportMixin {
readonly type: "POLYGON"
clone(): PolygonNode // cloned node starts off inserted into current page
pointCount: number
}
interface StarNode extends BaseNodeMixin, BlendMixin, CornerMixin, GeometryMixin, LayoutMixin, ExportMixin {
readonly type: "STAR"
clone(): StarNode // cloned node starts off inserted into current page
pointCount: number
// This is a percentage value from 0 to 1
innerRadius: number
}
interface VectorNode extends BaseNodeMixin, BlendMixin, CornerMixin, GeometryMixin, LayoutMixin, ExportMixin {
readonly type: "VECTOR"
clone(): VectorNode // cloned node starts off inserted into current page
vectorNetwork: VectorNetwork
vectorPaths: VectorPaths
handleMirroring: HandleMirroring | symbol // This can return figma.mixed if vertices have different handleMirroring values
}
interface TextNode extends BaseNodeMixin, BlendMixin, GeometryMixin, LayoutMixin, ExportMixin {
readonly type: "TEXT"
clone(): TextNode // cloned node starts off inserted into current page
characters: string
textAlignHorizontal: "LEFT" | "CENTER" | "RIGHT" | "JUSTIFIED"
textAlignVertical: "TOP" | "CENTER" | "BOTTOM"
textAutoResize: "NONE" | "WIDTH_AND_HEIGHT" | "HEIGHT"
paragraphIndent: number
paragraphSpacing: number
autoRename: boolean
// These properties can all return figma.mixed if the text has multiple values for the property
textStyleId: string | symbol
fontSize: number | symbol
fontName: FontName | symbol
textCase: "ORIGINAL" | "UPPER" | "LOWER" | "TITLE" | symbol
textDecoration: "NONE" | "UNDERLINE" | "STRIKETHROUGH" | symbol
letterSpacing: NumberWithUnits | symbol
lineHeight: NumberWithUnits | symbol
}
interface ComponentNode extends BaseNodeMixin, BlendMixin, ChildrenMixin, FrameMixin, LayoutMixin, ExportMixin {
readonly type: "COMPONENT"
clone(): ComponentNode // cloned node starts off inserted into current page
createInstance(): InstanceNode // instance starts off inserted into current page
description: string
readonly remote: boolean
readonly key: string // The key to use with "importComponentByKeyAsync"
}
interface InstanceNode extends BaseNodeMixin, BlendMixin, ChildrenMixin, FrameMixin, LayoutMixin, ExportMixin {
readonly type: "INSTANCE"
clone(): InstanceNode // cloned node starts off inserted into current page
masterComponent: ComponentNode
}
interface BooleanOperationNode extends BaseNodeMixin, BlendMixin, ChildrenMixin, CornerMixin, GeometryMixin, LayoutMixin, ExportMixin {
readonly type: "BOOLEAN_OPERATION"
clone(): BooleanOperationNode // cloned node starts off inserted into current page
booleanOperation: "UNION" | "INTERSECT" | "SUBTRACT" | "EXCLUDE"
}
type BaseNode =
DocumentNode |
PageNode |
SliceNode |
FrameNode |
ComponentNode |
InstanceNode |
BooleanOperationNode |
VectorNode |
StarNode |
LineNode |
EllipseNode |
PolygonNode |
RectangleNode |
TextNode
type NodeType =
"DOCUMENT" |
"PAGE" |
"SLICE" |
"FRAME" |
"GROUP" |
"COMPONENT" |
"INSTANCE" |
"BOOLEAN_OPERATION" |
"VECTOR" |
"STAR" |
"LINE" |
"ELLIPSE" |
"POLYGON" |
"RECTANGLE" |
"TEXT"
////////////////////////////////////////////////////////////////////////////////
// Styles
type StyleType = "PAINT" | "TEXT" | "EFFECT" | "GRID"
interface BaseStyle {
// The string to uniquely identify a style by
readonly id: string
readonly type: StyleType
name: string // Note: setting this also sets "autoRename" to false on TextNodes
description: string
remote: boolean
readonly key: string // The key to use with "importStyleByKeyAsync"
remove(): void
}
interface PaintStyle extends BaseStyle {
type: "PAINT"
paints: ReadonlyArray<Paint>
}
interface TextStyle extends BaseStyle {
type: "TEXT"
fontSize: number
textDecoration: "NONE" | "UNDERLINE" | "STRIKETHROUGH"
fontName: FontName
letterSpacing: NumberWithUnits
lineHeight: NumberWithUnits
paragraphIndent: number
paragraphSpacing: number
textCase: "ORIGINAL" | "UPPER" | "LOWER" | "TITLE"
}
interface EffectStyle extends BaseStyle {
type: "EFFECT"
effects: ReadonlyArray<Paint>
}
interface GridStyle extends BaseStyle {
type: "GRID"
layoutGrids: ReadonlyArray<LayoutGrid>
}
////////////////////////////////////////////////////////////////////////////////
// Other
interface Image {
// Returns a unique hash for the image
readonly hash: string
// The contents of the image file
getBytesAsync(): Promise<Uint8Array>
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -0,0 +1,7 @@
{
"name": "Lucide Icons",
"id": "939567362549682242",
"api": "1.0.0",
"ui": "build/ui.html",
"main": "build/main.js"
}

View File

@@ -0,0 +1,26 @@
{
"name": "lucide-figma",
"version": "0.13.0",
"license": "ISC",
"main": "build/ui.js",
"scripts": {
"build": "webpack --mode=production",
"watch": "webpack --mode=development --watch"
},
"dependencies": {
"@emotion/core": "^10.0.14",
"@types/react": "^16.8.23",
"@types/react-dom": "^16.8.4",
"css-loader": "^3.0.0",
"html-webpack-inline-source-plugin": "^0.0.10",
"html-webpack-plugin": "^3.2.0",
"lucide-react": "^0.13.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"style-loader": "^0.23.1",
"ts-loader": "^6.0.4",
"typescript": "^3.5.2",
"webpack": "^4.35.2",
"webpack-cli": "^3.3.5"
}
}

View File

@@ -0,0 +1,44 @@
import { jsx } from '@emotion/core'
import theme from '../theme'
import { renderToString } from 'react-dom/server'
import { FC } from 'react';
interface IconButtonProps {
name: string,
component: FC,
}
function IconButton({ name, component: IconComponent }: IconButtonProps) {
const onIconclick = () => {
const svg = renderToString(<IconComponent/>);
parent.postMessage({ pluginMessage: { name, svg }}, '*')
}
return (
<button
key={name}
aria-label={name}
onClick={onIconclick}
css={{
padding: theme.space[2],
color: '#333',
background: 'transparent',
border: 0,
borderRadius: theme.radii[1],
appearance: 'none',
outline: 0,
'&:hover': {
background: 'rgba(0, 0, 0, 0.06)',
},
'&:focus, &:active': {
boxShadow: `inset 0 0 0 2px ${theme.colors.blue}`,
},
}}
>
<IconComponent />
</button>
)
}
export default IconButton

View File

@@ -0,0 +1,24 @@
import { createElement, forwardRef } from 'react'
const SearchIcon = forwardRef((props: any, ref) => createElement(
'svg',
{
xmlns: "http://www.w3.org/2000/svg",
width: 32,
height: 32,
clipRule: 'evenodd',
fillRule: 'evenodd',
ref,
...props,
},
[
createElement(
'path', {
d: 'm20 15c0 2.7614-2.2386 5-5 5s-5-2.2386-5-5 2.2386-5 5-5 5 2.2386 5 5zm-1.1256 4.5815c-1.0453.8849-2.3975 1.4185-3.8744 1.4185-3.3137 0-6-2.6863-6-6s2.6863-6 6-6 6 2.6863 6 6c0 1.4769-.5336 2.8291-1.4185 3.8744l4.2721 4.272-.7072.7072z',
key: 'path'
}
)
]
))
export default SearchIcon

View File

@@ -0,0 +1,43 @@
import { jsx } from '@emotion/core'
import theme from '../theme'
import SearchIcon from './search-icon'
interface SearchInputProps extends React.HTMLProps<HTMLDivElement> {
value: string,
iconCount: number,
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void
}
function SearchInput({ value, onChange, iconCount, ...props }: SearchInputProps) {
return (
<div css={{ position: 'relative' }} {...props}>
<div
css={{
position: 'absolute',
top: 0,
left: 0,
padding: theme.space[1],
}}
>
<SearchIcon fill="#333" />
</div>
<input
autoFocus
type="search"
value={value}
onChange={onChange}
placeholder={`Search ${iconCount} icons`}
css={{
width: '100%',
height: 40,
padding: `0 ${theme.space[4]} 0 36px`,
fontFamily: 'inherit',
fontSize: theme.fontSizes[0],
border: 0,
outline: 0,
}}
/>
</div>
)
}
export default SearchInput

View File

@@ -0,0 +1,21 @@
/**
* Converts string to camelcase
*
* @param {string} string
*/
export const toCamelCase = (string: string) =>
string.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) =>
p2 ? p2.toUpperCase() : p1.toLowerCase(),
);
/**
* Converts string to PascalCase
*
* @param {string} string
*/
export const toPascalCase = (string: string) => {
const camelCase = toCamelCase(string);
return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
};

View File

@@ -0,0 +1,9 @@
figma.showUI(__html__, { width: 300, height: 400 })
figma.ui.onmessage = ({name, svg}) => {
const icon = figma.createNodeFromSvg(svg)
icon.name = name
icon.x = figma.viewport.center.x
icon.y = figma.viewport.center.y
figma.currentPage.selection = [icon]
}

View File

@@ -0,0 +1,8 @@
export default {
space: [0, 4, 8, 12, 16],
fontSizes: [12, 14, 16],
colors: {
blue: '#18a0fb',
},
radii: [0, 2],
}

View File

@@ -0,0 +1,4 @@
declare module 'lucide-react';
declare module 'lucide';
declare module 'lucide/icons';
declare module 'lucide/build/icons';

View File

@@ -0,0 +1,10 @@
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('https://rsms.me/inter/font-files/Inter-Regular.woff2?v=3.9')
format('woff2'),
url('https://rsms.me/inter/font-files/Inter-Regular.woff?v=3.9')
format('woff');
}

View File

@@ -0,0 +1 @@
<div id="root"></div>

View File

@@ -0,0 +1,82 @@
import { Global, jsx } from '@emotion/core'
import { version } from '../package.json'
import React, { useMemo } from 'react'
import ReactDOM from 'react-dom'
import IconButton from './components/icon-button'
import SearchInput from './components/search-input'
import theme from './theme'
import './ui.css'
import tags from '../../../tags.json'
import * as iconComponents from 'lucide-react'
import { toPascalCase } from './helpers/naming';
import useSearch from '../../../site/src/lib/useSearch';
declare var ICONS: [];
function App() {
const [query, setQuery] = React.useState('')
const icons = ICONS.map(name => {
const componentName = toPascalCase(name);
return {
name,
tags: tags[name] || [],
component: iconComponents[componentName] || null
}
}).filter(({component}) => !!component)
const searchResults = useMemo(() => useSearch(icons, query), [icons, query])
return (
<div>
<Global
styles={{ body: { margin: 0, fontFamily: 'Inter, sans-serif' } }}
/>
<SearchInput
value={query}
iconCount={icons.length}
onChange={event => setQuery(event.target.value)}
css={{
position: 'sticky',
top: 0,
borderBottom: '1px solid #e5e5e5',
backfaceVisibility: 'hidden'
}}
/>
<div css={{ padding: theme.space[2] }}>
<div
css={{
display: 'grid',
gridTemplateColumns: 'repeat(6, 1fr)',
gridGap: theme.space[1],
}}
>
{searchResults.map(({name, component: Icon} :any) => (
<IconButton
name={name}
key={name}
component={Icon}
/>
))}
</div>
<div
css={{
marginTop: theme.space[2],
padding: theme.space[2],
fontSize: theme.fontSizes[0],
color: 'rgba(0, 0, 0, 0.5)',
}}
>
<a
href="https://lucide.dev"
target="_blank"
css={{ color: 'inherit' }}
>
Lucide v{version}
</a>
</div>
</div>
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))

View File

@@ -0,0 +1,12 @@
{
"compilerOptions": {
"esModuleInterop": true,
"lib": ["dom", "esnext"],
"jsx": "react",
"jsxFactory": "jsx",
"resolveJsonModule": true,
"strict": true,
"allowJs": true,
},
"exclude": ["node_modules"],
}

View File

@@ -0,0 +1,48 @@
const fs = require('fs');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');
const webpack = require('webpack');
module.exports = (env, argv) => ({
// This is necessary because Figma's 'eval' works differently than normal eval
devtool: argv.mode === 'production' ? false : 'inline-source-map',
entry: {
ui: './src/ui.tsx',
main: './src/main.ts',
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'build'),
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.css$/,
loader: [{ loader: 'style-loader' }, { loader: 'css-loader' }],
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
plugins: [
new webpack.DefinePlugin({
ICONS: JSON.stringify(
fs.readdirSync(path.join(process.cwd(), '../../icons')).map(name => name.split('.')[0]),
),
}),
new HtmlWebpackPlugin({
template: './src/ui.html',
filename: 'ui.html',
inlineSource: '.(js)$',
chunks: ['ui'],
}),
new HtmlWebpackInlineSourcePlugin(),
],
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
stats
node_modules
tests
scripts
build
src
babel.config.js
jest.config.js
rollup.config.js

View File

@@ -15,7 +15,7 @@ npm install lucide-react
## How to use ## How to use
It's build with ESmodules so it's completely threeshakable. It's build with ESmodules so it's completely threeshakable.
Each icon can be imported as an react component. Each icon can be imported as a react component.
### Example ### Example
@@ -56,7 +56,7 @@ const App = () => {
It is possible to create one generic icon component to load icons. It is possible to create one generic icon component to load icons.
> :warning: Example below importing all EsModules, caution using this example, not recommended when you using bundlers. > :warning: Example below importing all EsModules, caution using this example, not recommended when you using bundlers, your application build size will grow strongly.
#### Icon Component Example #### Icon Component Example

View File

@@ -1,4 +1,3 @@
// module.exports = require('../../babel.config');
module.exports = { module.exports = {
presets: ['react-app'], presets: ['react-app'],
}; };

View File

@@ -1,37 +1,41 @@
{ {
"name": "lucide-react", "name": "lucide-react",
"description": "Lucide React package, Lucide is a community-run fork of Feather Icons, open for anyone to contribute icons.", "description": "Lucide React package, Lucide is a community-run fork of Feather Icons, open for anyone to contribute icons.",
"version": "0.12.0", "version": "0.14.1",
"license": "ISC", "license": "ISC",
"homepage": "https://lucide.dev",
"bugs": "https://github.com/lucide-icons/lucide/issues",
"repository": {
"type": "git",
"url": "https://github.com/lucide-icons/lucide.git",
"directory": "packages/lucide-react"
},
"author": "Eric Fennis",
"amdName": "lucide-react", "amdName": "lucide-react",
"source": "build/lucide-react.js",
"main": "dist/cjs/lucide-react.js", "main": "dist/cjs/lucide-react.js",
"main:umd": "dist/umd/lucide-react.js", "main:umd": "dist/umd/lucide-react.js",
"module": "dist/esm/lucide-react.js", "module": "dist/esm/lucide-react.js",
"unpkg": "dist/umd/lucide-react.min.js", "unpkg": "dist/umd/lucide-react.min.js",
"repository": "github:lucide-icons/lucide",
"author": "Eric Fennis",
"scripts": { "scripts": {
"build": "yarn clean && yarn build:move && yarn build:icons && yarn build:es && yarn build:bundles", "build": "yarn clean && yarn build:move && yarn build:icons && yarn build:es && yarn build:bundles",
"clean": "rm -rf dist && rm -rf build", "clean": "rm -rf dist && rm -rf build",
"build:move": "cp -av src build", "build:move": "cp -av src build",
"build:icons": "yarn --cwd ../../ build:icons --output=../packages/lucide-react/build --templateSrc=../packages/lucide-react/scripts/exportTemplate --camelizeAttrs --renderUniqueKey", "build:icons": "yarn --cwd ../../ build:icons --output=../packages/lucide-react/build --templateSrc=../packages/lucide-react/scripts/exportTemplate --camelizeAttrs --noDefaultAttrs --renderUniqueKey",
"build:es": "yarn --cwd ../../ babel packages/lucide-react/build -d packages/lucide-react/dist/esm", "build:es": "yarn --cwd ../../ babel packages/lucide-react/build -d packages/lucide-react/dist/esm",
"build:bundles": "yarn --cwd ../../ rollup -c packages/lucide-react/rollup.config.js", "build:bundles": "yarn --cwd ../../ rollup -c packages/lucide-react/rollup.config.js",
"test": "jest" "test": "jest"
}, },
"dependencies": { "dependencies": {
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"react": "^17.0.1" "react": "^16.5.1"
}, },
"devDependencies": { "devDependencies": {
"babel-preset-react-app": "^10.0.0", "babel-preset-react-app": "^10.0.0",
"jest": "^26.6.3", "jest": "^26.6.3",
"lucide": "file:../..", "react-test-renderer": "^16.5.1"
"react-test-renderer": "^17.0.1"
}, },
"peerDependencies": { "peerDependencies": {
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"react": "^17.0.1" "react": "^16.5.1"
} }
} }

View File

@@ -1,7 +1,8 @@
const plugins = require('lucide/rollup.plugins'); const plugins = require('../../rollup.plugins');
const pkg = require('./package.json'); const pkg = require('./package.json');
const outputFileName = pkg.name; const packageName = 'LucideReact';
const outputFileName = 'lucide-react';
const rootDir = 'packages/lucide-react'; // It runs from the root const rootDir = 'packages/lucide-react'; // It runs from the root
const outputDir = `${rootDir}/dist`; const outputDir = `${rootDir}/dist`;
const inputs = [`${rootDir}/build/lucide-react.js`]; const inputs = [`${rootDir}/build/lucide-react.js`];
@@ -31,7 +32,7 @@ const configs = bundles
plugins: plugins(pkg, minify), plugins: plugins(pkg, minify),
external: ['react', 'prop-types'], external: ['react', 'prop-types'],
output: { output: {
name: outputFileName, name: packageName,
file: `${outputDir}/${format}/${outputFileName}${minify ? '.min' : ''}.js`, file: `${outputDir}/${format}/${outputFileName}${minify ? '.min' : ''}.js`,
format, format,
sourcemap: true, sourcemap: true,

View File

@@ -1,7 +1,8 @@
export default ({ componentName, node }) => ` export default ({ componentName, node }) => `
import createReactComponent from '../createReactComponent'; import createReactComponent from '../createReactComponent';
import defaultAttributes from '../defaultAttributes';
const ${componentName} = createReactComponent('${componentName}', ${node}); const ${componentName} = createReactComponent('${componentName}', ['svg', defaultAttributes, ${node}]);
export default ${componentName}; export default ${componentName};
`; `;

View File

@@ -11,7 +11,7 @@ export default (iconName, [tag, attrs, children]) => {
...attrs, ...attrs,
width: size, width: size,
height: size, height: size,
color, stroke: color,
strokeWidth, strokeWidth,
...rest, ...rest,
}, },
@@ -22,7 +22,7 @@ export default (iconName, [tag, attrs, children]) => {
Component.propTypes = { Component.propTypes = {
color: PropTypes.string, color: PropTypes.string,
size: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), size: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), strokeWidth: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
}; };
Component.displayName = `${iconName}`; Component.displayName = `${iconName}`;

View File

@@ -0,0 +1,11 @@
export default {
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',
};

View File

@@ -2,7 +2,6 @@
exports[`Using lucide icon components should adjust the size, stroke color and stroke width 1`] = ` exports[`Using lucide icon components should adjust the size, stroke color and stroke width 1`] = `
<svg <svg
color="currentColor"
fill="none" fill="none"
height={48} height={48}
stroke="red" stroke="red"
@@ -50,7 +49,6 @@ exports[`Using lucide icon components should adjust the size, stroke color and s
exports[`Using lucide icon components should render an component 1`] = ` exports[`Using lucide icon components should render an component 1`] = `
<svg <svg
color="currentColor"
fill="none" fill="none"
height={24} height={24}
stroke="currentColor" stroke="currentColor"

View File

@@ -3246,9 +3246,6 @@ lru-cache@^6.0.0:
dependencies: dependencies:
yallist "^4.0.0" yallist "^4.0.0"
"lucide@file:../..":
version "0.11.0"
make-dir@^3.0.0: make-dir@^3.0.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
@@ -3661,7 +3658,7 @@ prompts@^2.0.1:
kleur "^3.0.3" kleur "^3.0.3"
sisteransi "^1.0.5" sisteransi "^1.0.5"
prop-types@^15.7.2: prop-types@^15.6.2, prop-types@^15.7.2:
version "15.7.2" version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@@ -3693,41 +3690,34 @@ qs@~6.5.2:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
"react-is@^16.12.0 || ^17.0.0", react-is@^17.0.1: react-is@^16.8.1, react-is@^16.8.6:
version "17.0.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339"
integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==
react-is@^16.8.1:
version "16.13.1" version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-shallow-renderer@^16.13.1: react-is@^17.0.1:
version "16.14.1" version "17.0.1"
resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.14.1.tgz#bf0d02df8a519a558fd9b8215442efa5c840e124" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339"
integrity sha512-rkIMcQi01/+kxiTE9D3fdS959U1g7gs+/rborw++42m1O9FAQiNI/UNRZExVUoAOprn4umcXf+pFRou8i4zuBg== integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==
react-test-renderer@^16.5.1:
version "16.14.0"
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.14.0.tgz#e98360087348e260c56d4fe2315e970480c228ae"
integrity sha512-L8yPjqPE5CZO6rKsKXRO/rVPiaCOy0tQQJbC+UjPNlobl5mad59lvPjwFsQHTvL03caVDIVr9x9/OSgDe6I5Eg==
dependencies: dependencies:
object-assign "^4.1.1" object-assign "^4.1.1"
react-is "^16.12.0 || ^17.0.0" prop-types "^15.6.2"
react-is "^16.8.6"
scheduler "^0.19.1"
react-test-renderer@^17.0.1: react@^16.5.1:
version "17.0.1" version "16.14.0"
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-17.0.1.tgz#3187e636c3063e6ae498aedf21ecf972721574c7" resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d"
integrity sha512-/dRae3mj6aObwkjCcxZPlxDFh73XZLgvwhhyON2haZGUEhiaY5EjfAdw+d/rQmlcFwdTpMXCSGVk374QbCTlrA== integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==
dependencies:
object-assign "^4.1.1"
react-is "^17.0.1"
react-shallow-renderer "^16.13.1"
scheduler "^0.20.1"
react@^17.0.1:
version "17.0.1"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127"
integrity sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w==
dependencies: dependencies:
loose-envify "^1.1.0" loose-envify "^1.1.0"
object-assign "^4.1.1" object-assign "^4.1.1"
prop-types "^15.6.2"
read-pkg-up@^7.0.1: read-pkg-up@^7.0.1:
version "7.0.1" version "7.0.1"
@@ -3962,10 +3952,10 @@ saxes@^5.0.0:
dependencies: dependencies:
xmlchars "^2.2.0" xmlchars "^2.2.0"
scheduler@^0.20.1: scheduler@^0.19.1:
version "0.20.1" version "0.19.1"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.1.tgz#da0b907e24026b01181ecbc75efdc7f27b5a000c" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196"
integrity sha512-LKTe+2xNJBNxu/QhHvDR14wUXHRQbVY5ZOYpOGWRzhydZUqrLb2JBvLPY7cAqFmqrWuDED0Mjk7013SZiOz6Bw== integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==
dependencies: dependencies:
loose-envify "^1.1.0" loose-envify "^1.1.0"
object-assign "^4.1.1" object-assign "^4.1.1"

View File

@@ -0,0 +1,9 @@
stats
node_modules
tests
scripts
build
src
babel.config.js
jest.config.js
rollup.config.js

View File

@@ -0,0 +1,103 @@
# Lucide Vue
Use the lucide icon library in you Vue app.
## Installation
```sh
yarn add lucide-vue
# or
npm install lucide-vue
```
## How to use
It's build with ESmodules so it's completely threeshakable.
Each icon can be imported as a vue component.
### Example
You can pass additional props to adjust the icon.
``` vue
<template>
<Camera
color="red"
:size="32"
/>
</template>
<script>
// Returns Vue component
import { Camera } from 'lucide-vue';
export default {
name: "My Component",
components: { Camera }
}
</script>
```
### Props
| name | type | default
| ------------ | -------- | --------
| `size` | *Number* | 24
| `color` | *String* | currentColor
| `strokeWidth`| *Number* | 2
| `defaultClass`| *String* | lucide-icon
### Custom props
You can also pass custom props that will be added in the svg as attributes.
``` vue
<template>
<Camera fill="red" />
</template>
```
### One generic icon component
It is possible to create one generic icon component to load icons.
> :warning: Example below importing all EsModules, caution using this example, not recommended when you using bundlers, your application build size will grow strongly.
#### Icon Component Example
``` vue
<template>
<component :is="icon" />
</template>
<script>
import * as icons from "lucide-vue";
export default {
props: {
name: {
type: String,
required: true,
},
},
computed: {
icon() {
return icons[this.name];
},
},
};
</script>
```
##### Then you can use it like this
``` vue
<template>
<div id="app">
<Icon name="Airplay" />
</div>
</template>
```

View File

@@ -0,0 +1,12 @@
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
node: 'current',
},
},
],
],
};

View File

@@ -0,0 +1,14 @@
module.exports = {
verbose: true,
roots: ['<rootDir>/src/', '<rootDir>/tests/'],
moduleFileExtensions: ['js'],
transform: {
'^.+\\.js$': 'babel-jest',
'^.+\\.vue$': 'vue-jest',
},
transformIgnorePatterns: [`/node_modules`],
snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
};

View File

@@ -0,0 +1,44 @@
{
"name": "lucide-vue",
"version": "0.14.0",
"author": "Eric Fennis",
"description": "Lucide Vue Package",
"license": "ISC",
"homepage": "https://lucide.dev",
"bugs": "https://github.com/lucide-icons/lucide/issues",
"repository": {
"type": "git",
"url": "https://github.com/lucide-icons/lucide.git",
"directory": "packages/lucide-vue"
},
"amdName": "lucide-vue",
"source": "build/lucide-vue.js",
"main": "dist/cjs/lucide-vue.js",
"main:umd": "dist/umd/lucide-vue.js",
"module": "dist/esm/lucide-vue.js",
"unpkg": "dist/umd/lucide-vue.min.js",
"dependencies": {
"vue": "^2.6.12"
},
"scripts": {
"build": "yarn clean && yarn build:move && yarn build:icons && yarn build:es && yarn build:bundles",
"clean": "rm -rf dist && rm -rf build",
"build:move": "cp -av src build",
"build:icons": "yarn --cwd ../../ build:icons --output=../packages/lucide-vue/build --templateSrc=../packages/lucide-vue/scripts/exportTemplate --noDefaultAttrs",
"build:es": "yarn --cwd ../../ babel packages/lucide-vue/build -d packages/lucide-vue/dist/esm",
"build:bundles": "yarn --cwd ../../ rollup -c packages/lucide-vue/rollup.config.js",
"test": "jest",
"test:watch": "jest --watchAll"
},
"devDependencies": {
"@vue/test-utils": "^1.1.2",
"babel-jest": "^26.6.3",
"jest": "^26.6.3",
"jest-serializer-vue": "^2.0.2",
"vue-jest": "^3.0.7",
"vue-template-compiler": "^2.6.12"
},
"peerDependencies": {
"vue": "^2.6.12"
}
}

View File

@@ -0,0 +1,47 @@
const plugins = require('../../rollup.plugins');
const pkg = require('./package.json');
const packageName = 'LucideVue';
const outputFileName = 'lucide-vue';
const rootDir = 'packages/lucide-vue'; // It runs from the root
const outputDir = `${rootDir}/dist`;
const inputs = [`${rootDir}/build/lucide-vue.js`];
const bundles = [
{
format: 'umd',
inputs,
outputDir,
minify: true,
},
{
format: 'umd',
inputs,
outputDir,
},
{
format: 'cjs',
inputs,
outputDir,
},
];
const configs = bundles
.map(({ inputs, outputDir, format, minify }) =>
inputs.map(input => ({
input,
plugins: plugins(pkg, minify),
external: ['vue'],
output: {
name: packageName,
file: `${outputDir}/${format}/${outputFileName}${minify ? '.min' : ''}.js`,
format,
sourcemap: true,
globals: {
vue: 'vue',
},
},
})),
)
.flat();
export default configs;

View File

@@ -0,0 +1,8 @@
export default ({ componentName, node }) => `
import createVueComponent from '../createVueComponent';
import defaultAttributes from '../defaultAttributes';
const ${componentName} = createVueComponent('${componentName}Icon', ['svg', defaultAttributes, ${node}]);
export default ${componentName};
`;

View File

@@ -0,0 +1,47 @@
export default (iconName, [tag, defaultAttrs, children]) => ({
name: iconName,
functional: true,
props: {
color: {
type: String,
default: 'currentColor',
},
size: {
type: Number,
default: 24,
},
strokeWidth: {
type: Number,
default: 2,
},
defaultClass: {
type: String,
default: 'lucide-icon',
},
},
render(
createElement,
{
props: { color, size, strokeWidth, defaultClass },
data,
},
) {
return createElement(
tag,
{
// eslint-disable-next-line prettier/prettier
class: [defaultClass, data.class, data.staticClass, data.attrs && data.attrs.class].filter(Boolean),
style: [data.style, data.staticStyle, data.attrs && data.attrs.style].filter(Boolean),
attrs: {
...defaultAttrs,
width: size,
height: size,
stroke: color,
'stroke-width': strokeWidth,
...data.attrs,
},
},
children.map(([childTag, childAttrs]) => createElement(childTag, { attrs: childAttrs })),
);
},
});

View File

@@ -0,0 +1,11 @@
export default {
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',
};

View File

@@ -0,0 +1,5 @@
/*
Icons exports.
Will be generated
*/

View File

@@ -0,0 +1 @@
export * from './icons';

View File

@@ -0,0 +1,37 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Using lucide icon components should add a class to the element 1`] = `
<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" class="lucide-icon my-icon">
<circle cx="12" cy="12" r="10"></circle>
<path d="M8 14s1.5 2 4 2 4-2 4-2"></path>
<line x1="9" y1="9" x2="9.01" y2="9"></line>
<line x1="15" y1="9" x2="15.01" y2="9"></line>
</svg>
`;
exports[`Using lucide icon components should add a style attribute to the element 1`] = `
<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="position: absolute" class="lucide-icon">
<circle cx="12" cy="12" r="10"></circle>
<path d="M8 14s1.5 2 4 2 4-2 4-2"></path>
<line x1="9" y1="9" x2="9.01" y2="9"></line>
<line x1="15" y1="9" x2="15.01" y2="9"></line>
</svg>
`;
exports[`Using lucide icon components should adjust the size, stroke color and stroke width 1`] = `
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="red" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" class="lucide-icon">
<circle cx="12" cy="12" r="10"></circle>
<path d="M8 14s1.5 2 4 2 4-2 4-2"></path>
<line x1="9" y1="9" x2="9.01" y2="9"></line>
<line x1="15" y1="9" x2="15.01" y2="9"></line>
</svg>
`;
exports[`Using lucide icon components should render an component 1`] = `
<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" class="lucide-icon">
<circle cx="12" cy="12" r="10"></circle>
<path d="M8 14s1.5 2 4 2 4-2 4-2"></path>
<line x1="9" y1="9" x2="9.01" y2="9"></line>
<line x1="15" y1="9" x2="15.01" y2="9"></line>
</svg>
`;

View File

@@ -0,0 +1,45 @@
import { mount } from '@vue/test-utils'
import { Smile } from '..'
describe('Using lucide icon components', () => {
it('should render an component', () => {
const wrapper = mount(Smile)
expect(wrapper).toMatchSnapshot();
});
it('should adjust the size, stroke color and stroke width', () => {
const wrapper = mount(Smile, {
propsData: {
size: 48,
stroke: 'red',
strokeWidth: 4
}
})
expect(wrapper).toMatchSnapshot();
});
it('should add a class to the element', () => {
const wrapper = mount(Smile, {
attrs: {
class: "my-icon"
}
})
expect(wrapper).toMatchSnapshot();
expect(String(wrapper.classes())).toBe(String(['lucide-icon', 'my-icon']))
});
it('should add a style attribute to the element', () => {
const wrapper = mount(Smile, {
attrs: {
style: 'position: absolute',
}
})
expect(wrapper).toMatchSnapshot();
expect(wrapper.attributes('style')).toContain('position: absolute')
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
import path from 'path';
import tags from '../tags.json';
import { readSvgDirectory, writeFile } from './helpers';
const ICONS_DIR = path.resolve(__dirname, '../icons');
console.log(`Read all tags`);
const svgFiles = readSvgDirectory(ICONS_DIR);
const iconNames = svgFiles.map(icon => icon.split('.')[0]);
const iconTags = iconNames
.map(iconName => ({
name: iconName,
tags: tags[iconName] || [],
}))
.sort((a, b) => {
const nameA = a.name;
const nameB = b.name;
if (nameA < nameB) {
return -1;
}
if (nameA > nameB) {
return 1;
}
// names must be equal
return 0;
});
const newTags = iconTags.reduce((acc, { name, tags }) => {
acc[name] = tags;
return acc;
}, {});
const tagsContent = JSON.stringify(newTags, null, 2);
writeFile(tagsContent, 'tags.json', path.resolve(__dirname, '..'));

View File

@@ -1,5 +1,6 @@
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
// eslint-disable-next-line import/no-extraneous-dependencies
import getArgumentOptions from 'minimist'; import getArgumentOptions from 'minimist';
import renderIconsObject from './render/renderIconsObject'; import renderIconsObject from './render/renderIconsObject';

View File

@@ -22,6 +22,13 @@ export const toPascalCase = string => {
return camelCase.charAt(0).toUpperCase() + camelCase.slice(1); return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
}; };
/**
* Converts string to PascalCase
*
* @param {string} string
*/
export const toKebabCase = string => string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
/** /**
* Resets the file contents. * Resets the file contents.
* *

View File

@@ -38,13 +38,15 @@ export default (iconsObject, options) => {
]; ];
}); });
iconNodes[icon] = [ iconNodes[icon] = !options.noDefaultAttrs
? [
'svg', 'svg',
{ {
...(options.camelizeAttrs ? camelizeAttrs(DEFAULT_ATTRS) : DEFAULT_ATTRS), ...(options.camelizeAttrs ? camelizeAttrs(DEFAULT_ATTRS) : DEFAULT_ATTRS),
}, },
children, children,
]; ]
: children;
}); });
return iconNodes; return iconNodes;

View File

@@ -13,7 +13,7 @@
"dependencies": { "dependencies": {
"@chakra-ui/core": "^1.0.0-rc.8", "@chakra-ui/core": "^1.0.0-rc.8",
"downloadjs": "^1.4.7", "downloadjs": "^1.4.7",
"framer-motion": "^2.9.4", "framer-motion": "^3.3.0",
"fuse.js": "^6.0.4", "fuse.js": "^6.0.4",
"jszip": "^3.4.0", "jszip": "^3.4.0",
"lodash": "^4.17.20", "lodash": "^4.17.20",
@@ -23,7 +23,6 @@
"react": "^16.13.1", "react": "^16.13.1",
"react-color": "2.17.3", "react-color": "2.17.3",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
"react-spring": "^8.0.27",
"react-svg-loader": "^3.0.3" "react-svg-loader": "^3.0.3"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -0,0 +1,18 @@
<svg xmlns="http://www.w3.org/2000/svg">
<rect width="24" height="24" fill="white"/>
<svg class="icon-grid" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#dedede" stroke-width="0.1" xmlns="http://www.w3.org/2000/svg"><g><line x1="0" y1="1" x2="24" y2="1"></line><line x1="1" y1="0" x2="1" y2="24"></line></g><g><line x1="0" y1="2" x2="24" y2="2"></line><line x1="2" y1="0" x2="2" y2="24"></line></g><g><line x1="0" y1="3" x2="24" y2="3"></line><line x1="3" y1="0" x2="3" y2="24"></line></g><g><line x1="0" y1="4" x2="24" y2="4"></line><line x1="4" y1="0" x2="4" y2="24"></line></g><g><line x1="0" y1="5" x2="24" y2="5"></line><line x1="5" y1="0" x2="5" y2="24"></line></g><g><line x1="0" y1="6" x2="24" y2="6"></line><line x1="6" y1="0" x2="6" y2="24"></line></g><g><line x1="0" y1="7" x2="24" y2="7"></line><line x1="7" y1="0" x2="7" y2="24"></line></g><g><line x1="0" y1="8" x2="24" y2="8"></line><line x1="8" y1="0" x2="8" y2="24"></line></g><g><line x1="0" y1="9" x2="24" y2="9"></line><line x1="9" y1="0" x2="9" y2="24"></line></g><g><line x1="0" y1="10" x2="24" y2="10"></line><line x1="10" y1="0" x2="10" y2="24"></line></g><g><line x1="0" y1="11" x2="24" y2="11"></line><line x1="11" y1="0" x2="11" y2="24"></line></g><g><line x1="0" y1="12" x2="24" y2="12"></line><line x1="12" y1="0" x2="12" y2="24"></line></g><g><line x1="0" y1="13" x2="24" y2="13"></line><line x1="13" y1="0" x2="13" y2="24"></line></g><g><line x1="0" y1="14" x2="24" y2="14"></line><line x1="14" y1="0" x2="14" y2="24"></line></g><g><line x1="0" y1="15" x2="24" y2="15"></line><line x1="15" y1="0" x2="15" y2="24"></line></g><g><line x1="0" y1="16" x2="24" y2="16"></line><line x1="16" y1="0" x2="16" y2="24"></line></g><g><line x1="0" y1="17" x2="24" y2="17"></line><line x1="17" y1="0" x2="17" y2="24"></line></g><g><line x1="0" y1="18" x2="24" y2="18"></line><line x1="18" y1="0" x2="18" y2="24"></line></g><g><line x1="0" y1="19" x2="24" y2="19"></line><line x1="19" y1="0" x2="19" y2="24"></line></g><g><line x1="0" y1="20" x2="24" y2="20"></line><line x1="20" y1="0" x2="20" y2="24"></line></g><g><line x1="0" y1="21" x2="24" y2="21"></line><line x1="21" y1="0" x2="21" y2="24"></line></g><g><line x1="0" y1="22" x2="24" y2="22"></line><line x1="22" y1="0" x2="22" y2="24"></line></g><g><line x1="0" y1="23" x2="24" y2="23"></line><line x1="23" y1="0" x2="23" y2="24"></line></g></svg>
<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>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

6
site/public/vercel.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@@ -1,4 +1,4 @@
import {Button, Flex, Link, Stack, Text,} from "@chakra-ui/core"; import {Button, Flex, Link, WrapItem, Text, Wrap,} from "@chakra-ui/core";
import download from "downloadjs"; import download from "downloadjs";
import JSZip from "jszip"; import JSZip from "jszip";
import { Download, Github } from 'lucide-react'; import { Download, Github } from 'lucide-react';
@@ -30,7 +30,14 @@ const Header = ({ data }) => {
<Text fontSize="lg" as="p" textAlign="center" mb="8"> <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>! 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> </Text>
<Stack isInline marginTop={3} marginBottom={10}> <Wrap
isInline
marginTop={3}
marginBottom={10}
spacing="15px"
justify="center"
>
<WrapItem>
<Button <Button
leftIcon={<Download/>} leftIcon={<Download/>}
size="lg" size="lg"
@@ -38,7 +45,11 @@ const Header = ({ data }) => {
> >
Download all Download all
</Button> </Button>
</WrapItem>
<WrapItem>
<IconCustomizerDrawer/> <IconCustomizerDrawer/>
</WrapItem>
<WrapItem>
<Button <Button
as="a" as="a"
leftIcon={<Github/>} leftIcon={<Github/>}
@@ -49,7 +60,8 @@ const Header = ({ data }) => {
> >
Github Github
</Button> </Button>
</Stack> </WrapItem>
</Wrap>
</Flex> </Flex>
) )
}; };

View File

@@ -1,41 +1,37 @@
import { useSpring, animated } from "react-spring"; import { Box, Text, IconButton, useColorMode, Flex, Slide, ButtonGroup, Button, useToast, Heading, Avatar, AvatarGroup, Link, Tooltip, useMediaQuery, useDisclosure } from "@chakra-ui/core";
import { Box, Text, IconButton, useColorMode, Flex, ButtonGroup, Button, useToast } from "@chakra-ui/core";
import theme from "../lib/theme"; import theme from "../lib/theme";
import download from 'downloadjs'; import download from 'downloadjs';
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import { X as Close } from 'lucide-react'; import { X as Close } from 'lucide-react';
import {useContext, useRef} from "react"; import {useContext, useEffect, useRef} from "react";
import {IconStyleContext} from "./CustomizeIconContext"; import {IconStyleContext} from "./CustomizeIconContext";
import {IconWrapper} from "./IconWrapper"; import {IconWrapper} from "./IconWrapper";
import ModifiedTooltip from "./ModifiedTooltip";
type IconDownload = { type IconDownload = {
src: string; src: string;
name: string; name: string;
}; };
const IconDetailOverlay = ({ isOpen = true, onClose, icon }) => { const IconDetailOverlay = ({ open = true, close, icon }) => {
const toast = useToast(); const toast = useToast();
const { colorMode } = useColorMode(); const { colorMode } = useColorMode();
const { tags = [], name } = icon; const { tags = [], name } = icon;
const {color, strokeWidth, size} = useContext(IconStyleContext); const {color, strokeWidth, size} = useContext(IconStyleContext);
const iconRef = useRef<SVGSVGElement>(null); const iconRef = useRef<SVGSVGElement>(null);
const [isMobile] = useMediaQuery("(max-width: 560px)")
const { transform, opacity } = useSpring({ const { isOpen, onOpen, onClose } = useDisclosure()
opacity: isOpen ? 1 : 0,
transform: `translateY(${isOpen ? -120 : 0}%)`,
config: { mass: 5, tension: 500, friction: 80 },
});
const handleClose = () => { const handleClose = () => {
onClose(); onClose();
close();
}; };
const panelStyling = { useEffect(() => {
transform: transform.interpolate(t => t), if(open) {
opacity: opacity.interpolate(o => o), onOpen()
width: "100%",
willChange: "transform"
} }
}, [open])
const iconStyling = (isLight) => ({ const iconStyling = (isLight) => ({
height: "25vw", height: "25vw",
@@ -88,6 +84,7 @@ const IconDetailOverlay = ({ isOpen = true, onClose, icon }) => {
height={0} height={0}
key={name} key={name}
> >
<Slide direction="bottom" in={isOpen} style={{ zIndex: 10 }}>
<Flex <Flex
alignItems="center" alignItems="center"
justifyContent="space-between" justifyContent="space-between"
@@ -98,9 +95,7 @@ const IconDetailOverlay = ({ isOpen = true, onClose, icon }) => {
w="full" w="full"
px={8} px={8}
> >
<animated.div
style={panelStyling}
>
<Box <Box
borderWidth="1px" borderWidth="1px"
rounded="lg" rounded="lg"
@@ -163,11 +158,25 @@ const IconDetailOverlay = ({ isOpen = true, onClose, icon }) => {
</svg> </svg>
</Box> </Box>
</Flex> </Flex>
<Flex marginLeft={[0, 8]}> <Flex marginLeft={[0, 8]} w="100%">
<Box> <Box w="100%">
<Text fontSize="3xl" style={{ cursor: "pointer" }} mb={1}> <Flex
justify={isMobile ? 'center' : 'flex-start'}
marginTop={isMobile ? 10 : 0}
>
<Box
position="relative"
mb={1}
display="inline-block"
style={{ cursor: "pointer" }}
pr={6}
>
<Text fontSize="3xl">
{icon.name} {icon.name}
</Text> </Text>
{ icon?.contributors?.length ? ( <ModifiedTooltip/> ) : null}
</Box>
</Flex>
<Box mb={4}> <Box mb={4}>
{ tags?.length ? ( { tags?.length ? (
<Text <Text
@@ -187,6 +196,7 @@ const IconDetailOverlay = ({ isOpen = true, onClose, icon }) => {
Edit Tags Edit Tags
</Button> */} </Button> */}
</Box> </Box>
<Box overflowY="auto" w="100%" pt={1} pb={1}>
<ButtonGroup spacing={4}> <ButtonGroup spacing={4}>
<Button variant="solid" onClick={() => downloadIcon({src: iconRef.current.outerHTML, name: icon.name})} mb={1}> <Button variant="solid" onClick={() => downloadIcon({src: iconRef.current.outerHTML, name: icon.name})} mb={1}>
Download SVG Download SVG
@@ -199,11 +209,29 @@ const IconDetailOverlay = ({ isOpen = true, onClose, icon }) => {
</Button> </Button>
</ButtonGroup> </ButtonGroup>
</Box> </Box>
{ icon?.contributors?.length ? (
<>
<Heading as="h5" size="sm" marginTop={4} marginBottom={2}>
Contributors:
</Heading>
<AvatarGroup size="md">
{ icon.contributors.map((commit, index) => (
<Link href={`https://github.com/${commit.author}`} isExternal key={`${index}_${commit.sha}`}>
<Tooltip label={commit.author} key={commit.sha}>
<Avatar name={commit.author} showBorder={false} src={`https://github.com/${commit.author}.png?size=88`} />
</Tooltip>
</Link>
)) }
</AvatarGroup>
</>
) : null }
</Box>
</Flex> </Flex>
</Flex> </Flex>
</Box> </Box>
</animated.div>
</Flex> </Flex>
</Slide>
</Box> </Box>
); );
}; };

View File

@@ -6,6 +6,7 @@ import {useContext, useMemo} from "react";
import {IconStyleContext} from "./CustomizeIconContext"; import {IconStyleContext} from "./CustomizeIconContext";
import {IconWrapper} from "./IconWrapper"; import {IconWrapper} from "./IconWrapper";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import ModifiedTooltip from './ModifiedTooltip';
const IconList = ({icons}) => { const IconList = ({icons}) => {
const router = useRouter() const router = useRouter()
@@ -17,13 +18,13 @@ const IconList = ({icons}) => {
return ( return (
<Grid <Grid
templateColumns={`repeat(auto-fill, minmax(160px, 1fr))`} templateColumns={`repeat(auto-fill, minmax(150px, 1fr))`}
gap={5} gap={5}
marginBottom="320px" marginBottom="320px"
> >
{ icons.map((icon) => { { icons.map((icon) => {
const actualIcon = icon.item ? icon.item : icon; const actualIcon = icon.item ? icon.item : icon;
const { name, content } = actualIcon; const { name, content, contributors } = actualIcon;
return ( return (
<Link <Link
@@ -42,6 +43,7 @@ const IconList = ({icons}) => {
borderWidth="1px" borderWidth="1px"
rounded="lg" rounded="lg"
padding={16} padding={16}
position="relative"
onClick={(event) => { onClick={(event) => {
if (event.shiftKey) { if (event.shiftKey) {
copy(actualIcon.src); copy(actualIcon.src);
@@ -63,6 +65,7 @@ const IconList = ({icons}) => {
key={name} key={name}
alignItems="center" alignItems="center"
> >
{ contributors?.length ? ( <ModifiedTooltip/> ) : null}
<Flex direction="column" align="center" justify="center"> <Flex direction="column" align="center" justify="center">
<IconWrapper <IconWrapper
content={content} content={content}

View File

@@ -38,7 +38,7 @@ const Layout = ({ children }) => {
maxW="1250px" maxW="1250px"
margin="0 auto" margin="0 auto"
w="full" w="full"
px={8} px={5}
> >
<Flex justifyContent="center" alignItems="center"> <Flex justifyContent="center" alignItems="center">
<NextLink href="/" passHref> <NextLink href="/" passHref>
@@ -77,9 +77,15 @@ const Layout = ({ children }) => {
</Flex> </Flex>
</Flex> </Flex>
</Flex> </Flex>
<Flex margin="0 auto" direction="column" maxW="1250px" px={8}> <Flex margin="0 auto" direction="column" maxW="1250px" px={5}>
{children} {children}
<Divider marginTop={4} marginBottom={8} /> <Divider marginBottom={8} />
<p style={{ alignSelf: "center" }}>
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
<img src="/vercel.svg" alt="Powered by Vercel" width="200" />
</a>
</p>
<br />
</Flex> </Flex>
</Box> </Box>
); );

View File

@@ -0,0 +1,31 @@
import { Box, Tooltip, useColorMode } from "@chakra-ui/core";
import theme from '../lib/theme';
const ModifiedTooltip = ({}) => {
const { colorMode } = useColorMode();
return (
<Tooltip
hasArrow
label="This is new or modified icon"
bg={colorMode === 'light' ? theme.colors.white : theme.colors.gray[700]}
color={colorMode === 'dark' ? theme.colors.white : null}
>
<Box
{
...{
position: 'absolute',
height: '8px',
width: '8px',
background: '#F56565',
top: '8px',
right: '8px',
borderRadius: '4px'
}
}
/>
</Tooltip>
)
}
export default ModifiedTooltip;

View File

@@ -0,0 +1,129 @@
import crypto from 'crypto';
import fs from 'fs';
import path from 'path';
const IGNORE_COMMIT_MESSAGES = ['fork', 'optimize'];
function getContentHashOfFile(path) {
return new Promise((resolve, reject) => {
const hash = crypto.createHash('md4');
const stream = fs.createReadStream(path);
stream.on('error', err => reject(err));
stream.on('data', chunk => hash.update(chunk));
stream.on('end', () => resolve(hash.digest('hex')));
});
}
const fetchCommitsOfIcon = (name) =>
new Promise(async (resolve, reject) => {
try {
const headers = new Headers();
const username = 'ericfennis';
const password = process.env.GITHUB_API_KEY;
headers.set(
'Authorization',
`Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`,
);
const res = await fetch(
`https://api.github.com/repos/lucide-icons/lucide/commits?path=icons/${name}.svg`,
{
method: 'GET',
headers,
},
);
const data = await res.json();
resolve({
name,
commits: data,
});
} catch (error) {
reject(error);
}
});
export const filterCommits = (commits) =>
commits.filter(({ commit }) =>
!IGNORE_COMMIT_MESSAGES.some(ignoreItem =>
commit.message.toLowerCase().includes(ignoreItem),
))
.map(({ sha, author, commit }) => ({
author: author && author.login ? author.login : null,
commit: sha,
}));
const getIconHash = async (icon) => await getContentHashOfFile(path.join(process.cwd(), "../icons", `${icon}.svg`))
const iconCacheDir = path.join(process.cwd(),'.next/cache/github-api');
const iconCache = (hash) => path.join(iconCacheDir, `${hash}.json`);
export async function checkIconCache(icon) {
const hash = await getIconHash(icon);
const cachePath = iconCache(hash);
if(fs.existsSync( cachePath )) {
const iconCache = fs.readFileSync(cachePath, "utf8");
return JSON.parse(iconCache)
}
return false
}
async function writeIconCache(icon, content) {
const hash = await getIconHash(icon);
const iconCachePath = iconCache(hash);
if (!fs.existsSync(iconCacheDir)){
fs.mkdirSync(iconCacheDir);
}
fs.writeFileSync(iconCachePath, JSON.stringify(content), 'utf-8');
}
export async function getContributors(icon) {
try {
let iconCommits
const iconCache = await checkIconCache(icon);
if (iconCache) {
iconCommits = iconCache
} else {
const { commits } : any = await fetchCommitsOfIcon(icon);
writeIconCache(icon, commits)
iconCommits = commits
}
if (iconCommits && iconCommits.length) {
return filterCommits(iconCommits);
}
return [];
} catch (error) {
throw new Error(error);
}
}
export async function getAllContributors(icons) {
try {
const AllIconCommits = await Promise.all(icons.map(fetchCommitsOfIcon));
const filteredCommits = AllIconCommits.reduce((acc, { name, commits }) => {
if (commits && commits.length) {
acc[name] = filterCommits(commits)
}
return acc;
}, {});
return filteredCommits
} catch (error) {
console.error(error);
}
}

View File

@@ -2,6 +2,7 @@ import fs from "fs";
import path from "path"; import path from "path";
import cheerio from 'cheerio'; import cheerio from 'cheerio';
import tags from '../../../tags.json'; import tags from '../../../tags.json';
import { getContributors } from "./fetchAllContributors";
const directory = path.join(process.cwd(), "../icons"); const directory = path.join(process.cwd(), "../icons");
@@ -13,25 +14,26 @@ export function getAllNames() {
}); });
} }
export function getData(name) { export async function getData(name:string) {
const fullPath = path.join(directory, `${name}.svg`); const fullPath = path.join(directory, `${name}.svg`);
const fileContents = fs.readFileSync(fullPath, "utf8"); const fileContents = fs.readFileSync(fullPath, "utf8");
const $ = cheerio.load(fileContents); const $ = cheerio.load(fileContents);
const content = $("svg").html(); const content = $("svg").html();
const contributors = await getContributors(name);
return { return {
name, name,
tags: tags[name] || [], tags: tags[name] || [],
contributors,
src: fileContents, src: fileContents,
content: content content: content
}; };
} }
export function getAllData() { export async function getAllData() {
const names = getAllNames(); const names = getAllNames();
return names.map((name) => { return Promise.all(names.map((name) => getData(name)));
return getData(name);
});
} }

View File

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

View File

@@ -2,6 +2,7 @@ import { ChakraProvider } from '@chakra-ui/core';
import customTheme from '../lib/theme'; import customTheme from '../lib/theme';
import '../assets/styling.css'; import '../assets/styling.css';
import Head from 'next/head'; import Head from 'next/head';
import { CustomizeIconContext } from "../components/CustomizeIconContext";
const App = ({ Component, pageProps }) => { const App = ({ Component, pageProps }) => {
return ( return (
@@ -10,7 +11,9 @@ const App = ({ Component, pageProps }) => {
<title>Lucide</title> <title>Lucide</title>
</Head> </Head>
<ChakraProvider theme={customTheme}> <ChakraProvider theme={customTheme}>
<CustomizeIconContext>
<Component {...pageProps} /> <Component {...pageProps} />
</CustomizeIconContext>
</ChakraProvider> </ChakraProvider>
</> </>
); );

View File

@@ -1,4 +1,3 @@
import { useEffect } from 'react'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import IconDetailOverlay from '../../components/IconDetailOverlay' import IconDetailOverlay from '../../components/IconDetailOverlay'
import { getAllData, getData } from '../../lib/icons'; import { getAllData, getData } from '../../lib/icons';
@@ -29,7 +28,7 @@ const IconPage = ({ icon, data }) => {
<IconDetailOverlay <IconDetailOverlay
key={icon.name} key={icon.name}
icon={icon} icon={icon}
onClose={onClose} close={onClose}
/> />
<Header {...{data}}/> <Header {...{data}}/>
<IconOverview {...{data}}/> <IconOverview {...{data}}/>
@@ -39,15 +38,17 @@ const IconPage = ({ icon, data }) => {
export default IconPage export default IconPage
export function getStaticProps({ params: { iconName } }) { export async function getStaticProps({ params: { iconName } }) {
const data = getAllData(); const data = await getAllData();
const icon = getData(iconName); const icon = await getData(iconName);
return { props: { icon, data } } return { props: { icon, data } }
} }
export function getStaticPaths() { export async function getStaticPaths() {
const data = await getAllData();
return { return {
paths: getAllData().map(({name: iconName }) => ({ paths: data.map(({ name: iconName }) => ({
params: { iconName }, params: { iconName },
})), })),
fallback: false, fallback: false,

View File

@@ -5,7 +5,6 @@ import IconOverview from "../components/IconOverview";
import IconDetailOverlay from "../components/IconDetailOverlay"; import IconDetailOverlay from "../components/IconDetailOverlay";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import Header from "../components/Header"; import Header from "../components/Header";
import {CustomizeIconContext} from "../components/CustomizeIconContext";
const IndexPage = ({ data }) => { const IndexPage = ({ data }) => {
const router = useRouter(); const router = useRouter();
@@ -13,21 +12,19 @@ const IndexPage = ({ data }) => {
return ( return (
<Layout> <Layout>
<CustomizeIconContext>
<IconDetailOverlay <IconDetailOverlay
isOpen={!!router.query.iconName} open={!!router.query.iconName}
icon={getIcon(router.query.iconName)} icon={getIcon(router.query.iconName)}
onClose={() => router.push('/')} close={() => router.push('/')}
/> />
<Header {...{data}}/> <Header {...{data}}/>
<IconOverview {...{data}}/> <IconOverview {...{data}}/>
</CustomizeIconContext>
</Layout> </Layout>
); );
}; };
export async function getStaticProps() { export async function getStaticProps() {
let data = getAllData(); let data = await getAllData();
return { return {
props: { props: {

Some files were not shown because too many files have changed in this diff Show More