Compare commits

...

148 commits

Author SHA1 Message Date
Pascal Jufer
2911b580e9
Fix building on Windows (#553) 2025-07-27 13:52:57 +02:00
Pascal Jufer
6e6d2e27ea
Update icon count 2025-07-27 12:42:18 +02:00
Pascal Jufer
ec9e470fda
Adjust discord label casing 2025-07-27 12:41:08 +02:00
Pascal Jufer
320c603674
Update readmes 2025-07-27 12:38:23 +02:00
Pascal Jufer
91f5ff77b3
Fix react-native module file path (#552) 2025-07-26 23:02:27 +02:00
Pascal Jufer
3a0e86816e
Updates & Clean-ups (#551) 2025-07-26 22:56:51 +02:00
Luca Burgio
ea8474ed13 fix: eslint 2025-07-21 09:42:55 +03:00
Luca Burgio
b12a69bf9e Merge branch 'main' of ssh://github.com/iconoir-icons/iconoir 2025-07-21 09:34:06 +03:00
Luca Burgio
569fa8246e chore: styling update on documentation nav 2025-07-21 09:28:15 +03:00
dependabot[bot]
4d9e934c61
build(deps-dev): bump vite from 6.2.6 to 6.2.7 (#533)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.2.6 to 6.2.7.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.2.7/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.2.7/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.2.7
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-21 09:25:29 +03:00
Pedro Simon
76b89cca9d
feat: add tags to existing icon in icons.csv (#536)
feat: add tags to existing icons in icons.csv
2025-07-21 09:23:44 +03:00
Luca Burgio
fc38183e2b chore: removed avatars 2025-07-21 09:21:25 +03:00
dependabot[bot]
e048feaae0
build(deps-dev): bump esbuild from 0.24.2 to 0.25.0 (#510)
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.24.2 to 0.25.0.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG-2024.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.24.2...v0.25.0)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-13 11:53:11 +02:00
Luca Burgio
48c6486a1d Merge branch 'main' of https://github.com/iconoir-icons/iconoir 2025-04-13 11:46:42 +02:00
Luca Burgio
80608c712a fix: add newline on package.json for eslint update 2025-04-13 11:46:19 +02:00
lucaburgio
99165c8e8e Update build artifacts 2025-04-13 09:30:05 +00:00
dependabot[bot]
a0357830a5
build(deps-dev): bump vite from 6.0.6 to 6.0.15 (#527)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.0.6 to 6.0.15.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.0.15/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.0.15/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.0.15
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-13 11:29:32 +02:00
dependabot[bot]
36cf751ed3
build(deps): bump next from 15.1.2 to 15.2.4 (#528)
Bumps [next](https://github.com/vercel/next.js) from 15.1.2 to 15.2.4.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v15.1.2...v15.2.4)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 15.2.4
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-13 11:09:07 +02:00
lucaburgio
b4b0e4bc54 Release Version v7.11.0 2025-04-12 16:34:37 +00:00
lucaburgio
5aab3794ad Update build artifacts 2024-12-27 13:51:32 +00:00
Luca Burgio
f581cf0571 add: 43 new icons 2024-12-27 14:50:50 +01:00
Pascal Jufer
02a224f444
Move path config back to main file 2024-12-26 17:07:49 +01:00
Pascal Jufer
1441fc20dc
Commit missing file 2024-12-26 17:02:55 +01:00
Pascal Jufer
c5b1c480a8
Use worker threads for building targets 2024-12-26 17:02:18 +01:00
Pascal Jufer
4b26faf0d2
Drop types-tsconfig again 2024-12-26 14:55:31 +01:00
Pascal Jufer
b386f30b5f
Update pnpm 2024-12-26 14:50:26 +01:00
Pascal Jufer
0805c8fd36
Fix react resolution for @types/react-window 2024-12-26 14:48:11 +01:00
Pascal Jufer
60cd60b0e7
Vue clean-ups / Drop support for Vue 2 (#502)
Vue Clean-ups / Drop support for Vue 2
2024-12-20 02:54:13 +01:00
Pascal Jufer
1ac85cc784
Fix linting & format files 2024-12-18 00:50:02 +01:00
Pascal Jufer
c2179807d1
Drop support for React < 18 (#501) 2024-12-18 00:38:33 +01:00
Pascal Jufer
51a93353db
Update example next 2024-12-17 02:40:33 +01:00
Pascal Jufer
7c5fd6103b
Fix flutter examples 2024-12-17 02:32:53 +01:00
Pascal Jufer
0d359ca1af
Add support for React 19 to iconoir-react (#500) 2024-12-17 02:32:23 +01:00
Pascal Jufer
2eb49e0fd0
Update deps in iconoir.com 2024-12-17 01:23:39 +01:00
Pascal Jufer
2bf8087c26
Refactor Lint/Format Setup (#499) 2024-12-17 00:25:26 +01:00
Pascal Jufer
a1003eb8bd
Update core deps (#497) 2024-12-15 19:23:17 +01:00
Pascal Jufer
e7919b7f5f
Enhance TS options (#496) 2024-12-15 13:32:00 +01:00
Pascal Jufer
401a53c779
Remove leftover logging 2024-12-15 13:23:15 +01:00
lucaburgio
a1d609dc47 Release Version v7.10.1 2024-11-28 22:03:07 +00:00
lucaburgio
8ac885f32e Update build artifacts 2024-11-28 21:55:30 +00:00
Luca Burgio
badca9126c fix: renamed 1 wrong icon
The regular icon droplet-snow-flake has been renamed to droplet-snow-flake-in to match the solid version.
2024-11-28 22:55:02 +01:00
lucaburgio
f7395255f4 Release Version v7.10.0 2024-11-10 23:14:56 +00:00
lucaburgio
a95cf03fb2 Update build artifacts 2024-11-10 23:11:13 +00:00
Luca Burgio
e57a02c201 add: 15 new icons 2024-11-10 23:10:44 +00:00
dependabot[bot]
3a50eb9483
build(deps-dev): bump next from 14.0.4 to 14.2.10 (#485)
Bumps [next](https://github.com/vercel/next.js) from 14.0.4 to 14.2.10.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.0.4...v14.2.10)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-10 22:51:57 +00:00
dependabot[bot]
c9ab55448d
build(deps): bump http-proxy-middleware from 2.0.6 to 2.0.7 in /examples/react-native (#486)
build(deps): bump http-proxy-middleware in /examples/react-native

Bumps [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) from 2.0.6 to 2.0.7.
- [Release notes](https://github.com/chimurai/http-proxy-middleware/releases)
- [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/v2.0.7/CHANGELOG.md)
- [Commits](https://github.com/chimurai/http-proxy-middleware/compare/v2.0.6...v2.0.7)

---
updated-dependencies:
- dependency-name: http-proxy-middleware
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-10 22:51:27 +00:00
Luca Burgio
06e672671c chore: removed framer sponsor 2024-11-10 19:52:06 +00:00
Luca Burgio
874784995b chore: removed banner and updated header 2024-10-26 13:53:18 +02:00
Luca Burgio
645e5d4200 fix: added missing categories 2024-10-26 13:43:33 +02:00
Pascal Jufer
47272d2a14
Update pnpm to 9.12.0 2024-10-05 21:45:09 +02:00
dependabot[bot]
307cff3b2f
build(deps-dev): bump vite from 5.0.10 to 5.1.8 (#482)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.0.10 to 5.1.8.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.1.8/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.1.8/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-19 22:38:49 +02:00
dependabot[bot]
08adaa2cd1
build(deps): bump path-to-regexp and express in /examples/react-native (#480)
Bumps [path-to-regexp](https://github.com/pillarjs/path-to-regexp) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.

Updates `path-to-regexp` from 0.1.7 to 0.1.10
- [Release notes](https://github.com/pillarjs/path-to-regexp/releases)
- [Changelog](https://github.com/pillarjs/path-to-regexp/blob/master/History.md)
- [Commits](https://github.com/pillarjs/path-to-regexp/compare/v0.1.7...v0.1.10)

Updates `express` from 4.19.2 to 4.21.0
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/4.21.0/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.19.2...4.21.0)

---
updated-dependencies:
- dependency-name: path-to-regexp
  dependency-type: indirect
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-18 08:37:44 +02:00
dependabot[bot]
f748429080
build(deps): bump body-parser and express in /examples/react-native (#481)
Bumps [body-parser](https://github.com/expressjs/body-parser) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.

Updates `body-parser` from 1.20.2 to 1.20.3
- [Release notes](https://github.com/expressjs/body-parser/releases)
- [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/body-parser/compare/1.20.2...1.20.3)

Updates `express` from 4.19.2 to 4.21.0
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/4.21.0/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.19.2...4.21.0)

---
updated-dependencies:
- dependency-name: body-parser
  dependency-type: indirect
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-18 08:37:32 +02:00
dependabot[bot]
45cf55d47a
build(deps): bump next from 14.1.1 to 14.2.10 (#478)
Bumps [next](https://github.com/vercel/next.js) from 14.1.1 to 14.2.10.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.1.1...v14.2.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-18 08:37:11 +02:00
dependabot[bot]
39fff4ccd7
build(deps-dev): bump vite from 5.0.10 to 5.2.14 (#477)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.0.10 to 5.2.14.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.2.14/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.2.14/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-18 08:36:06 +02:00
lucaburgio
65a0a1e2b6 Release Version v7.9.0 2024-09-07 10:38:29 +00:00
lucaburgio
276b734666 Update build artifacts 2024-09-07 10:35:28 +00:00
Luca Burgio
e5f3b49a55 add: 16 new icons and changes on 2 2024-09-07 13:34:53 +03:00
Luca Burgio
db399c987f add: framer sponsor 2024-09-04 09:34:51 +03:00
dependabot[bot]
635fb03f77
build(deps): bump micromatch from 4.0.5 to 4.0.8 in /examples/react-native (#474)
build(deps): bump micromatch in /examples/react-native

Bumps [micromatch](https://github.com/micromatch/micromatch) from 4.0.5 to 4.0.8.
- [Release notes](https://github.com/micromatch/micromatch/releases)
- [Changelog](https://github.com/micromatch/micromatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/micromatch/compare/4.0.5...4.0.8)

---
updated-dependencies:
- dependency-name: micromatch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-31 09:46:33 +02:00
dependabot[bot]
062e4adc50
build(deps): bump webpack from 5.89.0 to 5.94.0 in /examples/react-native (#473)
build(deps): bump webpack in /examples/react-native

Bumps [webpack](https://github.com/webpack/webpack) from 5.89.0 to 5.94.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.89.0...v5.94.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-31 09:39:15 +02:00
dependabot[bot]
1542558ad8
build(deps): bump fast-xml-parser from 4.3.2 to 4.4.1 in /examples/react-native (#467)
build(deps): bump fast-xml-parser in /examples/react-native

Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 4.3.2 to 4.4.1.
- [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases)
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v4.3.2...v4.4.1)

---
updated-dependencies:
- dependency-name: fast-xml-parser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-13 09:18:07 +02:00
dependabot[bot]
c922ad1412
build(deps): bump braces from 3.0.2 to 3.0.3 in /examples/react-native (#465)
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-21 11:28:53 +02:00
lucaburgio
514a3b6f92 Release Version v7.8.0 2024-07-21 07:36:39 +00:00
lucaburgio
e5f1b85231 Update build artifacts 2024-07-21 07:32:27 +00:00
Luca Burgio
7cb5d92018 add: 14 new icons 2024-07-21 10:31:58 +03:00
dependabot[bot]
4498d4c609
build(deps): bump fast-loops from 1.1.3 to 1.1.4 in /examples/react-native (#463)
build(deps): bump fast-loops in /examples/react-native

Bumps [fast-loops](https://github.com/robinweser/fast-loops) from 1.1.3 to 1.1.4.
- [Commits](https://github.com/robinweser/fast-loops/commits)

---
updated-dependencies:
- dependency-name: fast-loops
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-16 11:51:22 +02:00
Dániel Földi
d668898a5a
chore: update actions steps (#459)
Co-authored-by: Dániel Földi <6977583+DaniFoldi@users.noreply.github.com>
2024-07-04 11:30:03 +02:00
Luca Burgio
932b424c54 fix: prettier on DonationPopup 2024-06-23 16:03:15 +03:00
Luca Burgio
eed1425e3d add: donate toast 2024-06-23 15:58:59 +03:00
dependabot[bot]
27a22d57ef
build(deps): bump ws in /examples/react-native (#456)
Bumps  and [ws](https://github.com/websockets/ws). These dependencies needed to be updated together.

Updates `ws` from 8.14.2 to 8.17.1
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.14.2...8.17.1)

Updates `ws` from 7.5.9 to 8.17.1
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.14.2...8.17.1)

Updates `ws` from 6.2.2 to 8.17.1
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.14.2...8.17.1)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: indirect
- dependency-name: ws
  dependency-type: indirect
- dependency-name: ws
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-18 23:42:03 +02:00
dependabot[bot]
186a212831
build(deps): bump next from 14.0.4 to 14.1.1 (#440)
Bumps [next](https://github.com/vercel/next.js) from 14.0.4 to 14.1.1.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.0.4...v14.1.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-10 06:42:46 +02:00
dependabot[bot]
f372adfafd
build(deps): bump next from 14.0.4 to 14.1.1 in /examples/next (#439)
Bumps [next](https://github.com/vercel/next.js) from 14.0.4 to 14.1.1.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.0.4...v14.1.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-10 06:37:38 +02:00
lucaburgio
3d3215bda0 Release Version v7.7.0 2024-04-25 12:40:38 +00:00
lucaburgio
e36fae0ae1 Update build artifacts 2024-04-25 12:36:59 +00:00
Luca Burgio
ac19a0d84c add: 10 new icons 2024-04-25 14:36:27 +02:00
dependabot[bot]
433eab8e90
build(deps): bump tar from 6.2.0 to 6.2.1 in /examples/react-native (#427)
Bumps [tar](https://github.com/isaacs/node-tar) from 6.2.0 to 6.2.1.
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v6.2.0...v6.2.1)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 18:32:38 +02:00
lucaburgio
791583941b Release Version v7.6.0 2024-04-06 06:55:16 +00:00
lucaburgio
2124819115 Update build artifacts 2024-04-06 06:52:33 +00:00
Luca Burgio
28288e091c Merge branch 'main' of https://github.com/iconoir-icons/iconoir 2024-04-06 08:52:05 +02:00
Luca Burgio
29255f5a62 fix: remove clip-path 2024-04-06 08:51:50 +02:00
lucaburgio
68ac2c7515 Update build artifacts 2024-04-06 06:47:54 +00:00
Luca Burgio
670825979d add: 8 new icons 2024-04-06 08:47:22 +02:00
dependabot[bot]
0e6b9fccfa
build(deps-dev): bump vite from 5.0.10 to 5.0.13 (#424)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.0.10 to 5.0.13.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.0.13/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.0.13/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-03 19:15:47 +02:00
dependabot[bot]
9f65fa8493
build(deps): bump express from 4.18.2 to 4.19.2 in /examples/react-native (#421)
build(deps): bump express in /examples/react-native

Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2)

---
updated-dependencies:
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-29 14:05:07 +01:00
dependabot[bot]
8cc0960eee
build(deps): bump webpack-dev-middleware from 5.3.3 to 5.3.4 in /examples/react-native (#418)
build(deps): bump webpack-dev-middleware in /examples/react-native

Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.3 to 5.3.4.
- [Release notes](https://github.com/webpack/webpack-dev-middleware/releases)
- [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4)

---
updated-dependencies:
- dependency-name: webpack-dev-middleware
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-23 23:31:27 +01:00
dependabot[bot]
b9ef950a5e
build(deps): bump follow-redirects from 1.15.3 to 1.15.6 in /examples/react-native (#415)
build(deps): bump follow-redirects in /examples/react-native

Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.3 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.3...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-23 23:31:19 +01:00
lucaburgio
ecda46b0b8 Release Version v7.5.0 2024-03-02 10:24:36 +00:00
lucaburgio
96cb5fc50a Update build artifacts 2024-03-02 10:21:58 +00:00
Luca Burgio
0f3f092228 feat: 15 new icons 2024-03-02 11:21:31 +01:00
rubabredwan
b9d30ab235
fix: remove SOLID div (#395)
Co-authored-by: Pascal Jufer <pascal-jufer@bluewin.ch>
2024-02-27 11:49:57 +01:00
dependabot[bot]
2f8360ec24
build(deps): bump ip from 1.1.8 to 1.1.9 in /examples/react-native (#406)
Bumps [ip](https://github.com/indutny/node-ip) from 1.1.8 to 1.1.9.
- [Commits](https://github.com/indutny/node-ip/compare/v1.1.8...v1.1.9)

---
updated-dependencies:
- dependency-name: ip
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-22 08:17:46 +01:00
Luca Burgio
235f6ed70c fix: changes to title and footer copy 2024-02-20 14:03:37 +01:00
Luca Burgio
04bf4952a7 fix: increased readability 2024-02-20 11:39:50 +01:00
Luca Burgio
f9d372b606 chore: prettier 2024-02-20 11:22:56 +01:00
Luca Burgio
d0a566a495 fix: removed bottom license url from README 2024-02-20 11:13:04 +01:00
Luca Burgio
b0f4479a65 fix: logic for canonical 2024-02-20 11:07:31 +01:00
Luca Burgio
390f392ea5 add: hono sponsor 2024-02-18 15:05:51 +01:00
Luca Burgio
f69b7f5c27 fix: all rel links 2024-02-10 12:48:52 +01:00
Luca Burgio
0f5f811e17 fix: streamline sponsor 2024-02-10 12:20:50 +01:00
lucaburgio
7cf649d870 Release Version v7.4.0 2024-02-10 10:30:38 +00:00
lucaburgio
04595f439d Update build artifacts 2024-02-10 10:27:56 +00:00
Luca Burgio
320c6df106 feat: 7 new solid icons 2024-02-10 11:27:22 +01:00
Luca Burgio
8ad02f8912 chore: prettier 2024-02-10 11:16:31 +01:00
Luca Burgio
bdc17791a5 fix: added sponsored attribute 2024-02-10 11:08:48 +01:00
Luca Burgio
82a1fc0437 revert: restored supporter avatars 2024-02-10 10:41:49 +01:00
lucaburgio
11da0d4b86 Update build artifacts 2024-01-27 08:45:14 +00:00
luxbe
4fb8bf0b9f
SVG code clean up: remove useless clipPaths (#366)
* refactor: remove useless clipPaths

* Delete hexagon-alt.svg

removed hexagon-alt

---------

Co-authored-by: Luca Burgio <burgio.luca@gmail.com>
2024-01-27 09:44:50 +01:00
lucaburgio
e102706245 Update build artifacts 2023-12-30 11:11:54 +00:00
Luca Burgio
2837f863b1 fixed naming with new solid icons 2023-12-30 12:11:28 +01:00
Luca Burgio
ff6dd8bd28 removed solid ref from icons.csv 2023-12-30 12:02:34 +01:00
lucaburgio
57ddd10bcc Release Version v7.3.0 2023-12-30 10:44:55 +00:00
lucaburgio
028db424bd Update build artifacts 2023-12-30 10:42:00 +00:00
Luca Burgio
1c7285fb8d 24 new solid icons and 4 icons fixed 2023-12-30 11:41:34 +01:00
Luca Burgio
0508eb3931 fix: prettier 2023-12-29 18:15:17 +01:00
Luca Burgio
5e510db0c5 fix: changed header url 2023-12-29 18:13:01 +01:00
Luca Burgio
6c358fad4b fix prettier error 2023-12-26 12:07:05 +01:00
Luca Burgio
777af4e27f added banner and removed avatars 2023-12-26 12:01:50 +01:00
Pascal Jufer
cf4d16369c
Update examples 2023-12-17 15:51:02 +01:00
Pascal Jufer
c4b034494e
Update all dependencies (#389) 2023-12-17 15:20:25 +01:00
Luca Burgio
7adc35d582 reverting prettier fixes 2023-12-17 11:45:26 +01:00
Luca Burgio
8585a9df7c Add Discord link on header 2023-12-17 11:26:10 +01:00
Luca Burgio
2bf04c12b0 fixed discord link and small prettier fixes 2023-12-17 10:48:14 +01:00
lucaburgio
55b5db79e2 Release Version v7.2.0 2023-12-16 11:23:56 +00:00
lucaburgio
8a93bdd98f Update build artifacts 2023-12-16 11:19:53 +00:00
Luca Burgio
6548056de5 8 new icons and changes 2023-12-16 12:19:23 +01:00
Luca Burgio
044843a3c3
Update README.md 2023-12-09 15:18:46 +01:00
lucaburgio
585e896a61 Release Version v7.1.0 2023-12-02 15:50:45 +00:00
lucaburgio
9908e62a90 Update build artifacts 2023-12-02 15:47:13 +00:00
Luca Burgio
29ad981ba8 add: 6 icons 2023-12-02 16:46:38 +01:00
Luca Burgio
ea5ec6c435 remove: media import from streamline ad 2023-11-22 15:30:37 +01:00
Luca Burgio
e6c9fe5e97 add: streamline ad 2023-11-22 15:26:27 +01:00
Pascal Jufer
5ff2b0d5cd
chore: fix lint setup 2023-11-21 22:38:48 +01:00
paescuj
282cf0d0a7 Release Version v7.0.3 2023-11-21 21:18:28 +00:00
Pascal Jufer
e983f9b1aa
fix: uningore flutter lib dir (#380) 2023-11-21 22:14:32 +01:00
Pascal Jufer
abe3530887
chore: update dependencies 2023-11-21 22:08:42 +01:00
Luca Burgio
46e146f41e fix: sitemap location 2023-11-20 22:05:56 +01:00
Luca Burgio
d35ee729bc fix: renamed hq folder to kw 2023-11-19 16:19:02 +01:00
Luca Burgio
151e4b53ed add: sitemap 2023-11-19 16:07:55 +01:00
Luca Burgio
bb1d22f970 fix: prettier 2023-11-19 10:40:52 +01:00
Luca Burgio
f2f579af1d fix: prettier 2023-11-19 10:37:39 +01:00
Luca Burgio
a315aacd42 fix: last prettier 2023-11-19 10:29:59 +01:00
Luca Burgio
1dac608fb5 fix: prettier 2023-11-19 10:25:34 +01:00
Luca Burgio
df19c352bd add: description var to SEO tsx + react landing 2023-11-19 10:02:47 +01:00
Pascal Jufer
2684fd353d
Use workflow_run event to run website workflow 2023-11-12 00:15:50 +01:00
lucaburgio
c82cc456e1 Release Version v7.0.2 2023-11-11 19:04:32 +00:00
lucaburgio
7e9115bc4a Update build artifacts 2023-11-11 18:58:38 +00:00
Ali Torbati
0be38ff1d0
Fix: refresh-circle icon stroke fix
The `circle` element in the "Refresh circle" icon included a hard coded `stroke-width`, which caused the circle to not inherit the parent `svg` `stroke-width`. This change fixes that bug.
2023-11-11 19:58:11 +01:00
336 changed files with 15906 additions and 27175 deletions

View file

@ -1,2 +0,0 @@
dist/
/examples/react-native/**/*.js

View file

@ -1,47 +0,0 @@
module.exports = {
root: true,
env: {
es2022: true,
node: true,
},
extends: ['eslint:recommended', 'plugin:prettier/recommended'],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
rules: {
'padding-line-between-statements': [
'error',
{
blankLine: 'always',
prev: [
'block',
'block-like',
'class',
'export',
'import',
'multiline-block-like',
'multiline-expression',
],
next: '*',
},
{
blankLine: 'always',
prev: ['const', 'let'],
next: ['block', 'block-like', 'class', 'export', 'import'],
},
{
blankLine: 'always',
prev: '*',
next: ['multiline-block-like', 'multiline-expression', 'return'],
},
{
blankLine: 'any',
prev: ['export', 'import'],
next: ['export', 'import'],
},
],
'prettier/prettier': ['error'],
},
};

View file

View file

@ -13,21 +13,21 @@ Before reporting an issue, please search to see if someone has filed a similar i
## Prerequisites
* Version:
* Are you running from source/main:
* Are you using a released build:
* Operating system:
- Version:
- Are you running from source/main:
- Are you using a released build:
- Operating system:
## Step to reproduce
*(Type here)*
_(Type here)_
### Actual behavior
## Any message or error
*(Type here)*
_(Type here)_
## Additional info or screenshots
* Screenshots
- Screenshots

View file

@ -13,6 +13,6 @@ Before creating an icon request, please search to see if someone has requested t
## Icon Request
* Icon name:
* Use case:
* Screenshots of similar icons:
- Icon name:
- Use case:
- Screenshots of similar icons:

View file

@ -12,11 +12,11 @@ runs:
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 22
registry-url: ${{ inputs.node-registry }}
- name: Install pnpm
uses: pnpm/action-setup@v2
uses: pnpm/action-setup@v4
- name: Get pnpm store directory
id: pnpm-cache-dir
@ -24,7 +24,7 @@ runs:
run: echo "dir=$(pnpm store path)" >> $GITHUB_OUTPUT
- name: Setup pnpm cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-cache-dir.outputs.dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}

View file

@ -22,6 +22,6 @@ jobs:
run: pnpm run build css
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v5
uses: stefanzweifel/git-auto-commit-action@v6
with:
commit_message: Update build artifacts

28
.github/workflows/ci.yaml vendored Normal file
View file

@ -0,0 +1,28 @@
name: CI
on:
pull_request:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v46
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup
uses: ./.github/actions/setup
- name: Run Linter
run: pnpm exec eslint ${{ steps.changed-files.outputs.all_changed_files }}

View file

@ -41,7 +41,7 @@ jobs:
TAG_NAME: ${{ github.ref_name }}
- name: Commit release
uses: stefanzweifel/git-auto-commit-action@v5
uses: stefanzweifel/git-auto-commit-action@v6
with:
commit_message: Release Version ${{ github.ref_name }}
branch: main
@ -62,14 +62,3 @@ jobs:
with:
credentialJson: ${{ secrets.PUB_CREDENTIAL_JSON }}
relativePath: ./packages/iconoir-flutter
- name: Trigger Website Workflow
uses: actions/github-script@v6
with:
script: |
github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'website.yaml',
ref: 'main',
})

View file

@ -1,17 +1,23 @@
name: Website
on:
workflow_dispatch:
workflow_run:
workflows:
- Release
types:
- completed
permissions:
actions: read
contents: read
pages: write
id-token: write
concurrency:
group: pages
group: ${{ github.workflow }}
cancel-in-progress: true
on:
workflow_dispatch: {}
jobs:
build:
name: Build
@ -27,27 +33,27 @@ jobs:
run: pnpm run build react
- name: Build website
run: ./node_modules/.bin/next build
run: pnpm run build
working-directory: iconoir.com
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Setup GitHub Pages
uses: actions/configure-pages@v3
uses: actions/configure-pages@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v2
uses: actions/upload-pages-artifact@v3
with:
path: './iconoir.com/out'
path: ./iconoir.com/out
deploy:
name: Deploy
runs-on: ubuntu-latest
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
uses: actions/deploy-pages@v4

1
.node-version Normal file
View file

@ -0,0 +1 @@
22

View file

@ -1 +0,0 @@
dist/

View file

@ -1,4 +0,0 @@
{
"singleQuote": true,
"quoteProps": "consistent"
}

3
.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,3 @@
{
"recommendations": ["dbaeumer.vscode-eslint"]
}

51
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,51 @@
{
// Disable the default formatter, use eslint instead
"prettier.enable": false,
"editor.formatOnSave": false,
// Auto fix
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},
// Silent the stylistic rules in you IDE, but still auto fix them
"eslint.rules.customizations": [
{ "rule": "style/*", "severity": "off", "fixable": true },
{ "rule": "format/*", "severity": "off", "fixable": true },
{ "rule": "*-indent", "severity": "off", "fixable": true },
{ "rule": "*-spacing", "severity": "off", "fixable": true },
{ "rule": "*-spaces", "severity": "off", "fixable": true },
{ "rule": "*-order", "severity": "off", "fixable": true },
{ "rule": "*-dangle", "severity": "off", "fixable": true },
{ "rule": "*-newline", "severity": "off", "fixable": true },
{ "rule": "*quotes", "severity": "off", "fixable": true },
{ "rule": "*semi", "severity": "off", "fixable": true }
],
// Enable eslint for all supported languages
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"json5",
"jsonc",
"yaml",
"toml",
"xml",
"gql",
"graphql",
"astro",
"svelte",
"css",
"less",
"scss",
"pcss",
"postcss"
]
}

View file

@ -17,23 +17,23 @@ diverse, inclusive, and healthy community.
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
- Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
- The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
@ -118,11 +118,11 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
Community Impact Guidelines were inspired by
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available
at [https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org

View file

@ -2,13 +2,12 @@
[![Version](https://img.shields.io/github/v/release/iconoir-icons/iconoir?style=flat-square)](https://github.com/iconoir-icons/iconoir/releases)
[![Project Stars](https://img.shields.io/github/stars/iconoir-icons/iconoir?style=flat-square)](https://github.com/iconoir-icons/iconoir)
[![React Library](https://img.shields.io/npm/dm/iconoir-react?color=98E8F3&label=react&style=flat-square)](https://www.npmjs.com/package/iconoir-react)
[![License](https://img.shields.io/github/license/iconoir-icons/iconoir?style=flat-square)](https://github.com/iconoir-icons/iconoir/blob/main/LICENSE)
[![Discord](https://img.shields.io/discord/998909400234348615?color=5865f2&label=Discord&style=flat-square)](https://discord.gg/txXcKCAmKW)
[![Discord](https://img.shields.io/discord/998909400234348615?color=5865f2&label=discord&style=flat-square)](https://discord.gg/txXcKCAmKW)
## What is Iconoir?
Iconoir is an open-source library with 1300+ unique SVG icons, designed on a 24x24 pixels grid. No premium icons, no email sign-up, no newsletters.
Iconoir is an open-source library with 1600+ unique SVG icons, designed on a 24x24 pixels grid.
<a href="https://iconoir.com"><strong>Browse at iconoir.com &rarr;</strong></a>
@ -65,10 +64,7 @@ You can switch between icons from the right sidebar in the editor.
Import the CSS file:
```html
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/iconoir-icons/iconoir@main/css/iconoir.css"
/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/iconoir-icons/iconoir@main/css/iconoir.css" />
```
Here is an example in HTML:
@ -121,4 +117,4 @@ struct ContentView: View {
## License
[MIT](./LICENSE)
MIT License.

View file

@ -1,8 +1,10 @@
import { Listr } from 'listr2';
import fs from 'node:fs/promises';
import path from 'node:path';
import process from 'node:process';
import { fileURLToPath } from 'node:url';
import { Listr } from 'listr2';
import { pascalCase, snakeCase } from 'scule';
import Tinypool from 'tinypool';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
@ -42,7 +44,7 @@ const tasks = new Listr(
{
title: 'Fetching icons',
task: async (ctx) => {
ctx.icons = {};
ctx.tasks = { global: { defaultVariant }, icons: {} };
const iconsVariantsDirs = Object.fromEntries(
iconsVariants.map((variant) => [
@ -71,31 +73,24 @@ const tasks = new Listr(
};
});
ctx.icons[variant] = icons;
ctx.tasks.icons[variant] = icons;
}
ctx.global = { defaultVariant };
},
},
{
title: 'Building targets',
task: (_, task) =>
task: (ctx, task) =>
task.newListr(
Object.entries(targets).map(([targetName, targetConfig]) => ({
title: targetConfig.title,
enabled: () =>
cliTargets.length === 0 || cliTargets.includes(targetName),
task: async (ctx) => {
const { default: task } = await import(
`./targets/${targetConfig.target || targetName}/index.js`
);
enabled: () => ctx.cliTargets.length === 0 || ctx.cliTargets.includes(targetName),
task: (ctx) => {
targetConfig.path = path.join(
rootDir,
...targetConfig.path.split(path.posix.sep),
);
return task(ctx, targetConfig);
return ctx.pool.run({ targetName, config: ctx.tasks, targetConfig });
},
})),
{ concurrent: true, exitOnError: false },
@ -130,4 +125,15 @@ for (const arg of process.argv.slice(2)) {
}
}
await tasks.run();
const pool = new Tinypool({
filename: new URL('./worker.js', import.meta.url).href,
minThreads: 0,
resourceLimits: {
// Vue target (Vite/Rollup) takes up a lot of memory
maxOldGenerationSizeMb: 8192,
},
});
await tasks.run({ cliTargets, pool });
await pool.destroy();

View file

@ -1,7 +1,8 @@
import path from 'node:path';
export function generateImport(name, from) {
if (Array.isArray(name)) name = `{${name.toString()}}`;
if (Array.isArray(name))
name = `{${name.toString()}}`;
return `import ${name} from "${from}";`;
}

View file

@ -1,6 +1,15 @@
import { normalize } from 'node:path';
import ts from 'typescript';
/**
*
* @param {string} path
* @param {string} content
* @param {object} options
*/
export function getDts(path, content, options) {
options = ts.convertCompilerOptionsFromJson(options, '').options;
let output;
const host = ts.createCompilerHost(options);
@ -8,7 +17,8 @@ export function getDts(path, content, options) {
const _readFile = host.readFile;
host.readFile = (filename) => {
if (filename === path) return content;
if (normalize(filename) === path)
return content;
return _readFile(filename);
};
@ -16,7 +26,8 @@ export function getDts(path, content, options) {
const dtsFilename = path.replace(/\.(m|c)?(ts|js)x?$/, '.d.$1ts');
host.writeFile = (filename, contents) => {
if (filename === dtsFilename) output = contents;
if (normalize(filename) === dtsFilename)
output = contents;
};
const program = ts.createProgram([path], options, host);
@ -32,6 +43,7 @@ export function getDts(path, content, options) {
diagnostic.file,
diagnostic.start,
);
const message = ts.flattenDiagnosticMessageText(
diagnostic.messageText,
'\n',

View file

@ -1,4 +1,5 @@
import fs from 'node:fs/promises';
import { EOL } from 'node:os';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
@ -18,10 +19,9 @@ export default async (ctx, target) => {
const variantCssContent = [header];
const cssTarget = (icon, suffixed) => {
const iconName =
suffixed && variant !== ctx.global.defaultVariant
? icon.nameVariant
: icon.name;
const iconName = suffixed && variant !== ctx.global.defaultVariant
? icon.nameVariant
: icon.name;
return `.iconoir-${iconName}::before`;
};
@ -30,9 +30,9 @@ export default async (ctx, target) => {
const fileContent = await fs.readFile(icon.path, 'utf8');
const transformedContent = fileContent
.replace(/\n/g, '')
.replace(/(width|height)="[0-9]+px"/g, '')
.replace(/[ ]+/g, ' ');
.replaceAll(EOL, '')
.replace(/(width|height)="\d+px"/g, '')
.replace(/ +/g, ' ');
const cssContent = `{mask-image:url('data:image/svg+xml;charset=utf-8,${transformedContent}');-webkit-mask-image:url('data:image/svg+xml;charset=utf-8,${transformedContent}');}`;

View file

@ -7,7 +7,7 @@ export default async (ctx, target) => {
const outDir = path.join(target.path, 'lib');
const entryContent = ['library iconoir_flutter;'];
const entryContent = ['library;'];
for (const [variant, icons] of Object.entries(ctx.icons)) {
const variantOutDir = path.join(outDir, variant);

View file

@ -1,5 +1,5 @@
const template = (name, svg) => `
import 'package:flutter/widgets.dart' as widgets;
function template(name, svg) {
return `import 'package:flutter/widgets.dart' as widgets;
import 'package:flutter_svg/flutter_svg.dart';
class ${name} extends widgets.StatelessWidget {
@ -7,19 +7,20 @@ class ${name} extends widgets.StatelessWidget {
final double? width;
final double? height;
const ${name}({widgets.Key? key, this.color, this.width, this.height})
: super(key: key);
const ${name}({super.key, this.color, this.width, this.height});
@override
widgets.Widget build(widgets.BuildContext context) => SvgPicture.string(
'''
'''
${svg}''',
colorFilter:
color != null ? widgets.ColorFilter.mode(color!, widgets.BlendMode.srcIn) : null,
width: width,
height: height,
);
colorFilter: color != null
? widgets.ColorFilter.mode(color!, widgets.BlendMode.srcIn)
: null,
width: width,
height: height,
);
}
`;
}
export default template;

View file

@ -1,7 +1,7 @@
import * as svgr from '@svgr/core';
import * as esbuild from 'esbuild';
import fs from 'node:fs/promises';
import path from 'node:path';
import * as svgr from '@svgr/core';
import * as esbuild from 'esbuild';
import {
generateExport,
generateImport,
@ -36,9 +36,7 @@ const jsTargets = [
/** @type {import('esbuild').TransformOptions} */
const defaultEsbuildOptions = { target: 'es6', minify: true };
/** @type {import('typescript').CompilerOptions} */
const defaultTsOptions = {
jsx: 'react',
declaration: true,
emitDeclarationOnly: true,
target: 'es6',
@ -80,6 +78,7 @@ export default async (ctx, target) => {
jsTarget.path,
'IconoirContext.tsx',
);
const iconoirContextDtsPath = path.join(
jsTarget.path,
`IconoirContext.${jsTarget.dtsExt}`,
@ -90,6 +89,7 @@ export default async (ctx, target) => {
iconoirContextDtsPath,
iconoirContext,
jsTarget.module,
target.native,
);
for (const variant of Object.keys(ctx.icons)) {
@ -106,10 +106,9 @@ export default async (ctx, target) => {
const variantIndex = prepareIndex(jsTarget, variant);
for (const icon of icons) {
const mainIndexComponentName =
variant === ctx.global.defaultVariant
? icon.pascalName
: icon.pascalNameVariant;
const mainIndexComponentName = variant === ctx.global.defaultVariant
? icon.pascalName
: icon.pascalNameVariant;
const jsPath = path.join(
jsTarget.path,
@ -152,6 +151,7 @@ export default async (ctx, target) => {
dtsPath,
reactComponent,
jsTarget.module,
target.native,
);
promises.push(iconDts);
@ -174,16 +174,20 @@ export default async (ctx, target) => {
async function getReactComponent(iconPath, native, template) {
const iconContent = await fs.readFile(iconPath, 'utf8');
const options = native ? nativeSvgrOptions : svgrOptions;
options.template = template;
const options = {
...(native ? nativeSvgrOptions : svgrOptions),
template,
};
return svgr.transform(iconContent, options);
}
async function generateDts(inputPath, outputPath, input, module) {
async function generateDts(inputPath, outputPath, input, module, native) {
const dts = getDts(inputPath, await input, {
...defaultTsOptions,
jsx: native ? 'react-native' : 'react',
module,
...(module === 'esnext' && { moduleResolution: 'bundler' }),
});
return fs.writeFile(outputPath, dts);

View file

@ -1,4 +1,4 @@
const template = (native) => {
function template(native) {
const useClientDirective = native ? '' : '"use client";';
const imports = [
@ -11,8 +11,8 @@ ${useClientDirective}
${imports}
type IconoirContextValue = Partial<${
native ? 'SvgProps' : 'React.SVGProps<SVGSVGElement>'
}>;
native ? 'SvgProps' : 'React.SVGProps<SVGSVGElement>'
}>;
export const IconoirContext = React.createContext<IconoirContextValue>({});
@ -29,7 +29,7 @@ export function IconoirProvider({ iconProps, children }: IconoirProviderProps) {
);
}
`;
};
}
export default template;

View file

@ -5,31 +5,30 @@ export function getTemplate(native, iconoirContextPath) {
variables.props[0].name = 'passedProps';
// Workaround to fix ref type for React Native
if (native) {
variables.props[1].typeAnnotation.typeAnnotation.typeParameters.params[0].typeName.name =
'Svg';
}
if (native)
variables.props[1].typeAnnotation.typeAnnotation.typeParameters.params[0].typeName.name = 'Svg';
const useClientDirective = native ? [] : '"use client"';
const useClientDirective = native ? '' : '"use client";';
const iconoirContextImport = generateImport(
['IconoirContext'],
iconoirContextPath,
);
return tpl`
${useClientDirective}
${variables.imports};
${iconoirContextImport};
${variables.interfaces};
${useClientDirective};
${variables.imports};
${iconoirContextImport}
${variables.interfaces};
const ${variables.componentName} = (${variables.props}) => {
const context = React.useContext(IconoirContext);
const props = { ...context, ...passedProps };
return ${variables.jsx};
};
const ${variables.componentName} = (${variables.props}) => {
const context = React.useContext(IconoirContext);
const props = { ...context, ...passedProps };
return ${variables.jsx};
};
${variables.exports};
${variables.exports};
`;
};
}

View file

@ -1,8 +1,8 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import vue from '@vitejs/plugin-vue';
import { fromHtml } from 'hast-util-from-html';
import { toHtml } from 'hast-util-to-html';
import fs from 'node:fs/promises';
import path from 'node:path';
import { build } from 'vite';
import dts from 'vite-plugin-dts';
import { generateExport } from '../../lib/import-export.js';
@ -44,10 +44,9 @@ export default async (ctx, target) => {
promises.push(generateIconFile(icon.path, vueFileName));
const mainIndexComponentName =
variant === ctx.global.defaultVariant
? icon.pascalName
: icon.pascalNameVariant;
const mainIndexComponentName = variant === ctx.global.defaultVariant
? icon.pascalName
: icon.pascalNameVariant;
mainIndexContent.push(
generateExport(
@ -85,12 +84,9 @@ export default async (ctx, target) => {
formats: ['cjs', 'es'],
},
rollupOptions: {
external: ['vue-demi', 'vue'],
external: ['vue'],
},
},
optimizeDeps: {
exclude: ['vue-demi'],
},
plugins: [
vue({
isProduction: true,

View file

@ -1,16 +1,21 @@
const template = (svg) => `<script lang="ts">
import { defineComponent, inject } from "vue-demi";
import type { SVGAttributes } from "vue-demi";
import providerKey from "../providerKey";
function template(svg) {
return `<script lang="ts">
import type { SVGAttributes } from 'vue';
import { defineComponent, inject } from 'vue';
import providerKey from '../providerKey';
export default defineComponent<SVGAttributes>(() => {
const context = inject(providerKey);
return { context };
export default defineComponent<SVGAttributes>({
setup() {
const context = inject(providerKey);
return { context };
},
});
</script>
<template>
${svg}
</template>`;
</template>
`;
}
export default template;

7
bin/build/worker.js Normal file
View file

@ -0,0 +1,7 @@
export default async ({ targetName, config, targetConfig }) => {
const { default: task } = await import(
`./targets/${targetConfig.target || targetName}/index.js`
);
return task(config, targetConfig);
};

View file

@ -1,10 +1,11 @@
import { updateYamlKey } from '@atomist/yaml-updater';
/* eslint-disable no-console */
import fs from 'node:fs';
import path from 'node:path';
import process from 'node:process';
import { updateYamlKey } from '@atomist/yaml-updater';
import semver from 'semver';
const PACKAGE_BASE = '';
const newVersion = semver.valid(semver.coerce(process.env.TAG_NAME));
console.info('New version is %s', newVersion);
@ -21,17 +22,13 @@ publishPubPackage('iconoir-flutter');
function publishNpmPackage(name) {
console.info('Publishing %s', name);
const packageJsonPath =
name === 'iconoir'
? 'package.json'
: path.join('packages', name, 'package.json');
const packageJsonPath = name === 'iconoir'
? 'package.json'
: path.join('packages', name, 'package.json');
const contents = JSON.parse(fs.readFileSync(packageJsonPath).toString());
contents.version = newVersion;
if (PACKAGE_BASE) {
contents.name = `${PACKAGE_BASE}/${name}`;
}
fs.writeFileSync(packageJsonPath, JSON.stringify(contents, undefined, 2));
console.info('package.json updated');
}

View file

@ -3,10 +3,7 @@
Import the CSS file:
```html
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/iconoir-icons/iconoir@main/css/iconoir.css"
/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/iconoir-icons/iconoir@main/css/iconoir.css" />
```
Here is an example in HTML:

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

115
eslint.config.js Normal file
View file

@ -0,0 +1,115 @@
// @ts-check
import antfu from '@antfu/eslint-config';
import nextPlugin from '@next/eslint-plugin-next';
import jsxA11yPlugin from 'eslint-plugin-jsx-a11y';
import reactPlugin from 'eslint-plugin-react';
import hooksPlugin from 'eslint-plugin-react-hooks';
export default antfu({
typescript: true,
formatters: true,
stylistic: {
semi: true,
overrides: {
'style/arrow-parens': 'error',
'style/brace-style': ['error', '1tbs', { allowSingleLine: true }],
},
},
javascript: {
overrides: {
'antfu/no-top-level-await': 'off',
},
},
ignores: [
'css/*.css',
'iconoir.com/out/',
'**/.expo/',
'packages/iconoir-flutter/.dart_tool/',
'packages/iconoir-flutter/build/',
'packages/iconoir-flutter/example/',
],
rules: {
'style/padding-line-between-statements': [
'error',
{
blankLine: 'always',
prev: [
'block',
'block-like',
'cjs-export',
'class',
'multiline-block-like',
'multiline-const',
'multiline-expression',
'multiline-let',
'multiline-var',
],
next: '*',
},
{
blankLine: 'always',
prev: ['const', 'let'],
next: [
'block',
'block-like',
'cjs-export',
'class',
],
},
{
blankLine: 'always',
prev: '*',
next: [
'multiline-block-like',
'multiline-const',
'multiline-expression',
'multiline-let',
'multiline-var',
],
},
],
},
}, {
files: ['iconoir.com/**'],
plugins: {
'@next/next': nextPlugin,
'react': reactPlugin,
'react-hooks': hooksPlugin,
'jsx-a11y': jsxA11yPlugin,
},
settings: {
next: {
rootDir: 'iconoir.com/',
},
react: {
version: 'detect',
},
},
// @ts-ignore
rules: {
...nextPlugin.configs.recommended.rules,
...nextPlugin.configs['core-web-vitals'].rules,
...reactPlugin.configs.recommended.rules,
...hooksPlugin.configs.recommended.rules,
// rules from "eslint-config-next"
'react/no-unknown-property': 'off',
'react/react-in-jsx-scope': 'off',
'react/prop-types': 'off',
'jsx-a11y/alt-text': [
'warn',
{
elements: ['img'],
img: ['Image'],
},
],
'jsx-a11y/aria-props': 'warn',
'jsx-a11y/aria-proptypes': 'warn',
'jsx-a11y/aria-unsupported-elements': 'warn',
'jsx-a11y/role-has-required-aria-props': 'warn',
'jsx-a11y/role-supports-aria-props': 'warn',
'react/jsx-no-target-blank': 'off',
},
});

View file

@ -3,8 +3,12 @@
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
@ -24,9 +28,10 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel

View file

@ -1,8 +0,0 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
optimizePackageImports: ['iconoir-react'],
},
};
module.exports = nextConfig;

View file

@ -0,0 +1,9 @@
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
experimental: {
optimizePackageImports: ['iconoir-react'],
},
};
export default nextConfig;

View file

@ -2,21 +2,21 @@
"name": "example-next",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"dev": "next dev",
"lint": "next lint",
"start": "next start"
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"next": "14.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"next": "15.4.4",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@types/node": "^20.8.9",
"@types/react": "^18.2.33",
"@types/react-dom": "^18.2.14",
"@types/node": "^22.10.2",
"@types/react": "^19.0.1",
"@types/react-dom": "^19.0.2",
"iconoir-react": "workspace:*",
"typescript": "^5.2.2"
"typescript": "^5.7.2"
}
}

View file

@ -1,26 +1,26 @@
{
"compilerOptions": {
"target": "es5",
"incremental": true,
"target": "ES2017",
"jsx": "preserve",
"lib": ["dom", "dom.iterable", "esnext"],
"module": "esnext",
"moduleResolution": "bundler",
"paths": {
"@/*": ["./*"]
},
"resolveJsonModule": true,
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"skipLibCheck": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
}
]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]

View file

@ -7,6 +7,7 @@ node_modules/
.expo/
dist/
web-build/
expo-env.d.ts
# Native
*.orig.*

View file

@ -1,13 +1,21 @@
{
"expo": {
"name": "Iconoir",
"slug": "react-native-expo",
"slug": "example-react-native",
"version": "1.0.0",
"orientation": "portrait",
"userInterfaceStyle": "light",
"assetBundlePatterns": ["**/*"],
"newArchEnabled": true,
"splash": {
"backgroundColor": "#ffffff"
},
"ios": {
"supportsTablet": true
},
"android": {
"adaptiveIcon": {
"backgroundColor": "#ffffff"
}
}
}
}

View file

@ -1,7 +0,0 @@
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
};
};

View file

@ -0,0 +1,8 @@
import { registerRootComponent } from 'expo';
import App from './App';
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in Expo Go or in a native build,
// the environment is set up appropriately
registerRootComponent(App);

View file

@ -0,0 +1,31 @@
/*
* Workaround to be able to import iconoir lib from workspace.
* See also: https://github.com/pnpm/pnpm/issues/4286
*/
const { makeMetroConfig } = require('@rnx-kit/metro-config');
const MetroSymlinksResolver = require('@rnx-kit/metro-resolver-symlinks');
const { getDefaultConfig } = require('expo/metro-config');
const symlinksResolver = MetroSymlinksResolver({
remapModule: (_context, moduleName) => {
if (moduleName === 'iconoir-react-native') {
return require.resolve(moduleName);
}
return moduleName;
},
},
);
/** @type {import('expo/metro-config').MetroConfig} */
const expoConfig = getDefaultConfig(__dirname);
/** @type {import('expo/metro-config').MetroConfig} */
module.exports = makeMetroConfig({
...expoConfig,
resolver: {
...expoConfig.resolver,
resolveRequest: symlinksResolver,
},
});

File diff suppressed because it is too large Load diff

View file

@ -1,25 +1,30 @@
{
"name": "example-react-native",
"version": "1.0.0",
"private": true,
"main": "node_modules/expo/AppEntry.js",
"main": "index.ts",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"start": "expo start",
"web": "expo start --web"
},
"dependencies": {
"@expo/webpack-config": "^19.0.0",
"expo": "~49.0.15",
"expo-status-bar": "~1.6.0",
"iconoir-react-native": "^7.0.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-native": "0.72.6",
"react-native-svg": "^13.9.0",
"react-native-web": "~0.19.6"
"expo": "^53.0.20",
"expo-status-bar": "^2.0.0",
"iconoir-react-native": "workspace:*",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-native": "^0.79.5",
"react-native-svg": "^15.11.2",
"react-native-web": "^0.20.0"
},
"devDependencies": {
"@babel/core": "^7.20.0"
"@babel/core": "^7.25.2",
"@react-native/metro-config": "^0.79.5",
"@rnx-kit/metro-config": "^2.0.1",
"@rnx-kit/metro-resolver-symlinks": "^0.2.1",
"@types/react": "^19.0.14",
"typescript": "~5.8.3"
}
}

View file

@ -0,0 +1,6 @@
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true
}
}

View file

@ -26,3 +26,5 @@ coverage
*.njsproj
*.sln
*.sw?
*.tsbuildinfo

View file

@ -2,7 +2,6 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Iconoir</title>
</head>

View file

@ -1,25 +1,27 @@
{
"name": "example-vue",
"type": "module",
"private": true,
"scripts": {
"build": "run-p type-check \"build-only {@}\" --",
"build-only": "vite build",
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false"
"build-only": "vite build",
"type-check": "vue-tsc --build"
},
"dependencies": {
"vue": "^3.3.7"
"vue": "^3.5.18"
},
"devDependencies": {
"@iconoir/vue": "workspace:*",
"@tsconfig/node18": "^18.2.2",
"@types/node": "^18.18.7",
"@vitejs/plugin-vue": "^4.4.0",
"@vue/tsconfig": "^0.4.0",
"npm-run-all2": "^6.1.1",
"typescript": "~5.2.2",
"vite": "^4.5.0",
"vue-tsc": "^1.8.22"
"@tsconfig/node22": "^22.0.2",
"@types/node": "^22.16.5",
"@vitejs/plugin-vue": "^6.0.0",
"@vue/tsconfig": "^0.7.0",
"npm-run-all2": "^8.0.4",
"typescript": "~5.8.3",
"vite": "^7.0.6",
"vite-plugin-vue-devtools": "^7.7.7",
"vue-tsc": "^3.0.4"
}
}

View file

@ -1,10 +1,10 @@
<script setup lang="ts">
import {
Check,
Iconoir,
IconoirProvider,
Medal1st,
Medal1stSolid,
IconoirProvider,
Check,
} from '@iconoir/vue';
</script>
@ -21,8 +21,6 @@ import {
'height': '2em',
}"
>
<SomeOtherContainer>
<Check />
</SomeOtherContainer>
<Check />
</IconoirProvider>
</template>

View file

@ -1,12 +1,13 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"composite": true,
"baseUrl": ".",
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"paths": {
"@/*": ["./src/*"]
}
}
},
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"]
}

View file

@ -1,5 +1,4 @@
{
"files": [],
"references": [
{
"path": "./tsconfig.node.json"
@ -7,5 +6,6 @@
{
"path": "./tsconfig.app.json"
}
]
],
"files": []
}

View file

@ -1,16 +1,19 @@
{
"extends": "@tsconfig/node18/tsconfig.json",
"extends": "@tsconfig/node22/tsconfig.json",
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"],
"noEmit": true
},
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"nightwatch.conf.*",
"playwright.config.*"
],
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"]
}
]
}

View file

@ -1,16 +1,18 @@
import { fileURLToPath, URL } from 'node:url'
import { fileURLToPath, URL } from 'node:url';
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vue from '@vitejs/plugin-vue';
import { defineConfig } from 'vite';
import vueDevTools from 'vite-plugin-vue-devtools';
// https://vitejs.dev/config/
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
vueDevTools(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
});

View file

@ -1,20 +0,0 @@
{
"extends": ["next/core-web-vitals"],
"settings": {
"next": {
"rootDir": "iconoir.com"
}
},
"rules": {
"react/no-unescaped-entities": ["off"]
},
"overrides": [
{
"files": ["*.js"],
"parser": "espree",
"parserOptions": {
"ecmaVersion": "latest"
}
}
]
}

View file

@ -1,30 +1,6 @@
import React from 'react';
import styled from 'styled-components';
export function Ad() {
const containerRef = React.useRef<HTMLDivElement>(null);
const addedScript = React.useRef(false);
React.useEffect(() => {
const container = containerRef.current;
if (container && !addedScript.current) {
addedScript.current = true;
const script = document.createElement('script');
script.async = true;
script.type = 'text/javascript';
script.src =
'//cdn.carbonads.com/carbon.js?serve=CESDK5QJ&placement=iconoircom';
script.id = '_carbonads_js';
container.appendChild(script);
}
}, []);
return <AdContainer ref={containerRef} />;
}
const AdContainer = styled.div`
#carbonads {
margin: 24px 0 0 0;
@ -54,3 +30,24 @@ const AdContainer = styled.div`
}
}
`;
export function Ad() {
const containerRef = React.useRef<HTMLDivElement>(null);
const addedScript = React.useRef(false);
React.useEffect(() => {
const container = containerRef.current;
if (container && !addedScript.current) {
addedScript.current = true;
const script = document.createElement('script');
script.async = true;
script.type = 'text/javascript';
script.src = '//cdn.carbonads.com/carbon.js?serve=CESDK5QJ&placement=iconoircom';
script.id = '_carbonads_js';
container.appendChild(script);
}
}, []);
return <AdContainer ref={containerRef} />;
}

View file

@ -1,7 +1,6 @@
import anime from 'animejs';
import React from 'react';
// eslint-disable-next-line no-unused-vars
type SetInstances = (instances: anime.AnimeInstance[]) => void;
function playWithLines1(setInstances: SetInstances): anime.AnimeInstance[] {
@ -11,7 +10,7 @@ function playWithLines1(setInstances: SetInstances): anime.AnimeInstance[] {
strokeDashoffset: [anime.setDashoffset, 0],
easing: 'easeInOutSine',
duration: 1500,
delay: function (el, i) {
delay(_el, i) {
return i * 250;
},
direction: 'alternate',
@ -27,7 +26,7 @@ function playWithLines2(setInstances: SetInstances): anime.AnimeInstance[] {
strokeDashoffset: [anime.setDashoffset, 0],
easing: 'easeInOutSine',
duration: 1500,
delay: function (el, i) {
delay(_el, i) {
return i * 250;
},
direction: 'alternate',
@ -43,7 +42,7 @@ function playWithLines3(setInstances: SetInstances): anime.AnimeInstance[] {
strokeDashoffset: [anime.setDashoffset, 0],
easing: 'easeInOutSine',
duration: 1500,
delay: function (el, i) {
delay(_el, i) {
return i * 250;
},
direction: 'alternate',
@ -68,7 +67,7 @@ function playWithLines4(setInstances: SetInstances): anime.AnimeInstance[] {
strokeDashoffset: [anime.setDashoffset, 0],
easing: 'easeInOutSine',
duration: 1500,
delay: function (el, i) {
delay(_el, i) {
return i * 250;
},
direction: 'alternate',

View file

@ -8,91 +8,12 @@ import {
import { media } from '../lib/responsive';
import { Text14 } from './Typography';
export function AvailableFor() {
const { ref, width } = useResizeObserver();
return (
<>
<MobileHeader>Available For</MobileHeader>
<AvailableForOuter>
<AvailableForContainer $contentWidth={width || 0} ref={ref}>
<DesktopHeader>Available for</DesktopHeader>
<a href={LIBRARY_LINKS.React} target={'_blank'} rel={'noreferrer'}>
<AvailableForImage
src={'/logo-react.svg'}
alt={'React Logo'}
title={'React'}
/>
</a>
<a
href={'https://github.com/iconoir-icons/iconoir#swift-package'}
target={'_blank'}
rel={'noreferrer'}
>
<AvailableForImage
src={'/logo-swift.svg'}
alt={'Swift Logo'}
title={'Swift'}
/>
</a>
<a href={LIBRARY_LINKS.Flutter} target={'_blank'} rel={'noreferrer'}>
<AvailableForImage
src={'/logo-flutter.svg'}
alt={'Flutter Logo'}
title={'Flutter'}
/>
</a>
<a href={LIBRARY_LINKS.Figma} target={'_blank'} rel={'noreferrer'}>
<AvailableForImage
src={'/logo-figma.svg'}
alt={'Figma Logo'}
title={'Figma'}
/>
</a>
<a
href={LIBRARY_LINKS.ReactNative}
target={'_blank'}
rel={'noreferrer'}
>
<AvailableForImage
src={'/logo-react-native.svg'}
alt={'React Native Logo'}
title={'React Native'}
/>
</a>
<a href={LIBRARY_LINKS.Vue} target={'_blank'} rel={'noreferrer'}>
<AvailableForImage
src={'/logo-vue.svg'}
alt={'Vue Logo'}
title={'Vue'}
/>
</a>
<a href={LIBRARY_LINKS.Framer} target={'_blank'} rel={'noreferrer'}>
<AvailableForImage
src={'/logo-framer.svg'}
alt={'Framer Logo'}
title={'Framer'}
/>
</a>
<AreYouUsing>
<a href={SUGGEST_LIBRARY_LINK} target={'_blank'} rel={'noreferrer'}>
<Text14>More?</Text14>
</a>
<a href={FEEDBACK_LINK} target={'_blank'} rel={'noreferrer'}>
<Text14>Are you using the library?</Text14>
</a>
</AreYouUsing>
</AvailableForContainer>
</AvailableForOuter>
</>
);
}
const AreYouUsing = styled.div`
* {
white-space: nowrap;
}
`;
const MobileHeader = styled(Text14)`
&&& {
display: block;
@ -103,6 +24,7 @@ const MobileHeader = styled(Text14)`
}
}
`;
const DesktopHeader = styled(Text14)`
&&& {
display: none;
@ -111,6 +33,7 @@ const DesktopHeader = styled(Text14)`
}
}
`;
const AvailableForAnimation = keyframes`
5% {
transform: translateX(0);
@ -125,6 +48,7 @@ const AvailableForAnimation = keyframes`
transform: translateX(0);
}
`;
const AvailableForOuter = styled.div`
max-width: 100vw;
margin: 16px -30px 70px -30px;
@ -135,6 +59,7 @@ const AvailableForOuter = styled.div`
padding: 0;
}
`;
const AvailableForContainer = styled.div<{ $contentWidth: number }>`
display: flex;
align-items: center;
@ -142,8 +67,8 @@ const AvailableForContainer = styled.div<{ $contentWidth: number }>`
width: max-content;
--content-width: ${(props) => props.$contentWidth}px;
${(props) =>
props.$contentWidth &&
css`
props.$contentWidth
&& css`
animation: ${AvailableForAnimation} 40s cubic-bezier(0.37, 0, 0.63, 1)
infinite;
`}
@ -161,6 +86,7 @@ const AvailableForContainer = styled.div<{ $contentWidth: number }>`
}
}
`;
const AvailableForImage = styled.img`
height: 40px;
display: block;
@ -173,3 +99,107 @@ const AvailableForImage = styled.img`
height: 50px;
}
`;
export function AvailableFor() {
const { ref, width } = useResizeObserver();
return (
<>
<MobileHeader>Available For</MobileHeader>
<AvailableForOuter>
<AvailableForContainer $contentWidth={width || 0} ref={ref}>
<DesktopHeader>Available for</DesktopHeader>
<a href={LIBRARY_LINKS.React} target="_blank" rel="noreferrer">
<AvailableForImage
src="/logo-react.svg"
alt="React Logo"
title="React"
/>
</a>
<a
href="https://github.com/iconoir-icons/iconoir#swift-package"
target="_blank"
rel="nofollow noreferrer"
>
<AvailableForImage
src="/logo-swift.svg"
alt="Swift Logo"
title="Swift"
/>
</a>
<a
href={LIBRARY_LINKS.Flutter}
target="_blank"
rel="nofollow noreferrer"
>
<AvailableForImage
src="/logo-flutter.svg"
alt="Flutter Logo"
title="Flutter"
/>
</a>
<a
href={LIBRARY_LINKS.Figma}
target="_blank"
rel="nofollow noreferrer"
>
<AvailableForImage
src="/logo-figma.svg"
alt="Figma Logo"
title="Figma"
/>
</a>
<a
href={LIBRARY_LINKS.ReactNative}
target="_blank"
rel="nofollow noreferrer"
>
<AvailableForImage
src="/logo-react-native.svg"
alt="React Native Logo"
title="React Native"
/>
</a>
<a
href={LIBRARY_LINKS.Vue}
target="_blank"
rel="nofollow noreferrer"
>
<AvailableForImage
src="/logo-vue.svg"
alt="Vue Logo"
title="Vue"
/>
</a>
<a
href={LIBRARY_LINKS.Framer}
target="_blank"
rel="nofollow noreferrer"
>
<AvailableForImage
src="/logo-framer.svg"
alt="Framer Logo"
title="Framer"
/>
</a>
<AreYouUsing>
<a
href={SUGGEST_LIBRARY_LINK}
target="_blank"
rel="nofollow noreferrer"
>
<Text14>More?</Text14>
</a>
<a
href={FEEDBACK_LINK}
target="_blank"
rel="nofollow noreferrer"
>
<Text14>Are you using the library?</Text14>
</a>
</AreYouUsing>
</AvailableForContainer>
</AvailableForOuter>
</>
);
}

View file

@ -50,7 +50,7 @@ export const LargeButton = styled(ResetButton)`
}
`;
export const Button = styled(LargeButton)`
const Button = styled(LargeButton)`
&&&& {
height: 40px;
font-size: 13px;

View file

@ -2,25 +2,6 @@ import styled from 'styled-components';
import { media } from '../lib/responsive';
import { Text15 } from './Typography';
export interface CategoryRowProps {
category: string;
numIcons: number;
style?: any;
}
export function CategoryRow({ category, numIcons, style }: CategoryRowProps) {
return (
<Container style={style}>
<InnerContainer>
<Title>{category}</Title>
<Text15>
{numIcons} Icon{numIcons === 1 ? '' : 's'}
</Text15>
<Separator />
</InnerContainer>
</Container>
);
}
const InnerContainer = styled.div`
display: flex;
align-items: center;
@ -32,6 +13,7 @@ const InnerContainer = styled.div`
margin-right: 10px;
}
`;
const Container = styled.div`
display: flex;
align-items: flex-end;
@ -41,6 +23,7 @@ const Container = styled.div`
padding-bottom: 40px;
}
`;
const Title = styled(Text15)`
&&& {
font-weight: 700;
@ -50,8 +33,32 @@ const Title = styled(Text15)`
padding: 6px 10px;
}
`;
const Separator = styled.div`
height: 1px;
flex: 1;
background: var(--g6);
`;
export interface CategoryRowProps {
category: string;
numIcons: number;
style?: any;
}
export function CategoryRow({ category, numIcons, style }: CategoryRowProps) {
return (
<Container style={style}>
<InnerContainer>
<Title>{category}</Title>
<Text15>
{numIcons}
{' '}
Icon
{numIcons === 1 ? '' : 's'}
</Text15>
<Separator />
</InnerContainer>
</Container>
);
}

View file

@ -1,72 +1,15 @@
import type { MDXRemoteSerializeResult } from 'next-mdx-remote';
import { BoxIso } from 'iconoir-react';
import moment from 'moment';
import { MDXRemoteSerializeResult } from 'next-mdx-remote';
import React from 'react';
import styled from 'styled-components';
import { MDXRemote } from './MDXRemote';
import { media } from '../lib/responsive';
import { Code, Text15, Text18 } from './Typography';
import { CopyButton } from './Button';
import { MDXRemote } from './MDXRemote';
import { Code, Text15, Text18 } from './Typography';
const EXPAND_HEIGHT = 400;
export interface ChangelogEntryProps {
name: string;
url: string;
created_at: string;
body?: MDXRemoteSerializeResult;
}
export function ChangelogEntry({
name,
url,
body,
created_at,
}: ChangelogEntryProps) {
const [expanded, setExpanded] = React.useState(false);
const [shouldExpand, setShouldExpand] = React.useState(false);
const containerRef = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
if (
containerRef.current &&
containerRef.current.clientHeight > EXPAND_HEIGHT
) {
setShouldExpand(true);
}
}, []);
return (
<Container ref={containerRef}>
<ContainerLeft>
<ContainerIcon>
<BoxIso />
</ContainerIcon>
<TitleContainer>
<a
href={url}
target={'_blank'}
rel={'noreferrer'}
style={{ textDecoration: 'none' }}
>
<EntryTitle>{name}</EntryTitle>
</a>
<Text15>{moment(created_at).format('MMM DD, YYYY')}</Text15>
</TitleContainer>
</ContainerLeft>
<EntryBody $expanded={expanded}>
{body ? <MDXRemote {...body} /> : 'No changelog'}
{shouldExpand ? (
<ExpandContainer>
<CopyButton onClick={() => setExpanded((e) => !e)}>
{expanded ? 'Collapse' : 'Expand'}
</CopyButton>
</ExpandContainer>
) : null}
</EntryBody>
</Container>
);
}
const Container = styled.div`
margin: 40px 0;
display: flex;
@ -79,6 +22,7 @@ const Container = styled.div`
margin: 24px 0;
}
`;
const ContainerLeft = styled.div`
display: flex;
align-items: flex-start;
@ -88,25 +32,30 @@ const ContainerLeft = styled.div`
margin-right: 30px;
}
`;
const ContainerIcon = styled.div`
font-size: 18px;
color: var(--black);
margin-right: 18px;
`;
const TitleContainer = styled.div`
width: 100px;
`;
const EntryTitle = styled(Text18)`
&&& {
color: var(--black);
font-weight: 700;
}
`;
const ExpandContainer = styled.div`
position: absolute;
bottom: 16px;
right: 23px;
`;
const EntryBody = styled(Code)<{ $expanded?: boolean }>`
&&& {
flex: 1;
@ -135,3 +84,63 @@ const EntryBody = styled(Code)<{ $expanded?: boolean }>`
}
}
`;
export interface ChangelogEntryProps {
name: string;
url: string;
created_at: string;
body?: MDXRemoteSerializeResult;
}
export function ChangelogEntry({
name,
url,
body,
created_at,
}: ChangelogEntryProps) {
const [expanded, setExpanded] = React.useState(false);
const [shouldExpand, setShouldExpand] = React.useState(false);
const containerRef = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
if (
containerRef.current
&& containerRef.current.clientHeight > EXPAND_HEIGHT
) {
setShouldExpand(true);
}
}, []);
return (
<Container ref={containerRef}>
<ContainerLeft>
<ContainerIcon>
<BoxIso />
</ContainerIcon>
<TitleContainer>
<a
href={url}
target="_blank"
rel="noreferrer"
style={{ textDecoration: 'none' }}
>
<EntryTitle>{name}</EntryTitle>
</a>
<Text15>{moment(created_at).format('MMM DD, YYYY')}</Text15>
</TitleContainer>
</ContainerLeft>
<EntryBody $expanded={expanded}>
{body ? <MDXRemote {...body} /> : 'No changelog'}
{shouldExpand
? (
<ExpandContainer>
<CopyButton onClick={() => setExpanded((e) => !e)}>
{expanded ? 'Collapse' : 'Expand'}
</CopyButton>
</ExpandContainer>
)
: null}
</EntryBody>
</Container>
);
}

View file

@ -2,33 +2,34 @@ import Link from 'next/link';
import styled from 'styled-components';
import { Text13 } from './Typography';
const Container = styled(Text13)`
&&& {
color: var(--g1);
font-weight: 700;
background: var(--g5);
line-height: 1;
padding: 7px 16px;
border-radius: 200px;
display: block;
text-decoration: none;
transition:
color 0.1s linear,
background 0.1s linear;
&:hover {
background: var(--black);
color: var(--white);
}
}
`;
export interface CurrentVersionProps {
version: string;
}
export function CurrentVersion({ version }: CurrentVersionProps) {
return (
<Link href={'/docs/changelog'} passHref legacyBehavior>
<Container as={'a'}>{version}</Container>
<Link href="/docs/changelog" passHref legacyBehavior>
<Container as="a">{version}</Container>
</Link>
);
}
const Container = styled(Text13)`
&&& {
color: var(--g1);
font-weight: 700;
background: var(--g5);
line-height: 1;
padding: 7px 16px;
border-radius: 200px;
display: block;
text-decoration: none;
transition:
color 0.1s linear,
background 0.1s linear;
&:hover {
background: var(--black);
color: var(--white);
}
}
`;

View file

@ -1,16 +1,62 @@
import type { IconListCustomizations } from './IconList';
import React from 'react';
import styled from 'styled-components';
import { DEFAULT_CUSTOMIZATIONS, IconListCustomizations } from './IconList';
import { media } from '../lib/responsive';
import { DEFAULT_CUSTOMIZATIONS } from './IconList';
import { ColorButton, ColorInput } from './Input';
import { Slider } from './Slider';
import { Text13, Text15 } from './Typography';
import { media } from '../lib/responsive';
const CustomizationBox = styled.div`
background-color: var(--g7);
width: 84%;
padding: 8%;
border-radius: 10px;
margin: 24px 0;
display: none;
${media.md} {
display: block;
}
`;
const Header = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 30px;
border-bottom: solid 1px var(--g6);
padding-bottom: 10px;
`;
const Field = styled.div`
margin-bottom: 18px;
`;
const HorizontalField = styled(Field)`
display: flex;
align-items: center;
justify-content: space-between;
`;
const ResetButton = styled(Field)`
&&& {
margin: initial;
text-decoration: underline;
color: var(--dark-gray);
font-size: 13px;
&:hover {
color: var(--black);
cursor: pointer;
}
}
`;
export interface CustomizationEditorProps {
customizations: IconListCustomizations;
// eslint-disable-next-line no-unused-vars
onChange: (customizations: IconListCustomizations) => void;
}
export function CustomizationEditor({
customizations,
onChange,
@ -18,6 +64,7 @@ export function CustomizationEditor({
const [, startTransition] = (React as any).useTransition();
const [color, setColor] = React.useState(customizations.hexColor);
const [size, setSize] = React.useState(customizations.size);
const [strokeWidth, setStrokeWidth] = React.useState(
customizations.strokeWidth,
);
@ -50,7 +97,7 @@ export function CustomizationEditor({
</Header>
<Field>
<Slider
label={'Optical Size'}
label="Optical Size"
minValue={16}
maxValue={64}
value={[size]}
@ -63,7 +110,7 @@ export function CustomizationEditor({
</Field>
<Field>
<Slider
label={'Stroke Weight'}
label="Stroke Weight"
minValue={0.5}
maxValue={3}
value={[strokeWidth]}
@ -78,7 +125,7 @@ export function CustomizationEditor({
<HorizontalField>
<Text13>Color</Text13>
<ColorInput
type={'color'}
type="color"
value={color}
onChange={(e) => {
setColor(e.target.value);
@ -91,44 +138,3 @@ export function CustomizationEditor({
</>
);
}
const CustomizationBox = styled.div`
background-color: var(--g7);
width: 84%;
padding: 8%;
border-radius: 10px;
margin: 24px 0;
display: none;
${media.md} {
display: block;
}
`;
const Header = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 30px;
border-bottom: solid 1px var(--g6);
padding-bottom: 10px;
`;
const Field = styled.div`
margin-bottom: 24px;
`;
const HorizontalField = styled(Field)`
display: flex;
align-items: center;
justify-content: space-between;
`;
const ResetButton = styled(Field)`
&&& {
margin: initial;
text-decoration: underline;
color: var(--dark-gray);
font-size: 13px;
&:hover {
color: var(--black);
cursor: pointer;
}
}
`;

View file

@ -1,15 +1,103 @@
import type { DocumentationItem } from '../pages/docs/[...slug]';
import { NavArrowUp } from 'iconoir-react';
import Link from 'next/link';
import { useRouter } from 'next/router';
import React from 'react';
import styled from 'styled-components';
import { DocumentationItem } from '../pages/docs/[...slug]';
import { media } from '../lib/responsive';
const HeaderItemIcon = styled.div<{ $active?: boolean }>`
font-size: 13px;
transition: transform 0.25s linear;
transform: rotate(${(props) => (props.$active ? 180 : 0)}deg);
margin-right: 7px;
position: relative;
top: 6px;
svg {
display: block;
}
${media.lg} {
display: none;
}
`;
const ChildrenContainer = styled.div<{ $expanded?: boolean }>`
display: ${(props) => (props.$expanded ? 'block' : 'none')};
${media.lg} {
display: block;
}
`;
const HeaderItem = styled.div`
padding: 10px 30px;
font-size: 15px;
line-height: 19px;
color: var(--g0);
font-weight: 700;
display: flex;
align-items: baseline;
cursor: pointer;
${media.lg} {
padding: 22px 45px;
cursor: default;
pointer-events: none;
&:not(:first-child) {
margin-top: 10px;
}
}
`;
const NavigationItem = styled.div<{ $active?: boolean }>`
padding: 12px 12px 12px 75px;
transition:
background 0.1s linear,
color 0.1s linear;
font-size: 16px;
line-height: 14.5px;
letter-spacing: -0.02em;
color: var(--g1);
display: flex;
align-items: center;
text-decoration: none;
span {
font-weight: 400;
}
> :not(:last-child) {
margin-right: 14px;
}
&:hover,
${(props) => (props.$active ? '&' : '&.noop')} {
color: var(--g0);
text-decoration: underline;
}
${(props) => (props.$active ? 'span' : '&.noop')} {
font-weight: 500;
}
${media.lg} {
padding: 12px 12px 12px 65px;
}
`;
const NavigationItemLabel = styled.span`
display: flex;
align-items: center;
justify-content: center;
padding: 2px 6px;
border-radius: 4px;
font-size: 11px;
line-height: 17.6px;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--g1);
background: var(--g5);
`;
export interface DocumentationNavigationProps {
documentationItems: DocumentationItem[];
pathPrefix?: string[];
}
export function DocumentationNavigation({
documentationItems,
pathPrefix,
@ -23,14 +111,15 @@ export function DocumentationNavigation({
const normalized = activePath.replace((pathPrefix || []).join('/'), '');
return (
normalized === item.path ||
item.children?.some((child) => {
normalized === item.path
|| item.children?.some((child) => {
return activePath.startsWith(
[item.path, child.path].filter(Boolean).join('/'),
);
})
);
});
setExpandedTitles(expandedItems.map((item) => item.title));
}, [activePath, pathPrefix, documentationItems]);
@ -81,13 +170,15 @@ export function DocumentationNavigation({
legacyBehavior
key={documentationItem.path}
>
<NavigationItem as={'a'} $active={activePath === path}>
<NavigationItem as="a" $active={activePath === path}>
<span>{documentationItem.title}</span>
{documentationItem.label ? (
<NavigationItemLabel>
{documentationItem.label}
</NavigationItemLabel>
) : null}
{documentationItem.label
? (
<NavigationItemLabel>
{documentationItem.label}
</NavigationItemLabel>
)
: null}
</NavigationItem>
</Link>
);
@ -96,87 +187,3 @@ export function DocumentationNavigation({
</>
);
}
const HeaderItemIcon = styled.div<{ $active?: boolean }>`
font-size: 13px;
transition: transform 0.25s linear;
transform: rotate(${(props) => (props.$active ? 180 : 0)}deg);
margin-right: 7px;
position: relative;
top: 6px;
svg {
display: block;
}
${media.lg} {
display: none;
}
`;
const ChildrenContainer = styled.div<{ $expanded?: boolean }>`
display: ${(props) => (props.$expanded ? 'block' : 'none')};
${media.lg} {
display: block;
}
`;
const HeaderItem = styled.div`
padding: 10px 30px;
font-size: 15px;
line-height: 19px;
color: var(--g0);
font-weight: 700;
display: flex;
align-items: baseline;
cursor: pointer;
${media.lg} {
padding: 22px 45px;
cursor: default;
pointer-events: none;
&:not(:first-child) {
margin-top: 10px;
}
}
`;
const NavigationItem = styled.div<{ $active?: boolean }>`
padding: 12px 12px 12px 75px;
transition:
background 0.1s linear,
color 0.1s linear;
font-weight: 500;
font-size: 16px;
line-height: 14.5px;
letter-spacing: -0.02em;
color: var(--g1);
display: flex;
align-items: center;
text-decoration: none;
span {
font-weight: 500;
}
> :not(:last-child) {
margin-right: 14px;
}
&:hover,
${(props) => (props.$active ? '&' : '&.noop')} {
color: var(--g0);
text-decoration: underline;
}
${(props) => (props.$active ? 'span' : '&.noop')} {
font-weight: 700;
}
${media.lg} {
padding: 12px 12px 12px 65px;
}
`;
const NavigationItemLabel = styled.span`
display: flex;
align-items: center;
justify-content: center;
padding: 2px 6px;
border-radius: 4px;
font-size: 11px;
line-height: 17.6px;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--g1);
background: var(--g5);
`;

View file

@ -0,0 +1,96 @@
import { Sparks } from 'iconoir-react';
import React, { useEffect, useRef, useState } from 'react';
import styled from 'styled-components';
import { media } from '../lib/responsive';
const Text = styled.span`
color: #ffffffba;
`;
const PopupContent = styled.div`
display: none;
${media.lg} {
display: flex;
align-items: center;
justify-content: center;
padding: 14px 0;
background: #8330c6;
color: var(--white);
font-weight: 500;
font-size: 15px;
position: fixed;
top: 16px;
left: 16px;
right: 16px;
border-radius: 8px;
z-index: 9999;
margin: auto;
}
> * {
margin: 0 4px;
}
> a {
color: var(--white);
opacity: 1;
text-decoration: underline;
}
`;
const CloseButton = styled.span`
color: var(--white);
float: right;
font-size: 28px;
font-weight: bold;
&:hover,
&:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
`;
export function DonationPopup() {
const [isVisible, setIsVisible] = useState(false);
const isInitialMount = useRef(true);
useEffect(() => {
if (isInitialMount.current) {
isInitialMount.current = false;
const isReturningUser = localStorage.getItem('returningUser');
const isReturningUserAge = localStorage.getItem('returningUserAge');
if (isReturningUser === 'true' && isReturningUserAge === 'false') {
setTimeout(() => setIsVisible(true), 15000);
localStorage.setItem('returningUserAge', 'true');
} else {
localStorage.setItem('returningUser', 'true');
localStorage.setItem('returningUserAge', 'false');
}
}
}, []);
const handleClose = () => {
setIsVisible(false);
};
return (
<>
{isVisible && (
<PopupContent>
<Sparks></Sparks>
<Text>
Your one-time or recurring contribution does a lot to keep Iconoir
going.
</Text>
<a
href="https://opencollective.com/iconoir/donate?interval=month&amount=10"
target="_blank"
>
Support the project!
</a>
<CloseButton onClick={handleClose}>&times;</CloseButton>
</PopupContent>
)}
</>
);
}

View file

@ -1,17 +1,64 @@
import type { Icon, IconListFilters } from './IconList';
import { IconoirProvider } from 'iconoir-react';
import React from 'react';
import styled from 'styled-components';
import { Ad } from './Ad';
import { Sponsor } from './Sponsor';
import { media } from '../lib/responsive';
import { CustomizationEditor } from './CustomizationEditor';
import { FiltersEditor } from './FiltersEditor';
import { Icon, IconList, IconListFilters } from './IconList';
import { media } from '../lib/responsive';
import { IconList } from './IconList';
import { Streamline } from './Streamline';
import { useCustomizationPersistence } from './useCustomizationPersistence';
const Container = styled.div`
display: flex;
flex-direction: column-reverse;
${media.md} {
align-items: flex-start;
flex-direction: row;
}
`;
const Left = styled.div`
flex: 1;
min-height: calc(100vh - 100px);
background: white;
${media.md} {
background: none;
}
`;
const Right = styled.div`
position: sticky;
top: 20px;
width: 275px;
display: block;
z-index: -1;
margin: 110px auto;
${media.md} {
margin-left: 68px;
z-index: 1;
}
`;
const FilterContainer = styled.div<{ $isMobile?: boolean }>`
display: ${(props) => (props.$isMobile ? 'block' : 'none')};
margin-bottom: 40px;
position: sticky;
top: 20px;
z-index: 100;
width: 100%;
${media.md} {
position: relative;
top: 0;
display: ${(props) => (props.$isMobile ? 'none' : 'block')};
margin-bottom: 10px;
}
`;
export interface ExploreProps {
allIcons: Icon[];
}
export function Explore({ allIcons }: ExploreProps) {
const [filters, setFilters] = React.useState<IconListFilters>({});
const [customizations, setCustomizations] = useCustomizationPersistence();
@ -39,8 +86,7 @@ export function Explore({ allIcons }: ExploreProps) {
<FilterContainer>
<FiltersEditor filters={filters} onChange={setFilters} />
</FilterContainer>
<Sponsor />
<Ad />
<Streamline />
<CustomizationEditor
customizations={customizations}
onChange={setCustomizations}
@ -49,46 +95,3 @@ export function Explore({ allIcons }: ExploreProps) {
</Container>
);
}
const Container = styled.div`
display: flex;
flex-direction: column-reverse;
${media.md} {
align-items: flex-start;
flex-direction: row;
}
`;
const Left = styled.div`
flex: 1;
min-height: calc(100vh - 100px);
background: white;
${media.md} {
background: none;
}
`;
const Right = styled.div`
position: sticky;
top: 50px;
width: 275px;
display: block;
z-index: -1;
margin: 110px auto;
${media.md} {
margin-left: 68px;
z-index: 1;
}
`;
const FilterContainer = styled.div<{ $isMobile?: boolean }>`
display: ${(props) => (props.$isMobile ? 'block' : 'none')};
margin-bottom: 40px;
position: sticky;
top: 20px;
z-index: 100;
width: 100%;
${media.md} {
position: relative;
top: 0;
display: ${(props) => (props.$isMobile ? 'none' : 'block')};
margin-bottom: 10px;
}
`;

View file

@ -1,12 +1,12 @@
import type { IconListFilters } from './IconList';
import React from 'react';
import { IconListFilters } from './IconList';
import { LargeInput } from './Input';
export interface FiltersEditorProps {
filters: IconListFilters;
// eslint-disable-next-line no-unused-vars
onChange: (filters: IconListFilters) => void;
}
export function FiltersEditor({ filters, onChange }: FiltersEditorProps) {
const [, startTransition] = (React as any).useTransition();
const [search, setSearch] = React.useState(filters.search);
@ -43,10 +43,10 @@ export function FiltersEditor({ filters, onChange }: FiltersEditorProps) {
return (
<LargeInput
placeholder={'Search...'}
placeholder="Search..."
value={search}
type={'search'}
autoCapitalize={'none'}
type="search"
autoCapitalize="none"
tabIndex={1}
onFocus={(e) => {
if (!didScrollRef.current) {

View file

@ -10,7 +10,64 @@ import {
import { Logo, LogoContainer, LogoIcon } from './Header';
import { Text13, Text17 } from './Typography';
export interface FooterCategoryProps {
const Container = styled.div`
display: block;
margin-top: 110px;
padding-top: 30px;
margin-top: 100px;
padding: 84px 12%;
background-color: var(--g7);
align-items: center;
> :not(:last-child) {
margin-right: 50px;
}
`;
const FooterEnd = styled.div`
border-top: 1px solid var(--g5);
padding-top: 20px;
margin-top: 74px;
width: 100%;
display: flex;
justify-content: space-between;
`;
const FooterCategories = styled.div`
width: 100%;
margin-top: 54px;
display: flex;
`;
const FooterCategoryContainer = styled.div`
width: 28%;
margin-right: 20px;
`;
const FooterCategoryTitle = styled(Text17)`
&&& {
margin-bottom: 24px;
font-weight: 600;
display: block;
color: var(--g0);
}
`;
const FooterCategoryLinks = styled.div``;
const FooterCategoryLink = styled.a`
display: block;
font-size: 17px;
color: var(--g1);
width: fit-content;
margin-bottom: 12px;
text-decoration: none;
&:hover {
text-decoration: underline;
color: var(--g0);
}
`;
interface FooterCategoryProps {
category: string;
links: { name: string; url: string }[];
}
@ -31,17 +88,19 @@ function FooterCategory({ category, links }: FooterCategoryProps) {
}
export function Footer() {
const year = new Date().getFullYear();
return (
<Container>
<LogoContainer>
<LogoIcon>
<PeaceHand />
</LogoIcon>
<Logo src={'/iconoir-logo.svg'} alt={'Iconoir Logo'} />
<Logo src="/iconoir-logo.svg" alt="Iconoir Logo" />
</LogoContainer>
<FooterCategories>
<FooterCategory
category={'Project'}
category="Project"
links={[
{ name: 'Our Mission', url: '/support' },
{ name: 'Contribute', url: '/docs/contributing' },
@ -52,7 +111,7 @@ export function Footer() {
]}
/>
<FooterCategory
category={'Support'}
category="Support"
links={[
{
name: 'License',
@ -69,7 +128,7 @@ export function Footer() {
]}
/>
<FooterCategory
category={'Developers'}
category="Developers"
links={[
{ name: 'Changelog', url: '/docs/changelog' },
{
@ -84,15 +143,19 @@ export function Footer() {
</FooterCategories>
<FooterEnd>
<Text13 style={{ fontWeight: 400 }}>
Parts of this content are &copy;2020-2023 by individual Iconoir
contributors. Content available under a{' '}
<a href={LICENSE_LINK} target={'_blank'} rel={'noreferrer'}>
Parts of this content are &copy;2020-
{year}
{' '}
by individual Iconoir
contributors. Content available under a
{' '}
<a href={LICENSE_LINK} target="_blank" rel="nofollow noreferrer">
MIT License
</a>
.
</Text13>
<Text13 style={{ fontWeight: 400 }}>
<a href={PRIVACY_LINK} target={'_blank'} rel={'noreferrer'}>
<a href={PRIVACY_LINK} target="_blank" rel="nofollow noreferrer">
Privacy
</a>
</Text13>
@ -100,54 +163,3 @@ export function Footer() {
</Container>
);
}
const Container = styled.div`
display: block;
margin-top: 110px;
padding-top: 30px;
margin-top: 100px;
padding: 84px 12%;
background-color: var(--g7);
align-items: center;
> :not(:last-child) {
margin-right: 50px;
}
`;
const FooterEnd = styled.div`
border-top: 1px solid var(--g5);
padding-top: 20px;
margin-top: 74px;
width: 100%;
display: flex;
justify-content: space-between;
`;
const FooterCategories = styled.div`
width: 100%;
margin-top: 54px;
display: flex;
`;
const FooterCategoryContainer = styled.div`
width: 28%;
margin-right: 20px;
`;
const FooterCategoryTitle = styled(Text17)`
&&& {
margin-bottom: 24px;
font-weight: 600;
display: block;
color: var(--g0);
}
`;
const FooterCategoryLinks = styled.div``;
const FooterCategoryLink = styled.a`
display: block;
font-size: 17px;
color: var(--g1);
width: fit-content;
margin-bottom: 12px;
text-decoration: none;
&:hover {
text-decoration: underline;
color: var(--g0);
}
`;

View file

@ -5,9 +5,9 @@ export function GA() {
<>
<Script
src="https://www.googletagmanager.com/gtag/js?id=UA-33344001-9"
strategy={'afterInteractive'}
strategy="afterInteractive"
/>
<Script id={'google-analytics'} strategy={'afterInteractive'}>
<Script id="google-analytics" strategy="afterInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}

View file

@ -1,9 +1,9 @@
import { Menu, Xmark } from 'iconoir-react';
import { Discord, Menu, Sparks, Xmark } from 'iconoir-react';
import { Heart } from 'iconoir-react/solid';
import Link from 'next/link';
import React from 'react';
import styled, { css } from 'styled-components';
import { SHARE_LINK } from '../lib/constants';
import { DISCORD_LINK, SHARE_LINK } from '../lib/constants';
import { media } from '../lib/responsive';
import { AnimatedSvg } from './AnimatedSvg';
import { ResetButton } from './Button';
@ -11,56 +11,18 @@ import { CurrentVersion } from './CurrentVersion';
import { NavigationItem } from './NavigationItem';
import { Text15 } from './Typography';
export interface HeaderProps {
currentVersion: string;
}
export function Header({ currentVersion }: HeaderProps) {
const [menuVisible, setMenuVisible] = React.useState(false);
return (
<Container>
<HeaderLeft>
<Link href={'/'}>
<LogoContainer>
<LogoIcon>
<AnimatedSvg />
</LogoIcon>
<Logo src={'/iconoir-logo.svg'} alt={'Iconoir Logo'} />
</LogoContainer>
</Link>
<CurrentVersion version={currentVersion} />
</HeaderLeft>
<HeaderCenter>
<MobileMenuContainer $visible={menuVisible}>
<NavigationItem href={'/'}>Icons</NavigationItem>
<NavigationItem href={'/docs/introduction'} activeMatch={'/docs'}>
Documentation
</NavigationItem>
<NavigationItem href={'/support'} style={{ marginRight: 0 }}>
Donate &mdash; Our Mission
</NavigationItem>
<Share $isMobile>
<a href={SHARE_LINK} target={'_blank'} rel={'noreferrer'}>
Share with <Heart width={'1em'} height={'1em'} /> on{' '}
<span>X (Twitter)</span>
</a>
</Share>
</MobileMenuContainer>
</HeaderCenter>
<HeaderRight>
<Share>
<a href={SHARE_LINK} target={'_blank'} rel={'noreferrer'}>
Share with <Heart width={'1em'} height={'1em'} /> on{' '}
<span>X (Twitter)</span>
</a>
</Share>
<MobileMenuButton onClick={() => setMenuVisible((v) => !v)}>
{menuVisible ? <Xmark /> : <Menu />}
</MobileMenuButton>
</HeaderRight>
</Container>
);
}
const StyledDiscord = styled(Discord)<{ $isMobile?: boolean }>`
display: none;
${media.lg} {
display: flex;
margin: 0 0 0 16px;
&:hover {
scale: 1.1;
transition: 0.2s;
color: #7289da;
}
}
`;
export const LogoContainer = styled.div`
position: relative;
@ -74,6 +36,26 @@ export const LogoContainer = styled.div`
}
`;
const Banner = styled(Text15)`
display: none;
${media.lg} {
display: flex;
align-items: center;
justify-content: center;
padding: 16px 0;
background: var(--g5);
color: var(--g0);
font-weight: 500;
position: absolute;
width: 100%;
text-decoration: underline;
top: 0;
}
> * {
margin: 0 4px;
}
`;
const MobileMenuButton = styled(ResetButton)`
&&& {
z-index: 101;
@ -91,6 +73,7 @@ const MobileMenuButton = styled(ResetButton)`
}
}
`;
const MobileMenuContainer = styled.div<{ $visible?: boolean }>`
position: absolute;
top: 0;
@ -110,8 +93,8 @@ const MobileMenuContainer = styled.div<{ $visible?: boolean }>`
flex-direction: column;
align-items: stretch;
${(props) =>
props.$visible &&
css`
props.$visible
&& css`
pointer-events: all;
transform: translateY(0);
opacity: 1;
@ -132,11 +115,16 @@ const MobileMenuContainer = styled.div<{ $visible?: boolean }>`
}
}
`;
const Container = styled.div`
display: flex;
align-items: center;
justify-content: center;
${media.lg} {
margin-top: 40px;
}
`;
const HeaderItem = styled.div`
flex: 1;
width: 33%;
@ -145,17 +133,20 @@ const HeaderItem = styled.div`
justify-content: center;
align-items: center;
`;
const HeaderCenter = styled(HeaderItem)`
padding: 0 16px;
> :not(:last-child) {
margin-right: 16px;
}
`;
const HeaderLeft = styled(HeaderItem)`
&&& {
justify-content: flex-start;
}
`;
const HeaderRight = styled(HeaderItem)`
&&& {
justify-content: flex-end;
@ -182,8 +173,8 @@ const Share = styled(Text15)<{ $isMobile?: boolean }>`
&&& {
display: none;
${(props) =>
props.$isMobile &&
css`
props.$isMobile
&& css`
display: flex;
justify-content: center;
padding: 12px 0;
@ -208,3 +199,78 @@ const Share = styled(Text15)<{ $isMobile?: boolean }>`
}
}
`;
export interface HeaderProps {
currentVersion: string;
}
export function Header({ currentVersion }: HeaderProps) {
const [menuVisible, setMenuVisible] = React.useState(false);
return (
<Container>
<Banner>
<Sparks></Sparks>
<a
href="https://opencollective.com/iconoir/donate?interval=month&amount=10"
target="_blank"
>
Your one-time or recurring contribution does a lot to keep Iconoir
going.
</a>
</Banner>
<HeaderLeft>
<Link href="/">
<LogoContainer>
<LogoIcon>
<AnimatedSvg />
</LogoIcon>
<Logo src="/iconoir-logo.svg" alt="Iconoir Logo" />
</LogoContainer>
</Link>
<CurrentVersion version={currentVersion} />
</HeaderLeft>
<HeaderCenter>
<MobileMenuContainer $visible={menuVisible}>
<NavigationItem href="/">Icons</NavigationItem>
<NavigationItem href="/docs/introduction" activeMatch="/docs">
Documentation
</NavigationItem>
<NavigationItem href="/support" style={{ marginRight: 0 }}>
Donate &mdash; Our Mission
</NavigationItem>
<Share $isMobile>
<a href={SHARE_LINK} target="_blank" rel="noreferrer nofollow">
Share with
{' '}
<Heart width="1em" height="1em" />
{' '}
on
{' '}
<span>X (Twitter)</span>
</a>
</Share>
</MobileMenuContainer>
</HeaderCenter>
<HeaderRight>
<Share>
<a href={SHARE_LINK} target="_blank" rel="noreferrer nofollow">
Share with
{' '}
<Heart width="1em" height="1em" />
{' '}
on
{' '}
<span>X (Twitter)</span>
</a>
</Share>
<a href={DISCORD_LINK} rel="nofollow noreferrer">
<StyledDiscord $isMobile />
</a>
<MobileMenuButton onClick={() => setMenuVisible((v) => !v)}>
{menuVisible ? <Xmark /> : <Menu />}
</MobileMenuButton>
</HeaderRight>
</Container>
);
}

View file

@ -1,16 +1,101 @@
import React from 'react';
import React, { useEffect, useRef } from 'react';
import styled from 'styled-components';
import { media } from '../lib/responsive';
import { useRef, useEffect } from 'react';
const HeaderContainer = styled.div`
position: relative;
width: fit-content;
margin: auto;
`;
const FloatingIcon = styled.div`
position: absolute;
display: none;
background-repeat: no-repeat;
z-index: -1;
pointer-events: none;
align-items: center;
justify-content: center;
${media.md} {
display: flex;
}
`;
const FloatingIconCellar = styled(FloatingIcon)`
-webkit-transform: rotate(6deg);
-moz-transform: rotate(6deg);
top: -120px;
right: 0px;
width: 200px;
height: 200px;
background-image: url(/cellar.gif);
background-size: 70%;
${media.lg} {
}
`;
const FloatingIconPay = styled(FloatingIcon)`
-webkit-transform: rotate(18deg);
-moz-transform: rotate(18deg);
top: -50px;
right: -100px;
width: 130px;
height: 130px;
background-image: url(/pay-bitcoin.gif);
background-size: 70%;
${media.lg} {
}
`;
const FloatingFaceID = styled(FloatingIcon)`
-webkit-transform: rotate(6deg);
-moz-transform: rotate(6deg);
top: -130px;
right: 380px;
width: 110px;
height: 110px;
background-image: url(/face-id.gif);
background-size: 70%;
${media.lg} {
}
`;
const FloatingCommand = styled(FloatingIcon)`
-webkit-transform: rotate(-7deg);
-moz-transform: rotate(-7deg);
top: -94px;
left: 150px;
width: 110px;
height: 110px;
background-image: url(/command.gif);
background-size: 70%;
${media.lg} {
}
`;
const FloatingFill = styled(FloatingIcon)`
-webkit-transform: rotate(-14deg);
-moz-transform: rotate(-14deg);
top: -64px;
left: -75px;
width: 110px;
height: 110px;
background-image: url(/fill.gif);
background-size: 70%;
${media.lg} {
}
`;
export interface HeaderBackgroundProps {
children: React.ReactElement;
}
export function HeaderBackground({ children }: HeaderBackgroundProps) {
const parallaxRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!parallaxRef.current) return;
if (!parallaxRef.current)
return;
const parallaxElements = parallaxRef.current.querySelectorAll(
'[data-parallax-factor]',
@ -21,7 +106,7 @@ export function HeaderBackground({ children }: HeaderBackgroundProps) {
const y = event.clientY / window.innerHeight;
parallaxElements.forEach((el) => {
const factor = parseFloat(
const factor = Number.parseFloat(
el.getAttribute('data-parallax-factor') || '1',
);
@ -49,81 +134,3 @@ export function HeaderBackground({ children }: HeaderBackgroundProps) {
</HeaderContainer>
);
}
const HeaderContainer = styled.div`
position: relative;
width: fit-content;
margin: auto;
`;
const FloatingIcon = styled.div`
position: absolute;
display: none;
background-repeat: no-repeat;
z-index: -1;
pointer-events: none;
align-items: center;
justify-content: center;
${media.md} {
display: flex;
}
`;
const FloatingIconCellar = styled(FloatingIcon)`
-webkit-transform: rotate(6deg);
-moz-transform: rotate(6deg);
top: -120px;
right: 0px;
width: 200px;
height: 200px;
background-image: url(/cellar.gif);
background-size: 70%;
${media.lg} {
}
`;
const FloatingIconPay = styled(FloatingIcon)`
-webkit-transform: rotate(18deg);
-moz-transform: rotate(18deg);
top: -50px;
right: -100px;
width: 130px;
height: 130px;
background-image: url(/pay-bitcoin.gif);
background-size: 70%;
${media.lg} {
}
`;
const FloatingFaceID = styled(FloatingIcon)`
-webkit-transform: rotate(6deg);
-moz-transform: rotate(6deg);
top: -130px;
right: 380px;
width: 110px;
height: 110px;
background-image: url(/face-id.gif);
background-size: 70%;
${media.lg} {
}
`;
const FloatingCommand = styled(FloatingIcon)`
-webkit-transform: rotate(-7deg);
-moz-transform: rotate(-7deg);
top: -94px;
left: 150px;
width: 110px;
height: 110px;
background-image: url(/command.gif);
background-size: 70%;
${media.lg} {
}
`;
const FloatingFill = styled(FloatingIcon)`
-webkit-transform: rotate(-14deg);
-moz-transform: rotate(-14deg);
top: -64px;
left: -75px;
width: 110px;
height: 110px;
background-image: url(/fill.gif);
background-size: 70%;
${media.lg} {
}
`;

View file

@ -1,15 +1,16 @@
import React from 'react';
import styled from 'styled-components';
export interface HeaderSecondaryProps {
children: React.ReactElement;
}
export function HeaderSecondary({ children }: HeaderSecondaryProps) {
return <HeaderContainer>{children}</HeaderContainer>;
}
const HeaderContainer = styled.div`
position: relative;
width: fit-content;
margin: auto;
`;
export interface HeaderSecondaryProps {
children: React.ReactElement;
}
export function HeaderSecondary({ children }: HeaderSecondaryProps) {
return <HeaderContainer>{children}</HeaderContainer>;
}

View file

@ -0,0 +1,63 @@
import React from 'react';
import styled from 'styled-components';
const PromoContainer = styled.div`
border-radius: 12px;
border: 1px solid var(--g6);
text-align: center;
margin-top: 24px;
text-decoration: none;
&:hover {
background-color: var(--g7);
}
`;
const SponsorLabel = styled.div`
font-size: 12px;
font-weight: 400;
color: var(--g4);
margin: 10px 0;
`;
const PromoContent = styled.div`
// Your content styles here, similar to SponsorText
`;
const PromoImage = styled.img`
width: 70%;
`;
const PromoInfo = styled.div`
// Styles for the text container, similar to SponsorRight
`;
const PromoTitle = styled.h2`
font-size: 16px;
font-weight: 600;
margin: 16px auto 0 auto;
`;
const PromoSub = styled.h2`
font-size: 14px;
margin: 0 auto 10px auto;
`;
export function Hono() {
return (
<a
href="https://wearehono.com/?utm_source=iconoir&utm_medium=sidebar"
style={{ textDecoration: 'none' }}
>
<PromoContainer>
<PromoContent>
<PromoInfo>
<PromoTitle>Buy high-quality logos</PromoTitle>
<PromoSub>with Hono.</PromoSub>
<PromoImage src="./hono-ad.png" />
<SponsorLabel>Our sponsor</SponsorLabel>
</PromoInfo>
</PromoContent>
</PromoContainer>
</a>
);
}

View file

@ -1,9 +1,10 @@
import type { Icon as IconType } from './IconList';
import * as AllIcons from 'iconoir-react';
import React from 'react';
import styled from 'styled-components';
import { showNotification } from '../lib/showNotification';
import { ResetButton } from './Button';
import { DEFAULT_CUSTOMIZATIONS, Icon as IconType } from './IconList';
import { DEFAULT_CUSTOMIZATIONS } from './IconList';
const HEADER = '<?xml version="1.0" encoding="UTF-8"?>';
@ -13,8 +14,8 @@ function bakeSvg(
strokeWidth: string | number,
) {
return (
HEADER +
svgString
HEADER
+ svgString
.replace(
/stroke="currentColor"/g,
`stroke="currentColor" stroke-width="${strokeWidth}"`,
@ -23,106 +24,6 @@ function bakeSvg(
);
}
export interface IconProps {
iconWidth: number;
icon: IconType;
}
export function Icon({ iconWidth, icon }: IconProps) {
const IconComponent = (AllIcons as any)[icon.iconComponentName];
const iconContainerRef = React.useRef<HTMLDivElement>(null);
const downloadRef = React.useRef<HTMLAnchorElement>(null);
const htmlContentsRef = React.useRef<string>('');
const iconContext = React.useContext(AllIcons.IconoirContext);
const [supportsClipboard, setSupportsClipboard] = React.useState(false);
React.useEffect(() => {
setSupportsClipboard(
typeof window !== 'undefined' &&
typeof window?.navigator?.clipboard?.writeText !== 'undefined',
);
}, []);
React.useEffect(() => {
if (iconContainerRef.current) {
htmlContentsRef.current = bakeSvg(
iconContainerRef.current.innerHTML,
iconContext.color || DEFAULT_CUSTOMIZATIONS.hexColor,
iconContext.strokeWidth || DEFAULT_CUSTOMIZATIONS.strokeWidth,
);
}
}, [iconContext, supportsClipboard]);
React.useEffect(() => {
const element =
downloadRef.current ||
(iconContainerRef.current as unknown as HTMLAnchorElement);
if (element) {
element.href = `data:image/svg+xml;base64,${btoa(
htmlContentsRef.current,
)}`;
}
}, [iconContext, supportsClipboard]);
return (
<div className={'icon-container'}>
<BorderContainer $iconWidth={iconWidth}>
<IconContainer
ref={iconContainerRef}
{...((supportsClipboard
? {}
: {
as: 'a',
href: '#',
rel: 'noreferrer',
download: `${icon.filename}.svg`,
}) as any)}
>
<IconComponent />
{icon.filename.includes('-solid') ? <IconTag>SOLID</IconTag> : ''}
</IconContainer>
{supportsClipboard ? (
<HoverContainer>
<CornerBR />
<CornerTR />
<CornerBL />
<CornerTL />
<HoverButton
onClick={() => {
if (htmlContentsRef.current) {
navigator.clipboard
.writeText(htmlContentsRef.current)
.then(() => {
showNotification('SVG code copied!');
})
.catch((err) => {
console.error(err);
});
}
}}
>
Copy SVG
</HoverButton>
<HoverButton
as={'a'}
ref={downloadRef}
href={'#'}
rel={'noreferrer'}
download={`${icon.filename}.svg`}
>
Download
</HoverButton>
</HoverContainer>
) : null}
</BorderContainer>
<Subtitle $iconWidth={iconWidth} title={icon.filename}>
{icon.filename}
</Subtitle>
</div>
);
}
const Overlay = styled.div`
position: absolute;
border-radius: 50%;
@ -131,23 +32,28 @@ const Overlay = styled.div`
width: 8px;
height: 8px;
`;
const CornerBR = styled(Overlay)`
bottom: -6px;
right: -6px;
z-index: 999;
`;
const CornerTR = styled(Overlay)`
top: -6px;
right: -6px;
`;
const CornerBL = styled(Overlay)`
bottom: -6px;
left: -6px;
`;
const CornerTL = styled(Overlay)`
top: -6px;
left: -6px;
`;
const HoverContainer = styled.div`
position: absolute;
inset: 0;
@ -161,6 +67,7 @@ const HoverContainer = styled.div`
opacity: 0;
pointer-events: none;
`;
const HoverButton = styled(ResetButton)`
&&& {
display: flex;
@ -184,6 +91,7 @@ const HoverButton = styled(ResetButton)`
}
}
`;
const BorderContainer = styled.div<{ $iconWidth: number }>`
width: ${(props) => props.$iconWidth}px;
box-sizing: border-box;
@ -200,6 +108,7 @@ const BorderContainer = styled.div<{ $iconWidth: number }>`
}
}
`;
const IconContainer = styled.div`
position: absolute;
inset: 0;
@ -207,6 +116,7 @@ const IconContainer = styled.div`
align-items: center;
justify-content: center;
`;
const IconTag = styled.div`
background-color: var(--g6);
position: absolute;
@ -219,14 +129,116 @@ const IconTag = styled.div`
font-size: 11px;
color: var(--g0);
`;
const Subtitle = styled.div<{ $iconWidth: number }>`
font-size: 11px;
font-weight: 500;
line-height: 14.74px;
color: var(--black-40);
color: var(--black-60);
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: ${(props) => props.$iconWidth}px;
`;
export interface IconProps {
iconWidth: number;
icon: IconType;
}
export function Icon({ iconWidth, icon }: IconProps) {
const IconComponent = (AllIcons as any)[icon.iconComponentName];
const iconContainerRef = React.useRef<HTMLDivElement>(null);
const downloadRef = React.useRef<HTMLAnchorElement>(null);
const htmlContentsRef = React.useRef<string>('');
const iconContext = React.useContext(AllIcons.IconoirContext);
const [supportsClipboard, setSupportsClipboard] = React.useState(false);
React.useEffect(() => {
setSupportsClipboard(
typeof window !== 'undefined'
&& typeof window?.navigator?.clipboard?.writeText !== 'undefined',
);
}, []);
React.useEffect(() => {
if (iconContainerRef.current) {
htmlContentsRef.current = bakeSvg(
(iconContainerRef.current.firstChild as SVGElement).outerHTML,
iconContext.color || DEFAULT_CUSTOMIZATIONS.hexColor,
iconContext.strokeWidth || DEFAULT_CUSTOMIZATIONS.strokeWidth,
);
}
}, [iconContext, supportsClipboard]);
React.useEffect(() => {
const element = downloadRef.current || (iconContainerRef.current as unknown as HTMLAnchorElement);
if (element) {
element.href = `data:image/svg+xml;base64,${btoa(
htmlContentsRef.current,
)}`;
}
}, [iconContext, supportsClipboard]);
return (
<div className="icon-container">
<BorderContainer $iconWidth={iconWidth}>
<IconContainer
ref={iconContainerRef}
{...((supportsClipboard
? {}
: {
as: 'a',
href: '#',
rel: 'noreferrer',
download: `${icon.filename}.svg`,
}) as any)}
>
<IconComponent />
{icon.filename.includes('-solid') ? <IconTag>SOLID</IconTag> : ''}
</IconContainer>
{supportsClipboard
? (
<HoverContainer>
<CornerBR />
<CornerTR />
<CornerBL />
<CornerTL />
<HoverButton
onClick={() => {
if (htmlContentsRef.current) {
navigator.clipboard
.writeText(htmlContentsRef.current)
.then(() => {
showNotification('SVG code copied!');
})
.catch((err) => {
console.error(err);
});
}
}}
>
Copy SVG
</HoverButton>
<HoverButton
as="a"
ref={downloadRef}
href="#"
rel="noreferrer"
download={`${icon.filename}.svg`}
>
Download
</HoverButton>
</HoverContainer>
)
: null}
</BorderContainer>
<Subtitle $iconWidth={iconWidth} title={icon.filename}>
{icon.filename}
</Subtitle>
</div>
);
}

View file

@ -1,14 +1,16 @@
import type {
ListChildComponentProps,
} from 'react-window';
import { chunk } from 'lodash';
import React from 'react';
import {
areEqual,
ListChildComponentProps,
VariableSizeList as List,
} from 'react-window';
import styled from 'styled-components';
import useResizeObserver from 'use-resize-observer';
import { CategoryRow } from './CategoryRow';
import { ICON_SPACE, ICON_WIDTH } from '../lib/constants';
import { CategoryRow } from './CategoryRow';
import { IconListEmpty } from './IconListEmpty';
import { IconsRow } from './IconsRow';
import { ReactWindowScroller } from './ReactWindowScroller';
@ -46,15 +48,17 @@ function filterIcons(allIcons: Icon[], filters: IconListFilters): Icon[] {
for (const term of normalSearch.split(' ')) {
result = result.filter((icon) => {
return (
normalizeString(icon.filename).includes(term) ||
normalizeString(icon.category).includes(term) ||
icon.tags.some((tag) => normalizeString(tag).includes(term))
normalizeString(icon.filename).includes(term)
|| normalizeString(icon.category).includes(term)
|| icon.tags.some((tag) => normalizeString(tag).includes(term))
);
});
}
return result;
} else return allIcons;
} else {
return allIcons;
}
}
interface IconCategoryRow {
@ -77,7 +81,8 @@ function getRowsFromIcons(
const categoryGroups: Record<string, Icon[]> = {};
for (const icon of filteredIcons) {
if (!categoryGroups[icon.category]) categoryGroups[icon.category] = [];
if (!categoryGroups[icon.category])
categoryGroups[icon.category] = [];
categoryGroups[icon.category].push(icon);
}
@ -117,71 +122,8 @@ interface IconListContextValue {
iconWidth: number;
iconsPerRow: number;
}
export const IconListContext = React.createContext<
IconListContextValue | undefined
>(undefined);
export interface IconListProps {
filters: IconListFilters;
allIcons: Icon[];
}
export function IconList({ filters, allIcons }: IconListProps) {
const filteredIcons = filterIcons(allIcons, filters);
const { ref, width = 400 } = useResizeObserver();
const iconsPerRow = width
? Math.floor((width + ICON_SPACE) / (ICON_WIDTH + ICON_SPACE))
: null;
let children = null;
const listRef = React.useRef<List<IconRow[]> | null>();
const [height, setHeight] = React.useState(400);
const iconWidth = iconsPerRow
? Math.floor((width + ICON_SPACE) / iconsPerRow) - ICON_SPACE
: null;
React.useEffect(() => {
setHeight(window.innerHeight);
}, []);
React.useEffect(() => {
if (listRef.current) {
listRef.current.resetAfterIndex(0, true);
}
}, [iconWidth, height]);
if (filteredIcons.length && iconsPerRow && width && iconWidth) {
const iconRows = getRowsFromIcons(filteredIcons, iconsPerRow);
children = (
<IconListContext.Provider value={{ iconsPerRow, iconWidth }}>
<ReactWindowScroller>
{({ ref, outerRef, style, onScroll }: any) => (
<List<IconRow[]>
ref={(c) => {
if (typeof ref === 'function') ref(c);
else ref.current = c;
listRef.current = c;
}}
itemData={iconRows}
width={width}
outerRef={outerRef}
style={style}
height={height}
itemCount={iconRows.length}
onScroll={onScroll}
itemSize={(index) => getItemSize(iconRows[index], iconWidth)}
>
{Row}
</List>
)}
</ReactWindowScroller>
</IconListContext.Provider>
);
} else if (width && filters.search) {
return <IconListEmpty searchTerm={filters.search} />;
}
return <Container ref={ref}>{children}</Container>;
}
const IconListContext = React.createContext<IconListContextValue | undefined>(undefined);
const Container = styled.div`
width: 100%;
@ -213,4 +155,73 @@ const Row = React.memo(
},
areEqual,
);
Row.displayName = 'Row';
export interface IconListProps {
filters: IconListFilters;
allIcons: Icon[];
}
export function IconList({ filters, allIcons }: IconListProps) {
const filteredIcons = filterIcons(allIcons, filters);
const { ref, width = 400 } = useResizeObserver();
const iconsPerRow = width
? Math.floor((width + ICON_SPACE) / (ICON_WIDTH + ICON_SPACE))
: null;
let children = null;
const listRef = React.useRef<List<IconRow[]> | null>(null);
const [height, setHeight] = React.useState(400);
const iconWidth = iconsPerRow
? Math.floor((width + ICON_SPACE) / iconsPerRow) - ICON_SPACE
: null;
React.useEffect(() => {
setHeight(window.innerHeight);
}, []);
React.useEffect(() => {
if (listRef.current) {
listRef.current.resetAfterIndex(0, true);
}
}, [iconWidth, height]);
if (filteredIcons.length && iconsPerRow && width && iconWidth) {
const iconRows = getRowsFromIcons(filteredIcons, iconsPerRow);
children = (
<IconListContext.Provider value={{ iconsPerRow, iconWidth }}>
<ReactWindowScroller>
{({ ref, outerRef, style, onScroll }: any) => (
<List<IconRow[]>
ref={(c) => {
if (typeof ref === 'function')
ref(c);
else
ref.current = c;
listRef.current = c;
}}
itemData={iconRows}
width={width}
outerRef={outerRef}
style={style}
height={height}
itemCount={iconRows.length}
onScroll={onScroll}
itemSize={(index) => getItemSize(iconRows[index], iconWidth)}
>
{Row}
</List>
)}
</ReactWindowScroller>
</IconListContext.Provider>
);
} else if (width && filters.search) {
return <IconListEmpty searchTerm={filters.search} />;
}
return <Container ref={ref}>{children}</Container>;
}

View file

@ -3,29 +3,6 @@ import styled from 'styled-components';
import { SUGGEST_ICON_LINK } from '../lib/constants';
import { Text18 } from './Typography';
export interface IconListEmptyProps {
searchTerm: string;
}
export function IconListEmpty({ searchTerm }: IconListEmptyProps) {
return (
<Container>
<IconContainer>
<SpockHandGesture />
</IconContainer>
<Title>
Unfortunately there are no icons for &apos;{searchTerm}&apos;
</Title>
<Text18 style={{ color: 'var(--black-60)' }}>
{"If you can't find the icon, you can make a"}
<br />
<a href={SUGGEST_ICON_LINK} target={'_blank'} rel={'noreferrer'}>
suggestion on GitHub.
</a>
</Text18>
</Container>
);
}
const Container = styled.div`
margin-top: 90px;
display: flex;
@ -33,6 +10,7 @@ const Container = styled.div`
flex-direction: column;
text-align: center;
`;
const IconContainer = styled.div`
svg {
width: 60px;
@ -41,6 +19,7 @@ const IconContainer = styled.div`
margin-bottom: 65px;
color: var(--black);
`;
const Title = styled(Text18)`
&&& {
font-weight: 700;
@ -48,3 +27,29 @@ const Title = styled(Text18)`
color: var(--black);
}
`;
export interface IconListEmptyProps {
searchTerm: string;
}
export function IconListEmpty({ searchTerm }: IconListEmptyProps) {
return (
<Container>
<IconContainer>
<SpockHandGesture />
</IconContainer>
<Title>
Unfortunately there are no icons for &apos;
{searchTerm}
&apos;
</Title>
<Text18 style={{ color: 'var(--black-60)' }}>
If you can&apos;t find the icon, you can make a
<br />
<a href={SUGGEST_ICON_LINK} target="_blank" rel="noreferrer">
suggestion on GitHub.
</a>
</Text18>
</Container>
);
}

View file

@ -1,13 +1,22 @@
import type { Icon } from './IconList';
import styled from 'styled-components';
import { ICON_SPACE } from '../lib/constants';
import { Icon as IconC } from './Icon';
import { Icon } from './IconList';
const RowContainer = styled.div`
display: flex;
align-items: center;
> :not(:last-child) {
margin-right: ${ICON_SPACE}px;
}
`;
export interface IconsRowProps {
icons: Icon[];
style?: any;
iconWidth: number;
}
export function IconsRow({ icons, style, iconWidth }: IconsRowProps) {
return (
<RowContainer style={style}>
@ -17,11 +26,3 @@ export function IconsRow({ icons, style, iconWidth }: IconsRowProps) {
</RowContainer>
);
}
const RowContainer = styled.div`
display: flex;
align-items: center;
> :not(:last-child) {
margin-right: ${ICON_SPACE}px;
}
`;

View file

@ -19,7 +19,7 @@ const ResetInput = styled.input`
}
`;
export const Input = styled(ResetInput)`
const Input = styled(ResetInput)`
&&& {
min-height: 35px;
background: var(--white);

View file

@ -3,19 +3,18 @@ import styled from 'styled-components';
import { media } from '../lib/responsive';
import { GA } from './GA';
export interface LayoutProps {}
export function Layout({ children }: React.PropsWithChildren<LayoutProps>) {
return (
<Container>
<GA />
{children}
</Container>
);
}
const Container = styled.div`
padding: 50px 30px;
${media.lg} {
padding: 30px 50px 50px 50px;
}
`;
export function Layout({ children }: React.PropsWithChildren) {
return (
<Container>
<GA />
{children}
</Container>
);
}

View file

@ -1,6 +1,7 @@
import { MDXRemote as CoreMDXRemote, MDXRemoteProps } from 'next-mdx-remote';
import type { MDXRemoteProps } from 'next-mdx-remote';
import { MDXRemote as CoreMDXRemote } from 'next-mdx-remote';
import { Table } from './Table';
import { Body, Code, InlineCode, H1, H2, H3, Pre, Li } from './Typography';
import { Body, Code, H1, H2, H3, InlineCode, Li, Pre } from './Typography';
export function MDXRemote(props: MDXRemoteProps) {
return (

View file

@ -5,41 +5,7 @@ import styled from 'styled-components';
import { media } from '../lib/responsive';
import { Text15 } from './Typography';
export interface NavigationItemProps {
href: string;
activeMatch?: string;
children: React.ReactElement | string;
style?: any;
}
export function NavigationItem({
href,
activeMatch,
children,
style,
}: NavigationItemProps) {
const router = useRouter();
return (
<Link href={href} passHref legacyBehavior>
<NavigationItemContainer
as={'a'}
style={style}
$text={children.toString()}
$isActive={
activeMatch
? router.asPath.startsWith(activeMatch)
: href.slice(1)
? router.asPath.slice(1).startsWith(href.slice(1))
: router.asPath === href
}
>
{children}
</NavigationItemContainer>
</Link>
);
}
export const NavigationItemContainer = styled(Text15)<{
const NavigationItemContainer = styled(Text15)<{
$text: string;
$isActive?: boolean;
}>`
@ -109,3 +75,38 @@ export const NavigationItemContainer = styled(Text15)<{
}
}
`;
export interface NavigationItemProps {
href: string;
activeMatch?: string;
children: React.ReactElement | string;
style?: any;
}
export function NavigationItem({
href,
activeMatch,
children,
style,
}: NavigationItemProps) {
const router = useRouter();
return (
<Link href={href} passHref legacyBehavior>
<NavigationItemContainer
as="a"
style={style}
$text={children.toString()}
$isActive={
activeMatch
? router.asPath.startsWith(activeMatch)
: href.slice(1)
? router.asPath.slice(1).startsWith(href.slice(1))
: router.asPath === href
}
>
{children}
</NavigationItemContainer>
</Link>
);
}

View file

@ -1,110 +1,10 @@
import React from 'react';
import styled from 'styled-components';
import { PraiseItem } from './PraiseItem';
import { media } from '../lib/responsive';
import { PraiseItem } from './PraiseItem';
const NUM_PRAISE_ITEMS = 3;
export function Praise() {
const containerRef = React.useRef<HTMLDivElement>(null);
const indicatorContainerRef = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
if (containerRef.current) {
const handle = () => {
if (indicatorContainerRef.current && containerRef.current) {
const currentScrollLeft = containerRef.current.scrollLeft;
const totalScroll = containerRef.current.scrollWidth;
const interval = totalScroll / NUM_PRAISE_ITEMS;
const currentIndex =
currentScrollLeft >=
containerRef.current.scrollWidth - window.innerWidth - 100
? indicatorContainerRef.current.children.length - 1
: Math.round(currentScrollLeft / interval);
for (
let i = 0;
i < indicatorContainerRef.current.children.length;
i++
) {
const child = indicatorContainerRef.current.children[i];
if (currentIndex === i) {
child.classList.add('active');
} else {
child.classList.remove('active');
}
}
}
};
const element = containerRef.current;
element.addEventListener('scroll', handle);
return () => {
element.removeEventListener('scroll', handle);
};
}
}, []);
return (
<>
<Container ref={containerRef}>
<PraiseItem
name={'Riccardo Suardi'}
position={'Nibol CEO'}
description={
<>
In Nibol we decided to use Iconoir to speed up the design process.
We want to focus on the product and let Iconoir help us with the
design.
</>
}
imageUrl={'./riccardo-suardi.png'}
logoUrl={'./nibol-logo.svg'}
logoLink={'https://www.nibol.com/'}
logoAlt={'Nibol Logo'}
/>
<PraiseItem
name={'Fabrizio Rinaldi'}
position={'Mailbrew and Typefully founder'}
description={
<>
There's no shortage of icon packs, and yet I always find myself
browsing iconoir. I love the style and attention to detail, and
how easy it is to grab the perfect icons for my projects.
</>
}
imageUrl={'./fabrizio-rinaldi.png'}
logoUrl={'./typefully-logo.png'}
logoLink={'https://typefully.com/'}
logoAlt={'Typefully Logo'}
/>
<PraiseItem
name={'Chris Messina'}
position={'Entrepreneur and # inventor'}
description={
<>
It's the tiny details that determine the degree of delight your
customers experience from your product. Adopting Iconoir icons
will easily boost your app's delight by a factor of 10!
</>
}
imageUrl={'./chris-messina.png'}
logoUrl={'./twitter-logo.png'}
logoLink={'https://twitter.com/chrismessina'}
logoAlt={'Twitter Logo'}
/>
</Container>
<IndicatorContainer ref={indicatorContainerRef}>
<Indicator className={'active'} />
<Indicator />
<Indicator />
</IndicatorContainer>
</>
);
}
const Container = styled.div`
max-width: 100%;
margin: 0 -30px;
@ -138,6 +38,7 @@ const Container = styled.div`
}
}
`;
const Indicator = styled.div`
width: 6px;
height: 6px;
@ -148,6 +49,7 @@ const Indicator = styled.div`
background: var(--black);
}
`;
const IndicatorContainer = styled.div`
margin: 40px auto 0 auto;
display: flex;
@ -160,3 +62,102 @@ const IndicatorContainer = styled.div`
display: none;
}
`;
export function Praise() {
const containerRef = React.useRef<HTMLDivElement>(null);
const indicatorContainerRef = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
if (containerRef.current) {
const handle = () => {
if (indicatorContainerRef.current && containerRef.current) {
const currentScrollLeft = containerRef.current.scrollLeft;
const totalScroll = containerRef.current.scrollWidth;
const interval = totalScroll / NUM_PRAISE_ITEMS;
const currentIndex = currentScrollLeft >= containerRef.current.scrollWidth - window.innerWidth - 100
? indicatorContainerRef.current.children.length - 1
: Math.round(currentScrollLeft / interval);
for (
let i = 0;
i < indicatorContainerRef.current.children.length;
i++
) {
const child = indicatorContainerRef.current.children[i];
if (currentIndex === i) {
child.classList.add('active');
} else {
child.classList.remove('active');
}
}
}
};
const element = containerRef.current;
element.addEventListener('scroll', handle);
return () => {
element.removeEventListener('scroll', handle);
};
}
}, []);
return (
<>
<Container ref={containerRef}>
<PraiseItem
name="Riccardo Suardi"
position="Nibol CEO"
description={(
<>
In Nibol we decided to use Iconoir to speed up the design process.
We want to focus on the product and let Iconoir help us with the
design.
</>
)}
imageUrl="./riccardo-suardi.png"
logoUrl="./nibol-logo.svg"
logoLink="https://www.nibol.com/"
logoAlt="Nibol Logo"
/>
<PraiseItem
name="Fabrizio Rinaldi"
position="Mailbrew and Typefully founder"
description={(
<>
There&apos;s no shortage of icon packs, and yet I always find myself
browsing iconoir. I love the style and attention to detail, and
how easy it is to grab the perfect icons for my projects.
</>
)}
imageUrl="./fabrizio-rinaldi.png"
logoUrl="./typefully-logo.png"
logoLink="https://typefully.com/"
logoAlt="Typefully Logo"
/>
<PraiseItem
name="Chris Messina"
position="Entrepreneur and # inventor"
description={(
<>
It&apos;s the tiny details that determine the degree of delight your
customers experience from your product. Adopting Iconoir icons
will easily boost your app&apos;s delight by a factor of 10!
</>
)}
imageUrl="./chris-messina.png"
logoUrl="./twitter-logo.png"
logoLink="https://twitter.com/chrismessina"
logoAlt="Twitter Logo"
/>
</Container>
<IndicatorContainer ref={indicatorContainerRef}>
<Indicator className="active" />
<Indicator />
<Indicator />
</IndicatorContainer>
</>
);
}

View file

@ -3,6 +3,40 @@ import styled from 'styled-components';
import { media } from '../lib/responsive';
import { Text14, Text18 } from './Typography';
const Container = styled.div`
display: flex;
align-items: flex-start;
flex-direction: row;
flex-shrink: 0;
width: calc(100vw - 60px);
scroll-snap-align: center;
${media.xs} {
width: 428px;
}
`;
const AuthorImage = styled.img`
height: 60px;
width: 60px;
margin-right: 28px;
`;
const Logo = styled.img`
height: 23px;
margin-top: 36px;
`;
const Header = styled(Text18)`
&&& {
font-weight: 700;
color: var(--black);
}
`;
const Body = styled(Text18)`
margin-top: 8px;
`;
export interface PraiseItemProps {
name: string;
position: string;
@ -12,6 +46,7 @@ export interface PraiseItemProps {
logoAlt: string;
imageUrl: string;
}
export function PraiseItem({
name,
position,
@ -28,40 +63,10 @@ export function PraiseItem({
<Header>{name}</Header>
<Text14>{position}</Text14>
<Body>{description}</Body>
<a href={logoLink} target={'_blank'} rel={'noreferrer'}>
<a href={logoLink} target="_blank" rel="noreferrer">
<Logo src={logoUrl} alt={logoAlt} />
</a>
</div>
</Container>
);
}
const Container = styled.div`
display: flex;
align-items: flex-start;
flex-direction: row;
flex-shrink: 0;
width: calc(100vw - 60px);
scroll-snap-align: center;
${media.xs} {
width: 428px;
}
`;
const AuthorImage = styled.img`
height: 60px;
width: 60px;
margin-right: 28px;
`;
const Logo = styled.img`
height: 23px;
margin-top: 36px;
`;
const Header = styled(Text18)`
&&& {
font-weight: 700;
color: var(--black);
}
`;
const Body = styled(Text18)`
margin-top: 8px;
`;

View file

@ -2,9 +2,9 @@
// Modified to remove scrollTo callback to support momentum scroll on iOS. We don't need it
// in this implementation anyway.
import type { GridProps, ListProps } from 'react-window';
import { throttle } from 'lodash';
import React, { useEffect, useRef } from 'react';
import { GridProps, ListProps } from 'react-window';
function isHtmlElement(
element: HTMLElement | typeof window,
@ -16,6 +16,7 @@ interface PositionKey {
x: string;
y: string;
}
const windowScrollPositionKey: PositionKey = {
y: 'pageYOffset',
x: 'pageXOffset',
@ -26,19 +27,16 @@ const documentScrollPositionKey: PositionKey = {
x: 'scrollLeft',
};
const getScrollPosition = (
axis: keyof PositionKey,
element?: HTMLElement | null,
): number =>
// @ts-ignore indexing as string
element?.[documentScrollPositionKey[axis] as any] ||
// @ts-ignore indexing as string
window[windowScrollPositionKey[axis] as any] ||
// @ts-ignore indexing as string
document.documentElement[documentScrollPositionKey[axis] as any] ||
// @ts-ignore indexing as string
document.body[documentScrollPositionKey[axis] as any] ||
0;
function getScrollPosition(axis: keyof PositionKey, element?: HTMLElement | null): number {
// @ts-expect-error indexing as string
return element?.[documentScrollPositionKey[axis] as any]
|| window[windowScrollPositionKey[axis] as any]
// @ts-expect-error indexing as string
|| document.documentElement[documentScrollPositionKey[axis] as any]
// @ts-expect-error indexing as string
|| document.body[documentScrollPositionKey[axis] as any]
|| 0;
}
interface ChildOpts<Props extends ListProps | GridProps> {
ref: React.MutableRefObject<any>;
@ -47,7 +45,6 @@ interface ChildOpts<Props extends ListProps | GridProps> {
onScroll: Props['onScroll'];
}
interface ReactWindowScrollerProps<Props extends ListProps | GridProps> {
// eslint-disable-next-line no-unused-vars
children: (opts: ChildOpts<Props>) => React.ReactElement;
throttleTime?: number;
isGrid?: boolean;
@ -60,29 +57,30 @@ export function ReactWindowScroller<
throttleTime = 10,
isGrid = false,
}: ReactWindowScrollerProps<Props>) {
const ref = useRef<any>();
const outerRef = useRef<HTMLElement>();
const targetElement =
typeof window === 'undefined' ? (undefined as any) : window;
const ref = useRef<any>(null);
const outerRef = useRef<HTMLElement>(null);
const targetElement = typeof window === 'undefined' ? (undefined as any) : window;
useEffect(() => {
const handleWindowScroll = throttle(() => {
const rect = outerRef.current?.parentElement?.getBoundingClientRect();
const offsetTop =
(rect?.top || 0) +
(isHtmlElement(targetElement)
const offsetTop = (rect?.top || 0)
+ (isHtmlElement(targetElement)
? targetElement.scrollTop
: targetElement.scrollY);
const offsetLeft =
(rect?.left || 0) +
(isHtmlElement(targetElement)
const offsetLeft = (rect?.left || 0)
+ (isHtmlElement(targetElement)
? targetElement.scrollLeft
: targetElement.scrollX);
const scrollTop = getScrollPosition('y') - offsetTop;
const scrollLeft = getScrollPosition('x') - offsetLeft;
if (isGrid)
ref.current && ref.current!.scrollTo({ scrollLeft, scrollTop });
if (!isGrid) ref.current && ref.current!.scrollTo(scrollTop);
if (!isGrid)
ref.current && ref.current!.scrollTo(scrollTop);
}, throttleTime);
targetElement.addEventListener('scroll', handleWindowScroll);

View file

@ -1,43 +1,9 @@
import { ArrowRight } from 'iconoir-react';
import styled from 'styled-components';
import { DonateContainer, DonateHeader, DonateRight } from '../pages/support';
import { GITHUB_TREE_PREFIX } from '../lib/constants';
import { Text18 } from './Typography';
import { DonateContainer, DonateHeader, DonateRight } from '../pages/support';
import { LargeButton } from './Button';
export interface ReadOnGitHubProps {
path: string;
resource?: string;
}
export function ReadOnGitHub({
path,
resource = 'our documentation',
}: ReadOnGitHubProps) {
return (
<DonateContainer style={{ marginTop: 88 }}>
<div>
<DonateHeader>Read it on GitHub</DonateHeader>
<Text18>
If you prefer, you can take a look at {resource} on our GitHub
repository.
</Text18>
</div>
<DonateRight>
<a
href={`${GITHUB_TREE_PREFIX}/${
path.startsWith('/') ? path.slice(1) : path
}`}
target={'_blank'}
rel={'noreferrer'}
>
<DonateIconButton>
<ArrowRight />
</DonateIconButton>
</a>
</DonateRight>
</DonateContainer>
);
}
import { Text18 } from './Typography';
export const DonateIconButton = styled(LargeButton)`
&&& {
@ -51,3 +17,42 @@ export const DonateIconButton = styled(LargeButton)`
}
}
`;
export interface ReadOnGitHubProps {
path: string;
resource?: string;
}
export function ReadOnGitHub({
path,
resource = 'our documentation',
}: ReadOnGitHubProps) {
return (
<DonateContainer style={{ marginTop: 88 }}>
<div>
<DonateHeader>Read it on GitHub</DonateHeader>
<Text18>
If you prefer, you can take a look at
{' '}
{resource}
{' '}
on our GitHub
repository.
</Text18>
</div>
<DonateRight>
<a
href={`${GITHUB_TREE_PREFIX}/${
path.startsWith('/') ? path.slice(1) : path
}`}
target="_blank"
rel="noreferrer"
>
<DonateIconButton>
<ArrowRight />
</DonateIconButton>
</a>
</DonateRight>
</DonateContainer>
);
}

View file

@ -1,23 +1,29 @@
import Head from 'next/head';
import { useRouter } from 'next/router';
const TITLE_SUFFIX = 'Iconoir | Free Icons';
export interface SEOProps {
title?: string;
description?: string;
}
export function SEO({ title }: SEOProps) {
export function SEO({ title, description }: SEOProps) {
const { asPath } = useRouter();
const pageTitle = title ? `${title} | ${TITLE_SUFFIX}` : TITLE_SUFFIX;
const pageDescription = description;
const pathWithoutQuery = asPath.split(/[?#]/)[0];
const canonicalUrl = `https://iconoir.com${
pathWithoutQuery !== '/' ? pathWithoutQuery : ''
}`;
return (
<Head>
<title>{pageTitle}</title>
<link rel="canonical" href="https://iconoir.com/" />
<meta
name="description"
content="Iconoir is the biggest open source icon library that provides a massive selection of high-quality icons, available for free download.
No premium options or email sign-up required, free for real. Icons available in SVG,
Font, React, React Native, and Flutter libraries, Figma and Framer."
/>
<link rel="canonical" href={canonicalUrl} />
<meta name="description" content={pageDescription} />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
@ -29,12 +35,7 @@ export function SEO({ title }: SEOProps) {
property="og:image"
content="https://iconoir.com/iconoir-brand.png"
/>
<meta
property="og:description"
content="Iconoir is the biggest open source icon library that provides a massive selection of high-quality icons, available for free download.
No premium options or email sign-up required, free for real. Icons available in SVG,
Font, React, React Native, and Flutter libraries, Figma and Framer."
/>
<meta property="og:description" content={pageDescription} />
<meta property="og:image:width" content="1270" />
<meta property="og:image:height" content="760" />
@ -53,10 +54,7 @@ export function SEO({ title }: SEOProps) {
name="twitter:image:alt"
content="The biggest open source icon library with tons of free icons."
/>
<meta
name="twitter:description"
content="The biggest open source icon library with tons of free icons."
/>
<meta name="twitter:description" content={pageDescription} />
<meta name="twitter:card" content="summary_large_image" />
</Head>
);

View file

@ -1,22 +1,77 @@
import type { SliderState } from '@react-stately/slider';
import type { SliderProps as ReactSliderProps } from '@react-types/slider';
import { useFocusRing } from '@react-aria/focus';
import { useNumberFormatter } from '@react-aria/i18n';
import { useSlider, useSliderThumb } from '@react-aria/slider';
import { mergeProps } from '@react-aria/utils';
import { VisuallyHidden } from '@react-aria/visually-hidden';
import { SliderState, useSliderState } from '@react-stately/slider';
import { SliderProps as ReactSliderProps } from '@react-types/slider';
import { useSliderState } from '@react-stately/slider';
import React from 'react';
import styled from 'styled-components';
import { Text13 } from './Typography';
const SliderContainer = styled.div`
position: relative;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
touch-action: none;
`;
const SliderHeader = styled.div`
display: flex;
align-self: stretch;
`;
const Output = styled(Text13)`
flex: 1 0 auto;
text-align: end;
margin-bottom: 6px;
`;
const Track = styled.div`
position: relative;
height: 30px;
width: 100%;
`;
const TrackBackground = styled.div`
position: absolute;
height: 2px;
top: 15px;
background: var(--black);
width: 100%;
`;
const ThumbContainer = styled.div`
position: absolute;
transform: translateX(-50%);
`;
const ThumbInner = styled.div`
width: 24px;
height: 24px;
border: solid 2px var(--black);
box-shadow: 0px 3px 0px 0px var(--g0);
border-radius: 50%;
cursor: pointer;
&:hover {
transition: 0.2s;
scale: 1.2;
}
`;
export interface SliderProps extends ReactSliderProps<number[]> {
formatOptions?: Parameters<typeof useNumberFormatter>[0];
}
export function Slider(props: SliderProps) {
let trackRef = React.useRef(null);
let numberFormatter = useNumberFormatter(props.formatOptions);
let state = useSliderState({ ...props, numberFormatter });
let { groupProps, trackProps, labelProps, outputProps } = useSlider(
const trackRef = React.useRef<HTMLDivElement>(null);
const numberFormatter = useNumberFormatter(props.formatOptions);
const state = useSliderState({ ...props, numberFormatter });
const { groupProps, trackProps, labelProps, outputProps } = useSlider(
props,
state,
trackRef,
@ -26,11 +81,11 @@ export function Slider(props: SliderProps) {
<SliderContainer {...groupProps}>
<SliderHeader>
{props.label && (
<Text13 as={'label'} {...labelProps}>
<Text13 as="label" {...labelProps}>
{props.label}
</Text13>
)}
<Output as={'output'} {...outputProps}>
<Output as="output" {...outputProps}>
{state.getThumbValueLabel(0)}
</Output>
</SliderHeader>
@ -44,13 +99,14 @@ export function Slider(props: SliderProps) {
interface ThumbProps {
state: SliderState;
trackRef: React.RefObject<HTMLElement>;
trackRef: React.RefObject<HTMLElement | null>;
index: number;
}
function Thumb({ state, trackRef, index }: ThumbProps) {
let inputRef = React.useRef(null);
let { thumbProps, inputProps } = useSliderThumb(
const inputRef = React.useRef(null);
const { thumbProps, inputProps } = useSliderThumb(
{
index,
trackRef,
@ -59,7 +115,7 @@ function Thumb({ state, trackRef, index }: ThumbProps) {
state,
);
let { focusProps, isFocusVisible } = useFocusRing();
const { focusProps, isFocusVisible } = useFocusRing();
return (
<ThumbContainer
@ -73,13 +129,13 @@ function Thumb({ state, trackRef, index }: ThumbProps) {
backgroundColor: isFocusVisible
? 'var(--accent)'
: state.isThumbDragging(index)
? 'var(--g6)'
: 'var(--white)',
? 'var(--g6)'
: 'var(--white)',
scale: isFocusVisible
? '1.0'
: state.isThumbDragging(index)
? '1.3'
: '1.0',
? '1.3'
: '1.0',
}}
>
<VisuallyHidden>
@ -89,49 +145,3 @@ function Thumb({ state, trackRef, index }: ThumbProps) {
</ThumbContainer>
);
}
const SliderContainer = styled.div`
position: relative;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
touch-action: none;
`;
const SliderHeader = styled.div`
display: flex;
align-self: stretch;
`;
const Output = styled(Text13)`
flex: 1 0 auto;
text-align: end;
margin-bottom: 6px;
`;
const Track = styled.div`
position: relative;
height: 30px;
width: 100%;
`;
const TrackBackground = styled.div`
position: absolute;
height: 2px;
top: 15px;
background: var(--black);
width: 100%;
`;
const ThumbContainer = styled.div`
position: absolute;
transform: translateX(-50%);
`;
const ThumbInner = styled.div`
width: 24px;
height: 24px;
border: solid 2px var(--black);
box-shadow: 0px 3px 0px 0px var(--g0);
border-radius: 50%;
cursor: pointer;
&:hover {
transition: 0.2s;
scale: 1.2;
}
`;

View file

@ -2,35 +2,14 @@ import React from 'react';
import styled from 'styled-components';
import { media } from '../lib/responsive';
export function Sponsor() {
return (
<SponsorContainer>
<SponsorText>
<SponsorLeft>
<SponsorLogo />
</SponsorLeft>
<SponsorRight>
<SponsorTitle>Get 3 months free of Framer with Iconoir.</SponsorTitle>
<SponsorDescr>
Click the link and use the code pro-yearly-partner.
</SponsorDescr>
</SponsorRight>
</SponsorText>
<a href="https://www.framer.com?via=iconoir">
<SponsorCTA>Get the offer</SponsorCTA>
</a>
</SponsorContainer>
);
}
const SponsorContainer = styled.div`
border: 1px solid var(--g7);
background-color: var(--g7);
border-radius: 10px;
width: 96%;
width: 88%;
font-size: 14px;
color: var(--g1);
padding: 2%;
padding: 6%;
margin-top: 24px;
& > a {
text-decoration: none;
@ -111,3 +90,24 @@ const SponsorCTA = styled.div`
color: var(--white);
}
`;
export function Sponsor() {
return (
<SponsorContainer>
<SponsorText>
<SponsorLeft>
<SponsorLogo />
</SponsorLeft>
<SponsorRight>
<SponsorTitle>Get 3 months free of Framer with Iconoir.</SponsorTitle>
<SponsorDescr>
Click the link and use the code pro-yearly-partner.
</SponsorDescr>
</SponsorRight>
</SponsorText>
<a href="https://www.framer.com?via=iconoir">
<SponsorCTA>Get the offer</SponsorCTA>
</a>
</SponsorContainer>
);
}

View file

@ -2,19 +2,6 @@ import styled from 'styled-components';
import { media } from '../lib/responsive';
import { Text15 } from './Typography';
export interface StatProps {
value: string;
description: string;
}
export function Stat({ value, description }: StatProps) {
return (
<StatContainer>
<StatText>{value}</StatText>
<Text15>{description}</Text15>
</StatContainer>
);
}
const StatText = styled.div`
font-size: 38px;
font-weight: 700;
@ -27,6 +14,7 @@ const StatText = styled.div`
text-stroke: 1.5px;
}
`;
const StatContainer = styled.div`
text-align: center;
width: 45%;
@ -52,3 +40,17 @@ export const StatsContainer = styled.div`
margin: 60px auto;
}
`;
export interface StatProps {
value: string;
description: string;
}
export function Stat({ value, description }: StatProps) {
return (
<StatContainer>
<StatText>{value}</StatText>
<Text15>{description}</Text15>
</StatContainer>
);
}

View file

@ -0,0 +1,112 @@
import React from 'react';
import styled from 'styled-components';
const PromoContainer = styled.div`
border-radius: 12px;
border: 1px solid var(--g6);
text-align: center;
margin-top: 24px;
text-decoration: none;
&:hover {
background-color: var(--g7);
}
`;
const SponsorLabel = styled.div`
font-size: 12px;
font-weight: 400;
color: var(--g4);
margin: 10px 0;
`;
const PromoContent = styled.div``;
const PromoLogo = styled.div`
@keyframes my-animation {
0% {
transform: scale(1);
}
45% {
transform: scale(1);
}
50% {
transform: scale(1.2);
}
55% {
transform: scale(1);
}
90% {
transform: scale(1);
}
95% {
transform: scale(1.2);
}
100% {
transform: scale(1);
}
}
background-image: url('./streamline-ad-logo.png');
background-repeat: no-repeat;
background-size: cover;
background-size: 100% 100%;
width: 32px;
height: 32px;
border-radius: 50%;
margin: 14px auto;
animation-name: my-animation;
animation-duration: 5s;
animation-timing-function: ease;
animation-iteration-count: infinite;
animation-delay: 5s;
`;
const PromoImage = styled.img`
width: 70%;
`;
const PromoInfo = styled.div``;
const PromoTitle = styled.h2`
font-size: 16px;
font-weight: 600;
margin: 0 auto;
`;
const PromoSub = styled.h2`
font-size: 14px;
margin: 0 auto 30px auto;
`;
const PromoDescription = styled.p`
border: 1px solid var(--g6);
margin: 6%;
border-radius: 8px;
font-size: 12px;
padding: 10px 0;
font-weight: 600;
`;
export function Streamline() {
return (
<a
rel="sponsored"
href="https://bit.ly/3SNgpKo"
style={{ textDecoration: 'none' }}
>
<PromoContainer>
<PromoContent>
<PromoInfo>
<PromoLogo />
<PromoTitle>Expand Your Icon Collection</PromoTitle>
<PromoSub>with Streamline</PromoSub>
<PromoImage src="./streamline-ad.png" />
<PromoDescription>170,000 Vector Icons</PromoDescription>
<SponsorLabel>Our sponsor</SponsorLabel>
</PromoInfo>
</PromoContent>
</PromoContainer>
</a>
);
}

View file

@ -1,6 +1,6 @@
import { ArrowRight } from 'iconoir-react';
import { DonateContainer, DonateHeader, DonateRight } from '../pages/support';
import { SUGGEST_LIBRARY_LINK } from '../lib/constants';
import { DonateContainer, DonateHeader, DonateRight } from '../pages/support';
import { DonateIconButton } from './ReadOnGitHub';
import { Text18 } from './Typography';
@ -16,7 +16,7 @@ export function SuggestLibrary() {
</Text18>
</div>
<DonateRight>
<a href={SUGGEST_LIBRARY_LINK} target={'_blank'} rel={'noreferrer'}>
<a href={SUGGEST_LIBRARY_LINK} target="_blank" rel="noreferrer">
<DonateIconButton>
<ArrowRight />
</DonateIconButton>

View file

@ -1,8 +1,8 @@
import React from 'react';
import styled from 'styled-components';
import { media } from '../lib/responsive';
import { showNotification } from '../lib/showNotification';
import { CopyButton } from './Button';
import { media } from '../lib/responsive';
export const Text15 = styled.div`
font-size: 15px;
@ -124,14 +124,6 @@ export const Li = styled.li`
margin: 4px 0;
`;
export const CodeElement = styled.code`
&&& {
display: inline-block;
padding: 0 4px;
color: var(--g0);
}
`;
const PreContainer = styled(Code)`
&&& {
position: relative;
@ -147,6 +139,7 @@ const PreContainer = styled(Code)`
}
}
`;
const CopyContainer = styled.div`
position: absolute;
top: 16px;
@ -159,34 +152,36 @@ export function Pre({ children, ...props }: React.PropsWithChildren<any>) {
React.useEffect(() => {
setSupportsClipboard(
typeof window !== 'undefined' &&
typeof window?.navigator?.clipboard?.writeText !== 'undefined',
typeof window !== 'undefined'
&& typeof window?.navigator?.clipboard?.writeText !== 'undefined',
);
}, []);
return (
<PreContainer {...props}>
<pre ref={containerRef}>{children}</pre>
{supportsClipboard ? (
<CopyContainer>
<CopyButton
onClick={() => {
if (containerRef.current) {
navigator.clipboard
.writeText(containerRef.current.innerText)
.then(() => {
showNotification('Code copied!');
})
.catch((err) => {
console.error(err);
});
}
}}
>
Copy
</CopyButton>
</CopyContainer>
) : null}
{supportsClipboard
? (
<CopyContainer>
<CopyButton
onClick={() => {
if (containerRef.current?.textContent) {
navigator.clipboard
.writeText(containerRef.current.textContent)
.then(() => {
showNotification('Code copied!');
})
.catch((err) => {
console.error(err);
});
}
}}
>
Copy
</CopyButton>
</CopyContainer>
)
: null}
</PreContainer>
);
}

View file

@ -1,11 +1,11 @@
import type { IconListCustomizations } from './IconList';
import React from 'react';
import { DEFAULT_CUSTOMIZATIONS, IconListCustomizations } from './IconList';
import { DEFAULT_CUSTOMIZATIONS } from './IconList';
const CUSTOMIZATIONS_KEY = 'iconoir-customize';
export function useCustomizationPersistence(): [
IconListCustomizations,
// eslint-disable-next-line no-unused-vars
(customizations: IconListCustomizations) => void,
] {
const [customizations, _setCustomizations] = React.useState(

View file

@ -227,6 +227,7 @@ filename,category,tags
"chat-plus-in","Communication",
"check","Actions",
"check-circle","Actions",
"check-square","Actions",
"chocolate","Food",
"chromecast","Devices",
"chromecast-active","Devices",
@ -505,7 +506,7 @@ filename,category,tags
"fish","Animals",
"fishing","Activities",
"flare","Shapes",
"flash","Photos and Videos",
"flash","Photos and Videos","trigger,ray,bolt,lightning",
"flash-off","Photos and Videos",
"flask","Science",
"flip","Design Tools",
@ -544,6 +545,7 @@ filename,category,tags
"gas-tank-droplet","Transport","fuel",
"gif-format","Photos and Videos",
"gift","Other",
"git","Git","git",
"git-branch","Git","git, github",
"git-cherry-pick-commit","Git",
"git-commit","Git","git, github",
@ -1330,6 +1332,7 @@ filename,category,tags
"wind","Weather","weather,air,fresh",
"window-check","System","browser,os",
"window-lock","System","browser,os",
"window-tabs","System","browser,os,tab,navigation",
"window-no-access","System","browser,os",
"window-xmark","System","browser,os,error,issue",
"windows","System",
@ -1356,3 +1359,26 @@ filename,category,tags
"z-square","Typography","coordinate,axis",
"zoom-in","Organization",
"zoom-out","Organization",
"asterisk","Typography","",
"dns","Connectivity","",
"hashtag","Social","",
"peerlist","Social","",
"polar-sh","Development","",
"x","Social","twitter",
"ice-cream","Food","gelato",
"mastodon","Social","",
"meter-arrow-down-right","Maps","",
"rhombus-arrow-right","Maps","",
"u-turn-arrow-left","Maps","",
"u-turn-arrow-right","Maps","",
"calendar-arrow-down","System","",
"calendar-arrow-up","System","",
"calendar-check","System","",
"calendar-rotate","System","",
"calendar-xmark","System","",
"cube-dots","3D Editor","",
"cube-scan","3D Editor","",
"droplet-snow-flake-in","Science","defrost",
"dots-grid-3x3","Navigation","",
"bug","Development","malware",
"whatsapp","Social","chat,text,texting,chatting,bubble"
Can't render this file because it has a wrong number of fields in line 2.

View file

@ -3,14 +3,11 @@ export const REPO = {
repo: 'iconoir',
} as const;
export const GITHUB_LINK =
`https://github.com/${REPO.owner}/${REPO.repo}` as const;
export const GITHUB_LINK = `https://github.com/${REPO.owner}/${REPO.repo}` as const;
export const ISSUE_LINK = `${GITHUB_LINK}/issues/new/choose` as const;
export const SUGGEST_ICON_LINK =
`${GITHUB_LINK}/issues/new?assignees=lucaburgio&labels=icon+request&template=icon_request.md&title=%5BICON%5D` as const;
export const SUGGEST_ICON_LINK = `${GITHUB_LINK}/issues/new?assignees=lucaburgio&labels=icon+request&template=icon_request.md&title=%5BICON%5D` as const;
export const LICENSE_LINK = `${GITHUB_LINK}/blob/main/LICENSE` as const;
export const SUGGEST_LIBRARY_LINK =
`${GITHUB_LINK}/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=%5BFEAT%5D` as const;
export const SUGGEST_LIBRARY_LINK = `${GITHUB_LINK}/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=%5BFEAT%5D` as const;
export const GITHUB_TREE_PREFIX = `${GITHUB_LINK}/tree/main` as const;
export const LIBRARY_LINKS = {
@ -22,14 +19,11 @@ export const LIBRARY_LINKS = {
Figma: 'https://www.figma.com/community/file/983248991460488027/Iconoir-Pack',
} as const;
export const SHARE_LINK =
'https://twitter.com/intent/tweet?text=Your%20new%20free%20icons%20library.%20No%20premium%20options%20or%20signups%20by%20%40burgioluca%20&url=https%3A%2F%2Ficonoir.com' as const;
export const SUPPORT_LINK =
'https://opencollective.com/iconoir/donate?interval=month&amount=10' as const;
export const DISCORD_LINK = 'https://discord.gg/c3uzjx6k' as const;
export const SHARE_LINK = 'https://twitter.com/intent/tweet?text=Your%20new%20free%20icons%20library.%20No%20premium%20options%20or%20signups%20by%20%40burgioluca%20&url=https%3A%2F%2Ficonoir.com' as const;
export const SUPPORT_LINK = 'https://opencollective.com/iconoir/donate?interval=month&amount=10' as const;
export const DISCORD_LINK = 'https://discord.gg/txXcKCAmKW' as const;
export const FEEDBACK_LINK = 'https://forms.gle/3HvwVYow7D6T8zad7' as const;
export const PRIVACY_LINK =
'https://www.freeprivacypolicy.com/live/ba00d743-a0cd-44f8-8cb5-6f58911db0fb' as const;
export const PRIVACY_LINK = 'https://www.freeprivacypolicy.com/live/ba00d743-a0cd-44f8-8cb5-6f58911db0fb' as const;
export const AUTHOR_LINKS = {
Luca: 'https://twitter.com/burgioluca',

View file

@ -1,4 +1,4 @@
import fs from 'fs';
import fs from 'node:fs';
export function getHeaderProps() {
const packageJson = JSON.parse(fs.readFileSync('../package.json').toString());

View file

@ -1,7 +1,7 @@
import type { Icon } from '../components/IconList';
import csv from 'csvtojson';
import * as AllIcons from 'iconoir-react';
import { kebabCase, pascalCase } from 'scule';
import { Icon } from '../components/IconList';
const ICONS_PATH = 'icons.csv';
const TAG_SEPARATOR = '|';
@ -19,18 +19,19 @@ export async function getAllIcons(): Promise<Icon[]> {
(icon) => icon === iconComponentName || icon === iconComponentSolidName,
);
if (iconComponents.length === 0)
if (iconComponents.length === 0) {
throw new Error(
`Couldn't find icons for ${row.filename} (${iconComponentName}) in 'iconoir-react'.`,
);
}
for (const iconComponent of iconComponents) {
icons.push({
filename: kebabCase(iconComponent),
category: row.category,
tags:
row.tags?.split(TAG_SEPARATOR).map((item: string) => item.trim()) ||
[],
row.tags?.split(TAG_SEPARATOR).map((item: string) => item.trim())
|| [],
iconComponentName: iconComponent,
});
}

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