Compare commits

...

126 Commits

Author SHA1 Message Date
Daniel Bayley
8b7ea73aa3 Improve bug icons (#988)
* Refine `bug` icon

* Add `bug-off` icon

* Add `bug-play` (debug) icon

* Add `bug-x` icon

* Add `bug-x` variant

* Delete bug-x.json

* Delete bug-x.svg

* Delete bug-x-2.json

* Delete bug-x-2.svg

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2023-08-28 20:08:41 +02:00
Eric Fennis
1bdeae5364 Add conditional exports property Lucide Svelte package.json (#1499)
* add exports property

* adjust wild card import

* Fix exports

* revert bundleSize plugin change

* Fix exports build warning
2023-08-28 19:41:24 +02:00
Jan
0e307087f6 replaced SvelteComponentTyped with SvelteComponent (#1517)
Co-authored-by: Jan Richter <j.richter@vanlanschotkempen>
2023-08-28 19:21:04 +02:00
Karsa
a46114b3e7 [github] Added issue template forms (#1535)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

---------

Co-authored-by: Karsa <karsa@karsa.org>
2023-08-28 19:20:30 +02:00
Karsa
fcafe0e7b7 [pull-request-template] Added some clarification to pull request requirements. (#1531) 2023-08-28 15:49:06 +02:00
Karsa
5312982b8f [docs] Fixes icon design guide heading levels + naming mix-up (#1533)
* Fixes icon design guide heading levels + naming mix-up

* Update icon-design-guide.md

---------

Co-authored-by: Karsa <karsa@karsa.org>
2023-08-28 09:05:26 +02:00
Daniel Bayley
3a13fab009 Add archive-x (junk) icon (#1203)
* Add `archive-x` (junk) icon

* Improve metadata

* Refine `archive` icon

* Refine `archive-restore` icon

* Refine `archive-*` icons

* Update icons/archive-restore.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/archive-x.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/archive.svg

Co-authored-by: Karsa <contact@karsa.org>

---------

Co-authored-by: Karsa <contact@karsa.org>
2023-08-25 14:44:53 +02:00
Karsa
30a69ee670 [lucide-angular] Added factory to LUCIDE_ICONS injection token so that standalone components will be able to import LucideAngularModule. (#1529) 2023-08-25 08:43:16 +02:00
Jakob Guddas
5f442122ab feat: added dpi preview (#1510)
* feat: added dpi preview

* fix: switched to resvg

* build: updated versions and nitro build config

* fix: switched to resvg-wasm

* fix: trying out fetch with import meta url

* fix: trying out copy wasm file manually

* fix: wrong file path

* fix: trying out esmImport

* fix: oups

* fix: giving up

* fix: await initialization

* fix: still nothing

* Revert "fix: still nothing"

This reverts commit dcb9fe3837a32ec4149f41c9b8925ae0e2fbdb79.

* fix: implement suggestions

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

* Update .github/workflows/pull-request.yml

* Update .github/workflows/pull-request.yml

* Update .github/workflows/pull-request.yml

* Update .github/workflows/pull-request.yml

* Update .github/workflows/pull-request.yml

* Update .github/workflows/pull-request.yml

* Update .github/workflows/pull-request.yml

* Update .github/workflows/pull-request.yml

* Update .github/workflows/pull-request.yml

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2023-08-24 11:37:07 +02:00
Jakob Guddas
e78d910a83 feat: defention of done (#1224)
* feat: defention of done

* Update new-icon.md

* Update new-icon.md

* move file pull_request_template.md

* updated the PR template

* update checklist items

* update heading

* Update .github/pull_request_template.md

Co-authored-by: Jakob Guddas <github@jguddas.de>

* Update .github/pull_request_template.md

Co-authored-by: Jakob Guddas <github@jguddas.de>

* Update .github/pull_request_template.md

Co-authored-by: Jakob Guddas <github@jguddas.de>

* Update .github/pull_request_template.md

Co-authored-by: Jakob Guddas <github@jguddas.de>

* Update .github/pull_request_template.md

Co-authored-by: Jakob Guddas <github@jguddas.de>

* Update .github/pull_request_template.md

Co-authored-by: Karsa <contact@karsa.org>

* Update .github/pull_request_template.md

Co-authored-by: Karsa <contact@karsa.org>

* Update .github/pull_request_template.md

Co-authored-by: Karsa <contact@karsa.org>

* Update .github/pull_request_template.md

Co-authored-by: Karsa <contact@karsa.org>

* Update .github/pull_request_template.md

Co-authored-by: Karsa <contact@karsa.org>

* Update .github/pull_request_template.md

Co-authored-by: Karsa <contact@karsa.org>

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Karsa <contact@karsa.org>
2023-08-21 10:30:09 +02:00
Karsa
ccc8dc2b34 Updated icon design guide (#1518)
* Update icon design guide with new design guidelines, naming convention & code convention additions.

* Fix heading levels.

* Update docs/guide/design/icon-design-guide.md
2023-08-21 10:29:51 +02:00
Jakob Guddas
96bcca0e08 Added more tags to party-popper meta data (#1519) 2023-08-19 15:15:35 +02:00
Alwyn Tan
d95b14a70b Fix: Copy SVG properties into the child in react-native-svg (#1485) 2023-08-16 23:01:41 +02:00
Daniel Bayley
a852a43ef4 Optimise box/package icons (#1481)
* Optimise `box` icon

* Optimise `package` icons

* Update icons/package-check.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/package-minus.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/package-plus.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/package-search.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/package-x.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/package.svg

Co-authored-by: Karsa <contact@karsa.org>

---------

Co-authored-by: Karsa <contact@karsa.org>
2023-08-15 19:34:12 +02:00
Jakob Guddas
4953a95e36 feat: added fallback background for browsers without prefers-color-scheme support (#1493)
* feat: added fallback background to svg preview for browsers without prefers-color-scheme support

* feat: added fallback background to svg stroke-width preview for browsers without prefers-color-scheme support
2023-08-15 19:32:25 +02:00
Martin Adamko
cad1b95b69 fix(icons): Rename send-horizonal to send-horizontal (#1496)
* fix(icons): Rename `send-horizonal` to `send-horizontal`

* feat(icons): Add `"send-horizonal"` alias for backward compatibility.
2023-08-14 09:00:42 +02:00
Daniel Bayley
92f3fb0f90 Fix database-backup/improve metadata/add database-zap (cache) icon (#1400)
* Fix `database-backup` icon

* Improve `database` icons metadata

* Add `database-zap` icon

* Update icons/database-zap.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/database-backup.svg

Co-authored-by: Karsa <contact@karsa.org>

* Revert "Update icons/database-backup.svg"

This reverts commit ef1cfd2065.

* Update icons/database-backup.svg

Co-authored-by: Karsa <contact@karsa.org>

---------

Co-authored-by: Karsa <contact@karsa.org>
2023-08-13 22:09:30 +02:00
Jakob Guddas
6e8895d075 fix: generate alias files for nextjs import mapping (#1486)
* fix: generate alias files for nextjs import mapping

* Add entry files in rollup build

* improve build flow other packages

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2023-08-13 22:07:54 +02:00
Daniel Bayley
a1b2ce5b7b Add browser/dev tools icons (#1319)
* Add `mouse-pointer-square-dashed` (inspect variant) icon

* Rename `inspect` to `mouse-pointer-square`

* Add `crosshair-square` (inspector) variant

* Add `computer-chip` icon

* Add `applications` icon

* Add `tablet-smartphone` (devices/responsive design) icon

* Add `waypoints` icon

* Revert "Add `crosshair-square` (inspector) variant"

This reverts commit 1610b85f66.

* Revert "Add `applications` icon"

This reverts commit 06bc672f0f.

* Remove `computer-chip` icon

* Improve metadata

* Revert "Add `waypoints` icon"

This reverts commit acc47f0816.

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2023-08-13 12:55:48 +02:00
Jakob Guddas
4a54e87e84 fix: cogs (#1225)
* fix: cogs

* fix: cogs

* fix: added folder-cog-2 as alias for folder-cog

* chore: removed file-cog-2

* feat: rotated cogs

* feat: rotated user-cogs-2 cog
2023-08-13 11:59:48 +02:00
Daniel Bayley
d8bdbff9c6 Improve utility-pole metadata (#1157)
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2023-08-13 11:49:25 +02:00
Zara
70cffa8dd2 Fix lucide-vue-next stroke-width must be number bug (#1492) 2023-08-11 15:29:23 +02:00
Eric Fennis
8cff59627b Update Site to Vitepress 1.0.0-rc.4 and fix gh-icon api (#1491)
* update site dependencies

* revert preact change

* update config
2023-08-09 17:09:21 +02:00
Eric Fennis
b684a0083b update snapshots 2023-08-07 22:20:14 +02:00
Eric Fennis
9fb4b0b161 Add multiline support 2023-08-07 12:59:26 +02:00
Daniel Bayley
9ec40ae506 Add transport icons (#1412)
* Refine `truck` icon

* Refine `train` icon

* Rename `train` icon to `tram`

* Add `rail-symbol` icon

* Add `m-square` (metro) icon

* Add `train-track` icon

* Add `cable-car` icon

* Add `coach` icon

* Add `traffic-cone` icon

* Add `traffic-cone` variant

* Improve metadata

* Refine `bus` icon

* Add `traffic-lights` icon

* Revert "Add `traffic-lights` icon"

This reverts commit 22dcfc7aad.

* Refine `train-track` icon

* Rename `train` to `train-high-speed`

* Refine `car` icon

* Add `car-front` variant

* Add `car-taxi` icon

* Add `car-sports` icon

* Add `parking-meter` icon

* Add `tractor` icon

* Refine `bus` icon

* Refine `tractor` icon

* Refine `car-front` icon

* Refine `car-taxi` icon

* Refine `car-sports` icon

* Add `bus-school` icon

* Remove `traffic-cone` variant

* Add `train-high-speed-tunnel` icon

* Refine `coach` icon

* Refine `cable-car` icon

* Refine `car` icon

* Refine `train-high-speed` icon

* Refine `train-high-speed-tunnel` icon

* Update icons/car-front.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/car.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/car-taxi.svg

Co-authored-by: Karsa <contact@karsa.org>

* Refine `coach` icon

* Add `coach` variant

* Rename `tram` to `tram-front`

* Rename `train-high-speed` to `train-front`

* Rename `train-high-speed-tunnel` to `train-front-tunnel`

* Rename `bus-school` to `bus-school-front`

* Rename `coach` to `coach-front`

* Rename `coach` variant to `coach-front` variant

* Rename `car-taxi` to `car-taxi-front`

* Rename `car-sports` to `car-sports-front`

* Add `car-electric-front` icon

* Refine `car-electric-front` icon

* Refine `car` icon

* Add `ship-wheel` icon

* Improve `ship` metadata

* Remove `bus-school-front` icon

* Remove `car-electric-front` icon

* Remove `car-sports-front` icon

* Remove `coach-front` variant

* Rename `coach-front` to `bus-front`

* Improve `train-front-tunnel` metadata

* Refine `car-taxi-front` icon

---------

Co-authored-by: Karsa <contact@karsa.org>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2023-08-07 11:21:42 +02:00
Daniel Bayley
890514de6c Add blocks icons (#1055)
* Optimise `grid` icon

* Rename `grid` to `grid-3x3`

* Add `grid-2x2` icon

* Add `blocks` icon

* Update icons/blocks.svg

Co-authored-by: Karsa <contact@karsa.org>

---------

Co-authored-by: Karsa <contact@karsa.org>
2023-08-06 16:24:50 +02:00
Eric Fennis
9463b2e445 Revert .mjs modules in build for Lucide React (#1471)
* switch to different build strategy for dynamicIconImports

* add remove for dynamicIconImports files
2023-08-06 15:47:07 +02:00
dependabot[bot]
0221022e24 Bump semver from 7.5.0 to 7.5.2 (#1469)
Bumps [semver](https://github.com/npm/node-semver) from 7.5.0 to 7.5.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v7.5.0...v7.5.2)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-04 10:47:48 +02:00
Eric Fennis
751f7cb1e3 Adjust workflow 2023-07-31 21:21:43 +02:00
Daniel Bayley
4c90b84236 Add rabbit & turtle/snail & shell/aminal icons (#1234)
* Add `rabbit` icon

* Add `turtle` icon

* Update icons/rabbit.svg

Co-authored-by: Jakob Guddas <github@jguddas.de>

* Refine `rabbit` icon

* Add `snail` icon

* Add `shell` icon

* Refine `snail` icon

* Refine `shell` icon

* Refine `snail` icon

* Refine `shell` icon

…to match `snail`.

* Flip `snail` icon to better match Lucide logo

* Refine `shell` icon

…to better match `snail` and the Lucide logo.

* Improve `shell` icon metadata

* Refine `turtle` icon

* Improve `contributor`s

* Reverse `snail` icon

* Reverse `shell` icon

* Reverse `lollipop` icon

* Add `paw-print` icon

* Add `fish-symbol` icon

* Improve metadata

* Update icons/egg-off.json

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/paw-print.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/rabbit.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/shell.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/snail.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/turtle.svg

Co-authored-by: Karsa <contact@karsa.org>

* Fix `turtle` icon

* Update icons/turtle.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update turtle.svg

Restores @danielbayley's original optimization.

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
Co-authored-by: Karsa <contact@karsa.org>
2023-07-31 12:32:44 +02:00
Eric Fennis
01be733532 update workflow 2023-07-30 20:39:47 +02:00
Ishan
4ffa38e013 Add: Missing README Image (#1456)
* Create powered-by-vercel.svg

* Update README.md

Add Missing Image

* Update README.md

* Delete assets directory

Cleanup
2023-07-25 12:53:28 +02:00
Alexandru Bacanu
0a38dccdae Update package.json (#1452) 2023-07-24 21:48:14 +02:00
Eric Fennis
3391b5b717 update workflows 2023-07-21 10:17:43 +02:00
Daniel Bayley
5c145f4e72 Refine [pencil-]ruler icons (#1442)
* Refine `pencil-ruler` icon

* Refine `ruler` icon

…to match `pencil-ruler`.

* Update icons/ruler.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/pencil-ruler.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/pencil-ruler.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/pencil-ruler.svg

Co-authored-by: Karsa <contact@karsa.org>

---------

Co-authored-by: Karsa <contact@karsa.org>
2023-07-21 09:47:16 +02:00
Daniel Bayley
74429a8e92 Optimise send icon/add variant (#1411)
* Add `send` variant

* Optimise `send` icon

* Refine `send` variant

* Refine `send` variant

* Rename `send-2` to `send-horizonal`
2023-07-19 21:17:31 +02:00
Ishan
1e99499dc8 Update createLucideIcon.ts (#1439)
* Update createLucideIcon.ts

Added Missing ref prop

* Update createLucideIcon.ts

* Update createLucideIcon.ts

ForwardRef need RefAttributes

* Update packages/lucide-react/src/createLucideIcon.ts

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

* Update createLucideIcon.ts

In needs RefAttributes for Forward Ref

* Update createLucideIcon.ts

* Update createLucideIcon.ts

cleanup

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2023-07-19 21:16:33 +02:00
Eric Fennis
724a43e3f0 fix site build 2023-07-19 19:37:07 +02:00
Eric Fennis
fb79059fca Move dynamicImportFile to dedicated file (#1440)
* test this

* Fixes dynamicImportsMap and types
2023-07-19 19:32:34 +02:00
Daniel Bayley
9a54838538 Sort out pen[cil]/edit icons/add pencil-ruler (stationery) icon/improve metadata (#1286)
* Add `pencil-ruler` icon

* Add `pencil-eraser` icon

* Optimise `pencil` icon

* Improve stationery metadata

* Fix/rename/refine/optimise `pen`[`cil`]/`edit` icons

* Improve metadata
2023-07-18 12:26:49 +02:00
James Yeoman
c7438c0c10 Add forward and backward overlap icons (#1425)
* Add forward and backward overlap icons

* Apply code suggestions

Co-authored-by: Jakob Guddas <github@jguddas.de>

* Apply metadata suggestions

Co-authored-by: Jakob Guddas <github@jguddas.de>

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
2023-07-18 09:56:25 +02:00
Eric Fennis
667ae303f8 add inputs in workflow 2023-07-18 09:55:07 +02:00
Eric Fennis
dc1d17181f Fix createdRelease in writeReleaseMetadata 2023-07-17 21:58:10 +02:00
Eric Fennis
26abdb904d Merge branch 'main' of https://github.com/lucide-icons/lucide 2023-07-17 21:28:46 +02:00
Eric Fennis
cf0264745e Remove alias 2023-07-17 21:28:43 +02:00
Daniel Bayley
510a5ec3b9 Add boombox icon (#1249)
* Add `boombox` icon

* Rename/refine `boom-box` icon

* Refine `boom-box` icon
2023-07-17 21:16:43 +02:00
Eric Fennis
e1bc19be4e Refactor to workflow call 2023-07-17 21:12:03 +02:00
Daniel Bayley
062894e113 Add computer (GitHub Codespaces) icon (#1269)
* Add `computer` (GitHub Codespaces)  icon

* Add `pilot` (GitHub Copilot) icon

* Improve ro`bot` metadata

* Refine co`pilot` icon

* Update icons/pilot.svg

Co-authored-by: Jakob Guddas <github@jguddas.de>

* Refine `pilot` icon

* Refine `computer` icon

* Remove (co)`pilot` icon

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
2023-07-17 20:40:34 +02:00
Eric Fennis
eaa99b35f6 Merge branch 'main' of https://github.com/lucide-icons/lucide 2023-07-17 20:16:55 +02:00
Eric Fennis
7c22ccfab4 Fix 3 Add back replace changelog formatter 2023-07-17 20:16:52 +02:00
Daniel Bayley
e0e171db81 Add shopping-basket icon (#1430)
* Add `shopping-basket` icon

* Improve `shopping-cart` metadata

* Optimise `shopping-bag` icon

* Fix `shopping-basket` icon
2023-07-17 20:14:49 +02:00
Eric Fennis
3b11552e5e Merge branch 'main' of https://github.com/lucide-icons/lucide 2023-07-17 20:09:25 +02:00
Eric Fennis
d8b455b614 Remove quote github actions 2023-07-17 20:09:23 +02:00
Daniel Bayley
79eecc89f6 Add TV/controller/connector icons (#1341)
* Add [aspect] `ratio` icon

* Rename `tv-2` to `tv-flatscreen`

* Add `triangle-right` (volume) icon

* Add `antenna` icon

* Add `connector-audio-video` icon

* Improve metadata

* Add [generic] `connector` icon

* Add `connector-usb` icon

* Improve metadata

* Add `hdmi-port` icon

* Improve metadata

* Add `connector-audio-video` icon

* Improve metadata

* Rename `connector-*` icons to `cable-*`

* Rename `cable` to `cable-plugs`

* Update icons/tv-flatscreen.json

Co-authored-by: Jakob Guddas <github@jguddas.de>

* Update icons/hdmi-port.svg

Co-authored-by: Jakob Guddas <github@jguddas.de>

* Update icons/ratio.json

Co-authored-by: Jakob Guddas <github@jguddas.de>

* Revert "Rename `tv-2` to `tv-flatscreen`"

This reverts commit 7df3555837.

* Remove `cable-audio-video` icon

* Rename `cable-plugs` to `cable`

* Remove `cable-usb` icon

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
2023-07-17 20:07:29 +02:00
Eric Fennis
b943430a08 Fix 2 Change log output 2023-07-17 20:06:18 +02:00
Daniel Bayley
cebb0ee84b Refine/add scissors[-square] (snippet) icon (#1266)
* Refine `scissors` icon

* Add `scissors-square` (snippet) icon

* Add `scissors-square-dashed-bottom` (snippet) icon

* Refine `scissors-square-dashed-bottom` icon

* Improve `scissors`[`-*`] icons metadata

* Add `scissors-line-dashed` (cut along dashed line) icon

* Refine `scissors` icons gap to 2px

* Fix `scissors` optimisation

* Fix `scissors-line-dashed` optimisation

* Fix `scissors-square` optimisation

* Fix `scissors-square-dashed-bottom` optimisation
2023-07-17 19:57:02 +02:00
Eric Fennis
210c56807e Try fix changelog output 2023-07-17 19:56:14 +02:00
Daniel Bayley
c2a8f31176 Add hard-drive-[up/down]load icons (#1318)
* Add `download-hard-drive` icon

* Improve `download` metadata

* Rename `download-hard-drive` to `hard-drive-download`

* Improve metadata

* Add `hard-drive-upload` icon
2023-07-14 13:17:08 +02:00
Daniel Bayley
a3f70d5b8b Add gauge alternate icon (#1256)
* Add `gauge` alternate icon

* Improve `gauge` icons metadata

* Rename `gauge` to `gauge-circle`

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2023-07-13 16:41:47 +02:00
Eric Fennis
c97c6ed9e4 Improve types export for lucide-react (#1424)
* Add types plugin

* Add js doc comment

* Only enable dynamic imports for CJS and ESM builds

* Add documentation

* Adjust docs

* Add test for dynamic import

* Adjust note

* Adjustment in docs
2023-07-13 16:39:02 +02:00
Eric Fennis
e9d69c6948 Fix workflow actions 2023-07-07 18:37:51 +02:00
Daniel Bayley
270c935cd4 Optimise/improve plus/minus icons/metadata (#1381)
* Optimise `plus`/`minus` icons

* Optimise `square` icon

* Improve `plus`/`minus` icons metadata
2023-07-07 14:16:39 +02:00
Daniel Bayley
b7377d21eb Sort out chevron-* icons (#1355)
* Optimise `chevron-*` icons

* Improve `chevron-*` icons metadata

* Add `chevron-left-circle` icon

* Add `chevron-right-circle` icon

* Add `chevron-up-circle` icon

* Add `chevron-down-circle` icon

* Improve metadata
2023-07-07 13:46:50 +02:00
Eric Fennis
7ccf155ad4 Adjust workflow 2023-07-02 21:42:41 +02:00
Eric Fennis
c696d1e907 update workflow 2023-07-02 21:36:30 +02:00
Daniel Bayley
a8cf24e75a Sort out project management (kanban/gantt) icons (#1405)
* Improve `kanban`/`gantt` icons metadata

* Add `kanban` icon

* Rename `square-kanban` to `kanban-square`

* Rename `square-kanban-dashed` to `kanban-square-dashed`

* Rename `square-gantt` to `gantt-chart-square`

* Fix `gantt-chart` icons metadata
2023-07-02 21:12:09 +02:00
Eric Fennis
4e2773bd2d Fix indentation ci workflow 2023-07-02 14:49:02 +02:00
Eric Fennis
7c8f898893 Improve release workflow (#1397)
* first fixes release workflow

* improve workflow

* remove legacy ::set-outputs
2023-07-02 14:46:53 +02:00
Viktor Szépe
c3951d36c7 Fix latest release link in README (#1415) 2023-07-02 14:10:05 +02:00
Daniel Bayley
f958310d91 Improve scroll icons metadata (#1380) 2023-06-29 21:58:22 +02:00
Daniel Bayley
d00cbebcc4 Optimise/add table icons/simplify metadata (#1391)
* Optimise `table` icon

* Add `table-properties` (property list) icon

* Improve `table` icons metadata
2023-06-29 21:57:35 +02:00
Eric Fennis
3619dfa7f2 Bump Vitest and his dependencies (#1414)
* migration to Svelte 4

* feat: update peer dep

* feat: update lockfile

* Bump vitest and deps

---------

Co-authored-by: joseph <josephrios56@gmail.com>
2023-06-29 21:42:47 +02:00
Eric Fennis
c844bc668f Small fixes site (#1408) 2023-06-28 21:04:37 +02:00
Daniel Bayley
32dec05e0b Add VS Code Lucide SVG snippets (#1359) 2023-06-28 09:20:22 +02:00
Daniel Bayley
5394d7fceb Add tally icons (#1244)
* Add `tally` icon

* Improve `calculator` metadata

* Refine `tally` icon

* Add `tally-1` icon

* Add `tally-2` icon

* Add `tally-3` icon

* Add `tally-4` icon

* Rename `tally` icon to `tally-5`
2023-06-28 08:59:50 +02:00
Daniel Bayley
8dea4c7b7f Add shipping/container (Docker) icons (#1372)
* Add `container` (Docker) icon

* Add `arrows-up-from-line` (this way up) icon
2023-06-27 21:39:20 +02:00
Nathaniel
b8a595275d Add area-chart icon (#1376)
* Add  icon

* Move top line up 1px

* Update paths

* Optimize paths
2023-06-27 21:38:18 +02:00
Daniel Bayley
a7b3ecde63 Add gallery icons (#1389)
* Add `gallery-vertical` icon

* Add `gallery-horizontal` icon

* Add `gallery-vertical-end` icon

* Add `gallery-horizontal-end` icon

* Add `gallery-thumbnails` icon
2023-06-27 21:12:50 +02:00
Daniel Bayley
8bcfc225ce Improve dashed icons metadata (#1361)
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2023-06-25 12:57:44 +02:00
Eric Fennis
1d608db223 Improve labeler paths for site label 2023-06-25 10:02:34 +02:00
Eric Fennis
c1cb5d9bc2 Add labeler (#1379) 2023-06-23 09:37:16 +02:00
Daniel Bayley
66f51474cd Optimise gem icon/improve metadata (#1305)
* Optimise `gem` icon

* Improve `gem` metadata
2023-06-23 09:36:47 +02:00
Daniel Bayley
af0b16bc10 Add wallet-cards icon/wallet variant/optimise credit-card icon/improve metadata (#1332)
* Add `wallet` variant

* Add `wallet-cards` icon

* Optimise `credit-card` icon

* Refine `wallet` icon

* Refine `wallet` variant

* Revert `credit-card` changes

…Handled in #1350.

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2023-06-23 09:21:54 +02:00
Daniel Bayley
46d6c3c119 Add [cassette/video]-tape icons (#1248)
* Add `cassette-tape` icon

* Add `videotape` (VHS) icon

* Improve `video` metadata

* Improve `voicemail` metadata

* Refine `videotape` icon

* Update icons/cassette-tape.svg

Co-authored-by: Karsa <contact@karsa.org>

---------

Co-authored-by: Karsa <contact@karsa.org>
2023-06-23 09:08:17 +02:00
Daniel Bayley
d3b8510602 Add generic folder-root/sync icons (#1215)
* Add generic `folder-root` icon

* Add `folder-dot` icon

* Add `folder-open-dot` icon

* Add `folder-sync` icon

* Refine `folder-sync` icon
2023-06-23 08:46:41 +02:00
Daniel Bayley
97e0f30627 Add square-kanban/gantt (project) icons (#1201)
* Add `project` icon

* Add `project-template` icon

* Add `project-roadmap` icon

* Rename `project*` icons to `square-kanban*`

* Rename `project-roadmap` to `square-gantt`

* Add `gantt-chart` icon

* Improve `target` metadata

* Add `folder-kanban` (project folder) icon
2023-06-23 08:46:19 +02:00
Daniel Bayley
24676ddc8e Add layout-list-check icon (#1232)
* Add `layout-list-check` icon

* Refine `layout-list` icon

* Rename `layout-list-check` icon to `list-todo`

* Add `list-checks` icon

* Update icons/list-checks.svg

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/list-todo.svg

Co-authored-by: Karsa <contact@karsa.org>

* Revert/refine `layout-list` icon

---------

Co-authored-by: Karsa <contact@karsa.org>
2023-06-23 08:02:08 +02:00
Daniel Bayley
92689a3328 Improve undo-2 metadata (#1339)
* Add `arrow-back` icon

* Improve `undo` variant metadata

* Improve metadata

* Revert "Add `arrow-back` icon"

This reverts commit 9e8c9d97cb.
2023-06-22 09:30:11 +02:00
Daniel Bayley
b578269ecb Add rainbow icon (#1357)
* Add `rainbow` icon

* Add `cloud` variant

* Revert "Add `cloud` variant"

This reverts commit ff23e27606.
2023-06-21 09:21:33 +02:00
Daniel Bayley
d041a2b02a Add square-* code snippet/gist icons (#1068)
* Add `square-dashed-bottom` (snippet) icon

* Add `square-code` (gist) icon

* Optimise `square-dashed-bottom` (snippet) icon

* Optimise `square-code` (gist) icon

* Add `square-dashed-bottom-code` (snippet/gist) icon

* Update icons/square-dashed-bottom.svg

Co-authored-by: Jakob Guddas <github@jguddas.de>

* Refine `square-dashed-bottom-code` icon

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
2023-06-21 09:16:02 +02:00
Daniel Bayley
0253326b17 Improve award/badge/medal icons metadata (#1371) 2023-06-19 09:20:10 +02:00
Daniel Bayley
275c2cbc69 Add candy-cane/lollipop/popsicle/dessert/popcorn icons (#1149)
* Add `lolly` icon

* Add `candy` alternate icon

* Add `ice-lolly` icon

* Rename `candy` to `candy-cane`

* Update icons/lolly.svg

Co-authored-by: Karsa <contact@karsa.org>

* Rename `lolly` to `lollipop`

* Rename `ice-lolly` to `popsicle`

* Update icons/candy-cane.svg

Co-authored-by: Karsa <contact@karsa.org>

* Refine `candy-cane` icon

* Fix `lollipop` to match `search` icon

* Update icons/candy-cane.svg

Co-authored-by: Karsa <contact@karsa.org>

* Improve `lollipop` metadata

* Add `pudding` icon

* Rename `pudding` icon to `dessert`

* Add `popcorn` icon

* Refine `popcorn` icon

* Optimise `lollipop` icon

* Optimise `candy-cane` icon

* Optimise `popsicle` icon

* Add `donut` icon

* Add `cake-slice` icon

* Improve `cake` metadata

* Improve `donut` metadata

---------

Co-authored-by: Karsa <contact@karsa.org>
2023-06-19 09:10:41 +02:00
Daniel Bayley
4bd6a273db Optimise/add arrow-*navigation icons/improve metadata (#1351)
* Optimise `arrow-*` icons

* Add `arrow-up-left-square` icon

* Add `arrow-up-right-square` (share variant) icon

* Add `arrow-down-left-square` icon

* Add `arrow-down-right-square` icon

* Improve `arrow-*` icons metadata

* Improve map icons metadata
2023-06-18 22:30:09 +02:00
Eric Fennis
34155d48e7 Site improvements (#1366)
* write cions details

* add details

* Add icons details

* update gitignore

* Add node details

* Move tags api to own composable

* remove overridden var

* remopve whitespace

* log directory existence

* Fix path name

* Fix build

* Fix tags fetching

* Move max related icons to prebuild

* Improve tags scroller

* Add categories call

* cleanup

* Add active state
2023-06-15 14:44:43 +02:00
Eric Fennis
82db590192 General improvements in documentation (#1348)
* General improvements in documentation

* fix typo

* Update lucide-angular.md

* Fix build

* remove duplicated key

---------

Co-authored-by: Karsa <contact@karsa.org>
2023-06-12 22:10:15 +02:00
Daniel Bayley
70be55b78f Improve/optimise null/void/x[-*]/linter icons/metadata (#1247)
* Improve null/void metadata

* Optimise `x`/linter icons
2023-06-12 21:39:49 +02:00
Daniel Bayley
7163aeaa6b Add activity-square icon (#1356)
* Add `activity-square` icon

* Optimise `activity` icon

* Improve `activity` metadata
2023-06-12 21:36:07 +02:00
Daniel Bayley
99cd76bb35 Fix circle-dot icons metadata (#1358) 2023-06-12 21:35:18 +02:00
Eric Fennis
f5fb1ec263 Small seo improvements 2023-06-12 21:13:22 +02:00
Daniel Bayley
6916aebee4 Add generic shapes icon (#1239)
* Add generic `shapes` icon

* Update icons/shapes.svg

Co-authored-by: Karsa <contact@karsa.org>

---------

Co-authored-by: Karsa <contact@karsa.org>
2023-06-12 21:03:39 +02:00
Jakob Guddas
65d213264f Changed binary (#1188)
* Changed `binary`

Felt to aggressive.

* feat: rounded radius to zero in file-digit
2023-06-11 20:51:26 +02:00
Daniel Bayley
ee77147aff Add circle-dot-* (GitHub issue) icons (#1066)
* Improve `circle-dot` (GitHub issue) metadata

* Add `refresh-ccw-dot` icon

* Add `circle-dot-dashed` icon

* Add `circle-dashed` icon

* Improve `circle-dot` metadata

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2023-06-11 19:38:12 +02:00
Daniel Bayley
3b7b74fe86 Improve toggle/binary metadata (#1238) 2023-06-08 13:12:42 +02:00
Daniel Bayley
3a2f052ce9 Add presentation/projector icons (#1327)
* Add `projector` icon

* Add `presentation-screen` icon

* Add `presentation` (whiteboard) icon

* Consolidate `presentation` icons

* Refine `presentation` icon

* Update icons/projector.svg

Co-authored-by: Karsa <contact@karsa.org>

---------

Co-authored-by: Karsa <contact@karsa.org>
2023-06-07 10:14:32 +02:00
Eric Fennis
cf34d61971 Revert title change 2023-06-07 08:38:21 +02:00
Eric Fennis
2814a63b8f Add sitemap and OG image to site (#1347)
* Add sitemap generation

* Add og image

* Fix links

* remove comments
2023-06-07 08:25:46 +02:00
Daniel Bayley
4bcab462dc Optimise/add search[-*] icons (#1261)
* Optimise `search` icon

* Add `search-code` icon

* Add `search-x` (stop/clear) icon

* Add `search-check` (complete) icon

* Add `circle-slash` (stop/clear) icon

* Refine `search-x` icon

* Refine `search-slash` icon
2023-06-06 16:03:11 +02:00
Daniel Bayley
6c93bb97c7 Add candlestick-chart icon/bar-chart-big variants (#1320)
* Add `candlestick-chart` icon

* Add `bar-chart-big` alternate icon

* Add `bar-chart-horizontal-big` alternate icon

* Refine `bar-chart-big` icons
2023-06-06 15:59:43 +02:00
Eric Fennis
3c1993c463 Fix orbit json 2023-06-06 15:57:12 +02:00
Daniel Bayley
7a57c306c3 Optimise book icons/add book-up (git force push) alternate icon (#1205)
* Optimise `book` icons

* Add `book-up` (force `push`) alternate icon
2023-06-06 15:18:40 +02:00
Jakob Guddas
32637199f5 Added round corners to network (#1190)
* Added round corners to `network`

* Update network.svg

* Update network.svg

* Update network.svg

* Update network.svg
2023-06-06 15:11:25 +02:00
Guillermo Angeles
e490bc35b8 Add Goal icon (#1251)
* Add more music icons and another mic icon (#746)

* Revert "Add more music icons and another mic icon (#746)" (#750)

This reverts commit 57cba6ae0e.

* Add `goal` icon

* Update icons/goal.svg

Co-authored-by: Jakob Guddas <github@jguddas.de>

* Update icons/goal.svg

Co-authored-by: Karsa <contact@karsa.org>

---------

Co-authored-by: it-is-not <72697755+it-is-not@users.noreply.github.com>
Co-authored-by: Karsa <contact@karsa.org>
Co-authored-by: Guillermo Angeles <guillermo.angeles@adoptaunabuelo.com>
Co-authored-by: Guillermo Angeles <67046262+g-angeles-aua@users.noreply.github.com>
Co-authored-by: Jakob Guddas <github@jguddas.de>
2023-06-06 15:09:18 +02:00
Jakob Guddas
496058cc15 Update orbit.json (#1342) 2023-06-06 15:03:01 +02:00
Daniel Bayley
4ee46673af Add key variants (#1257)
* Add `key` alternate icon

* Update icons/key-2.svg

Co-authored-by: Karsa <contact@karsa.org>

* Refine `key` alternate icon

* Add `key` variant

* Refine `key` alternate icons

* Rename `key-2` to `key-round`

* Rename `key-3` to `key-square`

* Improve metadata

---------

Co-authored-by: Karsa <contact@karsa.org>
2023-06-04 23:16:11 +02:00
Jakob Guddas
5a46f4b87c Optimized circle-off (#1262)
* Optimized `circle-off`

* Update icons/circle-off.svg

Co-authored-by: Karsa <contact@karsa.org>

---------

Co-authored-by: Karsa <contact@karsa.org>
2023-06-04 22:20:07 +02:00
Daniel Bayley
875e8a2d06 Add versions icons (#1141)
* Add `versions` icon

* Add `versions-files` icon

* Rename `versions-files` icon to `file-stack`

* Rename `versions` icon to `square-stack`
2023-06-04 22:12:28 +02:00
Daniel Bayley
e006a171c1 Improve operator icons metadata (#1263)
* Improve operator icons metadata

* Update icons/square-slash.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2023-06-04 22:10:29 +02:00
Eric Fennis
606706e8e0 Fix gh-api for strokewidths 2023-06-04 17:52:03 +02:00
Daniel Bayley
ffc03ea1f6 Add pocket-knife icon (#1140)
* Add `pocket-knife` icon

* Update icons/pocket-knife.svg

Co-authored-by: Jakob Guddas <github@jguddas.de>

* Update icons/pocket-knife.svg

Co-authored-by: Jakob Guddas <github@jguddas.de>

* Refine `pocket-knife` icon

* Preview alternate `pocket knife` icon

* Refine `pocket-knife` icon

* Rename `pocket-knife-2` to `pocket-knife`

* Update icons/pocket-knife.svg

Co-authored-by: Karsa <contact@karsa.org>

* Improve `pocket-knife` twizzle/swoosh

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
Co-authored-by: Karsa <contact@karsa.org>
2023-06-04 17:23:01 +02:00
Daniel Bayley
b2e685262b Fix list-end icon (#1325) 2023-06-04 17:19:15 +02:00
Eric Fennis
5bfc736b61 New site! 🚀 (#1275)
* add new docs

* Add styling

* Move files

* Add active selection

* improve grid overview

* improve grid

* Add icon detail page

* Minor changes

* Fix icon preview

* optimize home card

* Add code examples

* Add gitignore

* correct temp directory

* Add first cusotmizer

* Add customizer

* fix images paths

* Add reset function

* Adjust category rendering

* Add packages section

* Some fixes

* Fix vercel build icon

* Small code adjustment

* move file

* Try this

* Add code groups with syntax highlighting

* Add search icon

* Cleanup

* update lockfile

* turnoff 404

* remove docs/iconMetaData.ts

* fix build

* Fix build 2

* cleanup

* Add icon customizer

* Fix build

* Add steps

* Add Button menu

* A lot off fixes!

* cleanup

* Fix build

* Css fixes

* Override menu icon

* try this to improve preformance

* minor improvements

* add comment

* add readme

* Add title

* minor fixes

* Fixes sliders + removes random backticks from index

* Added package list base, still WIP

* Added Guide+Source buttons to package list items

* Responsive support for XS screens

* Map categories count

* Adjust tooltip hover position

* Add see in action icon

* Add download options

* Aligns category list items to baseline and decreases category count weight

* Fixes event target error for categorylistitems

* Added icon release metadata builder

* Adds version badges to hero + icon detail overlay

* Added contributors.
Added Copy Angular.
Added release info to icon page.

* Centres contributor tooltip

* Fixed stroke step + added reset button

* Extracted reset button as a separate component for reusability

* Makes HomeIconCustomizerIcons less dense

* Fixes Button menu

* adjust versions and contributors styling on detail page

* Fix build?

* Fix build 2?

* Fix build 3

* Fix build 4

* Fix build 5?

* Add latest gh-icon changes

* Add comment

* Try fetch tags to retrieve release data

* try fetch all tags

* Add related icons

* Add stikcy search bar

* Add no results components

* Try to fix animation

* Try optimizing for categories

* Hide buggy animated icon

* minor fixes

* Add footer

* Add contributute link in footer

* Add copy name

* Add 100% preview icons

* remove site directory

* clean up

* Add redirects

* Fix build?

* fix redirect?

* minor improvements

* Fix icons preview on mobile

* Small preformance improvement

* Dark mode fixes for package icons

* Sort related icons by similarity + somewhat better name similarity matching

* Replace icon design guide images with uniform SVGs

* update lockfile

* Adds git clone to manually fetch the main repository for creating release metadata

* Remove initial v0.0.0 from release metadata

* Add extra CTA to no results behaviour

* Remove tags, as they are too overused

* Revert "Remove tags, as they are too overused"

This reverts commit 909b7563c0.

* Checkout icons from main

* Add absoluteStrokeWidth switch

* Add absolute strokewidth to home customizer

* Add absolute strokewidth to copy code button

* remove unused import

* compare build time

* improve build speed

* Try new release meta data script

* add fetch tags

* try with branch and remote

* try with url

* try without ssh

* Fix fetch tags in build file

* Cleanup

* Fix fallback

* improve release data

* delete relatedIcons.json, because it should be gitignored

* Add icon details

* Fix import

* minor fixes

* Try running script parallel

* Revert icon details

* include aliases in  release meta data

* Final fixes

* Final fixes 2

* minor code adjustment

* Fix build

* test

* Revert concurrent build flow

* switch back to concurrent build strategy

* revert icon changes

* update package.json

* update package.json

* dedube packages

---------

Co-authored-by: Karsa <karsa@karsa.org>
2023-06-04 16:59:38 +02:00
Karsa
2ebf99f591 Restores removed aliases (#1324)
* Restores removed aliases.

* Makes sure not to remove aliases next time.
2023-06-01 17:58:36 +02:00
Jakob Guddas
7a17a2f343 feat: update contributors on release (#1218)
* feat: add contributors on release

* fix: added contributors to icon schema

* fix: added missing dependencies

* feat: changed lost contributor handling

* fix: improved git log arguments

* Add all previous contributors

* Reordered icon JSON attributes

* Merged main and updated contributors

* fix: resolved contributor resolution issue

* chore: added previous contributors

* Added commits with odd e-mails manually

* Added latest icons + removed github workflow

---------

Co-authored-by: Karsa <karsa@karsa.org>
2023-06-01 12:17:23 +02:00
Daniel Bayley
4b5d343791 Add squirrel icon (#1229)
* Add `squirrel` icon

* Refine `squirrel` icon

* Refine `rat` icon

---------

Co-authored-by: Karsa <contact@karsa.org>
2023-06-01 09:25:52 +02:00
Daniel Bayley
b19b01d323 Add text-quote icon (#1230)
* Add `text-quote` icon

* Update icons/text-quote.json

Co-authored-by: Karsa <contact@karsa.org>

---------

Co-authored-by: Karsa <contact@karsa.org>
2023-06-01 09:23:58 +02:00
1853 changed files with 27654 additions and 16666 deletions

View File

@@ -0,0 +1,49 @@
name: Icon request
description: Suggest a new icon for this project
labels: ['🙌 icon request']
body:
- type: markdown
attributes:
value: |
Before submitting an icon request check if it has already been requested. If there is an open request, please add a 👍.
**Important note**: No new brand logos are allowed, see https://github.com/lucide-icons/lucide/issues/670.
Existing brand icons will also be phased out. For brand icons, consider using https://simpleicons.org, which offers purpose-built SVGs that are also on a 24×24px grid.
- type: input
id: name
attributes:
label: Icon name
description: What should this icon depict? For multiple icons, provide a comma-separated list.
validations:
required: true
- type: textarea
id: use-cases
attributes:
label: Use cases
description: Why do you need this icon? Include at least two real-life use cases per requested icon, avoiding generic descriptions like "it's a car icon".
placeholder: e.g. I need a star icon to use in my rating system.
validations:
required: true
- type: textarea
id: design-ideas
attributes:
label: Design ideas
description: What should this icon look like? Provide simple, minimalistic icon examples from other sets or your own drafts to help us visualize your request.
validations:
required: true
- type: checkboxes
id: checklist
attributes:
label: Checklist
description: Please review the following checklist before submitting your request.
options:
- label: I have searched if someone has submitted a similar issue before and there weren't any. (Please make sure to also search closed issues, as this issue might already have been resolved.)
required: true
- label: I have searched existing icons to make sure it does not already exist and I didn't find any.
required: true
- label: I am not requesting a brand logo and the art is not protected by copyright.
required: true
- label: I am not requesting an icon that includes religious, political imagery or hate symbols.
required: true
- label: I have provided appropriate use cases for the icon(s) requested.
required: true

View File

@@ -0,0 +1,90 @@
name: Bug report
description: Create a report to help us improve
labels: ['🐛 bug']
body:
- type: markdown
attributes:
value: |
Before reporting an issue, please search to see if someone has filed a similar issue before. If there is already an open issue, please add a 👍 and/or leave a comment with additional information.
- type: checkboxes
id: packages
attributes:
label: Package
description: Which Lucide packages are affected? You may select more than one.
options:
- label: lucide
- label: lucide-angular
- label: lucide-flutter
- label: lucide-preact
- label: lucide-react
- label: lucide-react-native
- label: lucide-solid
- label: lucide-svelte
- label: lucide-vue
- label: lucide-vue-next
- label: Figma plugin
- label: source/main
- label: other/not relevant
validations:
required: true
- type: input
id: version
attributes:
label: Version
description: What version of Lucide are you running?
placeholder: e.g. 0.289.1
validations:
required: true
- type: checkboxes
id: browsers
attributes:
label: Browser
description: In which browser(s) are you experiencing the issue? You may select more than one.
options:
- label: Chrome/Chromium
- label: Firefox
- label: Safari
- label: Edge
- label: iOS Safari
- label: Opera
- label: Other/not relevant
validations:
required: false
- type: checkboxes
id: operating-systems
attributes:
label: Operating system
description: In which operating systems a you experiencing the issue? You may select more than one.
options:
- label: Windows
- label: Linux
- label: macOS
- label: Other/not relevant
- type: textarea
id: description
attributes:
label: Description
description: Try to describe in detail the problem you're running into and provide additional context about your working environment if necessary.
placeholder: e.g. When I do X, Y happens instead of Z
validations:
required: true
- type: textarea
id: steps-to-reproduce
attributes:
label: Steps to reproduce
description: Please provide a detailed guide on how this issue can be reproduced or a live example with a working reproduction on Codesandbox, JSFiddle or similar.
placeholder: |
1. Import `check` icon
2. Add to a React component/view
3. Run the react app
4. Notice that the `check` isn't rendering correctly which seems a encoding problem
validations:
required: true
- type: checkboxes
id: checklist
attributes:
label: Checklist
description: Please review the following checklist before submitting your issue.
options:
- label: I have searched if someone has submitted a similar issue before and there weren't any. (Please make sure to also search closed issues, as this issue might already have been resolved.)
required: true

View File

@@ -0,0 +1,62 @@
name: lucide.dev bug report
description: Help us improve the Lucide site
labels: ['🐛 bug', '🌍 site']
body:
- type: markdown
attributes:
value: |
Before reporting an issue, please search to see if someone has filed a similar issue before. If there is already an open issue, please add a 👍 and/or leave a comment with additional information.
- type: checkboxes
id: browsers
attributes:
label: Browser
description: In which browser(s) are you experiencing the issue? You may select more than one.
options:
- label: Chrome/Chromium
- label: Firefox
- label: Safari
- label: Edge
- label: iOS Safari
- label: Opera
- label: Other/not relevant
validations:
required: false
- type: checkboxes
id: operating-systems
attributes:
label: Operating system
description: In which operating systems are you experiencing the issue? You may select more than one.
options:
- label: Windows
- label: Linux
- label: macOS
- label: Other/not relevant
- type: textarea
id: description
attributes:
label: Description
description: Try to describe in detail the problem you're running into and provide additional context about your working environment if necessary.
placeholder: e.g. When I do X, Y happens instead of Z
validations:
required: true
- type: textarea
id: stepsToReproduce
attributes:
label: Steps to reproduce
description: Please provide a detailed guide on how this issue can be reproduced.
placeholder: |
1. I click on an icon
2. I click on `Copy SVG` in the drawer
3. I paste from the clipboard
4. A base64 encoded data URI is inserted.
...
validations:
required: true
- type: checkboxes
id: checklist
attributes:
label: Checklist
description: Please review the following checklist before submitting your issue.
options:
- label: I have searched if someone has submitted a similar issue before and there weren't any. (Please make sure to also search closed issues, as this issue might already have been resolved.)
required: true

View File

@@ -0,0 +1,53 @@
name: Feature request
description: Share with us your ideas on how Lucide could be improved upon.
labels: ['💡 idea']
body:
- type: markdown
attributes:
value: |
Before submitting a new feature request, please search to see if someone has filed a similar request before. If there is already an open issue, please add a 👍 and/or leave a comment with additional information.
- type: checkboxes
id: packages
attributes:
label: Package
description: Which Lucide project do you wish this feature were added to? You may select more than one.
options:
- label: lucide
- label: lucide-angular
- label: lucide-flutter
- label: lucide-preact
- label: lucide-react
- label: lucide-react-native
- label: lucide-solid
- label: lucide-svelte
- label: lucide-vue
- label: lucide-vue-next
- label: Figma plugin
- label: all JS packages
- label: site
validations:
required: true
- type: textarea
id: description
attributes:
label: Description
description: Try to describe in detail the feature you wish existed.
placeholder: e.g. I want to be able to set extra CSS classes on icon components.
validations:
required: true
- type: textarea
id: use-cases
attributes:
label: Use cases
description: Why do you need this feature? Provide real-life use cases as to why this feature will be useful for others.
placeholder: e.g. I could use the extra classes to add animation or global custom styling to some icons.
validations:
required: true
- type: checkboxes
id: checklist
attributes:
label: Checklist
description: Please check the following items before submitting your issue.
options:
- label: I have searched the existing issues to make sure this bug has not already been reported.
required: true

View File

@@ -1,68 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
labels: "🐛 bug"
---
<!--
Before reporting an issue, please search to see if someone has filed a similar issue before. If there is already an open issue, please add a 👍 and/or leave a comment with additional information.
-->
## Prerequisites
* Version:
* Are you running from source/main:
* Are you using a released build:
* Operating system:
* Bits:
## Step to reproduce
*(Type here)*
### Actual behavior
## Any message or error
*(Type here)*
## Resources
* Links
* Screenshots
Here is what a great bug report would look like:
```
## Prerequisites
Version: Release v3.1.0
Running from: Import using webpack
Operating system: Mac OSX
Bits: 64 bits
## Step to reproduce
- Import `check` icon
- Add to a React component/view
- Run the react app
- Notice that the `check` isn't rendering correctly which seems a encoding problem
### Actual behavior:
- Import `check` icon
- Add to a React component/view
- Run the react app
- Check is displayed with correct encoding (e.g UTF-8)
## Any message or error
No console output
...
## Resources
No resources
...
```

View File

@@ -1,18 +0,0 @@
---
name: Icon request
about: Suggest an new icon for this project
labels: "🙌 icon request"
---
<!--
Before creating an icon request, please search to see if someone has requested the icon already. If there is an open request, please add a 👍.
A note about brand logos and related material : We follow the decision from Feather Icons (https://github.com/feathericons/feather/issues/763) to deprecate icons relating to brands.
You will find some in the set, but we won't add any new ones. https://simpleicons.org has 24x24 SVG icons for this purpose.
-->
## Icon Request
* Icon name:
* Use case:
* _Screenshots_ of similar icons:

View File

@@ -1,15 +0,0 @@
---
name: New icon
about: Add a new icon to the library
labels: "🎨 <icon"
---
<!-- Thanks for submitting an icon! Please make sure you read the icon design guide
at https://github.com/lucide-icons/lucide/blob/main/docs/icon-design-guide.md beforehand,
and please fill everything below. -->
- **Name of the icon** : <!-- `icon` -->
- **Tags (alternative names for this icon)** (add them in as a separate json file using the same icon name) :
- **What is the purpose of this icon?** : <!-- Shows that one can click it to... / Is used to denote or label... -->
- **100% scale preview** : <!-- upload an image -->
- **Have you considered alternative possibilities** for its naming or design? :

View File

@@ -24,15 +24,15 @@ runs:
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-lucide-preact-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-lucide-preact-pnpm-store-
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --filter lucide-preact

View File

@@ -24,15 +24,15 @@ runs:
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-lucide-preact-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-lucide-preact-pnpm-store-
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --filter .

62
.github/labeler.yml vendored Normal file
View File

@@ -0,0 +1,62 @@
# For changed dependencies
📦 dependencies:
- pnpm-lock.yaml
# For changes in documentation
📖 documentation:
- docs/*.md
- docs/**/*.md
# For changes in the site, but not markdown files
🌍 site:
- 'docs/**'
# For changes in the metadata
🫧 metadata:
- 'icons/*.json'
- categories/*
# For changes or added icons
🎨 icon:
- 'icons/*.svg'
# For changes in the lucide package
🧳 lucide package:
- 'packages/lucide/*'
# For changes in the lucide React package
⚛️ react package:
- 'packages/lucide-react/*'
# For changes in the lucide React Native package
⚛️ react native package:
- 'packages/lucide-react-native/*'
# For changes in the lucide vue packages
💎 vue package:
- 'packages/lucide-vue/*'
- 'packages/lucide-vue-next/*'
# For changes in the lucide angular package
🅰️ angular package:
- 'packages/lucide-angular/*'
# For changes in the lucide preact package
⚛️ preact package:
- 'packages/lucide-preact/*'
# For changes in the lucide svelte package
🧣 svelte package:
- 'packages/lucide-svelte/*'
# For changes in the lucide solid package
🪝 solid package:
- 'packages/lucide-solid/*'
# For changes in the lucide static package
🪨 static package:
- 'packages/lucide-static/*'
# For changes in the lucide flutter package
🏹 flutter package:
- 'packages/lucide-flutter/*'

55
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,55 @@
<!-- Thank you for contributing! -->
<!-- Insert `closes #issueNumber` here if merging this PR will resolve an existing issue -->
## What is the purpose of this pull request?
<!-- Please choose one of the following, and put an "x" next to it. -->
- [ ] New Icon
- [ ] Bug fix
- [ ] New Feature
- [ ] Documentation update
- [ ] Other:
### Description
<!-- Please insert your description here and provide info about the "what" this PR is contribution -->
### Icon use case <!-- ONLY for new icons, remove this part if not icon PR -->
<!-- What is the purpose of this icon? For each icon added, please insert at least two real life use cases (the more the better). Text like "it's a car icon" is not accepted. -->
### Alternative icon designs <!-- ONLY for new icons, remove this part if not icon PR -->
<!-- If you have any alternative icon designs, please attach them here. -->
## Icon Design Checklist <!-- ONLY for new icons, remove this part if not icon PR -->
### Concept <!-- ONLY for new icons -->
<!-- All of these requirements must be fulfilled. -->
- [ ] I have provided valid use cases for each icon.
- [ ] I have not added any a brand or logo icon.
- [ ] I have not used any hate symbols.
- [ ] I have not included any religious or political imagery.
### Author, credits & license<!-- ONLY for new icons. -->
<!-- Please choose one of the following, and put an "x" next to it. -->
- [ ] The icons are solely my own creation.
- [ ] The icons were originally created in #<issueNumber> by @<githubUser>
- [ ] I've based them on the following Lucide icons: <!-- provide the list of icons -->
- [ ] I've based them on the following design: <!-- provide source URL and license permitting use -->
### Naming <!-- ONLY for new icons -->
<!-- All of these requirements must be fulfilled. -->
- [ ] I've read and followed the [naming conventions](https://lucide.dev/guide/design/icon-design-guide#naming-conventions)
- [ ] I've named icons by what they are rather than their use case.
- [ ] I've provided meta JSON files in `icons/[iconName].json`.
### Design <!-- ONLY for new icons -->
<!-- All of these requirements must be fulfilled. -->
- [ ] I've read and followed the [icon design guidelines](https://lucide.dev/guide/design/icon-design-guide)
- [ ] I've made sure that the icons look sharp on low DPI displays.
- [ ] I've made sure that the icons look consistent with the icon set in size, optical volume and density.
- [ ] I've made sure that the icons are visually centered.
- [ ] I've correctly optimized all icons to two points of precision.
## Before Submitting <!-- For every PR! -->
<!-- All of these requirements must be fulfilled. -->
- [ ] I've read the [Contribution Guidelines](https://github.com/lucide-icons/lucide/blob/main/CONTRIBUTING.md).
- [ ] I've checked if there was an existing PR that solves the same issue.

View File

@@ -10,8 +10,9 @@ on:
jobs:
create-release:
if: github.repository == 'lucide-icons/lucide'
runs-on: ubuntu-latest
outputs:
VERSION: ${{ steps.new-version.outputs.NEW_VERSION }}
steps:
- uses: actions/checkout@v3
@@ -29,12 +30,12 @@ jobs:
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
@@ -47,7 +48,7 @@ jobs:
- name: Get latest tag
id: latest-tag
run: echo "::set-output name=LATEST_TAG::$(git describe --tags `git rev-list --tags --max-count=1`)"
run: echo "LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)" >> $GITHUB_OUTPUT
- name: Install dependencies
run: pnpm install
@@ -57,17 +58,18 @@ jobs:
- name: Create new version
id: new-version
run: echo "::set-output name=NEW_VERSION::$(.github/workflows/version-up.sh --minor)"
run: echo "NEW_VERSION=$(.github/workflows/version-up.sh --minor)" >> $GITHUB_OUTPUT
- name: Create change log
id: change-log
run: |
CHANGE_LOG=$(pnpm run generate:changelog --old-tag=${{ steps.latest-tag.outputs.LATEST_TAG }})
CHANGE_LOG="${CHANGE_LOG//'%'/'%25'}"
CHANGE_LOG="${CHANGE_LOG//$'\n'/'%0A'}"
CHANGE_LOG="${CHANGE_LOG//$'\r'/'%0D'}"
CHANGE_LOG=$(tail -n +5 <<< $CHANGE_LOG)
echo $CHANGE_LOG
echo "::set-output name=CHANGE_LOG::$CHANGE_LOG"
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
echo "CHANGE_LOG<<$EOF" >> $GITHUB_OUTPUT
echo "$CHANGE_LOG" >> $GITHUB_OUTPUT
echo "$EOF" >> $GITHUB_OUTPUT
env:
GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }}
@@ -76,11 +78,17 @@ jobs:
echo '${{ steps.new-version.outputs.NEW_VERSION }}'
echo '${{ steps.change-log.outputs.CHANGE_LOG }}'
- name: Release
- name: Create Release
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.CREATE_RELEASE_TOKEN }}
with:
tag_name: ${{ steps.new-version.outputs.NEW_VERSION }}
name: New icons ${{ steps.new-version.outputs.NEW_VERSION }}
body: ${{ steps.change-log.outputs.CHANGE_LOG }}
start-release:
if: github.repository == 'lucide-icons/lucide'
needs: create-release
uses: './.github/workflows/release.yml'
secrets: inherit
with:
version: ${{ needs.create-release.outputs.VERSION }}

12
.github/workflows/labeler.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
name: "Pull Request Labeler"
on:
- pull_request_target
jobs:
triage:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v4

View File

@@ -4,6 +4,7 @@ on:
pull_request:
paths:
- packages/lucide-angular/**
- tools/build-icons/**
- pnpm-lock.yaml
jobs:
@@ -25,12 +26,12 @@ jobs:
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

View File

@@ -4,6 +4,7 @@ on:
pull_request:
paths:
- icons/**
- tools/build-font/**
- pnpm-lock.yaml
jobs:
@@ -26,15 +27,15 @@ jobs:
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-lucide-font-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-lucide-font-pnpm-store-
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --filter outline-svg

View File

@@ -4,6 +4,7 @@ on:
pull_request:
paths:
- packages/lucide-preact/**
- tools/build-icons/**
- pnpm-lock.yaml
jobs:
@@ -25,12 +26,12 @@ jobs:
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

View File

@@ -4,6 +4,7 @@ on:
pull_request:
paths:
- packages/lucide-react-native/**
- tools/build-icons/**
- pnpm-lock.yaml
jobs:
@@ -25,12 +26,12 @@ jobs:
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

View File

@@ -4,6 +4,7 @@ on:
pull_request:
paths:
- packages/lucide-react/**
- tools/build-icons/**
- pnpm-lock.yaml
jobs:
@@ -25,12 +26,12 @@ jobs:
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

View File

@@ -4,6 +4,7 @@ on:
pull_request:
paths:
- packages/lucide-solid/**
- tools/build-icons/**
- pnpm-lock.yaml
jobs:
@@ -25,12 +26,12 @@ jobs:
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

View File

@@ -4,6 +4,7 @@ on:
pull_request:
paths:
- packages/lucide-static/**
- tools/build-icons/**
- pnpm-lock.yaml
jobs:
@@ -25,12 +26,12 @@ jobs:
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

View File

@@ -4,6 +4,7 @@ on:
pull_request:
paths:
- packages/lucide-svelte/**
- tools/build-icons/**
- pnpm-lock.yaml
jobs:
@@ -25,12 +26,12 @@ jobs:
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

View File

@@ -4,6 +4,7 @@ on:
pull_request:
paths:
- packages/lucide-vue-next/**
- tools/build-icons/**
- pnpm-lock.yaml
jobs:
@@ -25,12 +26,12 @@ jobs:
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

View File

@@ -4,6 +4,7 @@ on:
pull_request:
paths:
- packages/lucide-vue/**
- tools/build-icons/**
- pnpm-lock.yaml
jobs:
@@ -25,12 +26,12 @@ jobs:
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

View File

@@ -4,6 +4,7 @@ on:
pull_request:
paths:
- packages/lucide/**
- tools/build-icons/**
- pnpm-lock.yaml
jobs:
@@ -25,12 +26,12 @@ jobs:
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

View File

@@ -22,6 +22,20 @@ jobs:
uses: tj-actions/changed-files@v35
with:
files: icons/*.svg
- name: Generate 24px dpi preview
id: generate-24px-dpi-preview
run: |
delimiter="$(openssl rand -hex 8)"
echo "body<<$delimiter" >> $GITHUB_OUTPUT
for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
cat "$file" | # get file content
tr '\n' ' ' | # remove line breaks
sed -e 's/<svg[^>]*>/<svg>/g' | # remove attributes from svg element
base64 -w 0 | # encode svg
sed "s|.*|<img title=\"$file\" alt=\"$file\" src=\"https://lucide.dev/api/gh-icon/dpi/24/&.svg\"/> |"
done | tr '\n' ' ' >> $GITHUB_OUTPUT
echo >> $GITHUB_OUTPUT
echo "$delimiter" >> $GITHUB_OUTPUT
- name: Generate cohesion check random
id: generate-cohesion-check-random
run: |
@@ -134,6 +148,10 @@ jobs:
${{ steps.generate-3px-stroke-width.outputs.body }}<br/>
</details>
<details>
<summary>DPI Preview (24px)</summary>
${{ steps.generate-24px-dpi-preview.outputs.body }}<br/>
</details>
<details>
<summary>Icon X-rays</summary>
${{ steps.generate-x-rays.outputs.body }}
</details>

View File

@@ -5,6 +5,13 @@ on:
tags:
- 'v*'
workflow_call:
inputs:
version:
required: true
description: Version
type: string
workflow_dispatch:
inputs:
version:
@@ -12,8 +19,8 @@ on:
required: true
jobs:
pre-build:
if: github.repository == 'lucide-icons/lucide' && contains('["locness3","ericfennis", "johnletey", "karsa-mistmere"]', github.actor)
pre-release:
if: github.repository == 'lucide-icons/lucide' && contains('["ericfennis", "karsa-mistmere"]', github.actor)
runs-on: ubuntu-latest
outputs:
VERSION: ${{ steps.get_version.outputs.VERSION }}
@@ -23,19 +30,32 @@ jobs:
id: get_version
run: |
echo $VERSION_REF
echo ::set-output name=VERSION::${VERSION_REF/refs\/tags\/\v}
echo "VERSION=${VERSION_REF/refs\/tags\/\v}" >> $GITHUB_OUTPUT
env:
VERSION_REF: ${{ github.event.inputs.version || github.ref }}
VERSION_REF: ${{ inputs.version || github.event.inputs.version || github.ref }}
lucide:
release:
if: github.repository == 'lucide-icons/lucide'
runs-on: ubuntu-latest
needs: pre-build
needs: pre-release
strategy:
matrix:
package: [
'lucide',
'lucide-react',
'lucide-react-native',
'lucide-vue',
'lucide-vue-next',
'lucide-angular',
'lucide-preact',
'lucide-solid',
'lucide-svelte',
]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- uses: pnpm/action-setup@v2.0.1
name: Install pnpm
@@ -47,484 +67,38 @@ jobs:
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
run: pnpm install --frozen-lockfile
- name: Set Auth Token
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
run: npm config set //registry.npmjs.org/:_authToken ${{ inputs.NPM_TOKEN || secrets.NPM_TOKEN }}
- name: Set new version
run: pnpm --filter lucide version --new-version ${{ needs.pre-build.outputs.VERSION }} --no-git-tag-version
run: pnpm --filter ${{ matrix.package }} version --new-version ${{ needs.pre-release.outputs.VERSION }} --no-git-tag-version
- name: Build
run: pnpm --filter lucide build
run: pnpm --filter ${{ matrix.package }} build
- name: Test
run: pnpm --filter lucide test
run: pnpm --filter ${{ matrix.package }} test
- name: Publish
run: pnpm --filter lucide publish --no-git-checks
- name: Upload package.json
uses: actions/upload-artifact@v2
with:
name: lucide-package-json
path: packages/lucide/package.json
lucide-react:
if: github.repository == 'lucide-icons/lucide'
runs-on: ubuntu-latest
needs: pre-build
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- uses: pnpm/action-setup@v2.0.1
name: Install pnpm
id: pnpm-install
with:
version: 7
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- name: Set Auth Token
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
- name: Set package.json version lucide
run: pnpm --filter lucide-react version --new-version ${{ needs.pre-build.outputs.VERSION }} --no-git-tag-version
- name: Build
run: pnpm --filter lucide-react build
- name: Test
run: pnpm --filter lucide-react test
- name: Publish
run: pnpm --filter lucide-react publish --no-git-checks
- name: Upload package.json
uses: actions/upload-artifact@v2
with:
name: lucide-react-package-json
path: packages/lucide-react/package.json
lucide-react-native:
if: github.repository == 'lucide-icons/lucide'
runs-on: ubuntu-latest
needs: pre-build
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- uses: pnpm/action-setup@v2.0.1
name: Install pnpm
id: pnpm-install
with:
version: 7
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- name: Set Auth Token
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
- name: Set package.json version lucide
run: pnpm --filter lucide-react-native version --new-version ${{ needs.pre-build.outputs.VERSION }} --no-git-tag-version
- name: Build
run: pnpm --filter lucide-react-native build
- name: Test
run: pnpm --filter lucide-react-native test
- name: Publish
run: pnpm --filter lucide-react-native publish --no-git-checks
- name: Upload package.json
uses: actions/upload-artifact@v2
with:
name: lucide-react-native-package-json
path: packages/lucide-react-native/package.json
lucide-vue:
if: github.repository == 'lucide-icons/lucide'
runs-on: ubuntu-latest
needs: pre-build
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- uses: pnpm/action-setup@v2.0.1
name: Install pnpm
id: pnpm-install
with:
version: 7
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- name: Set Auth Token
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
- name: Set new version
run: pnpm --filter lucide-vue version --new-version ${{ needs.pre-build.outputs.VERSION }} --no-git-tag-version
- name: Build
run: pnpm --filter lucide-vue build
- name: Test
run: pnpm --filter lucide-vue test
- name: Publish
run: pnpm --filter lucide-vue publish --no-git-checks
- name: Upload package.json
uses: actions/upload-artifact@v2
with:
name: lucide-vue-package-json
path: packages/lucide-vue/package.json
lucide-vue-next:
if: github.repository == 'lucide-icons/lucide'
runs-on: ubuntu-latest
needs: pre-build
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- uses: pnpm/action-setup@v2.0.1
name: Install pnpm
id: pnpm-install
with:
version: 7
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- name: Set Auth Token
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
- name: Set new version
run: pnpm --filter lucide-vue-next version --new-version ${{ needs.pre-build.outputs.VERSION }} --no-git-tag-version
- name: Build
run: pnpm --filter lucide-vue-next build
- name: Test
run: pnpm --filter lucide-vue-next test
- name: Publish
run: pnpm --filter lucide-vue-next publish --no-git-checks
- name: Upload package.json
uses: actions/upload-artifact@v2
with:
name: lucide-vue-next-package-json
path: packages/lucide-vue-next/package.json
lucide-angular:
if: github.repository == 'lucide-icons/lucide'
runs-on: ubuntu-latest
needs: pre-build
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- uses: pnpm/action-setup@v2.0.1
name: Install pnpm
id: pnpm-install
with:
version: 7
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- name: Set Auth Token
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
- name: Set package.json version lucide
run: pnpm --filter lucide-angular version --new-version ${{ needs.pre-build.outputs.VERSION }} --no-git-tag-version
- name: Build
run: pnpm --filter lucide-angular build
- name: Test
run: pnpm --filter lucide-angular test
- name: Publish
run: pnpm --filter lucide-angular publish --no-git-checks --ignore-scripts
- name: Upload package.json
uses: actions/upload-artifact@v2
with:
name: lucide-angular-package-json
path: packages/lucide-angular/package.json
lucide-preact:
if: github.repository == 'lucide-icons/lucide'
runs-on: ubuntu-latest
needs: pre-build
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- uses: pnpm/action-setup@v2.0.1
name: Install pnpm
id: pnpm-install
with:
version: 7
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- name: Set Auth Token
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
- name: Set package.json version lucide
run: pnpm --filter lucide-preact version --new-version ${{ needs.pre-build.outputs.VERSION }} --no-git-tag-version
- name: Build
run: pnpm --filter lucide-preact build
- name: Test
run: pnpm --filter lucide-preact test
- name: Publish
run: pnpm --filter lucide-preact publish --no-git-checks
- name: Upload package.json
uses: actions/upload-artifact@v2
with:
name: lucide-preact-package-json
path: packages/lucide-preact/package.json
lucide-solid:
if: github.repository == 'lucide-icons/lucide'
runs-on: ubuntu-latest
needs: pre-build
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- uses: pnpm/action-setup@v2.0.1
name: Install pnpm
id: pnpm-install
with:
version: 7
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- name: Set Auth Token
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
- name: Set package.json version lucide
run: pnpm --filter lucide-solid version --new-version ${{ needs.pre-build.outputs.VERSION }} --no-git-tag-version
- name: Build
run: pnpm --filter lucide-solid build
- name: Test
run: pnpm --filter lucide-solid test
- name: Publish
run: pnpm --filter lucide-solid publish --no-git-checks
- name: Upload package.json
uses: actions/upload-artifact@v2
with:
name: lucide-solid-package-json
path: packages/lucide-solid/package.json
lucide-svelte:
if: github.repository == 'lucide-icons/lucide'
runs-on: ubuntu-latest
needs: pre-build
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- uses: pnpm/action-setup@v2.0.1
name: Install pnpm
id: pnpm-install
with:
version: 7
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- name: Set Auth Token
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
- name: Set package.json version lucide
run: pnpm --filter lucide-svelte version --new-version ${{ needs.pre-build.outputs.VERSION }} --no-git-tag-version
- name: Build
run: pnpm --filter lucide-svelte build
- name: Test
run: pnpm --filter lucide-svelte test
- name: Publish
run: pnpm --filter lucide-svelte publish --no-git-checks
- name: Upload package.json
uses: actions/upload-artifact@v2
with:
name: lucide-svelte-package-json
path: packages/lucide-svelte/package.json
run: pnpm --filter ${{ matrix.package }} publish --no-git-checks --ignore-scripts
lucide-static:
if: github.repository == 'lucide-icons/lucide'
runs-on: ubuntu-latest
needs: [pre-build, lucide-font]
needs: [pre-release, lucide-font]
steps:
- uses: actions/checkout@v3
- uses: actions/download-artifact@v2
@@ -543,24 +117,24 @@ jobs:
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
run: pnpm install --frozen-lockfile
- name: Set Auth Token
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
- name: Set new version
run: pnpm --filter lucide-static version --new-version ${{ needs.pre-build.outputs.VERSION }} --no-git-tag-version
run: pnpm --filter lucide-static version --new-version ${{ needs.pre-release.outputs.VERSION }} --no-git-tag-version
- name: Move Font
run: cp -r lucide-font packages/lucide-static/font
@@ -571,16 +145,10 @@ jobs:
- name: Publish
run: pnpm --filter lucide-static publish --no-git-checks
- name: Upload package.json
uses: actions/upload-artifact@v2
with:
name: lucide-static-package-json
path: packages/lucide-static/package.json
lucide-font:
if: github.repository == 'lucide-icons/lucide'
runs-on: ubuntu-latest
needs: pre-build
needs: pre-release
container: ericfennis/lucide-font:latest
steps:
- uses: actions/checkout@v3
@@ -598,12 +166,12 @@ jobs:
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
@@ -626,130 +194,26 @@ jobs:
name: lucide-font
path: lucide-font
lucide-flutter:
if: github.repository == 'lucide-icons/lucide'
runs-on: ubuntu-latest
needs: [pre-build, lucide-font]
container:
image: cirrusci/flutter:latest
steps:
- uses: actions/checkout@v3
- uses: actions/download-artifact@v2
- uses: actions/cache@v2
with:
path: ~/.pub-cache
key: ${{ runner.os }}-pub-${{ hashFiles('~/.pub-cache') }}
restore-keys: |
${{ runner.os }}-pub-
- name: Setup credentials
run: |
mkdir -p ~/.pub-cache
cat <<EOF > ~/.pub-cache/credentials.json
{
"accessToken": "${{ secrets.GOOGLE_OAUTH_ACCESS_TOKEN }}",
"refreshToken": "${{ secrets.GOOGLE_OAUTH_REFRESH_TOKEN }}",
"idToken": "${{ secrets.GOOGLE_OAUTH_ID_TOKEN }}",
"tokenEndpoint":"https://accounts.google.com/o/oauth2/token",
"scopes": [ "openid", "https://www.googleapis.com/auth/userinfo.email" ],
"expiration": 1629835569218
}
EOF
- name: Get packages
run: flutter pub get
working-directory: packages/lucide-flutter
- name: List lucide-font folder
run: ls lucide-font
- name: Copy assets from lucide-font directory
run: |
mkdir packages/lucide-flutter/assets
cp lucide-font/lucide.ttf packages/lucide-flutter/assets/lucide.ttf
cp lucide-font/lucide-preview.html packages/lucide-flutter/assets/lucide-preview.html
- name: Generate exports file
run: |
dart tool/generate_fonts.dart assets/lucide-preview.html
flutter format .
working-directory: packages/lucide-flutter
- name: Test
run: flutter test
working-directory: packages/lucide-flutter
- name: Copy License
run: cp ../../LICENSE ./LICENSE
working-directory: packages/lucide-flutter
- name: Update yaml
run: sed -E 's/(version:)[^\n]*/\1 ${{ needs.pre-build.outputs.VERSION }}/;' pubspec.yaml > pubspec && mv pubspec pubspec.yaml
working-directory: packages/lucide-flutter
- name: Flutter publish
run: flutter pub publish -f
working-directory: packages/lucide-flutter
- name: Upload pubspec.yaml
uses: actions/upload-artifact@v2
with:
name: lucide-flutter-pubspec-yaml
path: packages/lucide-flutter/pubspec.yaml
post-release:
if: github.repository == 'lucide-icons/lucide'
runs-on: ubuntu-latest
needs:
[
pre-build,
lucide,
lucide-react,
lucide-react-native,
lucide-vue,
lucide-vue-next,
lucide-angular,
lucide-svelte,
lucide-preact,
lucide-flutter,
lucide-font,
]
needs: [
pre-release,
lucide-font,
]
steps:
- uses: actions/checkout@v3
- uses: actions/download-artifact@v2
- name: Commit package files
run: |
mv lucide-package-json/package.json packages/lucide/package.json
mv lucide-react-package-json/package.json packages/lucide-react/package.json
mv lucide-react-native-package-json/package.json packages/lucide-react-native/package.json
mv lucide-vue-package-json/package.json packages/lucide-vue/package.json
mv lucide-preact-package-json/package.json packages/lucide-preact/package.json
mv lucide-svelte-package-json/package.json packages/lucide-svelte/package.json
mv lucide-vue-next-package-json/package.json packages/lucide-vue-next/package.json
mv lucide-angular-package-json/package.json packages/lucide-angular/package.json
mv lucide-flutter-pubspec-yaml/pubspec.yaml packages/lucide-flutter/pubspec.yaml
- name: Commit package.jsons
run: |
git add packages/*/package.json packages/lucide-flutter/pubspec.yaml
git -c user.name="Lucide Bot" -c user.email="lucide-bot@users.noreply.github.com" \
commit -m ":package: Bump lucide package versions to ${{ needs.pre-build.outputs.VERSION }}" --no-verify --quiet
git remote set-url --push origin https://lucide-bot:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY.git
git push origin HEAD:main
- name: Zip font and icons
run: |
zip -r lucide-font-${{ needs.pre-build.outputs.VERSION }}.zip lucide-font
zip -r lucide-icons-${{ needs.pre-build.outputs.VERSION }}.zip icons
zip -r lucide-font-${{ needs.pre-release.outputs.VERSION }}.zip lucide-font
zip -r lucide-icons-${{ needs.pre-release.outputs.VERSION }}.zip icons
- name: Release zip and fonts
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.CREATE_RELEASE_TOKEN }}
with:
tag_name: v${{ needs.pre-build.outputs.VERSION }}
tag_name: ${{ needs.pre-release.outputs.VERSION }}
files: |
lucide-font-${{ needs.pre-build.outputs.VERSION }}.zip
lucide-icons-${{ needs.pre-build.outputs.VERSION }}.zip
lucide-font-${{ needs.pre-release.outputs.VERSION }}.zip
lucide-icons-${{ needs.pre-release.outputs.VERSION }}.zip

18
.gitignore vendored
View File

@@ -17,6 +17,24 @@ packages/**/src/icons/*.js
packages/**/src/icons/*.ts
packages/**/src/icons/*.tsx
packages/**/src/aliases.ts
packages/**/src/dynamicIconImports.ts
packages/**/dynamicIconImports.js
packages/**/dynamicIconImports.d.ts
packages/**/dynamicIconImports.js.map
packages/**/LICENSE
categories.json
tags.json
.vercel
# docs
docs/.vitepress/cache
docs/.vitepress/dist
docs/.vitepress/.temp
docs/.vitepress/data/iconNodes
docs/.vitepress/data/iconMetaData.ts
docs/.vitepress/data/releaseMetaData.json
docs/.vitepress/data/releaseMetaData
docs/.vitepress/data/iconDetails
docs/.vitepress/data/relatedIcons.json
docs/.vercel
docs/.nitro

95
.vscode/svg.code-snippets vendored Normal file
View File

@@ -0,0 +1,95 @@
{
"Lucide SVG": {
"scope": "xml",
"description": "Base SVG with Lucide attributes.",
"prefix": [
"svg",
"lucide"
],
"body": [
"<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\"",
">",
" $0",
"</svg>"
]
},
"Rectangle": {
"scope": "xml",
"description": "SVG `rect`angle, with Lucide defaults.",
"prefix": [
"rect",
"<rect"
],
"body": "<rect width=\"${1:20}\" height=\"${2:12}\" x=\"${3:2}\" y=\"${4:6}\" rx=\"${5|2,1|}\"/>"
},
"Square": {
"scope": "xml",
"description": "SVG square `rect`angle, with Lucide defaults.",
"prefix": [
"square",
"rect",
"<rect",
"tile"
],
"body": "<rect width=\"${1:18}\" height=\"$1\" x=\"${2:3}\" y=\"${3:$2}\" rx=\"${4|2,1|}\" />"
},
"Circle": {
"scope": "xml",
"description": "SVG `circle`, with Lucide defaults.",
"prefix": [
"circle",
"<circle"
],
"body": "<circle cx=\"${2:12}\" cy=\"${3:$2}\" r=\"${1|10,2,.5|}\" />"
},
"Ellipse": {
"scope": "xml",
"description": "SVG `ellipse`.",
"prefix": [
"ellipse",
"<ellipse"
],
"body": "<ellipse cx=\"${3:12}\" cy=\"${4:$3}\" rx=\"${1:10}\" ry=\"${2:$1}\" />"
},
"Path": {
"scope": "xml",
"description": "SVG custom `path`.",
"prefix": [
"path",
"<path",
"polyline",
"<polyline",
"polygon",
"<polygon"
],
"body": "<path d=\"${1|M,m|}$0\" />"
},
"Line": {
"scope": "xml",
"description": "SVG `path`, preffered to `line` in Lucide.",
"prefix": [
"line",
"<line",
"minus"
],
"body": "<path d=\"M${3:5} ${4:12}${1|h,v|}${2:14}\" />"
},
"Dot": {
"scope": "xml",
"description": "SVG small dot, within the Lucide guidelines.",
"prefix": [
"dot",
"."
],
"body": "<path d=\"M ${1:12} ${2:$1}h.01\" />"
}
}

View File

@@ -25,19 +25,19 @@ Guidelines for pull requests:
Please make sure you follow the icon guidelines, that should be followed to keep quality and consistency when making icons for Lucide.
Read it here: [ICON_GUIDELINES](/docs/icon-design-guide.md).
Read it here: [ICON_GUIDELINES](https://lucide.dev/docs/icon-design-guide).
### Editor guides
Here you can find instructions on how to implement the guidelines with different vector graphics editors:
#### [Adobe Illustrator Guide](/docs/illustrator-guide.md)
#### [Adobe Illustrator Guide](https://lucide.dev/docs/illustrator-guide)
You can also [download an Adobe Illustrator template](/docs/templates/illustrator-template.ai).
You can also [download an Adobe Illustrator template](https://lucide.dev/templates/illustrator-template.ai).
#### [Inkscape Guide](/docs/inkscape-guide.md)
#### [Inkscape Guide](https://lucide.dev/docs/inkscape-guide)
#### [Figma Guide](/docs/figma-guide.md)
#### [Figma Guide](https://lucide.dev/docs/figma-guide)
### Submitting Multiple Icons
@@ -70,7 +70,7 @@ pnpm install # Install dependencies, including the workspace packages
### Packages -> PNPM Workspaces
To distribute different packages we use PNPM workspaces. Before you start make sure you are familiar with this concept. The concept of working in workspaces is created by Yarn, they have a well written introduction: [yarn workspaces](https://classic.yarnpkg.com/lang/en/docs/workspaces).
To distribute different packages we use PNPM workspaces. Before you start make sure you are familiar with this concept. The concept of working in workspaces is created by Yarn, they have a well written introduction: [yarn workspaces](https://classic.yarnpkg.com/lang/enhttps://lucide.dev/docs/workspaces).
The configured directory for workspaces is the [packages](./packages) directory, located in the root directory. There you will find all the current packages from lucide.
There are more workspaces defined, see [`pnpm-workspace.yaml`](./pnpm-workspace.yaml).
@@ -172,11 +172,11 @@ Includes usefully scripts to automate certain jobs. Big part of the scripts is t
### site
The lucide.dev website using [Nextjs](https://nextjs.org).
The lucide.dev website is using [vitepress](https://vitepress.dev/) to generate the static website. The markdown files are located in the docs directory.
## Documentation
The documentation files are located in the [docs](./docs) directory. All these markdown files will be loaded in the build of the lucide.dev website.
The documentation files are located in the [docs](https://github.com/lucide-icons/lucide/tree/main/docs) directory. All these markdown files will be loaded in the build of the lucide.dev website.
Feel free to write, adjust or add new markdown files to improve our documentation.

View File

@@ -166,8 +166,8 @@ For more details, see the [documentation](https://github.com/lucide-icons/lucide
### Static (svg sprite, font, icons ..)
Assets:
[Font Files](https://github.com/lucide-icons/lucide/releases/tag/latest)
[SVG Files](https://github.com/lucide-icons/lucide/releases/tag/latest)
[Font Files](https://github.com/lucide-icons/lucide/releases/latest)
[SVG Files](https://github.com/lucide-icons/lucide/releases/latest)
[SVG Sprite](https://cdn.jsdelivr.net/npm/lucide-static@latest/sprite.svg)
NPM package
@@ -286,5 +286,5 @@ Thank you to all the people who contributed to Lucide!
## Sponsors
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
<img src="./site/public/vercel.svg" alt="Powered by Vercel" width="200" />
<img src="/docs/public/vercel.svg" alt="Powered by Vercel" width="200" />
</a>

View File

@@ -1,5 +1,5 @@
{
"$schema": "../category.schema.json",
"title": "Transportation",
"icon": "train"
"icon": "train-front"
}

View File

@@ -0,0 +1,11 @@
import { eventHandler, setResponseHeader } from 'h3'
import iconMetaData from '../../data/iconMetaData'
export default eventHandler((event) => {
setResponseHeader(event, 'Cache-Control', 'public, max-age=86400')
setResponseHeader(event, 'Access-Control-Allow-Origin', '*')
return Object.fromEntries(
Object.entries(iconMetaData).map(([name, { categories }]) => [ name, categories ])
)
})

View File

@@ -0,0 +1,41 @@
import { eventHandler, setResponseHeader, defaultContentType } from 'h3'
import { renderToString, renderToStaticMarkup } from 'react-dom/server'
import { createElement } from 'react'
import SvgPreview from '../../lib/SvgPreview/index.tsx';
import iconNodes from '../../data/iconNodes'
import createLucideIcon from 'lucide-react/src/createLucideIcon'
import Backdrop from '../../lib/SvgPreview/Backdrop.tsx';
export default eventHandler((event) => {
const { params } = event.context
const [name, svgData] = params.data.split('/');
const data = svgData.slice(0, -4);
const src = Buffer.from(data, 'base64').toString('utf8');
const children = []
if (name in iconNodes) {
const iconNode = iconNodes[name]
const LucideIcon = createLucideIcon(name, iconNode)
const svg = renderToStaticMarkup(createElement(LucideIcon))
const backdropString = svg.replace(/<svg[^>]*>|<\/svg>/g, '');
children.push(createElement(Backdrop, { backdropString, src }))
}
const svg = Buffer.from(
// We can't use jsx here, is not supported here by nitro.
renderToString(createElement(SvgPreview, {src, showGrid: true}, children)).replace(
/>/,
'><style>@media screen and (prefers-color-scheme: dark) { svg { stroke: #fff } }</style>'
)
).toString('utf8');
defaultContentType(event, 'image/svg+xml')
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000')
return svg
})

View File

@@ -0,0 +1,73 @@
import { eventHandler, setResponseHeader, defaultContentType } from 'h3';
import { Resvg, initWasm } from '@resvg/resvg-wasm';
import wasm from './loadWasm';
var initializedResvg = initWasm(wasm);
export default eventHandler(async (event) => {
const { params = {} } = event.context;
await initializedResvg;
const imageSize = 96;
const [iconSizeString, svgData] = params.data.split('/');
const iconSize = parseInt(iconSizeString, 10);
const data = svgData.slice(0, -4);
const src = Buffer.from(data, 'base64').toString('utf8');
const svg = (src.includes('<svg') ? src : `<svg>${src}</svg>`)
.replace(/(\r\n|\n|\r)/gm, '')
.replace(
/<svg[^>]*/,
`<svg
xmlns="http://www.w3.org/2000/svg"
width="${iconSize}"
height="${iconSize}"
viewBox="0 0 24 24"
fill="none"
stroke="#fff"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
`
);
const resvg = new Resvg(svg, { background: '#000' });
const pngData = resvg.render();
const pngBuffer = Buffer.from(pngData.asPng());
defaultContentType(event, 'image/svg+xml');
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000');
return `
<svg xmlns="http://www.w3.org/2000/svg" width="${imageSize}" height="${imageSize}" viewBox="0 0 ${imageSize} ${imageSize}">
<style>
@media screen and (prefers-color-scheme: light) {
#fallback-background { fill: transparent; }
}
@media screen and (prefers-color-scheme: dark) {
#fallback-background { fill: transparent; }
rect { fill: #fff; }
}
</style>
<mask id="mask">
<image
width="${imageSize}"
height="${imageSize}"
href="data:image/png;base64,${pngBuffer.toString('base64')}"
image-rendering="pixelated"
/>
</mask>
<rect
id="fallback-background"
width="${imageSize}"
height="${imageSize}" ry="${imageSize / 24}"
fill="#fff"
/>
<rect
width="${imageSize}"
height="${imageSize}"
fill="#000"
mask="url(#mask)"
/>
</svg>`;
});

View File

@@ -0,0 +1,15 @@
import fs from 'fs';
import module from 'node:module';
/* WASM_IMPORT */
let wasm;
if (process.env.NODE_ENV === 'development') {
const require = module.createRequire(import.meta.url);
wasm = fs.readFileSync(require.resolve('@resvg/resvg-wasm/index_bg.wasm'));
} else {
wasm = resvg_wasm;
}
export default wasm;

View File

@@ -0,0 +1,44 @@
import { eventHandler, setResponseHeader, defaultContentType } from 'h3'
import { renderToString } from 'react-dom/server'
import { createElement } from 'react'
import SvgPreview from '../../../lib/SvgPreview/index.tsx';
import createLucideIcon, { IconNode } from 'lucide-react/src/createLucideIcon'
import { parseSync } from 'svgson';
export default eventHandler((event) => {
const { params } = event.context
const [strokeWidth, svgData] = params.data.split('/');
const data = svgData.slice(0, -4);
const src = Buffer.from(data, 'base64').toString('utf8');
const Icon = createLucideIcon(
'icon',
parseSync(src.includes('<svg') ? src : `<svg>${src}</svg>`).children.map(
({ name, attributes }) => [name, attributes]
) as IconNode
);
const svg = Buffer.from(
// We can't use jsx here, is not supported here by nitro.
renderToString(createElement(Icon, { strokeWidth }))
.replace(/fill\="none"/, 'fill="#fff"')
.replace(
/>/,
`><style>
@media screen and (prefers-color-scheme: light) {
svg { fill: transparent !important; }
}
@media screen and (prefers-color-scheme: dark) {
svg { stroke: #fff; fill: transparent !important; }
}
</style>`
)
).toString('utf8');
defaultContentType(event, 'image/svg+xml')
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000')
return svg
})

View File

@@ -0,0 +1,30 @@
import { eventHandler, getQuery, setResponseHeader } from 'h3'
import iconNodes from '../../data/iconNodes'
import { IconNodeWithKeys } from '../../theme/types'
export default eventHandler((event) => {
const query = getQuery(event)
const withUniqueKeys = query.withUniqueKeys === 'true'
setResponseHeader(event, 'Cache-Control', 'public, max-age=86400')
setResponseHeader(event, 'Access-Control-Allow-Origin', '*')
if (withUniqueKeys) {
return iconNodes
}
return Object.entries(iconNodes).reduce((acc, [name, iconNode]) => {
if (withUniqueKeys) {
return [name, iconNode]
}
const newIconNode = (iconNode as IconNodeWithKeys).map(([name, { key, ...attrs}]) => {
return [name, attrs]
})
acc[name] = newIconNode
return acc
}, {})
})

View File

@@ -0,0 +1,45 @@
import { eventHandler, getQuery, setResponseHeader, createError } from 'h3'
import iconNodes from '../../data/iconNodes'
import createLucideIcon from 'lucide-react/src/createLucideIcon'
import { renderToString } from 'react-dom/server'
import { createElement } from 'react'
export default eventHandler((event) => {
const { params } = event.context
const iconNode = iconNodes[params.iconName]
if (iconNode == null) {
const error = createError({
statusCode: 404,
message: `Icon "${params.iconName}" not found`,
})
return sendError(event, error)
}
const width = getQuery(event).width || undefined
const height = getQuery(event).height || undefined
const color = getQuery(event).color || undefined
const strokeWidth = getQuery(event).strokeWidth || undefined
const LucideIcon = createLucideIcon(params.iconName, iconNode)
const svg = Buffer.from(
renderToString(
createElement(LucideIcon, {
width,
height,
color: color ? `#${color}` : undefined,
strokeWidth,
}
))
).toString('utf8');
defaultContentType(event, 'image/svg+xml')
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000')
setResponseHeader(event, 'Access-Control-Allow-Origin', '*')
return svg
})

View File

@@ -0,0 +1,11 @@
import { eventHandler, setResponseHeader } from 'h3'
import iconMetaData from '../../data/iconMetaData'
export default eventHandler((event) => {
setResponseHeader(event, 'Cache-Control', 'public, max-age=86400')
setResponseHeader(event, 'Access-Control-Allow-Origin', '*')
return Object.fromEntries(
Object.entries(iconMetaData).map(([name, { tags }]) => [ name, tags ])
)
})

View File

@@ -0,0 +1,3 @@
export default eventHandler(() => {
return { nitro: 'Is Awesome! asda' }
})

124
docs/.vitepress/config.ts Normal file
View File

@@ -0,0 +1,124 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vitepress'
import sidebar from './sidebar';
const title = "Lucide";
const socialTitle = "Lucide Icons";
const description = "Beautiful & consistent icon toolkit made by the community."
// https://vitepress.dev/reference/site-config
export default defineConfig({
title,
description,
cleanUrls: true,
outDir: '.vercel/output/static',
vite: {
resolve: {
alias: [
{
find: /^.*\/VPIconAlignLeft\.vue$/,
replacement: fileURLToPath(
new URL('./theme/components/overrides/VPIconAlignLeft.vue', import.meta.url)
)
},
{
find: /^.*\/VPFooter\.vue$/,
replacement: fileURLToPath(
new URL('./theme/components/overrides/VPFooter.vue', import.meta.url)
)
}
]
},
},
head: [
[ 'script', {
src: 'https://analytics.lucide.dev/js/script.js',
'data-domain': 'lucide.dev',
defer: ''
}],
[ 'meta', {
property:"og:locale",
content:"en_US"
}],
[ 'meta', {
property:"og:type",
content:"website"
}],
[ 'meta', {
property:"og:site_name",
content: title,
}],
[ 'meta', {
property:"og:title",
content: socialTitle,
}],
[ 'meta', {
property:"og:description",
content: description
}],
[ 'meta', {
property:"og:url",
content:"https://lucide.dev"
}],
[ 'meta', {
property:"og:image",
content: "https://lucide.dev/og.png"
}],
[ 'meta', {
property:"og:image:width",
content:"1200"
}],
[ 'meta', {
property:"og:image:height",
content:"630"
}],
[ 'meta', {
property:"og:image:type",
content:"image/png"
}],
[ 'meta', {
property:"twitter:card",
content:"summary_large_image"
}],
[ 'meta', {
property:"twitter:title",
content: socialTitle,
}],
[ 'meta', {
property:"twitter:description",
content: description
}],
[ 'meta', {
property:"twitter:image",
content:"https://lucide.dev/og.png"
}],
],
themeConfig: {
// https://vitepress.dev/reference/default-theme-config
logo: {
light: '/logo.light.svg',
dark: '/logo.dark.svg'
},
nav: [
{ text: 'Icons', link: '/icons/' },
{ text: 'Guide', link: '/guide/' },
{ text: 'Packages', link: '/packages' },
{ text: 'License', link: '/license' },
],
sidebar,
socialLinks: [
{ icon: 'github', link: 'https://github.com/lucide-icons/lucide' },
{ icon: 'discord', link: 'https://discord.gg/EH6nSts' }
],
footer: {
message: 'Released under the ISC License.',
copyright: `Copyright © ${new Date().getFullYear()} Lucide Contributors`
},
editLink: {
pattern: 'https://github.com/lucide-icons/lucide/edit/main/docs/:path'
},
},
sitemap: {
hostname: 'https://lucide.dev/'
}
})

View File

@@ -22,6 +22,7 @@
"name": "hyva-lucide-icons",
"description": "Implementation of Lucide icon's using Hyvä's svg php viewmodal to render icons for Magento 2 Hyva theme based projects.",
"icon": "/framework-logos/hyva.svg",
"iconDark": "/framework-logos/hyva-dark.svg",
"shields": [
{
"alt": "Latest Stable Version",
@@ -41,6 +42,7 @@
"name": "eleventy-lucide-icons",
"description": "Using this plugin, Eleventy projects can incorporate Lucide icons. it makes it simple to use Lucide icons into your themes via shortcodes, improving your website's overall usability and visual appeal.",
"icon": "/framework-logos/11ty.svg",
"iconClass": "package-icon-invert",
"shields": [
{
"alt": "Latest Stable Version",

View File

@@ -0,0 +1,71 @@
import React from 'react';
interface BackdropProps {
src: string
backdropString: string
}
const Backdrop = ({ src, backdropString }: BackdropProps): JSX.Element => {
return (
<>
<defs xmlns="http://www.w3.org/2000/svg">
<pattern
id="pattern"
width=".1"
height=".1"
patternUnits="userSpaceOnUse"
patternTransform="rotate(45 50 50)"
>
<line stroke="red" strokeWidth={0.1} y2={1} />
<line stroke="red" strokeWidth={0.1} y2={1} />
</pattern>
</defs>
<mask id="svg-preview-backdrop-mask-outline" maskUnits="userSpaceOnUse">
<g stroke="#fff" dangerouslySetInnerHTML={{ __html: backdropString }} />
<g dangerouslySetInnerHTML={{ __html: src }} strokeWidth={2.05} />
</mask>
<mask id="svg-preview-backdrop-mask-fill" maskUnits="userSpaceOnUse">
<g stroke="#fff" dangerouslySetInnerHTML={{ __html: backdropString }} />
<g dangerouslySetInnerHTML={{ __html: src }} strokeWidth={2.05} />
<g strokeWidth={1.75} dangerouslySetInnerHTML={{ __html: backdropString }} />
</mask>
<g
strokeWidth={2.25}
stroke="url(#pattern)"
mask={'url(#svg-preview-backdrop-mask-outline)'}
>
<rect
x="0"
y="0"
width="24"
height="24"
fill="url(#pattern)"
opacity={0.5}
stroke="none"
/>
</g>
<rect
x="0"
y="0"
width="24"
height="24"
fill="url(#pattern)"
stroke="none"
mask={'url(#svg-preview-backdrop-mask-fill)'}
/>
<rect
x="0"
y="0"
width="24"
height="24"
fill="red"
opacity={0.5}
stroke="none"
mask={'url(#svg-preview-backdrop-mask-fill)'}
/>
</>
)
}
export default Backdrop;

View File

@@ -4,7 +4,7 @@ import { getPaths, assert } from './utils';
const Grid = ({
radius,
fill,
fill = '#fff',
...props
}: {
strokeWidth: number;
@@ -12,6 +12,7 @@ const Grid = ({
} & PathProps<'stroke', 'strokeWidth'>) => (
<g className="svg-preview-grid-group" strokeLinecap="butt" {...props}>
<rect
className="svg-preview-grid-rect"
width={24 - props.strokeWidth}
height={24 - props.strokeWidth}
x={props.strokeWidth / 2}
@@ -207,7 +208,12 @@ const SvgPreview = React.forwardRef<
>(({ src, children, showGrid = false, ...props }, ref) => {
const paths = typeof src === 'string' ? getPaths(src) : src;
const darkModeCss = `@media screen and (prefers-color-scheme: dark) {
const darkModeCss = `@media screen and (prefers-color-scheme: light) {
.svg-preview-grid-rect { fill: none }
}
@media screen and (prefers-color-scheme: dark) {
.svg-preview-grid-rect { fill: none }
.svg
.svg-preview-grid-group,
.svg-preview-radii-group,
.svg-preview-shadow-mask-group,

View File

@@ -0,0 +1,28 @@
import fs from "fs";
import path from "path";
import {Category, IconEntity} from "../theme/types";
const directory = path.join(process.cwd(), "../categories");
export function getAllCategoryFiles(): Category[] {
const fileNames = fs.readdirSync(directory).filter((file) => path.extname(file) === '.json');
return fileNames.map((fileName) => {
const name = path.basename(fileName, '.json')
const fileContent = fs.readFileSync(path.join(directory, fileName), 'utf8')
const parsedFileContent = JSON.parse(fileContent)
return {
name,
title: parsedFileContent.title,
}
});
}
export function mapCategoryIconCount(categories: Category[], icons: { categories: IconEntity['categories'] }[]) {
return categories.map((category) => ({
...category,
iconCount: icons.reduce((acc, curr) => (curr.categories.includes(category.name) ? ++acc : acc), 0)
}))
}

View File

@@ -0,0 +1,218 @@
import {
BUNDLED_LANGUAGES,
type IThemeRegistration
} from 'shiki'
import {
getHighlighter,
} from 'shiki-processor'
type CodeExampleType = {
title: string,
lang: string,
codes: {
language?: string,
code: string,
metastring?: string,
}[],
}[]
const getIconCodes = (): CodeExampleType => {
return [
{
lang: 'html',
title: 'HTML',
codes: [
{
language: 'html',
code: `<i data-lucide-name="Name"></i>
`,
},
],
},
{
lang: 'tsx',
title: 'React',
codes: [
{
language: 'tsx',
code: `import { PascalCase } from 'lucide-react';
const App = () => {
return (
<PascalCase />
);
};
export default App;
`,
},
],
},
{
lang: 'vue',
title: 'Vue 3',
codes: [
{
language: 'vue',
code: `<script setup>
import { PascalCase } from 'lucide-vue-next';
</script>
<template>
<PascalCase />
</template>
`,
},
],
},
{
lang: 'svelte',
title: 'Svelte',
codes: [
{
language: 'svelte',
code: `<script>
import { PascalCase } from 'lucide-svelte';
</script>
<PascalCase />
`,
},
],
},
{
lang: 'preact',
title: 'Preact',
codes: [
{
language: 'tsx',
code: `import { PascalCase } from 'lucide-preact';
const App = () => {
return (
<PascalCase />
);
};
export default App;
`,
},
],
},
{
lang: 'solid',
title: 'Solid',
codes: [
{
language: 'tsx',
code: `import { PascalCase } from 'lucide-solid';
const App = () => {
return (
<PascalCase />
);
};
export default App;
`,
},
],
},
{
lang: 'angular',
title: 'Angular',
codes: [
{
language: 'tsx',
code: `// app.module.ts
import { LucideAngularModule, PascalCase } from 'lucide-angular';
@NgModule({
imports: [
LucideAngularModule.pick({ PascalCase })
],
})
// app.component.html
<lucide-icon name="Name"></lucide-icon>
`,
},
],
},
{
lang: 'html',
title: 'Icon Font',
codes: [
{
language: 'html',
code: `<style>
@import ('~lucide-static/font/Lucide.css');
</style>
<div class="icon-Name"></div>
`,
},
],
},
{
lang: 'dart',
title: 'Flutter',
codes: [
{
language: 'dart',
code: `Icon(LucideIcons.Name);
`,
},
],
},
]
}
export type ThemeOptions =
| IThemeRegistration
| { light: IThemeRegistration; dark: IThemeRegistration }
const highLightCode = async (code: string, lang: string, active?: boolean) => {
const highlighter = await getHighlighter({
themes: ['material-theme-palenight'],
langs: [...BUNDLED_LANGUAGES],
processors: []
})
const highlightedCode = highlighter.codeToHtml(code, {
lang,
// lineOptions,
theme: 'material-theme-palenight'
}).replace('background-color: #292D3E', '')
return `<div class="language-${lang} ${active ? 'active' : ''}">
<button title="Copy Code" class="copy"></button>
<span class="lang">${lang}</span>
${highlightedCode}
</div>`
}
export default async function createCodeExamples() {
const codes = getIconCodes();
const codeExamplePromises = codes.map(async (codeTemplate, index) => {
const { title, lang, codes } = codeTemplate;
const isFirst = index === 0;
const code = await highLightCode(codes[0].code, codes[0].language || lang, isFirst);
return {
title,
language: codes[0].language || lang,
code,
};
})
return Promise.all(codeExamplePromises);
}

View File

@@ -1,7 +1,7 @@
import { promises as fs, constants } from 'fs';
import path from 'path';
import yaml from 'js-yaml'
import { PackageItem } from '../components/Package';
import { PackageItem } from '../theme/types';
const fileExist = (filePath) => fs.access(filePath, constants.F_OK).then(() => true).catch(() => false)

View File

@@ -1,12 +1,13 @@
import { createLucideIcon, LucideProps } from "lucide-react"
import { IconEntity } from "src/types"
import { createLucideIcon } from "lucide-react/src/lucide-react"
import { type LucideProps, type IconNode } from "lucide-react/src/createLucideIcon"
import { IconEntity } from "../theme/types"
import { renderToStaticMarkup } from 'react-dom/server';
import { IconContent } from "./generateZip";
const getFallbackZip = (icons: IconEntity[], params: LucideProps) => {
return icons
.map<IconContent>((icon) => {
const Icon = createLucideIcon(icon.name, icon.iconNode)
const Icon = createLucideIcon(icon.name, icon.iconNode as IconNode)
const src = renderToStaticMarkup(<Icon {...params} />)
return [icon.name, src]
})

View File

@@ -0,0 +1,47 @@
import fs from "fs";
import path from "path";
import { IconNodeWithKeys } from "../theme/types";
import iconNodes from '../data/iconNodes'
import releaseMeta from "../data/releaseMetaData.json";
const DATE_OF_FORK = '2020-06-08T16:39:52+0100';
const directory = path.join(process.cwd(), "../icons");
export interface GetDataOptions {
withChildKeys?: boolean
}
export async function getData(name: string) {
const jsonPath = path.join(directory, `${name}.json`);
const jsonContent = fs.readFileSync(jsonPath, "utf8");
const { tags, categories, contributors } = JSON.parse(jsonContent);
const iconNode = iconNodes[name]
const releaseData = releaseMeta?.[name] ?? {
"createdRelease": {
"version": "0.0.0",
"date": DATE_OF_FORK
},
"changedRelease": {
"version": "0.0.0",
"date": DATE_OF_FORK
}
}
return {
name,
tags,
categories,
iconNode,
contributors,
...releaseData
};
}
export async function getAllData(): Promise<{ name: string, iconNode: IconNodeWithKeys}[]> {
const names = Object.keys(iconNodes);
return Promise.all(names.map((name) => getData(name)));
}

118
docs/.vitepress/sidebar.ts Normal file
View File

@@ -0,0 +1,118 @@
import { DefaultTheme, UserConfig } from "vitepress"
const sidebar: UserConfig<DefaultTheme.Config>['themeConfig']['sidebar'] = {
'guide':[
{
text: 'Introduction',
items: [
{ text: 'What is lucide?', link: '/guide/' },
{ text: 'Installation', link: '/guide/installation' },
{ text: 'Comparison', link: '/guide/comparison' }
]
},
// {
// text: 'Using Icons',
// items: [
// {
// text: 'How to use icons',
// link: 'how-to-use-icons'
// },
// {
// text: 'Styling icons',
// link: 'styling-icons'
// },
// {
// text: 'Accessibility',
// link: 'accessibility'
// },
// {
// text: 'What should I use',
// link: 'what-should-i-use'
// },
// ]
// },
{
text: 'Packages',
items: [
{
text: 'Lucide',
link: '/guide/packages/lucide'
},
{
text: 'Lucide React',
link: '/guide/packages/lucide-react'
},
{
text: 'Lucide React Native',
link: '/guide/packages/lucide-react-native'
},
{
text: 'Lucide Vue',
link: '/guide/packages/lucide-vue'
},
{
text: 'Lucide Vue Next (Vue 3)',
link: '/guide/packages/lucide-vue-next'
},
{
text: 'Lucide Svelte',
link: '/guide/packages/lucide-svelte'
},
{
text: 'Lucide Solid',
link: '/guide/packages/lucide-solid'
},
{
text: 'Lucide Preact',
link: '/guide/packages/lucide-preact'
},
{
text: 'Lucide Angular',
link: '/guide/packages/lucide-angular'
},
{
text: 'Lucide Static',
link: '/guide/packages/lucide-static'
},
{
text: 'Lucide Flutter',
link: '/guide/packages/lucide-flutter'
},
]
},
{
text: 'Contributing',
items: [
{
text: 'Icon Design Principles',
link: '/guide/design/icon-design-guide'
},
{
text: 'Designing in Illustrator',
link: '/guide/design/illustrator-guide'
},
{
text: 'Designing in InkScape',
link: '/guide/design/inkscape-guide'
},
{
text: 'Designing in Figma',
link: '/guide/design/figma-guide'
},
]
},
],
'icons': [
{ text: '', link: '/' },
// { text: 'Categorized', link: '/icons/categorized' },
// {
// text: 'Categories',
// items: [
// ...(getAllCategoryFiles().map((category) => ({ text: category, link: `/icons/category/${category}` })))
// ]
// }
],
}
export default sidebar

View File

@@ -0,0 +1,11 @@
<template>
<div class="container">
<slot />
</div>
</template>
<style scoped>
.container {
padding: 32px;
}
</style>

View File

@@ -0,0 +1,62 @@
<script setup lang="ts">
import { computed } from 'vue';
import { useRouter } from 'vitepress';
const { go } = useRouter()
const props = defineProps<{
href?: string
}>()
const component = computed(() => props.href ? 'a' : 'div')
</script>
<template>
<component
:is="component"
:href="href"
class="badge"
@click="props?.href ? go(href) : undefined"
>
<slot/>
</component>
</template>
<style>
.badge, a.badge {
display: block;
border: 1px solid transparent;
text-align: center;
font-weight: 600;
padding: 2px 12px;
white-space: nowrap;
transition: color 0.25s, border-color 0.25s, background-color 0.25s;
border-radius: 6px;
background-color: var(--vp-c-bg-alt);
color: var(--vp-c-text-1);
/* width: 56px;
height: 56px; */
font-size: 16px;
}
.badge[href]:hover, a.badge:hover {
border-color: var(--vp-button-alt-hover-border);
color: var(--vp-button-alt-hover-text);
background-color: var(--vp-button-alt-hover-bg);
text-decoration: none;
}
.badge[href]:active {
border-color: var(--vp-button-alt-active-border);
color: var(--vp-button-alt-active-text);
background-color: var(--vp-button-alt-active-bg);
}
.badge.active {
border-color: var(--vp-c-brand);
/* color: var(--vp-button-alt-active-text);
background-color: var(--vp-button-alt-active-bg); */
}
</style>

View File

@@ -0,0 +1,187 @@
<script setup lang="ts">
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue'
import { computed, ref } from 'vue'
import {
Listbox,
ListboxButton,
ListboxOptions,
ListboxOption,
} from '@headlessui/vue'
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import { chevronUp } from '../../../data/iconNodes'
import { useStorage } from '@vueuse/core'
interface Props {
options: {
text: string
onClick?: () => void
}[],
callOptionOnClick?: boolean
buttonClass?: string
id: string
popoverPosition?: 'top' | 'bottom'
}
const props = withDefaults(defineProps<Props>(), {
callOptionOnClick: false,
popoverPosition: 'bottom'
})
const emit = defineEmits(['click', 'optionClick'])
const buttonRef = ref(null)
const selectedOption = useStorage(props.id, props.options[0].text)
const selectionOptionAction = computed(() => props.options.find(option => option.text === selectedOption.value).onClick)
function onClick(event) {
selectionOptionAction.value()
emit('click', event)
}
function onOptionClick(event, option) {
if(!props.callOptionOnClick) {
return
}
option.onClick()
emit('optionClick', event)
}
const ChevronUp = createLucideIcon('ChevronUp', chevronUp)
</script>
<template>
<Listbox v-model="selectedOption">
<div class="menu" >
<div class="button-wrapper">
<VPButton
v-bind="$attrs"
:text="selectedOption"
@click="onClick"
theme="alt"
class="main-button"
:class="[props.buttonClass]"
ref="buttonRef"
/>
<ListboxButton
:as="VPButton"
:text="''"
theme="alt"
class="arrow-up-button"
:class="popoverPosition"
/>
</div>
<ListboxOptions class="menu-items" :class="popoverPosition">
<ListboxOption
as="button"
class="menu-item"
v-for="option in options"
:value="option.text"
@click="onOptionClick($event, option)"
>
{{ option.text }}
</ListboxOption>
</ListboxOptions>
</div>
</Listbox>
</template>
<style>
.menu {
position: relative;
}
.menu-items {
--menu-offset: 44px;
position: absolute;
display: flex;
flex-direction: column;
border-radius: 12px;
padding: 12px;
min-width: 128px;
border: 1px solid var(--vp-c-divider);
background-color: var(--vp-c-bg);
box-shadow: var(--vp-shadow-3);
transition: background-color 0.5s;
max-height: calc(100vh - var(--vp-nav-height));
overflow-y: auto;
z-index: 90;
right: 0;
}
.menu-item {
padding: 2px 8px;
text-align: left;
display: block;
border-radius: 6px;
padding: 0 12px;
line-height: 32px;
font-size: 14px;
font-weight: 500;
color: var(--vp-c-text-1);
white-space: nowrap;
transition: background-color .25s,color .25s;
}
.menu-item:hover {
color: var(--vp-c-brand);
background-color: var(--vp-c-bg-elv-mute);
}
.menu-item:active {
color: var(--vp-c-brand);
background-color: var(--vp-c-bg-elv);
}
.main-button {
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
padding-right: 12px !important;
}
.button-wrapper {
display: flex;
}
.arrow-up-button {
display: inline-flex;
height: 40px;
border-top-left-radius: 0 !important;
border-bottom-left-radius: 0 !important;
padding-left: 4px !important;
padding-right: 8px !important;
position: relative;
left: -1px;
}
.arrow-up-button::before {
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%0A%3E%3Cpolyline points='18 15 12 9 6 15' /%3E%3C/svg%3E%0A");
width: 20px;
height: 28px;
margin: auto;
display: block;
}
.dark .arrow-up-button::before {
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%0A%3E%3Cpolyline points='18 15 12 9 6 15' /%3E%3C/svg%3E%0A");
}
.menu-items.bottom {
top: var(--menu-offset);
}
.menu-items.top {
bottom: var(--menu-offset);
}
.arrow-up-button.top::before {
transform: rotate(0deg);
}
.arrow-up-button.bottom::before {
transform: rotate(180deg);
}
</style>

View File

@@ -0,0 +1,51 @@
<script setup lang="ts">
import { computed, defineProps, onMounted } from 'vue'
const props = defineProps<{
groups: string[] | undefined,
groupName: string,
}>()
const getSaveIdname = (name: string) => {
return name.toLowerCase().replace(/\s/g, '-')
}
const tabs = computed(() => props.groups?.map((group) => {
return {
id: getSaveIdname(group),
name: group,
}
}))
const saveTabId = (id: string) => {
localStorage.setItem(props.groupName, id)
}
onMounted(() => {
const id = localStorage.getItem(props.groupName)
if (id) {
const tab = document.getElementById(`label-tab-${id}`)
if (tab) {
tab.click()
}
}
})
</script>
<template>
<div class="vp-code-group">
<div class="tabs">
<template v-for="(tab, index) in tabs">
<input
type="radio"
:id="`tab-${tab.id}`"
:name="`group-${groupName}`"
:checked="index === 0"
@change="saveTabId(tab.id)"
>
<label :for="`tab-${tab.id}`" :id="`label-tab-${tab.id}`">{{ tab.name }}</label>
</template>
</div>
<slot />
</div>
</template>

View File

@@ -0,0 +1,93 @@
<script setup lang="ts">
import { computed } from 'vue';
const props = defineProps<{
modelValue: string
id: string
}>()
const emit = defineEmits(['update:modelValue'])
const value = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
</script>
<template>
<div class="color-picker">
<div class="color-input-wrapper">
<!-- TODO: Add currentColor div if value is currentColor -->
<input
type="color"
:id="id"
:name="id"
class="color-input"
v-model="value"
/>
</div>
<input
type="text"
:id="`${id}-input`"
:name="`${id}-input`"
class="color-input-text"
aria-label="Color picker input"
v-model="value"
/>
</div>
</template>
<style scoped>
.color-input {
width: 34px;
height: 34px;
position: absolute;
top: -5px;
left: -5px;
}
.color-input-wrapper {
height: 24px;
width: 24px;
overflow: hidden;
position: relative;
border-radius: 12px;
flex-shrink: 0;
}
.color-picker {
background: var(--color-picker-bg, var(--vp-c-bg-soft));
border-radius: 8px;
color: var(--vp-c-text-2);
padding: 4px 8px;
height: auto;
font-size: 14px;
text-align: left;
border: 1px solid transparent;
cursor: text;
display: flex;
align-items: center;
gap: 2px;
}
.color-input-text {
width: 100%;
height: 100%;
padding: 0 8px;
border: none;
background: transparent;
color: var(--vp-c-text-2);
font-size: 14px;
text-align: left;
border-radius: 8px;
cursor: text;
transition: border-color 0.25s, background 0.4s ease;
}
.color-picker:hover, .color-picker:focus {
border-color: var(--vp-c-brand);
background: var(--vp-c-bg-alt);
}
.color-input[value="currentColor"] {
}
</style>

View File

@@ -0,0 +1,15 @@
<script setup lang="ts">
import { vIntersectionObserver } from '@vueuse/components'
const emit = defineEmits(['end-of-page'])
const onIntersectionObserver: IntersectionObserverCallback = ([{ isIntersecting }]) => {
if (isIntersecting) {
emit('end-of-page')
}
}
</script>
<template>
<div v-intersection-observer="onIntersectionObserver" />
</template>

View File

@@ -0,0 +1,36 @@
<script setup>
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import { search } from '../../../data/iconNodes'
const SearchIcon = createLucideIcon('search', search)
</script>
<template>
<button class="fake-input">
<component :is="SearchIcon" class="search-icon"/>
<slot/>
</button>
</template>
<style scoped>
.fake-input {
background: var(--vp-c-bg-soft);
border-radius: 8px;
color: var(--vp-c-text-2);
padding: 12px 16px;
height: auto;
font-size: 14px;
/* box-shadow: var(--vp-shadow-4), var(--vp-shadow-2); */
text-align: left;
border: 1px solid transparent;
cursor: text;
display: flex;
gap: 12px;
transition: color 0.25s, border-color 0.25s, background-color 0.25s;
}
.fake-input:hover, .fake-input:focus {
border-color: var(--vp-c-brand);
background: var(--vp-c-bg-alt);
}
</style>

View File

@@ -0,0 +1,43 @@
<template>
<button v-bind="$attrs" class="icon-button">
<slot />
</button>
</template>
<style scoped>
.icon-button {
display: inline-flex;
border: 1px solid transparent;
text-align: center;
font-weight: 600;
padding: 6px;
border-radius: 8px;
white-space: nowrap;
transition: color 0.25s, border-color 0.25s, background-color 0.25s;
border-radius: 6px;
background-color: var(--vp-c-bg-alt);
/* width: 56px;
height: 56px; */
font-size: 24px;
transition: color 0.1s, border-color 0.1s, background-color 0.1s;
}
.icon-button:hover {
border-color: var(--vp-button-alt-hover-border);
color: var(--vp-button-alt-hover-text);
background-color: var(--vp-button-alt-hover-bg);
}
.icon-button:active {
border-color: var(--vp-button-alt-active-border);
color: var(--vp-button-alt-active-text);
background-color: var(--vp-button-alt-active-bg);
}
.icon-button.active {
border-color: var(--vp-c-brand);
/* color: var(--vp-button-alt-active-text);
background-color: var(--vp-button-alt-active-bg); */
}
</style>

View File

@@ -0,0 +1,81 @@
<script lang="ts">
export default {
inheritAttrs: false,
}
export interface InputProps {
type: string
modelValue: string
}
</script>
<script setup lang="ts">
import { ref } from 'vue'
const props = withDefaults(defineProps<InputProps>(), {
type: 'text'
})
const input = ref()
defineEmits(['change', 'input', 'update:modelValue'])
defineExpose({
focus: () => {
input.value.focus()
}
})
</script>
<template>
<div class="input-wrapper">
<slot name="icon" class="icon" />
<input
:type="type"
class="input"
:class="{'has-icon': $slots.icon}"
ref="input"
:value="modelValue"
v-bind="$attrs"
@input="$emit('update:modelValue', $event.target.value)"
/>
</div>
</template>
<style scoped>
.input-wrapper {
position: relative;
}
.input {
justify-content: flex-start;
border: 1px solid transparent;
border-radius: 8px;
padding: 0 10px 0 12px;
width: 100%;
height: 40px;
background-color: var(--vp-c-bg-alt);
font-size: 14px;
}
.input:hover, .input:focus {
border-color: var(--vp-c-brand);
background: var(--vp-c-bg-alt);
}
.input.has-icon {
padding-left: 52px;
}
</style>
<style>
.input-wrapper svg {
position: absolute;
left: 16px;
top: 12px;
z-index: 1;
color: var(--vp-c-text-2);
pointer-events: none;
}
</style>

View File

@@ -0,0 +1,48 @@
<script setup lang="ts">
defineProps<{
label: string
id: string
}>()
</script>
<template>
<div class="input-field">
<div class="input-label" v-if="label">
<label :for="id" class="customize-label">
{{ label }}
</label>
<div class="display-value" >
<slot name="display"/>
</div>
</div>
<slot />
</div>
</template>
<style scoped>
.customize-label {
line-height: 20px;
font-size: 13px;
font-weight: 600;
color: var(--vt-c-text-1);
transition: color .5s;
display: block;
}
.input-field:not(:last-child) {
margin-bottom: 16px;
}
.display-value {
font-size: 13px;
color: var(--vp-c-text-2);
}
.input-label {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
gap: 12px;
}
</style>

View File

@@ -0,0 +1,74 @@
<script lang="ts">
export default {
inheritAttrs: false,
}
</script>
<script setup lang="ts">
import { computed, ref } from 'vue'
import Input from './Input.vue'
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import { search } from '../../../data/iconNodes'
const SearchIcon = createLucideIcon('search', search)
interface Props {
modelValue: string
}
const props = defineProps<Props>()
const input = ref()
const emit = defineEmits(['update:modelValue'])
defineExpose({
focus: () => {
input.value.focus()
}
})
const value = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
</script>
<template>
<Input
ref="input"
type="search"
v-bind="$attrs"
v-model="value"
class="input-wrapper"
>
<template #icon>
<component :is="SearchIcon" class="search-icon" />
</template>
</Input>
</template>
<style scoped>
.input {
justify-content: flex-start;
border: 1px solid transparent;
border-radius: 8px;
padding: 0 10px 0 12px;
width: 100%;
height: 40px;
background-color: var(--vp-c-bg-alt);
}
.input:hover, .input:focus {
border-color: var(--vp-c-brand);
background: var(--vp-c-bg-alt);
}
.input-wrapper:deep(.input) {
/* padding: 12px 24px; */
padding-block: 12px;
font-size: 14px;
height: 48px;
}
</style>

View File

@@ -0,0 +1,16 @@
<template>
<h2 class="label"><slot/></h2>
</template>
<style scoped>
.label {
letter-spacing: 0.4px;
line-height: 28px;
font-size: 14px;
font-weight: 600;
border: 0;
padding: 0;
margin: 0;
}
</style>

View File

@@ -0,0 +1,19 @@
<script setup lang="ts">
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
export type IconNode = [elementName: string, attrs: Record<string, string>][]
const props = defineProps<{
name: string;
tags: string[];
categories: string[];
// contributors: Contributor[];
iconNode: IconNode;
}>()
const icon = createLucideIcon(props.name, props.iconNode)
</script>
<template>
<component :is="icon" />
</template>

View File

@@ -0,0 +1,127 @@
<script lang="ts">
export default {
inheritAttrs: false
}
</script>
<script lang="ts" setup>
import { computed } from "vue";
interface Props {
modelValue: number | string;
min?: number;
max?: number;
step?: number;
id: string;
}
const props = withDefaults(defineProps<Props>(), {
min: 0,
max: 48,
})
defineEmits(['update:modelValue'])
const percentage = computed<string>(() => `${((Number(props.modelValue) - props.min) / (props.max - props.min)) * 100}%`);
// TODO: Steps must be implemented
</script>
<template>
<div class="slider">
<input
:id="id"
type="range"
v-bind="$attrs"
v-bind:value="modelValue"
v-on:input="$emit('update:modelValue', Number($event.target.value))"
:min="min"
:max="max"
:step="step"
/>
<div class="bar"></div>
</div>
</template>
<style>
.slider {
position: relative;
width: 100%;
line-height: 10px;
height: 20px;
--bar-color: var(--slider-bar-color, var(--vp-c-bg-soft));
}
.slider:hover input{
opacity: 1;
}
.slider .bar {
position: absolute;
top: 8px;
left: 0;
width: 100%;
height: 100%;
}
.slider .bar:before, .slider .bar:after {
content: "";
position: absolute;
height: 4px;
pointer-events: none;
}
.slider .bar:before {
width: v-bind(percentage);
z-index: 1;
left: 0;
background: var(--vp-c-brand);
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.slider .bar:after {
background: var(--bar-color);
width: calc(100% - v-bind(percentage));
right: 0;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
.slider input {
-webkit-appearance: none;
width: calc(100% + 20px);
height: 20px;
left: -10px;
position: relative;
z-index: 2;
background: transparent;
outline: none;
/* opacity: 0.7; */
-webkit-transition: 0.2s;
transition: opacity 0.2s;
margin: 0;
cursor: pointer;
/* @apply
md:opacity-0 */
}
.slider input::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
background: #FFFFFF;
border-radius: 10px;
box-shadow: var(--vp-shadow-2);
cursor: pointer;
}
.slider input::-moz-range-thumb {
width: 20px;
height: 20px;
appearance: none;
background: #FFFFFF;
border-radius: 10px;
outline: none;
border: none;
box-shadow: var(--vp-shadow-2);
cursor: pointer;
}
</style>

View File

@@ -0,0 +1,43 @@
<script setup lang="ts">
import { rotateCw } from '../../../data/iconNodes'
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import IconButton from "./IconButton.vue";
const RotateIcon = createLucideIcon('RotateIcon', rotateCw)
</script>
<template>
<IconButton class="reset-button">
<RotateIcon :size="20"/>
</IconButton>
</template>
<style scoped>
.reset-button {
background: none;
padding: 0;
}
.reset-button .lucide {
transition: ease-in-out 0.1s transform;
}
.reset-button:hover {
background: none;
border-color: transparent;
}
/* a rotate css animation keyframes */
@keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(359deg);
}
}
.reset-button:active .lucide {
transform: rotate(45deg);
}
</style>

View File

@@ -0,0 +1,77 @@
<script setup>
import { ref } from 'vue'
import { Switch } from '@headlessui/vue'
const enabled = ref(false)
</script>
<template>
<Switch
v-model="enabled"
class="switch"
:class="{ enabled }"
>
<span class="thumb" />
</Switch>
</template>
<style>
.switch {
display: inline-flex;
position: relative;
width: 40px;
height: 22px;
flex-shrink: 0;
border: 1px solid var(--vp-input-border-color);
background-color: var(--vp-input-switch-bg-color);
transition: border-color 0.25s, background-color 0.4s ease;
border-radius: 11px;
}
.switch.enabled {
background-color: var(--vp-c-brand);
}
</style>
<style scoped>
.switch:hover {
border-color: var(--vp-input-hover-border-color);
}
.thumb {
display: inline-block;
background-color: #fff;
transition: transform 0.25s;
width: 20px;
height: 20px;
border-radius: 50%;
/* background-color: var(--vp-c-neutral); */
box-shadow: var(--vp-shadow-1);
}
.switch.enabled .thumb {
transform: translateX(18px);
}
/* ------------------- */
.VPSwitch {
position: relative;
border-radius: 11px;
display: block;
width: 40px;
height: 22px;
flex-shrink: 0;
border: 1px solid var(--vp-input-border-color);
background-color: var(--vp-input-switch-bg-color);
transition: border-color 0.25s;
}
.VPSwitch:hover {
border-color: var(--vp-input-hover-border-color);
}
.dark .icon :deep(svg) {
fill: var(--vp-c-text-1);
transition: opacity 0.25s;
}
</style>

View File

@@ -0,0 +1,55 @@
<script setup lang="ts">
import {onMounted, ref} from 'vue';
import {useFloating, offset, shift} from '@floating-ui/vue';
const reference = ref(null);
const tooltip = ref(null);
const middleware = ref([shift(), offset(8)]);
const { floatingStyles, update } = useFloating(reference, tooltip, {
middleware,
transform: false
});
defineProps<{
title: string
}>()
onMounted(() => {
update()
});
</script>
<template>
<span ref="reference" class="reference">
<slot/>
</span>
<div ref="tooltip" class="tooltip" :style="floatingStyles">
{{title}}
</div>
</template>
<style scoped>
.reference:hover + .tooltip{
opacity: 1;
transform: scale(1);
}
.tooltip {
display: block;
font-size: 12px;
line-height: 20px;
transform: scale(0.9);
font-weight: 400;
background: var(--vp-c-brand-dark);
color: white;
z-index: 10;
white-space: nowrap;
padding: 2px 8px;
border-radius: 4px;
box-shadow: var(--vp-shadow-1);
opacity: 0;
pointer-events: none;
transition: cubic-bezier(0.19, 1, 0.22, 1) .2s;
transition-property: opacity, transform;
}
</style>

View File

@@ -0,0 +1,35 @@
<template>
<div class="home-container">
<slot />
</div>
</template>
<style scoped>
.home-container {
position: relative;
padding-inline: 24px;
margin-block: 24px;
}
@media (min-width: 640px) {
.home-container {
padding-inline: 48px;
margin-block: 48px;
}
}
@media (min-width: 960px) {
.home-container {
padding-inline: 64px;
margin-block: 64px;
}
}
@media (min-width: 1280px) {
.home-container {
margin: 64px auto;
max-width: 1152px;
padding: 0;
}
}
</style>

View File

@@ -0,0 +1,16 @@
export default {
async load() {
const version = await fetch('https://api.github.com/repos/lucide-icons/lucide/releases/latest').then(res => {
if (res.ok) {
const releaseData = res.json() as Promise<{ tag_name: string }>
return releaseData
}
return null
}).then(res => res.tag_name)
return {
version
}
}
}

View File

@@ -0,0 +1,44 @@
<script setup lang="ts">
import Badge from '../base/Badge.vue';
import HomeContainer from './HomeContainer.vue';
import { data } from './HomeHeroBefore.data'
</script>
<template>
<HomeContainer class="container">
<Badge
:href="`https://github.com/lucide-icons/lucide/releases/tag/${data.version}`"
target="_blank"
rel="noreferrer noopener"
>{{ data.version }}</Badge>
</HomeContainer>
</template>
<style scoped>
.container {
margin-block: 0;;
margin-top: 37px;
margin-bottom: -96px;
display: flex;
justify-content: center;
}
.badge {
display: inline-block;
}
@media (min-width: 640px) {
.container {
margin-bottom: -131px;
}
}
@media (min-width: 960px) {
.container {
justify-content: flex-start;
}
.badge {
display: inline-block;
}
}
</style>

View File

@@ -0,0 +1,16 @@
import iconNodes from '../../../data/iconNodes'
const getRandomItem = <Item>(items: Item[]): Item => items[Math.floor(Math.random()*items.length)];
export default {
async load() {
const icons = Object.entries(iconNodes).map(([name, iconNode]) => ({ name, iconNode }))
const randomIcons = Array.from({ length: 200 }, () => getRandomItem(icons))
return {
icons: randomIcons,
iconsCount: icons.length,
}
}
}

View File

@@ -0,0 +1,160 @@
<script setup lang="ts">
import { ref, onMounted, shallowRef, onBeforeUnmount} from 'vue';
import { data } from './HomeHeroIconsCard.data'
import LucideIcon from '../base/LucideIcon.vue'
import { useRouter } from 'vitepress';
import { random } from 'lodash-es'
import FakeInput from '../base/FakeInput.vue'
const { go } = useRouter()
const intervalTime = shallowRef()
const getInitialItems = () => data.icons.slice(0, 48)
const items = ref(getInitialItems())
let id = items.value.length + 1
function getRandomNewIcon() {
const randomIndex = random(0, 200)
const newRandomIcon = data.icons[randomIndex]
if (items.value.some((item) => item.name === newRandomIcon.name)) {
return getRandomNewIcon()
}
return newRandomIcon
}
function insert() {
const replaceIndex = random(0, 48)
const newIcon = getRandomNewIcon()
// items.value.splice(replaceIndex, 0, newIcon);
items.value[replaceIndex] = newIcon
}
function startInterval() {
intervalTime.value = setInterval(() => {
insert()
}, 2000)
}
// TODO: Try maybe something else for better pref performance
onMounted(() => {
window.addEventListener('mousemove', startInterval, { once: true })
})
onBeforeUnmount(() => {
clearInterval(intervalTime.value)
})
</script>
<template>
<div class="card-wrapper">
<div class="icons-card">
<div class="card-grid">
<TransitionGroup name="list" mode="out-in">
<div
v-for="icon in items"
:key="icon.name"
class="random-icon"
>
<LucideIcon
v-bind="icon"
/>
</div>
</TransitionGroup>
</div>
<FakeInput @click="go('/icons/?focus')" class="search-box">
Search {{ data.iconsCount }} icons...
</FakeInput>
</div>
</div>
</template>
<style scoped>
.card-wrapper {
/* padding: 0 24px; */
margin-left: auto;
margin-bottom: auto;
margin-top: 48px;
}
.icons-card {
background: var(--vp-c-bg-alt);
padding: 24px;
border-radius: 8px;
width: 100%;
height:100%;
/* box-shadow: var(--vp-shadow-2); */
max-height: 220px;
max-width: 560px;
margin: 0 auto;
position: relative;
/* max-height: 240px; */
/* margin-top: 96px; */
}
.card-grid {
display: grid;
gap: 8px;
grid-template-columns: repeat(auto-fill, minmax(36px, 1fr));
grid-template-rows: repeat(auto-fill, minmax(36px, 1fr));
width: 100%;
height:100%;
max-height: 168px;
max-width: 512px;
overflow: hidden;
position: relative;
/* white-space: nowrap; */
}
.list-enter-active {
transition: all 0.5s cubic-bezier(.85,.85,.25,1.1);
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: scale(0.01);
}
.list-leave-active {
position: absolute;
opacity: 0;
display: none;
}
.search-box {
position: absolute;
width: 100%;
left: 0;
top: -64px;
}
.random-icon {
display: inline-flex;
justify-content: center;
align-items: center;
}
@media (min-width: 960px) {
.search-box {
top: unset;
bottom: -24px;
left: -24px;
box-shadow: var(--vp-shadow-3);
background: var(--vp-c-bg);
}
.dark .search-box {
background: var(--vp-c-bg-soft);
}
.card-wrapper {
margin-top: 8px;
}
}
</style>

View File

@@ -0,0 +1,195 @@
<script setup lang="ts">
import { ref, watch } from 'vue'
import { syncRef, useCssVar } from '@vueuse/core'
import HomeContainer from './HomeContainer.vue'
import RangeSlider from '../base/RangeSlider.vue'
import InputField from '../base/InputField.vue'
import ColorPicker from '../base/ColorPicker.vue'
import ResetButton from '../base/ResetButton.vue'
import HomeIconCustomizerIcons from './HomeIconCustomizerIcons.vue'
import Switch from '../base/Switch.vue'
const iconContainer = ref<HTMLElement | null>()
const color = ref('currentColor')
const strokeWidth = ref(2)
const size = ref(24)
const absoluteStrokeWidth = ref(false)
const colorCssVar = useCssVar(
'--customize-color',
iconContainer,
{
initialValue: 'default'
}
)
const strokeWidthCssVar = useCssVar(
'--customize-strokeWidth',
iconContainer,
{
initialValue: '2'
}
)
const sizeCssVar = useCssVar(
'--customize-size',
iconContainer,
{
initialValue: '24'
}
)
syncRef(color, colorCssVar)
syncRef(strokeWidth, strokeWidthCssVar)
syncRef(size, sizeCssVar)
function resetStyle () {
color.value = 'currentColor'
strokeWidth.value = 2
size.value = 24
}
watch(absoluteStrokeWidth, (enabled) => {
iconContainer.value?.classList.toggle('absolute-stroke-width', enabled)
})
</script>
<template>
<HomeContainer>
<div class="card">
<div class="card-column">
<h2 class="title">
Style as you please
<ResetButton @click="resetStyle"></ResetButton>
</h2>
<p class="copy">
Lucide has a lot of customization options to match the icons with your UI.
</p>
<div class="customizer">
<InputField
id="icon-color"
label="Color"
class="color-picker-field"
>
<template #display>
<ColorPicker v-model="color" id="icon-color" />
</template>
</InputField>
<InputField
id="stroke-width"
label="Stroke width"
>
<template #display>
<span class="customize-label">{{ strokeWidth }}px</span>
</template>
<RangeSlider
id="stroke-width"
name="stroke-width"
v-model="strokeWidth"
:min="1"
:max="3"
:step="0.25"
/>
</InputField>
<InputField
id="size"
label="Size"
>
<template #display>
<span class="customize-label">{{ size }}px</span>
</template>
<RangeSlider
id="size"
name="size"
v-model="size"
:min="16"
:max="48"
:step="4"
/>
</InputField>
<InputField
id="absolute-stroke-width"
label="Absolute Stroke width"
>
<template #display>
<Switch
id="absolute-stroke-width"
name="absolute-stroke-width"
v-model="absoluteStrokeWidth"
/>
</template>
</InputField>
</div>
</div>
<div class="icons-container card-column" ref="iconContainer">
<HomeIconCustomizerIcons />
</div>
</div>
</HomeContainer>
</template>
<style scoped>
.card {
display: block;
border-radius: 12px;
height: 100%;
background-color: var(--vp-c-bg-soft);
padding: 24px;
--slider-bar-color: var(--vp-c-bg-soft-down);
--color-picker-bg: var(--vp-c-bg-soft-down);
}
.title {
line-height: 32px;
font-size: 24px;
font-weight: 600;
display: flex;
flex-direction: row;
gap: 8px;
justify-content: space-between;
align-items: center;
}
.copy {
padding-top: 8px;
line-height: 24px;
font-size: 14px;
font-weight: 500;
color: var(--vp-c-text-2);
}
.customizer {
margin-top: 32px;
padding: 0;
background: none;
max-width: 280px;
}
@media (min-width: 640px) {
.card {
display: grid;
grid-template-columns: 8fr 10fr;
}
/*
.card-column {
flex: 1;
} */
}
@media (min-width: 960px) {
.card {
grid-template-columns: 1fr 2fr;
}
}
.color-picker-field:deep(.display-value) {
width: 138px;
}
</style>

View File

@@ -0,0 +1,77 @@
<script setup lang="ts">
import { ref } from 'vue';
import { data } from './HomeHeroIconsCard.data'
import LucideIcon from '../base/LucideIcon.vue'
import { vIntersectionObserver } from '@vueuse/components'
const getInitialItems = () => data.icons.slice(0, 64)
const items = ref(getInitialItems())
const showIcons = ref(false)
// Added intersection observer to improve performance
const onIntersectionObserver: IntersectionObserverCallback = ([{ isIntersecting }]) => {
if (isIntersecting) {
showIcons.value = true
}
}
</script>
<template>
<div class="icon-grid" v-intersection-observer="onIntersectionObserver">
<template v-if="showIcons">
<div
v-for="icon in items"
class="icon-grid-item"
>
<LucideIcon v-bind="icon" class="lucide-icon"/>
</div>
</template>
</div>
</template>
<style scoped>
.icon-grid {
display: grid;
gap: 1px;
grid-template-columns: repeat(auto-fill, minmax(68px, 1fr));
grid-template-rows: repeat(auto-fill, minmax(68px, 1fr));
width: 100%;
height:100%;
max-height: 360px;
gap: 1px;
overflow: hidden;
position: relative;
border-radius: 12px;
border: 8px solid var(--vp-c-bg);
position: relative;
top: 48px;
right: 0;
box-shadow: var(--vp-shadow-4);
}
.icon-grid-item {
display: flex;
align-items: center;
justify-content: center;
background: var(--vp-c-bg);
}
@media (min-width: 640px) {
.icon-grid {
top: 0;
right: -48px;
}
}
.lucide-icon {
will-change: width, height, stroke-width, stroke;
color: var(--customize-color, currentColor);
stroke-width: var(--customize-strokeWidth, 2);
width: calc(var(--customize-size, 24) * 1px);
height: calc(var(--customize-size, 24) * 1px);
}
.icons-container.absolute-stroke-width .lucide-icon {
stroke-width: calc(var(--customize-strokeWidth, 2) * 24 / var(--customize-size, 24));
}
</style>

View File

@@ -0,0 +1,53 @@
export default {
async load() {
return {
packages: [
{
name: 'lucide',
logo: '/framework-logos/js.svg',
label: 'Lucide documentation for JavaScript',
},
{
name: 'lucide-react',
logo: '/framework-logos/react.svg',
label: 'Lucide documentation for React',
},
{
name: 'lucide-vue-next',
logo: '/framework-logos/vue.svg',
label: 'Lucide documentation for Vue 3',
},
{
name: 'lucide-svelte',
logo: '/framework-logos/svelte.svg',
label: 'Lucide documentation for Svelte',
},
{
name: 'lucide-preact',
logo: '/framework-logos/preact.svg',
label: 'Lucide documentation for Preact',
},
{
name: 'lucide-solid',
logo: '/framework-logos/solid.svg',
label: 'Lucide documentation for Solid',
},
{
name: 'lucide-angular',
logo: '/framework-logos/angular.svg',
label: 'Lucide documentation for Angular',
},
{
name: 'lucide-react-native',
logo: '/framework-logos/react-native.svg',
label: 'Lucide documentation for React Native',
},
{
name: 'lucide-flutter',
logo: '/framework-logos/flutter.svg',
label: 'Lucide documentation for Flutter',
},
]
}
}
}

View File

@@ -0,0 +1,67 @@
<script setup lang="ts">
import HomeContainer from './HomeContainer.vue'
import { useRouter } from 'vitepress';
import { data } from './HomePackagesSection.data'
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue';
const { go } = useRouter()
</script>
<template>
<HomeContainer>
<h2 class="section-title">Available For:</h2>
<div class="packages-list">
<a
v-for="{ name, logo } in data.packages"
:href="`/guide/packages/${name}`"
class="package-logo"
:aria-label="`Read more about: ${name} package`"
@click.prevent="go(`/guide/packages/${name}`)"
>
<img
:src="logo"
height="36"
width="36"
loading="lazy"
:alt="`${name} logo`"
/>
</a>
</div>
<div class="more-button-wrapper">
<VPButton text="And more" href="/packages" theme="alt" class="more-button"/>
</div>
</HomeContainer>
</template>
<style scoped>
.section-title {
color: var(--vp-c-text-2);
font-weight: 500;
line-height: 32px;
font-size: 16px;
text-align: center;
margin-bottom: 16px;
}
.packages-list {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
margin: 0 -0.5rem;
gap: 16px;
}
.more-button-wrapper {
margin-top: 24px;
display: flex;
justify-content: center;
}
.package-logo {
transition: opacity ease-in .15s;
}
.package-logo:hover {
opacity: .6;
}
</style>

View File

@@ -0,0 +1,16 @@
import { getAllData } from '../../../lib/icons';
import { getAllCategoryFiles, mapCategoryIconCount } from '../../../lib/categories';
import iconsMetaData from '../../../data/iconMetaData'
export default {
async load() {
let categories = getAllCategoryFiles()
categories = mapCategoryIconCount(categories, Object.values(iconsMetaData))
return {
categories,
}
}
}

View File

@@ -0,0 +1,99 @@
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useData } from 'vitepress'
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue'
import { isActive } from 'vitepress/dist/client/shared'
import { useActiveAnchor } from '../../composables/useActiveAnchor'
import { data } from './CategoryList.data'
import CategoryListItem from './CategoryListItem.vue'
const { page } = useData()
const categoriesIsActive = computed(() => {
return isActive(page.value.relativePath, '/icons/categories');
});
const overviewIsActive = computed(() => {
return isActive(page.value.relativePath, '/icons/');
});
const headers = computed(() => {
const linkPrefix = page.value.relativePath.startsWith('icons/categories')
? '' : '/icons/categories'
return data.categories.map(({ name, title, iconCount }) => ({
level: 2,
link: `${linkPrefix}#${name}`,
title,
iconCount
}))
})
const container = ref()
const marker = ref()
useActiveAnchor(container, marker)
</script>
<template>
<div class="category-list" ref="container">
<VPLink class="sidebar-title" href="/icons/" :class="{ 'active': overviewIsActive } ">
All
</VPLink>
<VPLink class="sidebar-title" href="/icons/categories" :class="{ 'active': categoriesIsActive } ">
Categories
</VPLink>
<div class="content">
<div class="outline-marker" ref="marker" />
<nav aria-labelledby="doc-outline-aria-label">
<CategoryListItem :headers="headers" :root="true" />
</nav>
</div>
</div>
</template>
<style scoped>
.sidebar-title {
font-weight: 500;
color: var(--vp-c-text-2);
margin-bottom: 6px;
line-height: 24px;
font-size: 14px;
display: block;
transition: color 0.25s;
}
.sidebar-title:hover, .sidebar-title.active {
color: var(--vp-c-brand);
}
.content {
margin-top: 12px;
position: relative;
border-left: 1px solid var(--vp-c-divider);
padding-left: 16px;
font-size: 13px;
font-weight: 500;
}
.outline-marker {
position: absolute;
top: 32px;
left: -1px;
z-index: 0;
opacity: 0;
width: 1px;
height: 18px;
background-color: var(--vp-c-brand);
transition: top 0.25s cubic-bezier(0, 1, 0.5, 1), background-color 0.5s, opacity 0.25s;
}
.outline-title {
letter-spacing: 0.4px;
line-height: 28px;
font-size: 13px;
font-weight: 600;
}
.root {
z-index: 0;
}
</style>

View File

@@ -0,0 +1,95 @@
<script setup lang="ts">
import { onMounted } from 'vue'
import { useCategoryView } from '../../composables/useCategoryView'
interface Header {
level: number
title: string
slug: string
iconCount: number
link: string
children: Header[]
}
type MenuItem = Omit<Header, 'slug' | 'children'> & {
children?: MenuItem[]
}
const props = defineProps<{
headers: MenuItem[]
root?: boolean
}>()
const { selectedCategory } = useCategoryView()
function onClick(event: Event) {
const target = (event.target as HTMLElement).nodeName === 'span' ? (event.target as HTMLElement).parentNode : event.target as HTMLElement
const id = '#' + (target as HTMLAnchorElement).href!.split('#')[1]
const decodedId = decodeURIComponent(id)
selectedCategory.value = decodedId.replace('#', '')
const heading = document.querySelector<HTMLAnchorElement>(decodedId)
heading?.focus()
}
</script>
<template>
<ul :class="root ? 'root' : 'nested'">
<li v-for="{ children, link, title, iconCount } in headers">
<a
class="outline-link"
:href="link"
@click="onClick"
:title="title"
>
<span>
{{ title }}
</span>
<span class="icon-count" :aria-label="`Count of icons in ${title}`">
{{ iconCount }}
</span>
</a>
</li>
</ul>
</template>
<style scoped>
.root {
position: relative;
z-index: 1;
}
.nested {
padding-left: 13px;
}
.outline-link {
display: flex;
align-items: baseline;
line-height: 28px;
color: var(--vp-c-text-2);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: color 0.5s;
font-weight: 500;
}
.outline-link:hover,
.outline-link.active {
color: var(--vp-c-text-1);
transition: color 0.25s;
}
.outline-link.nested {
padding-left: 13px;
}
.icon-count {
opacity: 0.5;
margin-left: auto;
font-size: 11px;
font-weight: 400;
}
</style>

View File

@@ -0,0 +1,111 @@
<script setup lang="ts">
import { computed, ref } from 'vue';
import { startCase, camelCase } from 'lodash-es'
import ButtonMenu from '../base/ButtonMenu.vue'
import { useIconStyleContext } from '../../composables/useIconStyle';
import useConfetti from '../../composables/useConfetti';
const props = defineProps<{
name: string
popoverPosition?: 'top' | 'bottom'
}>()
const { size, color, strokeWidth, absoluteStrokeWidth } = useIconStyleContext()
const { animate, confetti } = useConfetti()
const componentName = computed(() => {
return startCase(camelCase(props.name)).replace(/\s/g, '')
})
function copyJSX() {
let attrs = ['']
if (size.value && size.value !== 24) {
attrs.push(`size={${size.value}}`)
}
if (color.value && color.value !== 'currentColor') {
attrs.push(`color="${color.value}"`)
}
if (strokeWidth.value && strokeWidth.value !== 2) {
attrs.push(`strokeWidth={${strokeWidth.value}}`)
}
if (absoluteStrokeWidth.value) {
attrs.push(`absoluteStrokeWidth`)
}
const code = `<${componentName.value}${attrs.join(' ')} />`
navigator.clipboard.writeText(code)
}
function copyVue() {
let attrs = ['']
if (size.value && size.value !== 24) {
attrs.push(`:size="${size.value}"`)
}
if (color.value && color.value !== 'currentColor') {
attrs.push(`color="${color.value}"`)
}
if (strokeWidth.value && strokeWidth.value !== 2) {
attrs.push(`:stroke-width="${strokeWidth.value}"`)
}
if (absoluteStrokeWidth.value) {
attrs.push(`absoluteStrokeWidth`)
}
const code = `<${componentName.value}${attrs.join(' ')} />`
navigator.clipboard.writeText(code)
}
function copyAngular() {
let attrs = ['']
attrs.push(`name="${props.name}"`)
if (size.value && size.value !== 24) {
attrs.push(`[size]="${size.value}"`)
}
if (color.value && color.value !== 'currentColor') {
attrs.push(`color="${color.value}"`)
}
if (strokeWidth.value && strokeWidth.value !== 2) {
attrs.push(`[strokeWidth]="${strokeWidth.value}"`)
}
if (absoluteStrokeWidth.value) {
attrs.push(`[absoluteStrokeWidth]="true"`)
}
const code = `<lucide-icon${attrs.join(' ')}></lucide-icon>`
navigator.clipboard.writeText(code)
}
</script>
<template>
<ButtonMenu
:buttonClass="`confetti-button ${animate ? 'animate' : ''}`"
id="copy-code-button"
callOptionOnClick
@click="confetti"
@optionClick="confetti"
data-confetti-text="Copied!"
:popoverPosition="popoverPosition"
:options="[
{ text: 'Copy JSX' , onClick: copyJSX },
{ text: 'Copy Vue' , onClick: copyVue },
{ text: 'Copy Svelte' , onClick: copyJSX },
{ text: 'Copy Angular' , onClick: copyAngular },
]"
/>
</template>
<style src="./confetti.css" />

View File

@@ -0,0 +1,86 @@
<script setup lang="ts">
import { ref } from 'vue';
import ButtonMenu from '../base/ButtonMenu.vue'
import { useIconStyleContext } from '../../composables/useIconStyle';
import useConfetti from '../../composables/useConfetti';
import getSVGIcon from '../../utils/getSVGIcon';
import downloadData from '../../utils/downloadData';
const downloadText = 'Download!'
const copiedText = 'Copied!'
const confettiText = ref(copiedText)
const props = defineProps<{
name: string
popoverPosition?: 'top' | 'bottom'
}>()
const { size } = useIconStyleContext()
const { animate, confetti } = useConfetti()
function copySVG() {
confettiText.value = copiedText
const svgString = getSVGIcon()
navigator.clipboard.writeText(svgString)
confetti()
}
function copyDataUrl() {
confettiText.value = copiedText
const svgString = getSVGIcon()
// Create SVG data url
const dataUrl = `data:image/svg+xml;base64,${btoa(svgString)}`
navigator.clipboard.writeText(dataUrl)
confetti()
}
function downloadSVG() {
confettiText.value = downloadText
const svgString = getSVGIcon()
downloadData(`${props.name}.svg`, `data:image/svg+xml;base64,${btoa(svgString)}`)
confetti()
}
function downloadPNG() {
confettiText.value = downloadText
const svgString = getSVGIcon()
const canvas = document.createElement('canvas');
canvas.width = size.value;
canvas.height = size.value;
const ctx = canvas.getContext("2d");
const image = new Image();
image.src = `data:image/svg+xml;base64,${btoa(svgString)}`;
image.onload = function() {
ctx.drawImage(image, 0, 0);
downloadData(`${props.name}.png`, canvas.toDataURL('image/png'))
confetti()
}
}
</script>
<template>
<ButtonMenu
:buttonClass="`confetti-button ${animate ? 'animate' : ''}`"
callOptionOnClick
id="copy-svg-button"
:data-confetti-text="confettiText"
:popoverPosition="popoverPosition"
:options="[
{ text: 'Copy SVG' , onClick: copySVG },
{ text: 'Copy Data URL' , onClick: copyDataUrl },
{ text: 'Download SVG' , onClick: downloadSVG },
{ text: 'Download PNG' , onClick: downloadPNG },
]"
/>
</template>
<style src="./confetti.css" />

View File

@@ -0,0 +1,88 @@
<script setup lang="ts">
import {IconEntity} from "../../types";
import Label from "../base/Label.vue";
const props = defineProps<{
icon: IconEntity
}>()
</script>
<template>
<div class="contributors" v-if="props.icon.contributors?.length>0">
<Label>Contributors:</Label>
<div class="avatar-group">
<a class="avatar"
v-for="contributor in props.icon.contributors"
:key="contributor"
:href="`https://github.com/${contributor}`"
target="_blank"
:data-name="contributor"
rel="noreferrer noopener"
>
<img class="avatar-image" :alt="contributor" :src="`https://github.com/${contributor}.png?size=128`" />
</a>
</div>
</div>
</template>
<style scoped>
.contributors {
display: flex;
flex-direction: row;
align-items: center;
/* justify-content: flex-end; */
gap: 16px;
}
.avatar-group {
display: flex;
}
.avatar:not(:first-child) {
margin-left: -24px;
}
.avatar {
position: relative;
}
.avatar:before {
content: attr(data-name);
display: block;
font-size: 12px;
line-height: 20px;
transform: translateX(-50%) scale(0.9);
font-weight: 400;
position: absolute;
top: -28px;
left: 50%;
background: var(--vp-c-brand-dark);
color: white;
z-index: 10;
padding: 2px 8px;
border-radius: 4px;
box-shadow: var(--vp-shadow-1);
opacity: 0;
pointer-events: none;
transition: cubic-bezier(0.19, 1, 0.22, 1) .2s;
transition-property: opacity, transform;
overflow: hidden;
white-space: nowrap;
word-break: break-word;
}
.avatar:hover:before {
opacity: 1;
transform: translateX(-50%) scale(1);
}
.avatar-image {
width: 48px;
height: 48px;
border-radius: 50%;
border: 2px solid var(--vp-c-bg-elv);
background-color: var(--vp-c-neutral);
}
.avatar:hover .avatar-image {
border: 2px solid var(--vp-c-bg-soft-mute);
}
.avatar:hover {
z-index: 2;
}
</style>

View File

@@ -0,0 +1,56 @@
<script setup>
import { onMounted, computed } from 'vue'
import { useData } from 'vitepress'
import IconPreview from './IconPreview.vue'
const { params } = useData()
onMounted(() => {
console.log(params, 'data')
})
const tags = computed(() => {
if (!params.tags) return []
return params.tags.join(' • ')
})
</script>
<template>
<!-- <PageContainer class="overview"> -->
<IconPreview
:name="$params.name"
:iconNode="$params.iconNode"
class="preview"
customizable
/>
<div class="details">
<h1 class="title">
{{ $params.name }}
</h1>
</div>
<!-- </PageContainer> -->
</template>
<style scoped>
.overview {
display: flex;
gap: 32px;
}
.preview {
flex: 1;
}
.details {
flex: 2
}
.title {
font-size: 32px;
margin: 0;
margin-bottom: 16px;
line-height: 1.2;
color: var(--vp-text-color);
font-family: monospace;
}
</style>

View File

@@ -0,0 +1,74 @@
<script setup lang="ts">
import { computed, useSlots } from 'vue';
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import { copy } from '../../../data/iconNodes'
import useConfetti from '../../composables/useConfetti';
const { animate, confetti } = useConfetti()
const slots = useSlots()
const copiedText = computed(() => slots.default?.()[0].children)
function copyText() {
navigator.clipboard.writeText(copiedText.value)
confetti()
}
const Copy = createLucideIcon('ChevronUp', copy)
</script>
<template>
<h1
class="icon-name confetti-button"
:class="{animate}"
data-confetti-text="Copied!"
@click="copyText"
>
<slot />
<Copy :size="20" class="copy-icon"/>
</h1>
</template>
<style scoped>
@import './confetti.css';
.icon-name {
font-size: 24px;
font-weight: 500;
line-height: 32px;
transition: background ease-in .15s;;
padding: 2px 8px;
border-radius: 8px;
width: auto;
display: inline-flex;
margin-left: -8px;
}
.icon-name:hover {
background-color: var(--vp-c-bg-alt);
}
.icon-name:hover .copy-icon {
opacity: .9;
}
.icon-name:before,
.icon-name:after {
left: unset !important;
right: -20%;
}
.icon-name:before {
text-align: center;
}
.copy-icon {
opacity: 0;
margin-left: 12px;
margin-top: 6px;
transition:ease .3s opacity;
}
.icon-name:hover .copy-icon:hover {
opacity: .6;
}
</style>

View File

@@ -0,0 +1,161 @@
<script setup lang="ts">
import type { IconEntity } from '../../types'
import { computed, ref, watch } from 'vue'
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
import IconButton from '../base/IconButton.vue';
import IconContributors from './IconContributors.vue';
import IconPreview from './IconPreview.vue';
import { x, expand } from '../../../data/iconNodes'
import { useRouter } from 'vitepress';
import IconInfo from './IconInfo.vue';
import Badge from '../base/Badge.vue';
import { computedAsync } from '@vueuse/core';
const props = defineProps<{
iconName: string
}>()
const icon = computedAsync<IconEntity | null>(async () => {
if (props.iconName) {
return (await import(`../../../data/iconDetails/${props.iconName}.ts`)).default as IconEntity
}
return null
}, null)
const emit = defineEmits(['close'])
const isOpen = computed(() => !!icon.value)
function onClose() {
emit('close')
}
const { go } = useRouter()
const CloseIcon = createLucideIcon('Close', x)
const Expand = createLucideIcon('Expand', expand)
</script>
<template>
<Transition name="drawer" appear>
<div class="overlay-container" v-if="icon">
<div class="overlay-panel">
<nav class="overlay-menu">
<Badge
v-if="icon.createdRelease"
class="version"
:href="`https://github.com/lucide-icons/lucide/releases/tag/v${icon.createdRelease.version}`"
target="_blank"
rel="noreferrer noopener"
>v{{ icon.createdRelease.version }}</Badge>
<IconButton @click="go(`/icons/${icon.name}`)">
<component :is="Expand" />
</IconButton>
<IconButton @click="onClose">
<component :is="CloseIcon" />
</IconButton>
</nav>
<IconPreview
id="previewer"
:name="icon.name"
:iconNode="icon.iconNode"
customizable
/>
<IconInfo :icon="icon" popoverPosition="top">
<template v-slot:footer>
<IconContributors :icon="icon" class="contributors" />
</template>
</IconInfo>
</div>
</div>
</Transition>
</template>
<style scoped>
.overlay-container {
position: fixed;
top: 0;
left: var(--left, 0);
right: var(--right, 0);
bottom: 0;
pointer-events: none;
height: 100%;
width: 100%;
display: flex;
align-items: flex-end;
width: auto;
}
@media (min-width: 960px) {
.overlay-container {
--left: var(--vp-sidebar-width);
}
}
@media (min-width: 1440px) {
.overlay-container {
--left: calc((100% - (var(--vp-layout-max-width) - 64px)) / 2 + var(--vp-sidebar-width) - 32px);
--right: calc(((100% - (var(--vp-layout-max-width) - var(--vp-sidebar-width))) - 272px) / 2);
}
.overlay-panel {
border-top-right-radius: 8px;
}
}
.overlay-panel {
position: relative;
width: 100%;
margin: 0 auto;
padding: 1rem;
background-color: var(--vp-c-bg-elv);
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
will-change: transform;
pointer-events: all;
height: 288px;
padding: 24px;
display: flex;
box-shadow: var(--vp-shadow-5);
}
.icon-info {
padding-left: 24px;
flex-basis: 100%;
}
.icon-tags {
font-size: 16px;
color: var(--vp-c-text-2);
font-weight: 500;
}
.overlay-menu {
position: absolute;
top: 24px;
right: 24px;
display: flex;
gap: 8px;
align-items: center;
}
.drawer-enter-active {
transition: all 0.2s cubic-bezier(.21,.8,.46,.9);
}
.drawer-leave-active {
transition: all 0.4s cubic-bezier(1, 0.5, 0.8, 1);
}
.drawer-enter-from,
.drawer-leave-to {
transform: translateY(100%);
opacity: 0;
}
.version {
margin-right: 24px;
}
.contributors {
justify-content: flex-end;
}
</style>

View File

@@ -0,0 +1,48 @@
<script setup lang="ts">
import type { IconEntity } from '../../types'
import IconItem from './IconItem.vue'
const emit = defineEmits(['setActiveIcon'])
const props = defineProps<{
icons: IconEntity[]
activeIcon?: string
overlayMode?: boolean
hideIcons?: boolean
}>()
function setActiveIcon(name: string) {
emit('setActiveIcon', name)
}
</script>
<template>
<div class="icons">
<div class="icon" v-for="icon in icons" :key="icon.name">
<IconItem
v-bind="icon"
@setActiveIcon="setActiveIcon(icon.name)"
:active="activeIcon === icon.name"
customizable
:overlayMode="overlayMode"
:hideIcon="hideIcons"
/>
</div>
</div>
</template>
<style>
.icons {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(56px, 1fr));
/* padding: 32px 32px 96px; */
gap: 8px;
width: 100%;
}
.icon {
aspect-ratio: 1/1;
position: relative;
}
</style>

View File

@@ -0,0 +1,137 @@
<script setup lang="ts">
import { IconEntity } from '../../types';
import IconDetailName from './IconDetailName.vue';
import Badge from '../base/Badge.vue';
import CopySVGButton from './CopySVGButton.vue';
import CopyCodeButton from './CopyCodeButton.vue';
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue';
import {useData, useRouter} from 'vitepress';
import { computed } from 'vue';
const props = defineProps<{
icon: IconEntity
popoverPosition?: 'top' | 'bottom'
}>()
const { go } = useRouter()
const { page } = useData()
const tags = computed(() => {
if (!props.icon || !props?.icon?.tags) return []
return props.icon.tags.join(' • ')
})
</script>
<template>
<div class="icon-info">
<IconDetailName class="icon-name">
{{ icon.name }}
</IconDetailName>
<div class="tags-scroller" v-if="tags.length">
<p class="icon-tags horizontal-scroller">
{{ tags }}
</p>
</div>
<div class="group">
<Badge
v-for="category in icon.categories"
class="category"
:href="`/icons/categories#${category}`"
>
{{ category }}
</Badge>
</div>
<div class="group buttons">
<VPButton
v-if="!page?.relativePath?.startsWith?.(`icons/${icon.name}`)"
:href="`/icons/${icon.name}`"
text="See in action"
@click="go(`/icons/${icon.name}`)"
/>
<CopySVGButton :name="icon.name" :popoverPosition="popoverPosition"/>
<CopyCodeButton :name="icon.name" :popoverPosition="popoverPosition"/>
</div>
<slot name="footer" />
</div>
</template>
<style scoped>
.group {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 16px;
}
.category {
text-transform: capitalize;
}
.icon-name {
margin-bottom: 4px;
}
.icon-tags {
font-size: 16px;
color: var(--vp-c-text-2);
font-weight: 500;
line-height: 28px;
white-space: nowrap;
position: absolute;
top: 0;
left: 0;
right: 0;
margin-top: 0;
margin-bottom: 0;
}
.tags-scroller {
position: relative;
max-width: 100%;
width: 100%;
height: 28px;
padding: 8px 0 16px;
margin-bottom: 16px;
margin-top: 8px;
align-items: center;
--gradient-background: var(--tags-gradient-background, var(--vp-c-bg-elv))
}
.horizontal-scroller {
overflow-x: scroll;
/* Hide Scrollbar */
-ms-overflow-style: none;
scrollbar-width: none;
scrollbar-width: thin; /* can also be normal, or none, to not render scrollbar */
scrollbar-color: currentColor transparent; /* foreground background */
}
.horizontal-scroller::-webkit-scrollbar {
width: 0;
display: none
}
.horizontal-scroller::-webkit-scrollbar-track {
background: transparent
}
.horizontal-scroller::-webkit-scrollbar-thumb {
background: transparent;
border: none
}
.tags-scroller::after {
content: '';
position: absolute;
bottom: 0;
width: 32px;
height: 100%;
/* Background Gradient left to right */
background: linear-gradient(to right, rgba(255,255,255,0) 0%,var(--gradient-background) 100%);
right: 0;
pointer-events: none;
}
.buttons {
margin-top: 24px;
}
</style>

View File

@@ -0,0 +1,162 @@
<script setup lang="ts">
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
import { useMediaQuery } from '@vueuse/core';
import { useRouter } from 'vitepress';
import getSVGIcon from '../../utils/getSVGIcon';
import useConfetti from '../../composables/useConfetti';
import Tooltip from '../base/Tooltip.vue';
const downloadText = 'Download!'
const copiedText = 'Copied!'
export type IconNode = [elementName: string, attrs: Record<string, string>][]
const props = defineProps<{
name: string;
iconNode: IconNode;
active: boolean;
customizable?: boolean;
overlayMode?: boolean
hideIcon?: boolean
}>()
const emit = defineEmits(['setActiveIcon'])
const { go } = useRouter()
const showOverlay = useMediaQuery('(min-width: 860px)');
const { animate, confetti, confettiText } = useConfetti()
const icon = createLucideIcon(props.name, props.iconNode)
async function navigateToIcon(event) {
if (event.shiftKey) {
event.preventDefault()
const svgString = getSVGIcon(event.target.firstChild, {
class: `lucide lucide-${props.name}`,
})
await navigator.clipboard.writeText(svgString)
confettiText.value = copiedText
confetti()
return
}
if(props.overlayMode && showOverlay.value) {
event.preventDefault()
window.history.pushState({}, '', `/icons/${props.name}`)
emit('setActiveIcon', props.name)
}
else {
event.preventDefault()
go(`/icons/${props.name}`)
}
}
</script>
<template>
<Tooltip :title="name">
<a
class="icon-button confetti-button vp-raw"
@click="navigateToIcon"
:class="{ active, animate }"
:aria-label="name"
:href="`/icons/${props.name}`"
:data-confetti-text="confettiText"
ref="ref"
>
<KeepAlive>
<component
v-if="!hideIcon"
:is="icon"
class="lucide-icon"
:class="{
customizable,
}"
/>
</KeepAlive>
</a>
</Tooltip>
</template>
<style src="./confetti.css" />
<style scoped>
.icon-button {
display: inline-block;
border: 1px solid transparent;
text-align: center;
font-weight: 600;
border-radius: 4px;
white-space: nowrap;
transition: color 0.25s, border-color 0.25s, background-color 0.25s;
border-radius: 6px;
background-color: var(--vp-c-bg-alt);
display: inline-flex;
width: 56px;
height: 56px;
font-size: 24px;
color: var(--vp-c-text-1);
}
.confetti-button:before,
.confetti-button:after {
z-index: 100;
}
.confetti-button:before {
line-height: 80px;
}
.icon-button:active {
transition: color 0.1s, border-color 0.1s, background-color 0.1s;
}
.icon-button.medium {
border-radius: 20px;
padding: 0 20px;
line-height: 38px;
font-size: 14px;
}
.icon-button.big {
border-radius: 24px;
padding: 0 24px;
line-height: 46px;
font-size: 16px;
}
.icon-button:hover {
border-color: var(--vp-button-alt-hover-border);
color: var(--vp-button-alt-hover-text);
background-color: var(--vp-button-alt-hover-bg);
}
.icon-button:active {
border-color: var(--vp-button-alt-active-border);
color: var(--vp-button-alt-active-text);
background-color: var(--vp-button-alt-active-bg);
}
.icon-button.active {
border-color: var(--vp-c-brand);
}
.lucide-icon {
margin: auto;
pointer-events: none;
}
.lucide-icon.customizable {
will-change: width, height, stroke-width, stroke;
color: var(--customize-color, currentColor);
stroke-width: var(--customize-strokeWidth, 2);
width: calc(var(--customize-size, 24) * 1px);
height: calc(var(--customize-size, 24) * 1px);
}
html.absolute-stroke-width .lucide-icon.customizable {
stroke-width: calc(var(--customize-strokeWidth, 2) * 24 / var(--customize-size, 24));
}
</style>

View File

@@ -0,0 +1,73 @@
<script setup lang="ts">
import type { IconEntity } from '../../types'
import { computed, ref } from 'vue'
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
import { useIconStyleContext } from '../../composables/useIconStyle';
const props = defineProps<{
name: IconEntity['name']
iconNode: IconEntity['iconNode']
customizable?: boolean
}>()
const { size, color, strokeWidth, absoluteStrokeWidth } = useIconStyleContext()
const previewIcon = ref()
const gridLines = computed(() => Array.from({ length:(size.value - 1) }))
const iconComponent = computed(() => {
if (!props.name || !props.iconNode) return null
return createLucideIcon(props.name, props.iconNode)
})
</script>
<template>
<div class="icon-container">
<component
ref="previewIcon"
:is="iconComponent"
:width="size"
:height="size"
:stroke="color"
:stroke-width="absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth"
/>
<svg class="icon-grid" :viewBox="`0 0 ${size} ${size}`" fill="none" stroke-width="0.1" xmlns="http://www.w3.org/2000/svg">
<g :key="`grid-${i}`" v-for="(_, i) in gridLines">
<line :key="`horizontal-${i}`" :x1="0" :y1="i + 1" :x2="size" :y2="i + 1" />
<line :key="`vertical-${i}`" :x1="i + 1" y1="0" :x2="i + 1" :y2="size" />
</g>
</svg>
</div>
</template>
<style scoped>
.icon-grid {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
stroke: var(--vp-c-divider);
}
.icon-container {
height: 100%;
aspect-ratio: 1/1;
position: relative;
background: var(--vp-c-bg-alt);
border-radius: 8px;
}
.icon-container > :deep(svg:not(.icon-grid)) {
width: 100%;
height: 100%;
position: relative;
z-index: 1;
color: var(--vp-c-neutral);
opacity: 0.8;
}
.icon-component.customizable {
will-change: width, height, stroke-width, stroke;
/* color: var(--customize-color, currentColor);
stroke-width: var(--customize-strokeWidth, 2); */
}
</style>

View File

@@ -0,0 +1,69 @@
<script setup lang="ts">
import type { IconEntity } from '../../types'
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
const props = defineProps<{
name: IconEntity['name']
iconNode: IconEntity['iconNode']
customizable?: boolean
}>()
const Icon = createLucideIcon(props.name, props.iconNode)
</script>
<template>
<div class="icons-small-preview">
<div class="icon-wrapper">
<Icon :size="48" class="lucide-icon"/>
</div>
<div class="icon-wrapper">
<Icon :size="32" class="lucide-icon"/>
</div>
<div class="icon-wrapper">
<Icon class="lucide-icon"/>
</div>
</div>
</template>
<style scoped>
.icons-small-preview {
display: flex;
width: 100%;
height: 100%;
/* align-items: center; */
justify-content: center;
gap: 24px;
/* margin-top: 24px; */
}
.icon-wrapper {
border: 1px solid transparent;
text-align: center;
font-weight: 600;
border-radius: 4px;
white-space: nowrap;
transition: color 0.25s, border-color 0.25s, background-color 0.25s;
border-radius: 6px;
background-color: var(--vp-c-bg-alt);
display: inline-flex;
width: 64px;
height: 64px;
font-size: 24px;
color: var(--vp-c-text-1);
align-items: center;
justify-content: center;
}
.lucide-icon {
will-change: width, height, stroke-width, stroke;
color: var(--customize-color, currentColor);
stroke-width: var(--customize-strokeWidth, 2);
/* Not sure if this is logical for 100% previews */
/* width: calc(var(--customize-size, 24) * 1px);
height: calc(var(--customize-size, 24) * 1px); */
}
html.absolute-stroke-width .lucide-icon {
stroke-width: calc(var(--customize-strokeWidth, 2) * 24 / var(--customize-size, 24));
}
</style>

View File

@@ -0,0 +1,56 @@
<script setup lang="ts">
import { ref } from 'vue';
import { Category } from '../../types';
import IconGrid from './IconGrid.vue'
import { vIntersectionObserver } from '@vueuse/components'
defineProps<{
activeIconName: string
category: Category
}>()
const emit = defineEmits(['setActiveIcon'])
const showIcons = ref(false)
// Added intersection observer to improve performance
const onIntersectionObserver: IntersectionObserverCallback = ([{ isIntersecting }]) => {
showIcons.value = isIntersecting
}
</script>
<template>
<section
class="category"
:key="category.name"
:id="category.name"
v-intersection-observer="onIntersectionObserver"
>
<h2 class="title" >
<a class="header-anchor" :href="`#${category.name}`" :aria-label="`Permalink to &quot;${category.title}&quot;`">&ZeroWidthSpace;</a>
{{ category.title }}
</h2>
<IconGrid
:activeIcon="activeIconName"
:icons="category.icons"
@setActiveIcon="$event => $emit('setActiveIcon', $event)"
overlayMode
:hideIcons="!showIcons"
/>
</section>
</template>
<style scoped>
.title {
margin-bottom: 24px;
font-size: 19px;
font-weight: 500;
padding-top: 86px;
/* scroll-padding-top: 240px; */
}
.category {
margin-bottom: calc(-86px + 32px);
}
</style>

View File

@@ -0,0 +1,130 @@
<script setup lang="ts">
import { ref, computed, defineAsyncComponent } from 'vue'
import type { IconEntity, Category } from '../../types'
import useSearch from '../../composables/useSearch'
import InputSearch from '../base/InputSearch.vue'
import useSearchInput from '../../composables/useSearchInput'
import StickyBar from './StickyBar.vue'
import IconsCategory from './IconsCategory.vue'
import { useFetch } from '@vueuse/core'
import useFetchTags from '../../composables/useFetchTags'
import useFetchCategories from '../../composables/useFetchCategories'
const props = defineProps<{
icons: IconEntity[]
categories: Category[]
iconCategories: Record<string, string[]>
}>()
const activeIconName = ref(null)
const { searchInput, searchQuery, searchQueryThrottled } = useSearchInput()
const isSearching = computed(() => !!searchQuery.value)
function setActiveIconName(name: string) {
activeIconName.value = name
}
const { execute: fetchTags, data: tags } = useFetchTags()
const { execute: fetchCategories, data: categoriesMap } = useFetchCategories()
const mappedIcons = computed(() => {
if(tags.value == null) {
return props.icons
}
return props.icons.map((icon) => {
const iconTags = tags.value[icon.name]
const iconCategories = categoriesMap.value?.[icon.name] ?? []
return {
...icon,
tags: iconTags,
categories: iconCategories,
}
})
})
const searchResults = useSearch(searchQuery, mappedIcons, [
{ name: 'name', weight: 2 },
{ name: 'tags', weight: 1 },
])
const categories = computed(() => {
if( !props.categories?.length || !props.icons?.length ) return []
return props.categories.map(({ name, title }) => {
const categoryIcons = props.icons.filter((icon) => {
const iconCategories = props.iconCategories[icon.name]
return iconCategories?.includes(name)
})
const searchedCategoryIcons = isSearching
? categoryIcons.filter(icon => searchResults.value.some((item) => item?.name === icon?.name))
: categoryIcons;
return {
title,
name,
icons: searchedCategoryIcons,
};
})
.filter(({ icons }) => icons.length)
})
function onFocusSearchInput() {
if (tags.value == null) {
fetchTags()
}
if (categoriesMap.value == null) {
fetchCategories()
}
}
const NoResults = defineAsyncComponent(() =>
import('./NoResults.vue')
)
const IconDetailOverlay = defineAsyncComponent(() =>
import('./IconDetailOverlay.vue')
)
</script>
<template>
<StickyBar class="search-bar category-search">
<InputSearch
:placeholder="`Search ${icons.length} icons ...`"
v-model="searchQuery"
class="input-wrapper"
ref="searchInput"
@focus="onFocusSearchInput"
/>
</StickyBar>
<NoResults
v-if="categories.length === 0"
:searchQuery="searchQuery"
@clear="searchQuery = ''"
/>
<IconsCategory
v-for="category in categories"
:key="category.name"
:category="category"
:activeIconName="activeIconName"
@setActiveIcon="setActiveIconName"
/>
<IconDetailOverlay
v-if="activeIconName != null"
:iconName="activeIconName"
@close="setActiveIconName('')"
/>
</template>
<style scoped>
.input-wrapper {
width: 100%;
}
.search-bar.category-search {
margin-bottom: -54px;
}
</style>

View File

@@ -0,0 +1,154 @@
<script setup lang="ts">
import { ref, computed, watch, defineAsyncComponent } from 'vue'
import type { IconEntity } from '../../types'
import { useMediaQuery, useOffsetPagination } from '@vueuse/core'
import IconGrid from './IconGrid.vue'
import InputSearch from '../base/InputSearch.vue'
import useSearch from '../../composables/useSearch'
import EndOfPage from '../base/EndOfPage.vue'
import useSearchInput from '../../composables/useSearchInput'
import StickyBar from './StickyBar.vue'
import useFetchTags from '../../composables/useFetchTags'
import useFetchCategories from '../../composables/useFetchCategories'
const props = defineProps<{
icons: IconEntity[]
}>()
const activeIconName = ref(null)
const isExtraLargeScreen = useMediaQuery('(min-width: 1440px)');
const isLargeScreen = useMediaQuery('(min-width: 1280px)');
const isMediumScreen = useMediaQuery('(min-width: 960px)');
const isSmallScreen = useMediaQuery('(min-width: 640px)');
const pageSize = computed(() => {
if(isExtraLargeScreen.value) {
return 16 * 20;
}
if(isLargeScreen.value) {
return 16 * 12;
}
if(isMediumScreen.value) {
return 13 * 12;
}
if(isSmallScreen.value) {
return 10 * 10;
}
return 10 * 5;
})
const { execute: fetchTags, data: tags } = useFetchTags()
const { execute: fetchCategories, data: categories } = useFetchCategories()
const mappedIcons = computed(() => {
if(tags.value == null) {
return props.icons
}
return props.icons.map((icon) => {
const iconTags = tags.value[icon.name]
const iconCategories = categories.value?.[icon.name] ?? []
return {
...icon,
tags: iconTags,
categories: iconCategories,
}
})
})
const { searchInput, searchQuery, searchQueryThrottled } = useSearchInput()
const searchResults = useSearch(searchQueryThrottled, mappedIcons, [
{ name: 'name', weight: 3 },
{ name: 'tags', weight: 2 },
{ name: 'categories', weight: 1 },
])
const { next, currentPage } = useOffsetPagination( { pageSize })
const paginatedIcons = computed(() => {
const end = pageSize.value * currentPage.value
return searchResults.value.slice(0, end)
})
function setActiveIconName(name: string) {
activeIconName.value = name
}
watch(searchQueryThrottled, (searchString) => {
currentPage.value = 1
})
function onFocusSearchInput() {
if (tags.value == null) {
fetchTags()
}
if (categories.value == null) {
fetchCategories()
}
}
const NoResults = defineAsyncComponent(() =>
import('./NoResults.vue')
)
const IconDetailOverlay = defineAsyncComponent(() =>
import('./IconDetailOverlay.vue')
)
</script>
<template>
<StickyBar>
<InputSearch
:placeholder="`Search ${icons.length} icons ...`"
v-model="searchQuery"
ref="searchInput"
class="input-wrapper"
@focus="onFocusSearchInput"
/>
</StickyBar>
<NoResults
v-if="paginatedIcons.length === 0"
:searchQuery="searchQuery"
@clear="searchQuery = ''"
/>
<IconGrid
overlayMode
:activeIcon="activeIconName"
:icons="paginatedIcons"
@setActiveIcon="setActiveIconName"
/>
<EndOfPage @end-of-page="next" class="bottom-page"/>
<IconDetailOverlay
v-if="activeIconName != null"
:iconName="activeIconName"
@close="setActiveIconName('')"
/>
</template>
<style>
.icons {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(56px, 1fr));
gap: 8px;
width: 100%;
}
.icon {
aspect-ratio: 1/1;
}
.input-wrapper {
width: 100%;
}
.bottom-page {
height: 288px;
}
</style>

View File

@@ -0,0 +1,83 @@
<script setup lang="ts">
import {ref} from 'vue'
import {bird} from '../../../data/iconNodes'
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import {useEventListener} from '@vueuse/core'
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue'
defineProps<{
searchQuery: string
}>()
defineEmits(['clear'])
const birdIcon = ref<HTMLElement>()
const Bird = createLucideIcon('bird', bird)
const flip = ref(false)
useEventListener(document, 'mousemove', (mouseEvent) => {
const {width, height, x, y} = birdIcon.value.getBoundingClientRect()
const centerX = (width / 2) + x
flip.value = mouseEvent.x < centerX
})
</script>
<template>
<div class="no-results">
<Bird class="bird-icon" ref="birdIcon" :class="{ flip }" :strokeWidth="1"/>
<h2 class="no-results-text">
No icons found for '{{ searchQuery }}'
</h2>
<VPButton text="Clear your search and try again" theme="alt" @click="$emit('clear')"/>
<span class="text-divider">or</span>
<VPButton text="Search on Github issues"
theme="alt"
:href="`https://github.com/lucide-icons/lucide/issues?q=is%3Aopen+${searchQuery}`"
target="_blank"
/>
</div>
</template>
<style scoped>
.no-results {
display: flex;
flex-direction: column;
align-items: center;
}
.bird-icon {
width: 160px;
height: 160px;
color: var(--vp-c-neutral);
opacity: 0.8;
margin-top: 72px;
}
.bird-icon.flip {
transform: rotateY(180deg);
}
@media (min-width: 960px) {
.bird-icon {
width: 240px;
height: 240px;
}
}
.no-results-text {
line-height: 40px;
font-size: 24px;
margin-top: 24px;
margin-bottom: 32px;
text-align: center;
}
.text-divider {
margin: 12px 0;
font-size: 16px;
color: var(--vp-c-neutral);
}
</style>

View File

@@ -0,0 +1,30 @@
<script setup lang="ts">
import type { IconEntity } from '../../types'
import IconGrid from './IconGrid.vue'
defineProps<{
icons: IconEntity[]
}>()
</script>
<template>
<section class="related-icons">
<h2 class="title">
Related Icons
</h2>
<IconGrid
:icons="icons"
/>
</section>
</template>
<style scoped>
.title {
margin-bottom: 24px;
font-size: 19px;
font-weight: 500;
}
.related-icons {
margin-bottom: 32px;
}
</style>

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