feat: add iconoir.com and React contexts (#185)

This commit is contained in:
Sam Marks 2022-08-28 14:12:09 -04:00 committed by GitHub
parent d08e8b1883
commit ee16ef452d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2403 changed files with 22141 additions and 6172 deletions

View file

@ -19,11 +19,11 @@ jobs:
${{ runner.os }}-
- uses: actions/setup-node@v2
with:
node-version: '14'
node-version: '16'
registry-url: 'https://registry.npmjs.org'
- uses: pnpm/action-setup@v2.1.0
with:
version: 6.27.1
version: 7.8.0
run_install: true
- run: pnpm run build
- uses: stefanzweifel/git-auto-commit-action@v4

View file

@ -21,11 +21,11 @@ jobs:
${{ runner.os }}-
- uses: actions/setup-node@v2
with:
node-version: '14'
node-version: '16'
registry-url: 'https://registry.npmjs.org'
- uses: pnpm/action-setup@v2.1.0
with:
version: 6.27.1
version: 7.8.0
run_install: true
- uses: rhysd/changelog-from-release/action@v2
with:
@ -52,3 +52,51 @@ jobs:
with:
credential: ${{ secrets.PUB_CREDENTIAL_JSON }}
package_directory: ./packages/iconoir-flutter
- name: Build
run: ./node_modules/.bin/next build
working-directory: iconoir.com
- name: Export
run: ./node_modules/.bin/next export
working-directory: iconoir.com
- name: Setup Pages
uses: actions/configure-pages@v1
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
path: './iconoir.com/out'
website:
runs-on: ubuntu-latest
name: deploy
needs: [release]
steps:
- uses: actions/checkout@v2
with:
ref: master # We have to checkout master or PNPM fails. Tag should be on master anyway.
- uses: actions/cache@v2
with:
path: ~/.pnpm-store
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-
- uses: actions/setup-node@v2
with:
node-version: '16'
registry-url: 'https://registry.npmjs.org'
- uses: pnpm/action-setup@v2.1.0
with:
version: 7.8.0
run_install: true
- name: Build Packages
run: pnpm run dist
- name: Build
run: ./node_modules/.bin/next build
working-directory: iconoir.com
- name: Export
run: ./node_modules/.bin/next export
working-directory: iconoir.com
- name: Setup Pages
uses: actions/configure-pages@v1
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
path: './iconoir.com/out'

1
.npmrc Normal file
View file

@ -0,0 +1 @@
strict-peer-dependencies=false

View file

@ -1,32 +1,48 @@
---
title: How to contribute
---
# How to contribute
:sparkles: First off, thanks for taking the time to contribute! :sparkles:
✨ First off, thanks for taking the time to contribute! ✨
The following is a quick set of guidelines for contributing to Iconoir.
## Pull Requests
> :information_source: **Note:** At the moment, to keep consistency, quality, and make the whole process time-efficient, we are not accepting pull requests containing icons. You can open an issue to request a new icon.
> ** Note:** At the moment, to keep consistency, quality, and make the whole
> process time-efficient, we are not accepting pull requests containing icons. You can open an issue
> to request a new icon.
Pull requests for new features, bug fixes, etc. are often appreciated.
:point_right: We're looking for help with fonts and libraries.
> 👉 We're looking for help with fonts and libraries.
Guidelines for pull requests:
- __Make your commit messages as descriptive as possible.__ Include as much information as you can. Explain anything that the file diffs themselves wont make apparent.
- __Target Master__. Most of bugfix or new feature should go to the `master` branch.
- __Include only related work__. If your pull request has unrelated commit, it won't be accepted.
- __Packages and Libraries__. If you're adding or updating a package or React/Vue library the target folder is `/packages/iconoir-[LIBRARY_NAME]`.
- __Fonts__. If you're adding or updating fonts the target folder is `/fonts/`.
> **Make your commit messages as descriptive as possible.** Include as much information as you can.
> Explain anything that the file diffs themselves won't make apparent.
> **Target Master.** Most of bugfixes or new features should go to the master branch.
> **Include only related work.** If your pull request has unrelated commits, it won't be accepted.
> **Packages and Libraries.** If you're adding or updating a package or React / Vue library, the
> target folder is `/packages/iconoir-[LIBRARY_NAME]`.
> **Fonts.** If you're adding or updating fonts, the target folder is `/fonts/`.
## Icon Requests
Before creating an icon request, please search to see if someone has requested the icon already. If there is an open request, please upvote it.
Before creating an icon request, please search to see if someone has requested the icon already. If
there is an open request, please upvote it.
If the icon has not already been requested, [create an issue](https://github.com/lucaburgio/iconoir/issues/new/choose) and add as much information as possible.
Follow the rules you see when opening a new bug report.
If the icon has not already been requested, [create an
issue](https://github.com/iconoir-icons/iconoir/issues/new/choose) and add as much information as
possible. Follow the rules you see when opening a new bug report.
## Bug Reports
Before reporting an issue, please search to see if someone has filed a similar issue before. If there is already an open issue, please upvote it and/or leave a comment with additional information.
Before reporting an issue, please search to see if someone has filed a similar issue before. If
there is already an open issue, please upvote it and/or leave a comment with additional information.
Follow the rules you see when opening a new bug report.

View file

@ -1,29 +1,29 @@
<p align="center">
<img src="assets/iconoir-cover.png" alt="Iconoir">
</p>
<div align="center">
<img src="assets/iconoir-cover.png" alt="Iconoir" />
</div>
<p align="center">
<div align="center">
Iconoir is an open-source library with 1000+ unique SVG icons, designed on a 24x24 pixels grid. No premium icons, no email sign-up, no newsletters.
<p>
</div>
<p align="center">
<div align="center">
<a href="https://iconoir.com"><strong>Browse at iconoir.com &rarr;</strong></a>
</p>
</div>
<p align="center">
<div align="center">
<a href="https://github.com/lucaburgio/iconoir/releases">
<img src="https://img.shields.io/github/v/release/lucaburgio/iconoir?style=flat-square" alt="Version">
<img src="https://img.shields.io/github/v/release/lucaburgio/iconoir?style=flat-square" alt="Version" />
</a>
<a href="https://github.com/lucaburgio/iconoir">
<img src="https://img.shields.io/github/stars/lucaburgio/iconoir?style=flat-square" alt="Project Stars">
<img src="https://img.shields.io/github/stars/lucaburgio/iconoir?style=flat-square" alt="Project Stars" />
</a>
<a href="https://www.npmjs.com/package/iconoir-react">
<img src="https://img.shields.io/npm/dm/iconoir-react?color=98E8F3&label=react&style=flat-square" alt="React Library">
<img src="https://img.shields.io/npm/dm/iconoir-react?color=98E8F3&label=react&style=flat-square" alt="React Library" />
</a>
<a href="https://github.com/lucaburgio/iconoir/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/lucaburgio/iconoir?style=flat-square" alt="License">
<img src="https://img.shields.io/github/license/lucaburgio/iconoir?style=flat-square" alt="License" />
</a>
</p>
</div>
## Basic Usage

View file

@ -5,25 +5,13 @@ import { Listr } from 'listr2';
import os from 'os';
import path, { basename, dirname } from 'path';
import { fileURLToPath } from 'url';
import { incompatibleNames } from '../constants.js';
// Paths
const __dirname = dirname(fileURLToPath(import.meta.url));
const rootDir = path.join(__dirname, '..');
const iconoirIconsDir = path.join(rootDir, 'icons');
// Icon files with incompatible names
const incompatibleNames = {
'1st-medal': 'medal-1st',
'4k-display': 'display-4k',
'2x2-cell': 'cell-2x2',
'360-view': 'view360',
github: 'gitHub',
'github-outline': 'gitHubOutline',
'gitlab-full': 'gitLabFull',
linkedin: 'linkedIn',
tiktok: 'tikTok',
youtube: 'youTube',
};
const ignoreCleanFilenames = ['IconoirContext.tsx'];
// Targets for building icons
const targets = {
@ -194,11 +182,18 @@ const tasks = new Listr(
const files = await fs.readdir(
builtIconsDir
);
const promises = files.map((file) => {
return fs.unlink(
path.join(builtIconsDir, file)
);
});
const promises = files
.filter(
(file) =>
!ignoreCleanFilenames.includes(
path.basename(file)
)
)
.map((file) => {
return fs.unlink(
path.join(builtIconsDir, file)
);
});
return Promise.all(promises).catch(
(err) => {
ctx[target] = { skip: true };
@ -224,13 +219,12 @@ const tasks = new Listr(
targets[target].path,
'.svgrrc.json'
),
'--prettier-config',
path.join(
rootDir,
'.prettierrc.json'
),
'--out-dir',
builtIconsDir,
'--template',
'bin/templates/icon-template.cjs',
'--index-template',
'bin/templates/index-template.cjs',
ctx.tmpDir,
],
{ preferLocal: true }
@ -377,9 +371,10 @@ const tasks = new Listr(
title: 'Building icon files',
skip: (ctx) => ctx[target]?.skip,
task: async (ctx) => {
const finalFileNames = [];
try {
ctx.dstFilePaths.forEach(
async (file) => {
await Promise.all(
ctx.dstFilePaths.map(async (file) => {
const svgfilename =
path.parse(file).name;
// Prefix with Svg if icon name starts with a number
@ -421,19 +416,29 @@ const tasks = new Listr(
'(snakeCase)',
},
async onComplete(results) {
await fs.appendFile(
path.join(
builtIconsDir,
'iconoir_flutter.dart'
),
`export './${basename(
results.output.path
)}';\n`
finalFileNames.push(
results.output.path
);
},
},
]);
}
})
);
finalFileNames.sort();
await fs.appendFile(
path.join(
builtIconsDir,
'iconoir_flutter.dart'
),
finalFileNames
.map(
(fileName) =>
`export './${basename(
fileName
)}';`
)
.join('\n')
);
} catch (err) {
throw new Error(err.message);

View file

@ -0,0 +1,26 @@
const template = (
{ template },
opts,
{ imports, interfaces, componentName, props, jsx, exports }
) => {
const plugins = ['jsx'];
if (opts.typescript) {
plugins.push('typescript');
}
const typeScriptTpl = template.smart({ plugins });
props[0].name = 'passedProps';
return typeScriptTpl.ast`${imports}
import { IconoirContext } from './IconoirContext'
${interfaces}
function ${componentName}(${props}) {
const context = React.useContext(IconoirContext);
const props = { ...context, ...passedProps };
return ${jsx};
}
${exports}
`;
};
module.exports = template;

View file

@ -0,0 +1,15 @@
const path = require('path');
function template(filePaths) {
const exportEntries = filePaths.map((filePath) => {
const basename = path.basename(filePath, path.extname(filePath));
const exportName = /^\d/.test(basename) ? `Svg${basename}` : basename;
return `export { default as ${exportName} } from './${basename}'`;
});
exportEntries.push(
"export { IconoirProvider, IconoirContext, IconoirContextValue } from './IconoirContext'"
);
return exportEntries.join('\n');
}
module.exports = template;

12
constants.js Normal file
View file

@ -0,0 +1,12 @@
export const incompatibleNames = {
'1st-medal': 'medal-1st',
'4k-display': 'display-4k',
'2x2-cell': 'cell-2x2',
'360-view': 'view360',
github: 'gitHub',
'github-outline': 'gitHubOutline',
'gitlab-full': 'gitLabFull',
linkedin: 'linkedIn',
tiktok: 'tikTok',
youtube: 'youTube',
};

17
css/README.md Normal file
View file

@ -0,0 +1,17 @@
# Iconoir CSS
Import the CSS File:
```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/iconoir-icons/iconoir@master/css/iconoir.css">
```
Here is an example in HTML:
```html
<i class="iconoir-hand-brake"></i>
```
The class must always be "iconoir-" and then the name of the icon. You can find the names of the
icons [here](https://iconoir.com).
<SuggestLibrary />

7
docs/framer.md Normal file
View file

@ -0,0 +1,7 @@
# Iconoir Framer
Iconoir is happily part of [Framer](https://framer.com) now. To start using the icons: On the top
menu, `Insert` > `Graphics` > `Iconoir`. You can switch between icons from the right sidebar in the
editor.
<SuggestLibrary />

6
iconoir.com/.eslintrc.json Executable file
View file

@ -0,0 +1,6 @@
{
"extends": "next/core-web-vitals",
"rules": {
"react/no-unescaped-entities": ["off"]
}
}

35
iconoir.com/.gitignore vendored Executable file
View file

@ -0,0 +1,35 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo

34
iconoir.com/README.md Executable file
View file

@ -0,0 +1,34 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

View file

@ -0,0 +1,209 @@
import React from 'react';
import anime from 'animejs';
// eslint-disable-next-line no-unused-vars
type SetInstances = (instances: anime.AnimeInstance[]) => void;
function playWithLines1(setInstances: SetInstances): anime.AnimeInstance[] {
return [
anime({
targets: '.playWithLines2 .roll1',
strokeDashoffset: [anime.setDashoffset, 0],
easing: 'easeInOutSine',
duration: 1500,
delay: function (el, i) {
return i * 250;
},
direction: 'alternate',
complete: () => setInstances(playWithLines2(setInstances)),
}),
];
}
function playWithLines2(setInstances: SetInstances): anime.AnimeInstance[] {
return [
anime({
targets: '.playWithLines2 .roll2',
strokeDashoffset: [anime.setDashoffset, 0],
easing: 'easeInOutSine',
duration: 1500,
delay: function (el, i) {
return i * 250;
},
direction: 'alternate',
complete: () => setInstances(playWithLines3(setInstances)),
}),
];
}
function playWithLines3(setInstances: SetInstances): anime.AnimeInstance[] {
return [
anime({
targets: '.playWithLines2 .roll3',
strokeDashoffset: [anime.setDashoffset, 0],
easing: 'easeInOutSine',
duration: 1500,
delay: function (el, i) {
return i * 250;
},
direction: 'alternate',
complete: () => setInstances(playWithLines4(setInstances)),
}),
anime({
targets: '.playWithLines2',
rotate: '130',
easing: 'spring(1, 80, 10, 0)',
duration: 30,
direction: 'alternate',
delay: 100,
}),
];
}
function playWithLines4(setInstances: SetInstances): anime.AnimeInstance[] {
return [
anime({
targets: '.playWithLines2 .roll4',
strokeDashoffset: [anime.setDashoffset, 0],
easing: 'easeInOutSine',
duration: 1500,
delay: function (el, i) {
return i * 250;
},
direction: 'alternate',
complete: () => setInstances(playWithLines1(setInstances)),
}),
];
}
export function AnimatedSvg() {
const instancesRef = React.useRef<anime.AnimeInstance[] | null>(null);
React.useEffect(() => {
instancesRef.current = playWithLines1((instances) => {
instancesRef.current = instances;
});
return () => {
for (const instance of instancesRef.current || []) {
instance.pause();
}
};
}, []);
return (
<svg
className="playWithLines2"
width="36"
height="36"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
className="roll1"
d="M14.1488 9.47163V3.61153C14.1488 2.72151 13.4273 2 12.5373 2V2C11.6473 2 10.9258 2.72151 10.9258 3.61153V8.44611"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
<path
className="roll1"
d="M16.346 12.841L18.5217 5.58862C18.7755 4.74265 18.2886 3.85248 17.4394 3.60984V3.60984C16.5943 3.3684 15.7142 3.8609 15.4779 4.70743L14.1484 9.47149"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
<path
className="roll1"
d="M7.61935 9.24985L8.67489 11.5913C9.03961 12.4003 8.68159 13.352 7.87404 13.72C7.06183 14.0901 6.10347 13.7296 5.73663 12.9159L4.68109 10.5745C4.31637 9.76542 4.67439 8.81376 5.48193 8.44574C6.29415 8.07559 7.25251 8.43614 7.61935 9.24985Z"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
<path
className="roll1"
d="M11.7192 12.2615V12.2615C11.9239 11.694 11.8998 11.0692 11.6518 10.5192L10.5787 8.13874C10.2181 7.33892 9.27613 6.98454 8.4778 7.34836V7.34836C7.66469 7.71892 7.31885 8.68832 7.71382 9.48986L7.84946 9.76511"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
<path
className="roll1"
d="M13.8566 17.6767L14.3487 16.6927C14.3976 16.5947 14.3461 16.4763 14.241 16.4454L10.6903 15.4011C9.97853 15.1918 9.51797 14.5038 9.59563 13.766V13.766C9.68372 12.9292 10.4284 12.3188 11.2662 12.3968L16.0542 12.8422C16.0542 12.8422 19.8632 13.4282 18.5447 17.2372C17.2262 21.0463 16.7867 22.3648 13.8566 22.3648C11.9521 22.3648 9.16855 22.3648 9.16855 22.3648H8.87555C6.52912 22.3648 4.62697 20.4627 4.62697 18.1163V18.1163L4.48047 9.91211"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
<path
className="roll2"
strokeDasharray="69px"
style={{
strokeDashoffset: '69px',
}}
d="M7 10.625H14.2C14.2 10.625 14.2 10.625 14.2 10.625C14.2 10.625 17 10.625 17 13.625C17 17 14.2 17 14.2 17H13.4M7 10.625L10.5 14M7 10.625L10.5 7"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
className="roll2"
strokeDasharray="69.1247px"
style={{
strokeDashoffset: '69.1247px',
}}
d="M12 23C18.0751 23 23 18.0751 23 12C23 5.92487 18.0751 1 12 1C5.92487 1 1 5.92487 1 12C1 18.0751 5.92487 23 12 23Z"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
className="roll3"
strokeDasharray="36.0466px"
style={{
strokeDashoffset: '36.0466px',
}}
d="M21.1683 8C19.6252 4.46819 16.1011 2 12.0004 2C6.81508 2 2.55153 5.94668 2.0498 11M21.1683 8H17.0004M21.1683 8H21.4004C21.7318 8 22.0004 7.73137 22.0004 7.4V3"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
className="roll3"
strokeDasharray="36.0466px"
style={{
strokeDashoffset: '36.0466px',
}}
d="M2.88146 16C4.42458 19.5318 7.94874 22 12.0494 22C17.2347 22 21.4983 18.0533 22 13M2.88146 16H7.04938M2.88146 16H2.64938C2.318 16 2.04938 16.2686 2.04938 16.6V21"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
className="roll4"
strokeDasharray="30.2841px"
style={{
strokeDashoffset: '30.2841px',
}}
d="M4 13V19C4 20.1046 4.89543 21 6 21H18C19.1046 21 20 20.1046 20 19V13"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
className="roll4"
strokeDasharray="21.8995px"
style={{
strokeDashoffset: '21.8995px',
}}
d="M12 3L12 15M12 15L8.5 11.5M12 15L15.5 11.5"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}

View file

@ -0,0 +1,142 @@
import React from 'react';
import styled, { keyframes } from 'styled-components';
import useResizeObserver from 'use-resize-observer';
import { FEEDBACK_LINK, LIBRARY_LINKS, SUGGEST_LIBRARY } from './constants';
import { media } from './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={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.Framer} target={'_blank'} rel={'noreferrer'}>
<AvailableForImage
src={'/logo-framer.svg'}
alt={'Framer Logo'}
title={'Framer'}
/>
</a>
<AreYouUsing>
<a href={SUGGEST_LIBRARY} 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;
margin-top: 5px;
text-align: center;
${media.lg} {
display: none;
}
`;
const DesktopHeader = styled(Text14)`
display: none;
${media.lg} {
display: block;
}
`;
const AvailableForAnimation = keyframes`
5% {
transform: translateX(0);
}
45% {
transform: translateX(calc((var(--content-width) + 60px - 100vw) * -1));
}
55% {
transform: translateX(calc((var(--content-width) + 60px - 100vw) * -1));
}
95% {
transform: translateX(0);
}
`;
const AvailableForOuter = styled.div`
max-width: 100vw;
margin: 16px -30px 70px -30px;
padding: 0 30px;
overflow: hidden;
${media.lg} {
margin: 120px auto;
padding: 0;
}
`;
const AvailableForContainer = styled.div<{ contentWidth: number }>`
display: flex;
align-items: center;
justify-content: flex-start;
width: max-content;
--content-width: ${(props) => props.contentWidth}px;
${(props) => (props.contentWidth ? '&' : '&.noop')} {
animation: ${AvailableForAnimation} 40s cubic-bezier(0.37, 0, 0.63, 1)
infinite;
}
> :not(:last-child) {
margin-right: 30px;
}
${media.sm} {
justify-content: center;
animation: none;
width: auto;
}
${media.md} {
> :not(:last-child) {
margin-right: 60px;
}
}
`;
const AvailableForImage = styled.img`
height: 40px;
display: block;
${media.lg} {
height: 50px;
}
`;

View file

@ -0,0 +1,66 @@
import styled from 'styled-components';
export const ResetButton = styled.button`
font-family: inherit;
font-size: 100%;
line-height: 1.15;
margin: 0;
text-transform: none;
-webkit-appearance: none;
border: none;
outline: none;
`;
export const LargeButton = styled(ResetButton)`
background: var(--black);
height: 75px;
display: inline-flex;
align-items: center;
text-decoration: none;
color: var(--white);
padding: 0 43px;
font-size: 20px;
line-height: 26px;
font-weight: 700;
position: relative;
cursor: pointer;
z-index: 12;
> :not(:last-child) {
margin-right: 15px;
}
* {
font-weight: 700;
}
&::after {
content: ' ';
display: block;
position: absolute;
inset: 0;
border: solid 3px var(--black);
opacity: 0;
transition: inset 0.5s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.15s linear;
z-index: 10;
}
&:focus::after,
&:hover::after {
inset: -7px;
opacity: 1;
}
`;
export const Button = styled(LargeButton)`
height: 40px;
font-size: 13px;
line-height: 21px;
padding: 0 18px;
&::after {
border-width: 2px;
}
&:focus::after,
&:hover::after {
inset: -4px;
}
&:active {
background: var(--darker-gray);
}
`;

View file

@ -0,0 +1,53 @@
import React from 'react';
import styled from 'styled-components';
import { media } from './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;
width: 100%;
* {
line-height: 1;
}
> :not(:last-child) {
margin-right: 10px;
}
`;
const Container = styled.div`
display: flex;
align-items: flex-end;
padding-bottom: 25px;
box-sizing: border-box;
${media.sm} {
padding-bottom: 40px;
}
`;
const Title = styled(Text15)`
font-weight: 700;
color: var(--black);
`;
const Separator = styled.div`
height: 1px;
flex: 1;
background: var(--light-gray);
`;

View file

@ -0,0 +1,129 @@
import { BoxIso } from 'iconoir-react';
import React from 'react';
import styled from 'styled-components';
import { Code, CopyButton, Text15, Text18 } from './Typography';
import moment from 'moment';
import { MDXRemoteSerializeResult } from 'next-mdx-remote';
import { MDXRemote } from './MDXRemote';
import { FILE_PREFIX } from './constants';
import { media } from './responsive';
const EXPAND_HEIGHT = 400;
export interface ChangelogEntryProps {
name: string;
body: MDXRemoteSerializeResult;
created_at: string;
}
export function ChangelogEntry({
name,
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={`${FILE_PREFIX}/../../releases/tag/${name}`}
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}>
<MDXRemote {...body} />
{shouldExpand ? (
<ExpandContainer>
<CopyButton onClick={() => setExpanded((e) => !e)}>
{expanded ? 'Collapse' : 'Expand'}
</CopyButton>
</ExpandContainer>
) : null}
</EntryBody>
</Container>
);
}
const Container = styled.div`
margin: 40px 0 !important;
display: flex;
align-items: stretch;
flex-direction: column;
width: 100%;
${media.lg} {
flex-direction: row;
align-items: flex-start;
margin: 24px 0 !important;
}
`;
const ContainerLeft = styled.div`
display: flex;
align-items: flex-start;
margin-bottom: 12px;
${media.lg} {
margin-bottom: 0;
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;
margin: 0;
max-height: ${(props) => (props.expanded ? 'none' : `${EXPAND_HEIGHT}px`)};
position: relative;
overflow: hidden;
* {
font-family: var(--code-family);
}
ul {
list-style: none none;
margin: 0;
padding: 0;
li {
margin-bottom: 8px;
}
}
code {
display: inline-block;
background: var(--gray) !important;
color: var(--black);
font-family: var(--font-family) !important;
padding: 0 4px;
font-size: 18px !important;
}
`;

View file

@ -0,0 +1,29 @@
import Link from 'next/link';
import React from 'react';
import styled from 'styled-components';
import { Text13 } from './Typography';
export interface CurrentVersionProps {
version: string;
color?: string;
}
export function CurrentVersion({ version, color }: CurrentVersionProps) {
return (
<Link href={'/docs/changelog'} passHref>
<Container as={'a'} style={color ? { background: color } : undefined}>
{version}
</Container>
</Link>
);
}
const Container = styled(Text13)`
color: var(--black);
font-weight: 700;
background: var(--pink);
line-height: 1;
padding: 7px 16px;
border-radius: 200px;
display: block;
text-decoration: none !important;
`;

View file

@ -0,0 +1,102 @@
import React from 'react';
import { DEFAULT_CUSTOMIZATIONS, IconListCustomizations } from './IconList';
import styled from 'styled-components';
import { Text13, Text18 } from './Typography';
import { Button } from './Button';
import { ColorInput } from './Input';
import { Slider } from './Slider';
export interface CustomizationEditorProps {
customizations: IconListCustomizations;
// eslint-disable-next-line no-unused-vars
onChange: (customizations: IconListCustomizations) => void;
}
export function CustomizationEditor({
customizations,
onChange,
}: CustomizationEditorProps) {
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
);
React.useEffect(() => {
setColor(customizations.hexColor);
setSize(customizations.size);
setStrokeWidth(customizations.strokeWidth);
}, [customizations]);
function updateCustomizations(partial: Partial<IconListCustomizations>) {
startTransition(() => {
onChange({
...customizations,
...partial,
});
});
}
return (
<>
<Header>
<Text18 style={{ fontWeight: 700, color: 'var(--black)' }}>
Customize
</Text18>
<Button onClick={() => onChange(DEFAULT_CUSTOMIZATIONS)}>Reset</Button>
</Header>
<Field>
<Slider
label={'Size'}
minValue={12}
maxValue={128}
value={[size]}
formatOptions={{ maximumFractionDigits: 0 }}
onChange={(values) => {
setSize(values[0]);
updateCustomizations({ size: values[0] });
}}
/>
</Field>
<Field>
<Slider
label={'Stroke Width'}
minValue={0.5}
maxValue={3}
value={[strokeWidth]}
step={0.01}
formatOptions={{ maximumFractionDigits: 1 }}
onChange={(values) => {
setStrokeWidth(values[0]);
updateCustomizations({ strokeWidth: values[0] });
}}
/>
</Field>
<HorizontalField>
<Text13>Color</Text13>
<ColorInput
type={'color'}
value={color}
onChange={(e) => {
setColor(e.target.value);
updateCustomizations({ hexColor: e.target.value });
}}
/>
</HorizontalField>
</>
);
}
const Header = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 45px;
`;
const Field = styled.div`
margin-bottom: 35px;
`;
const HorizontalField = styled(Field)`
display: flex;
align-items: center;
justify-content: space-between;
`;

View file

@ -0,0 +1,170 @@
import React from 'react';
import { DocumentationItem } from '../pages/docs/[...slug]';
import styled from 'styled-components';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { media } from './responsive';
import { NavArrowUp } from 'iconoir-react';
export interface DocumentationNavigationProps {
documentationItems: DocumentationItem[];
pathPrefix?: string[];
}
export function DocumentationNavigation({
documentationItems,
pathPrefix,
}: DocumentationNavigationProps) {
const router = useRouter();
const activePath = router.asPath.replace('/docs/', '');
const [expandedTitles, setExpandedTitles] = React.useState<string[]>([]);
React.useEffect(() => {
const expandedItems = documentationItems.filter((item) => {
const normalized = activePath.replace((pathPrefix || []).join('/'), '');
return (
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]);
return (
<>
{documentationItems.map((documentationItem) => {
const path = [...(pathPrefix || []), documentationItem.path]
.filter(Boolean)
.join('/');
if (documentationItem.children?.length) {
const active = expandedTitles.includes(documentationItem.title);
return (
<React.Fragment key={documentationItem.title}>
<HeaderItem
onClick={() => {
setExpandedTitles((et) => {
const includes = et.includes(documentationItem.title);
return includes
? et.filter((i) => i !== documentationItem.title)
: [...et, documentationItem.title];
});
}}
>
<HeaderItemIcon active={active}>
<NavArrowUp />
</HeaderItemIcon>
{documentationItem.title}
</HeaderItem>
<ChildrenContainer expanded={active}>
<DocumentationNavigation
documentationItems={documentationItem.children}
pathPrefix={[
...(pathPrefix || []),
documentationItem.path,
].filter(Boolean)}
/>
</ChildrenContainer>
</React.Fragment>
);
} else {
return (
<Link href={`/docs/${path}`} passHref key={documentationItem.path}>
<NavigationItem as={'a'} active={activePath === path}>
<span>{documentationItem.title}</span>
{documentationItem.label ? (
<NavigationItemLabel>
{documentationItem.label}
</NavigationItemLabel>
) : null}
</NavigationItem>
</Link>
);
}
})}
</>
);
}
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;
text-transform: uppercase;
font-size: 12px;
line-height: 19px;
color: var(--black);
letter-spacing: 0.12em;
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 45px 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(--black-60);
display: flex;
align-items: center;
text-decoration: none;
span {
font-weight: 500;
}
> :not(:last-child) {
margin-right: 14px;
}
&:hover,
${(props) => (props.active ? '&' : '&.noop')} {
background: var(--light-gray);
color: var(--black);
}
${(props) => (props.active ? 'span' : '&.noop')} {
font-weight: 700;
}
${media.lg} {
padding: 12px 45px 12px 65px;
}
`;
const NavigationItemLabel = styled.span`
display: flex;
align-items: center;
justify-content: center;
padding: 0 4px;
font-size: 11px;
line-height: 17.6px;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--white);
background: var(--black);
`;

View file

@ -0,0 +1,79 @@
import { IconoirProvider } from 'iconoir-react';
import React from 'react';
import styled from 'styled-components';
import { CustomizationEditor } from './CustomizationEditor';
import { FiltersEditor } from './FiltersEditor';
import { Icon, IconList, IconListFilters } from './IconList';
import { media } from './responsive';
import { useCustomizationPersistence } from './useCustomizationPersistence';
export interface ExploreProps {
allIcons: Icon[];
}
export function Explore({ allIcons }: ExploreProps) {
const [filters, setFilters] = React.useState<IconListFilters>({});
const [customizations, setCustomizations] = useCustomizationPersistence();
return (
<Container>
<Left>
<FilterContainer isMobile>
<FiltersEditor filters={filters} onChange={setFilters} />
</FilterContainer>
<IconoirProvider
iconProps={{
color: customizations.hexColor,
width: customizations.size ? `${customizations.size}px` : undefined,
height: customizations.size
? `${customizations.size}px`
: undefined,
strokeWidth: customizations.strokeWidth,
}}
>
<IconList filters={filters} allIcons={allIcons} />
</IconoirProvider>
</Left>
<Right>
<FilterContainer>
<FiltersEditor filters={filters} onChange={setFilters} />
</FilterContainer>
<CustomizationEditor
customizations={customizations}
onChange={setCustomizations}
/>
</Right>
</Container>
);
}
const Container = styled.div`
display: flex;
align-items: flex-start;
flex-direction: row;
`;
const Left = styled.div`
flex: 1;
min-height: calc(100vh - 100px);
`;
const Right = styled.div`
position: sticky;
top: 50px;
width: 275px;
margin-left: 68px;
display: none;
${media.md} {
display: block;
}
`;
const FilterContainer = styled.div<{ isMobile?: boolean }>`
display: ${(props) => (props.isMobile ? 'block' : 'none')};
margin-bottom: 40px;
position: sticky;
top: 20px;
z-index: 100;
${media.md} {
position: relative;
top: 0;
display: ${(props) => (props.isMobile ? 'none' : 'block')};
margin-bottom: 50px;
}
`;

View file

@ -0,0 +1,39 @@
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);
React.useEffect(() => {
setSearch(filters.search);
}, [filters]);
function updateFilters(partial: Partial<IconListFilters>) {
startTransition(() => {
onChange({
...filters,
...partial,
});
});
}
return (
<LargeInput
placeholder={'Search...'}
value={search}
type={'search'}
autoCapitalize={'none'}
onChange={(e) => {
const value = e.target.value;
setSearch(value);
updateFilters({ search: value });
}}
/>
);
}

View file

@ -0,0 +1,42 @@
import { PeaceHand } from 'iconoir-react';
import React from 'react';
import styled from 'styled-components';
import { LICENSE_LINK } from './constants';
import { Logo, LogoContainer, LogoIcon } from './Header';
import { NavigationItemContainer } from './NavigationItem';
export function Footer() {
return (
<Container>
<LogoContainer>
<LogoIcon>
<PeaceHand />
</LogoIcon>
<Logo src={'/iconoir-logo.svg'} alt={'Iconoir Logo'} />
</LogoContainer>
<FooterNavigationItem
as={'a'}
href={LICENSE_LINK}
target={'_blank'}
rel={'noreferrer'}
>
License
</FooterNavigationItem>
</Container>
);
}
const Container = styled.div`
margin-top: 100px;
padding-top: 30px;
border-top: solid 2px var(--light-gray);
display: flex;
align-items: center;
> :not(:last-child) {
margin-right: 50px;
}
`;
const FooterNavigationItem = styled(NavigationItemContainer)`
color: var(--black);
`;

View file

@ -0,0 +1,21 @@
import React from 'react';
import Script from 'next/script';
export function GA() {
return (
<>
<Script
src="https://www.googletagmanager.com/gtag/js?id=UA-33344001-9"
strategy={'afterInteractive'}
/>
<Script id={'google-analytics'} strategy={'afterInteractive'}>
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-33344001-9');
`}
</Script>
</>
);
}

View file

@ -0,0 +1,198 @@
import React from 'react';
import styled from 'styled-components';
import { AUTHOR_LINKS } from './constants';
import { Cancel, Heart, Menu } from 'iconoir-react';
import { CurrentVersion } from './CurrentVersion';
import { media } from './responsive';
import { ResetButton } from './Button';
import { AnimatedSvg } from './AnimatedSvg';
import { NavigationItem, NavigationItemContainer } from './NavigationItem';
import Link from 'next/link';
export interface HeaderProps {
currentVersion: string;
currentVersionColor?: string;
}
export function Header({ currentVersion, currentVersionColor }: HeaderProps) {
const [menuVisible, setMenuVisible] = React.useState(false);
return (
<Container>
<HeaderLeft>
<Link href={'/'}>
<a>
<LogoContainer>
<LogoIcon>
<AnimatedSvg />
</LogoIcon>
<Logo src={'/iconoir-logo.svg'} alt={'Iconoir Logo'} />
</LogoContainer>
</a>
</Link>
<CurrentVersion version={currentVersion} color={currentVersionColor} />
</HeaderLeft>
<HeaderCenter>
<MobileMenuContainer visible={menuVisible}>
<NavigationItem href={'/'}>Icons</NavigationItem>
<NavigationItem href={'/docs'}>Documentation</NavigationItem>
<NavigationItem href={'/support'} style={{ marginRight: 0 }}>
Donate &mdash; Our Mission
</NavigationItem>
<BuiltWith isMobile>
Made with <Heart width={'1em'} height={'1em'} /> by{' '}
<a href={AUTHOR_LINKS.Luca} target={'_blank'} rel={'noreferrer'}>
Luca
</a>{' '}
&amp;{' '}
<a href={AUTHOR_LINKS.Sam} target={'_blank'} rel={'noreferrer'}>
Sam
</a>
</BuiltWith>
</MobileMenuContainer>
</HeaderCenter>
<HeaderRight>
<BuiltWith>
Designed and built with <Heart width={'1em'} height={'1em'} /> by{' '}
<a href={AUTHOR_LINKS.Luca} target={'_blank'} rel={'noreferrer'}>
Luca
</a>{' '}
&amp;{' '}
<a href={AUTHOR_LINKS.Sam} target={'_blank'} rel={'noreferrer'}>
Sam
</a>
</BuiltWith>
<MobileMenuButton onClick={() => setMenuVisible((v) => !v)}>
{menuVisible ? <Cancel /> : <Menu />}
</MobileMenuButton>
</HeaderRight>
</Container>
);
}
export const LogoContainer = styled.div`
position: relative;
z-index: 101;
display: inline-flex;
align-items: center;
`;
const MobileMenuButton = styled(ResetButton)`
z-index: 101;
color: var(--black);
background: transparent;
display: inline-block;
margin-left: auto !important;
cursor: pointer;
svg {
width: 24px;
height: 24px;
}
${media.lg} {
display: none;
}
`;
const MobileMenuContainer = styled.div<{ visible?: boolean }>`
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 100;
background: white;
padding-top: 100px;
transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.25s linear;
box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.1);
transform: translateY(-100%);
pointer-events: none;
opacity: 0;
display: flex;
flex-direction: column;
align-items: stretch;
${(props) => (props.visible ? '&' : '&.noop')} {
pointer-events: all;
transform: translateY(0);
opacity: 1;
}
${media.lg} {
margin-left: auto;
background: none;
padding-top: 0;
box-shadow: none;
display: flex;
flex-direction: row;
position: relative;
align-items: center;
transform: none;
pointer-events: all;
opacity: 1;
> :not(:last-child) {
margin-right: 60px;
}
}
`;
const Container = styled.div`
display: flex;
align-items: center;
justify-content: center;
`;
const HeaderItem = styled.div`
flex: 1;
width: 33%;
display: flex;
flex-direction: row;
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;
`;
export const Logo = styled.img`
height: 24px;
color: var(--black);
margin-right: 16px !important;
z-index: 101;
`;
export const LogoIcon = styled.div`
color: var(--black);
margin-right: 4px !important;
svg {
width: 36px;
height: 36px;
}
`;
const BuiltWith = styled(NavigationItemContainer)<{ isMobile?: boolean }>`
display: ${(props) => (props.isMobile ? 'flex' : 'none')};
${media.lg} {
display: ${(props) => (props.isMobile ? 'none' : 'flex')};
}
align-items: center;
justify-content: center;
color: var(--black-60);
border-bottom: none !important;
svg {
fill: var(--black);
margin: 0 0.22em;
}
> * {
margin: 0 0.22em;
}
a {
color: var(--black);
font-weight: 700;
}
> :last-child {
margin-right: 0;
}
${media.lg} {
justify-content: flex-start;
a {
font-weight: normal;
}
}
`;

View file

@ -0,0 +1,46 @@
import React from 'react';
import styled from 'styled-components';
import { media } from './responsive';
export interface HeaderBackgroundProps {
children: React.ReactElement;
src: string;
}
export function HeaderBackground({ children, src }: HeaderBackgroundProps) {
return (
<HeaderContainer>
<ImageContainer>
<BackgroundImage src={src} />
</ImageContainer>
{children}
</HeaderContainer>
);
}
const HeaderContainer = styled.div`
position: relative;
`;
const ImageContainer = styled.div`
position: absolute;
top: -100px;
bottom: -100px;
left: -30px;
right: -30px;
${media.lg} {
left: -100px;
right: -100px;
}
z-index: -1;
pointer-events: none;
display: flex;
align-items: center;
justify-content: center;
`;
const BackgroundImage = styled.img`
width: 90%;
max-width: calc(min(1100px, 90vw));
max-height: 60%;
${media.md} {
max-height: 100%;
}
`;

View file

@ -0,0 +1,175 @@
import React from 'react';
import { DEFAULT_CUSTOMIZATIONS, Icon as IconType } from './IconList';
import styled from 'styled-components';
import * as AllIcons from 'iconoir-react';
import { ResetButton } from './Button';
import { showNotification } from '../helpers/showNotification';
const HEADER = '<?xml version="1.0" encoding="UTF-8"?>';
function bakeSvg(
svgString: string,
color: string,
strokeWidth: string | number
) {
return (
HEADER +
svgString
.replace(
/stroke="currentColor"/g,
`stroke="currentColor" stroke-width="${strokeWidth}"`
)
.replace(/currentColor/g, color)
);
}
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 />
</IconContainer>
{supportsClipboard ? (
<HoverContainer>
<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>{icon.filename}</Subtitle>
</div>
);
}
const HoverContainer = styled.div<{ supportsCopy?: boolean }>`
position: absolute;
display: ${(props) => (props.supportsCopy ? 'block' : 'none')};
inset: 0;
display: flex;
align-items: stretch;
justify-content: stretch;
flex-direction: column;
border-radius: 12px;
overflow: hidden;
transform: translateZ(0px); // Safari Fix
transition: opacity 0.1s linear;
opacity: 0;
pointer-events: none;
`;
const HoverButton = styled(ResetButton)`
display: flex;
align-items: center;
justify-content: center;
background: var(--light-gray);
border-radius: 0 !important;
transition: background 0.1s linear;
color: var(--black);
font-size: 14px;
line-height: 23px;
font-weight: 700;
text-align: center;
flex: 1;
cursor: pointer;
text-decoration: none;
&:hover,
&:active {
background: var(--gray);
}
`;
const BorderContainer = styled.div<{ iconWidth: number }>`
width: ${(props) => props.iconWidth}px;
box-sizing: border-box;
padding-bottom: 100%;
position: relative;
border: solid 1px var(--light-gray);
border-radius: 12px;
margin-bottom: 10px;
@media (hover: hover) {
&:hover ${HoverContainer} {
opacity: 1;
pointer-events: all;
}
}
`;
const IconContainer = styled.div`
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
`;
const Subtitle = styled.div`
font-size: 11px;
font-weight: 500;
line-height: 14.74px;
color: var(--black-40);
text-align: center;
`;

View file

@ -0,0 +1,200 @@
import React from 'react';
import {
VariableSizeList as List,
ListChildComponentProps,
areEqual,
} from 'react-window';
import { chunk } from 'lodash';
import useResizeObserver from 'use-resize-observer';
import { ICON_SPACE, ICON_WIDTH } from './constants';
import { CategoryRow } from './CategoryRow';
import { IconsRow } from './IconsRow';
import { ReactWindowScroller } from './ReactWindowScroller';
import styled from 'styled-components';
import { IconListEmpty } from './IconListEmpty';
export interface IconListFilters {
search?: string;
}
export interface IconListCustomizations {
size: number;
strokeWidth: number;
hexColor: string;
}
export interface Icon {
filename: string;
category: string;
tags: string[];
iconComponentName: string;
}
export const DEFAULT_CUSTOMIZATIONS: IconListCustomizations = {
size: 24,
strokeWidth: 1.5,
hexColor: '#000000',
};
function normalizeString(s: string) {
return s.toLowerCase().replace(/[!@#$%^&*(),.\][-]/g, '');
}
function filterIcons(allIcons: Icon[], filters: IconListFilters): Icon[] {
if (filters.search) {
return allIcons.filter((icon) => {
const normalSearch = normalizeString(filters.search!);
return (
normalizeString(icon.filename).includes(normalSearch) ||
normalizeString(icon.category).includes(normalSearch) ||
icon.tags.some((tag) => normalizeString(tag).includes(normalSearch))
);
});
} else return allIcons;
}
interface IconCategoryRow {
category: string;
numIcons: number;
}
interface IconIconsRow {
icons: Icon[];
}
type IconRow = IconCategoryRow | IconIconsRow;
function isCategoryRow(iconRow: IconRow): iconRow is IconCategoryRow {
return !!(iconRow as IconCategoryRow).category;
}
function getRowsFromIcons(
filteredIcons: Icon[],
iconsPerRow: number
): IconRow[] {
const categoryGroups: Record<string, Icon[]> = {};
for (const icon of filteredIcons) {
if (!categoryGroups[icon.category]) categoryGroups[icon.category] = [];
categoryGroups[icon.category].push(icon);
}
const result: IconRow[] = [];
const sortedCategories = Object.keys(categoryGroups).sort();
for (const sortedCategory of sortedCategories) {
result.push({
category: sortedCategory,
numIcons: categoryGroups[sortedCategory].length,
});
const iconRows = chunk(categoryGroups[sortedCategory], iconsPerRow);
for (const iconRow of iconRows) {
result.push({ icons: iconRow });
}
}
return result;
}
const ICON_BOTTOM_PADDING = 65;
const HEADER_HEIGHT = 150;
const HEADER_INNER_HEIGHT = 15 + 40;
const HEADER_TOP_PADDING = HEADER_HEIGHT - HEADER_INNER_HEIGHT;
function getItemSize(row: IconRow, iconWidth: number): number {
if (isCategoryRow(row)) {
return HEADER_HEIGHT;
} else {
return iconWidth + ICON_BOTTOM_PADDING;
}
}
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 Container = styled.div`
width: 100%;
margin-top: -${HEADER_TOP_PADDING}px;
> :first-child {
overflow: visible;
> :first-child {
-webkit-overflow-scrolling: touch;
}
}
`;
const Row = React.memo(
({ data, index, style }: ListChildComponentProps<IconRow[]>) => {
const { iconWidth } = React.useContext(IconListContext)!;
const row = data[index];
if (isCategoryRow(row)) {
return (
<CategoryRow
category={row.category}
numIcons={row.numIcons}
style={style}
/>
);
} else {
return <IconsRow icons={row.icons} style={style} iconWidth={iconWidth} />;
}
},
areEqual
);
Row.displayName = 'Row';

View file

@ -0,0 +1,49 @@
import { SpockHandGesture } from 'iconoir-react';
import React from 'react';
import styled from 'styled-components';
import { SUGGEST_ICON } from './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} target={'_blank'} rel={'noreferrer'}>
suggestion on GitHub.
</a>
</Text18>
</Container>
);
}
const Container = styled.div`
margin-top: 90px;
display: flex;
align-items: center;
flex-direction: column;
text-align: center;
`;
const IconContainer = styled.div`
svg {
width: 60px;
height: 60px;
}
margin-bottom: 65px;
color: var(--black);
`;
const Title = styled(Text18)`
font-weight: 700;
margin-bottom: 30px;
color: var(--black);
`;

View file

@ -0,0 +1,28 @@
import React from 'react';
import { ICON_SPACE } from './constants';
import { Icon } from './IconList';
import styled from 'styled-components';
import { Icon as IconC } from './Icon';
export interface IconsRowProps {
icons: Icon[];
style?: any;
iconWidth: number;
}
export function IconsRow({ icons, style, iconWidth }: IconsRowProps) {
return (
<RowContainer style={style}>
{icons.map((icon) => (
<IconC iconWidth={iconWidth} icon={icon} key={icon.filename} />
))}
</RowContainer>
);
}
const RowContainer = styled.div`
display: flex;
align-items: center;
> :not(:last-child) {
margin-right: ${ICON_SPACE}px;
}
`;

View file

@ -0,0 +1,56 @@
import styled from 'styled-components';
const ResetInput = styled.input`
font-family: inherit;
font-size: 100%;
line-height: 1.15;
margin: 0;
text-transform: none;
-webkit-appearance: none;
border: none;
&::-webkit-search-cancel-button {
-webkit-appearance: none;
height: 14px;
width: 14px;
background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='51px' height='51px' stroke-width='2.3' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg' color='%23000000'%3E%3Cpath d='M6.758 17.243L12.001 12m5.243-5.243L12 12m0 0L6.758 6.757M12.001 12l5.243 5.243' stroke='%23000000' stroke-width='2.3' stroke-linecap='round' stroke-linejoin='round'%3E%3C/path%3E%3C/svg%3E");
background-repeat: no-repeat;
background-size: 14px;
}
`;
export const Input = styled(ResetInput)`
min-height: 35px;
background: var(--super-light-gray);
border: 1px solid var(--gray);
border-radius: 10px;
overflow: hidden;
padding: 6px;
text-align: center;
font-size: 13px;
line-height: 21px;
font-weight: 500;
color: var(--black);
`;
export const LargeInput = styled(Input)`
height: 75px;
font-size: 16px;
line-height: 26px;
border-radius: 12px;
padding: 0 23px;
text-align: left;
width: 100%;
box-sizing: border-box;
outline: none;
`;
export const ColorInput = styled(Input)`
padding: 0px;
border: none;
cursor: pointer;
&::-webkit-color-swatch,
&::-moz-color-swatch {
border: none;
}
`;

View file

@ -0,0 +1,12 @@
import React from 'react';
import { GA } from './GA';
export interface LayoutProps {}
export function Layout({ children }: React.PropsWithChildren<LayoutProps>) {
return (
<div>
<GA />
{children}
</div>
);
}

View file

@ -0,0 +1,24 @@
import React from 'react';
import { MDXRemote as CoreMDXRemote, MDXRemoteProps } from 'next-mdx-remote';
import { SuggestLibrary } from './SuggestLibrary';
import { Pre, Body, H1, H2, H3, Code } from './Typography';
import { Table } from './Table';
export function MDXRemote(props: MDXRemoteProps) {
return (
<CoreMDXRemote
{...props}
components={{
...props.components,
pre: Pre,
blockquote: Code,
p: Body,
h1: H1,
h2: H2,
h3: H3,
table: Table,
SuggestLibrary,
}}
/>
);
}

View file

@ -0,0 +1,71 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
import React from 'react';
import styled from 'styled-components';
import { media } from './responsive';
import { Text15 } from './Typography';
export interface NavigationItemProps {
href: string;
children: React.ReactElement | string;
style?: any;
}
export function NavigationItem({ href, children, style }: NavigationItemProps) {
const router = useRouter();
return (
<Link href={href} passHref>
<NavigationItemContainer
as={'a'}
isActive={
href.slice(1)
? router.asPath.slice(1).startsWith(href.slice(1))
: router.asPath === href
}
style={style}
>
{children}
</NavigationItemContainer>
</Link>
);
}
export const NavigationItemContainer = styled(Text15)<{ isActive?: boolean }>`
font-weight: 700;
font-size: 18px;
line-height: 28px;
text-decoration: none;
white-space: nowrap;
padding: 25px;
color: var(--black);
text-align: center;
width: 100%;
box-sizing: border-box;
position: relative;
z-index: 2;
&:not(:last-child) {
border-bottom: solid 1px var(--light-gray);
}
${media.lg} {
font-size: 15px;
line-height: 20px;
font-weight: 500;
padding: 0;
color: var(--black-60);
width: auto;
border-bottom: none !important;
${(props) => (props.isActive ? '&' : '&.noop')} {
color: var(--white);
&::before {
position: absolute;
z-index: -1;
content: '';
display: block;
top: -18px;
bottom: -18px;
left: -24px;
right: -24px;
background: var(--black);
}
}
}
`;

View file

@ -0,0 +1,155 @@
import React from 'react';
import styled from 'styled-components';
import { PraiseItem } from './PraiseItem';
import { media } from './responsive';
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.png'}
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 the 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;
padding: 0 30px;
scroll-snap-type: x mandatory;
overflow-x: scroll;
scrollbar-width: none;
display: flex;
align-items: flex-start;
flex-direction: row;
> :not(:last-child) {
margin-right: 20px;
}
&::-webkit-scrollbar {
display: none;
}
> :last-child {
padding-right: 30px;
}
${media.xl} {
justify-content: center;
margin: 0;
padding: 0;
overflow-x: visible;
> :not(:last-child) {
margin-right: 48px;
}
> :last-child {
padding-right: 0;
}
}
`;
const Indicator = styled.div`
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--gray);
transition: background 0.25s linear;
&.active {
background: var(--black);
}
`;
const IndicatorContainer = styled.div`
margin: 40px auto 0 auto;
display: flex;
align-items: center;
justify-content: center;
> :not(:last-child) {
margin-right: 14px;
}
${media.xl} {
display: none;
}
`;

View file

@ -0,0 +1,65 @@
import React from 'react';
import styled from 'styled-components';
import { media } from './responsive';
import { Text14, Text18 } from './Typography';
export interface PraiseItemProps {
name: string;
position: string;
description: string | React.ReactElement;
logoUrl: string;
logoLink: string;
logoAlt: string;
imageUrl: string;
}
export function PraiseItem({
name,
position,
description,
logoUrl,
logoLink,
logoAlt,
imageUrl,
}: PraiseItemProps) {
return (
<Container>
<AuthorImage src={imageUrl} alt={`Picture of ${name}`} />
<div>
<Header>{name}</Header>
<Text14>{position}</Text14>
<Body>{description}</Body>
<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

@ -0,0 +1,117 @@
// From: https://github.com/FedericoDiRosa/react-window-scroller/blob/master/src/index.jsx
// Modified to remove scrollTo callback to support momentum scroll on iOS. We don't need it
// in this implementation anyway.
import { throttle } from 'lodash';
import React, { useRef, useEffect, useCallback } from 'react';
import { GridProps, ListProps } from 'react-window';
function isHtmlElement(
element: HTMLElement | typeof window
): element is HTMLElement {
return (element as HTMLElement).scrollTop !== undefined;
}
interface PositionKey {
x: string;
y: string;
}
const windowScrollPositionKey: PositionKey = {
y: 'pageYOffset',
x: 'pageXOffset',
};
const documentScrollPositionKey: PositionKey = {
y: 'scrollTop',
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;
interface ChildOpts<Props extends ListProps | GridProps> {
ref: React.MutableRefObject<any>;
outerRef: React.MutableRefObject<any>;
style: object;
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;
}
export function ReactWindowScroller<
Props extends ListProps | GridProps = ListProps
>({
children,
throttleTime = 10,
isGrid = false,
}: ReactWindowScrollerProps<Props>) {
const ref = useRef<any>();
const outerRef = useRef<HTMLElement>();
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)
? targetElement.scrollTop
: targetElement.scrollY);
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);
}, throttleTime);
targetElement.addEventListener('scroll', handleWindowScroll);
return () => {
handleWindowScroll.cancel();
targetElement.removeEventListener('scroll', handleWindowScroll);
};
}, [isGrid, targetElement]);
const onScroll = useCallback(() => {
// We are purposefully doing nothing here in order to support momentum scroll on iOS.
}, [isGrid, targetElement]);
React.useEffect(() => {
// We have to get rid of the scroll handlers here, because they will cause the list
// to go blank whenever adjusting the number of items.
ref.current._onScrollVertical = () => {};
ref.current._onScrollHorizontal = () => {};
}, [outerRef]);
return children({
ref,
outerRef,
style: {
width: isGrid ? 'auto' : '100%',
height: '100%',
display: 'inline-block',
overflow: 'hidden',
zIndex: 3,
},
onScroll,
});
}

View file

@ -0,0 +1,50 @@
import { ArrowRight } from 'iconoir-react';
import React from 'react';
import styled from 'styled-components';
import {
DonateButton,
DonateContainer,
DonateHeader,
DonateRight,
} from '../pages/support';
import { FILE_PREFIX } from './constants';
import { Text18 } from './Typography';
export interface ReadOnGitHubProps {
path: string;
}
export function ReadOnGitHub({ path }: ReadOnGitHubProps) {
return (
<DonateContainer style={{ marginTop: 88 }}>
<div>
<DonateHeader>Read it on GitHub</DonateHeader>
<Text18>
If you prefer, you can take a look at our documentation on our Github
repository.
</Text18>
</div>
<DonateRight>
<a
href={`${FILE_PREFIX}/${path.startsWith('/') ? path.slice(1) : path}`}
target={'_blank'}
rel={'noreferrer'}
>
<DonateIconButton>
<ArrowRight />
</DonateIconButton>
</a>
</DonateRight>
</DonateContainer>
);
}
export const DonateIconButton = styled(DonateButton)`
border-radius: 50%;
width: 50px;
height: 50px;
padding: 0;
justify-content: center;
&::after {
border-radius: 50%;
}
`;

View file

@ -0,0 +1,58 @@
import React from 'react';
import Head from 'next/head';
const TITLE_SUFFIX = 'Iconoir | Free Icons';
export interface SEOProps {
title?: string;
}
export function SEO({ title }: SEOProps) {
const pageTitle = title ? `${title} | ${TITLE_SUFFIX}` : TITLE_SUFFIX;
return (
<Head>
<title>{pageTitle}</title>
<meta
name="description"
content="The Simple and Definitive hand-crafted SVG Icons library."
/>
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta property="og:title" content={pageTitle} />
<meta property="og:site_name" content="Iconoir" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://iconoir.com" />
<meta
property="og:image"
content="https://iconoir.com/images/iconoir-brand-03.png"
/>
<meta
property="og:description"
content="The Simple and Definitive hand-crafted SVG Icons Library."
/>
<meta property="og:image:width" content="1270" />
<meta property="og:image:height" content="760" />
<meta name="twitter:title" content={pageTitle} />
<meta name="twitter:domain" content="iconoir.com" />
<meta name="twitter:url" content="https://iconoir.com" />
<meta
name="twitter:image"
content="https://iconoir.com/images/iconoir-brand-03.png"
/>
<meta
name="twitter:image:secure_url"
content="https://iconoir.com/images/iconoir-brand-03.png"
/>
<meta
name="twitter:image:alt"
content="The Simple and Definitive hand-crafted SVG Icons Library."
/>
<meta
name="twitter:description"
content="The Simple and Definitive hand-crafted SVG Icons Library."
/>
<meta name="twitter:card" content="summary_large_image" />
</Head>
);
}

View file

@ -0,0 +1,126 @@
import React from 'react';
import { useSlider, useSliderThumb } from '@react-aria/slider';
import { SliderState, useSliderState } from '@react-stately/slider';
import { useFocusRing } from '@react-aria/focus';
import { VisuallyHidden } from '@react-aria/visually-hidden';
import { mergeProps } from '@react-aria/utils';
import { useNumberFormatter } from '@react-aria/i18n';
import { SliderProps as ReactSliderProps } from '@react-types/slider';
import { NumberFormatOptions } from '@internationalized/number';
import styled from 'styled-components';
import { Text13 } from './Typography';
export interface SliderProps extends ReactSliderProps<number[]> {
formatOptions?: NumberFormatOptions;
}
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(
props,
state,
trackRef
);
return (
<SliderContainer {...groupProps}>
<SliderHeader>
{props.label && (
<Text13 as={'label'} {...labelProps}>
{props.label}
</Text13>
)}
<Output as={'output'} {...outputProps}>
{state.getThumbValueLabel(0)}
</Output>
</SliderHeader>
<Track {...trackProps} ref={trackRef}>
<TrackBackground />
<Thumb index={0} state={state} trackRef={trackRef} />
</Track>
</SliderContainer>
);
}
interface ThumbProps {
state: SliderState;
trackRef: React.RefObject<HTMLElement>;
index: number;
}
function Thumb({ state, trackRef, index }: ThumbProps) {
let inputRef = React.useRef(null);
let { thumbProps, inputProps } = useSliderThumb(
{
index,
trackRef,
inputRef,
},
state
);
let { focusProps, isFocusVisible } = useFocusRing();
return (
<ThumbContainer
style={{
left: `${state.getThumbPercent(index) * 100}%`,
}}
>
<ThumbInner
{...thumbProps}
style={{
backgroundColor: isFocusVisible
? 'var(--accent)'
: state.isThumbDragging(index)
? 'var(--accent)'
: 'var(--black)',
}}
>
<VisuallyHidden>
<input ref={inputRef} {...mergeProps(inputProps, focusProps)} />
</VisuallyHidden>
</ThumbInner>
</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: 10px;
`;
const Track = styled.div`
position: relative;
height: 30px;
width: 100%;
`;
const TrackBackground = styled.div`
position: absolute;
height: 3px;
top: 13px;
background: #f4f3f4;
width: 100%;
`;
const ThumbContainer = styled.div`
position: absolute;
top: 4px;
transform: translateX(-50%);
`;
const ThumbInner = styled.div`
width: 20px;
height: 20px;
border-radius: 50%;
cursor: pointer;
`;

View file

@ -0,0 +1,52 @@
import React from 'react';
import styled from 'styled-components';
import { Text15 } from './Typography';
import { media } from './responsive';
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;
line-height: 62px;
color: var(--black);
${media.md} {
font-size: 50px;
line-height: 82px;
}
`;
const StatContainer = styled.div`
text-align: center;
width: 45%;
margin-bottom: 55px;
${media.md} {
width: 205px;
margin-bottom: 0;
}
`;
export const StatsContainer = styled.div`
display: flex;
align-items: flex-start;
justify-content: space-between;
max-width: 1200px;
margin: 40px auto 5px auto;
flex-wrap: wrap;
> :not(:last-child) {
margin-right: 8px;
}
${media.md} {
margin: 64px auto 120px auto;
}
`;

View file

@ -0,0 +1,28 @@
import { ArrowRight } from 'iconoir-react';
import React from 'react';
import { DonateContainer, DonateHeader, DonateRight } from '../pages/support';
import { SUGGEST_LIBRARY } from './constants';
import { DonateIconButton } from './ReadOnGitHub';
import { Text18 } from './Typography';
export function SuggestLibrary() {
return (
<DonateContainer style={{ marginTop: 88 }}>
<div>
<DonateHeader>More?</DonateHeader>
<Text18>
If you would like to use Iconoir on a different library or if you want
to contribute to support a new library, open a feature request in our
official GitHub repository.
</Text18>
</div>
<DonateRight>
<a href={SUGGEST_LIBRARY} target={'_blank'} rel={'noreferrer'}>
<DonateIconButton>
<ArrowRight />
</DonateIconButton>
</a>
</DonateRight>
</DonateContainer>
);
}

View file

@ -0,0 +1,29 @@
import styled from 'styled-components';
import { media } from './responsive';
export const Table = styled.table`
width: 100%;
border-collapse: collapse;
margin: 24px 0;
td,
th {
border: solid 2px var(--light-gray);
font-size: 16px;
line-height: 25.6px;
padding: 12px 20px;
color: var(--black-60);
${media.lg} {
padding: 12px 30px 12px 60px;
}
}
thead td,
thead th {
color: var(--black);
background: var(--lighter-gray);
font-weight: 700;
text-align: left;
}
tbody tr:nth-child(even) td {
background: var(--lighter-gray);
}
`;

View file

@ -0,0 +1,182 @@
import React from 'react';
import styled from 'styled-components';
import { showNotification } from '../helpers/showNotification';
import { Button } from './Button';
import { media } from './responsive';
export const Text15 = styled.div`
font-size: 15px;
line-height: 24px;
color: var(--black-60);
`;
export const Text14 = styled.div`
font-size: 14px;
line-height: 22px;
color: var(--black);
`;
export const Text13 = styled.div`
font-size: 13px;
color: var(--dark-gray);
line-height: 21px;
font-weight: 500;
`;
export const Text18 = styled.div`
font-size: 16px;
line-height: 25px;
color: var(--black-60);
${media.md} {
font-size: 18px;
line-height: 29px;
}
`;
export const Heading2 = styled.h2`
font-size: 24px;
line-height: 40px;
color: var(--black);
letter-spacing: -0.02em;
margin: 24px 0;
font-weight: 700;
`;
export const Code = styled.div`
background: var(--light-gray) !important;
border-radius: 0 !important;
padding: 26px 32px;
color: var(--black-60);
font-size: 16px;
line-height: 26px;
font-weight: 400;
margin: 24px 0;
font-family: var(--code-family);
> :first-child {
margin-top: 0;
}
> :last-child {
margin-bottom: 0 !important;
}
span,
code,
p,
strong {
font-family: var(--code-family) !important;
font-size: 16px !important;
background: transparent !important;
}
strong {
font-weight: 700;
}
`;
export const H1 = styled.h1`
font-size: 50px;
font-weight: 700;
line-height: 49px;
letter-spacing: -0.02em;
color: var(--black);
margin-bottom: 40px;
${media.lg} {
font-size: 90px;
line-height: 81px;
letter-spacing: -0.05em;
}
`;
export const H2 = styled.h2`
font-size: 24px;
line-height: 40px;
font-weight: 700;
color: var(--black);
margin: 24px 0;
`;
export const H3 = styled.h3`
font-size: 20px;
line-height: 36px;
font-weight: 700;
color: var(--black);
margin: 24px 0;
`;
export const Body = styled.p`
font-size: 18px;
color: var(--black-60);
line-height: 30px;
margin: 24px 0;
`;
export const CodeElement = styled.code`
display: inline-block;
padding: 0 4px;
color: var(--black);
`;
const PreContainer = styled(Code)`
position: relative;
padding: 0;
* {
white-space: pre;
}
> pre {
padding: 26px 32px;
box-sizing: border-box;
overflow-x: auto;
}
`;
const CopyContainer = styled.div`
position: absolute;
top: 16px;
right: 23px;
`;
export const CopyButton = styled(Button)`
text-transform: uppercase;
background: var(--gray);
height: 30px;
padding: 0 12px;
font-size: 11px;
letter-spacing: 0.12em;
line-height: 17.6px;
font-weight: 700;
color: var(--black);
transition: background 0.1s linear, color 0.1s linear;
font-family: var(--font-family) !important;
&:hover {
background: var(--black) !important;
color: var(--white);
}
`;
export function Pre({ children, ...props }: React.PropsWithChildren<any>) {
const containerRef = React.useRef<HTMLPreElement>(null);
const [supportsClipboard, setSupportsClipboard] = React.useState(false);
React.useEffect(() => {
setSupportsClipboard(
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}
</PreContainer>
);
}

View file

@ -0,0 +1,31 @@
export const REPO = 'iconoir-icons/iconoir';
export const GITHUB = `https://github.com/${REPO}`;
export const SUGGEST_ICON =
'https://github.com/iconoir-icons/iconoir/issues/new?assignees=lucaburgio&labels=icon+request&template=icon_request.md&title=%5BICON%5D';
export const SUPPORT_LINK = 'https://opencollective.com/iconoir/donate';
export const LICENSE_LINK =
'https://github.com/iconoir-icons/iconoir/blob/master/LICENSE';
export const ICON_WIDTH = 140;
export const ICON_SPACE = 20;
export const SUGGEST_LIBRARY =
'https://github.com/iconoir-icons/iconoir/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=%5BFEAT%5D';
export const FEEDBACK_LINK = 'https://forms.gle/3HvwVYow7D6T8zad7';
export const FILE_PREFIX =
'https://github.com/iconoir-icons/iconoir/tree/master';
export const LIBRARY_LINKS = {
React:
'https://github.com/iconoir-icons/iconoir/tree/master/packages/iconoir-react',
ReactNative:
'https://github.com/iconoir-icons/iconoir/tree/master/packages/iconoir-react-native',
Flutter:
'https://github.com/iconoir-icons/iconoir/tree/master/packages/iconoir-flutter',
Framer: 'https://framer.com/',
Figma: 'https://www.figma.com/community/file/983248991460488027/Iconoir-Pack',
};
export const AUTHOR_LINKS = {
Luca: 'https://twitter.com/burgioluca',
Sam: 'https://twitter.com/therealsammarks',
};

View file

@ -0,0 +1,15 @@
export const BREAKPOINTS = {
xs: 500,
sm: 800,
md: 1000,
lg: 1100,
xl: 1400,
};
export const media = {
xs: `@media (min-width: ${BREAKPOINTS.xs}px)`,
sm: `@media (min-width: ${BREAKPOINTS.sm}px)`,
md: `@media (min-width: ${BREAKPOINTS.md}px)`,
lg: `@media (min-width: ${BREAKPOINTS.lg}px)`,
xl: `@media (min-width: ${BREAKPOINTS.xl}px)`,
};

View file

@ -0,0 +1,36 @@
import React from 'react';
import { DEFAULT_CUSTOMIZATIONS, IconListCustomizations } 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(
DEFAULT_CUSTOMIZATIONS
);
React.useEffect(() => {
const localStorageValue = localStorage.getItem(CUSTOMIZATIONS_KEY);
if (localStorageValue) {
try {
const parsedValue = JSON.parse(localStorageValue);
_setCustomizations({ ...DEFAULT_CUSTOMIZATIONS, ...parsedValue });
} catch {
// Do nothing...
}
}
}, []);
return [
customizations,
(newCustomizations) => {
localStorage.setItem(
CUSTOMIZATIONS_KEY,
JSON.stringify(newCustomizations)
);
_setCustomizations(newCustomizations);
},
];
}

View file

@ -0,0 +1,73 @@
const fs = require('fs');
const path = require('path');
const previewPath = process.argv[2];
const resultPath = 'icons.csv';
const categoryMap = {
actions: 'Actions',
activities: 'Activities',
analytics: 'Analytics',
animations: 'Animations',
audio: 'Audio',
buildings: 'Buildings',
clothing: 'Clothing',
cloud: 'Cloud',
communication: 'Communication',
'communication-1': 'Communication',
connectivity: 'Connectivity',
database: 'Database',
designtools: 'Design Tools',
development: 'Development',
devices: 'Devices',
docs: 'Docs',
editor: 'Editor',
'3deditor': '3D Editor',
emojis: 'Emojis',
finance: 'Finance',
food: 'Food',
gaming: 'Gaming',
gestures: 'Gestures',
git: 'Git',
health: 'Health',
home: 'Home',
identity: 'Identity',
layout: 'Layout',
maps: 'Maps',
music: 'Music',
nature: 'Nature',
navigation: 'Navigation',
organization: 'Organization',
other: 'Other',
photosvideos: 'Photos and Videos',
security: 'Security',
shapes: 'Shapes',
shopping: 'Shopping',
science: 'Science',
social: 'Social',
system: 'System',
transport: 'Transport',
users: 'Users',
weather: 'Weather',
};
const allIcons = fs.readdirSync(previewPath);
const resultLines = ['filename,category,tags'];
for (const filename of allIcons) {
const [icon, category] = path
.basename(filename)
.replace(path.extname(filename), '')
.split('@');
if (!icon || !category) {
console.error('invalid filename %s', filename);
process.exit(1);
}
const mappedCategory = categoryMap[category];
if (!mappedCategory) {
console.error('category %s does not exist', category);
process.exit(1);
}
resultLines.push(`"${icon}","${mappedCategory}",`);
}
console.info('found %d mappings', resultLines.length - 1);
fs.writeFileSync(resultPath, resultLines.join('\n'));

View file

@ -0,0 +1,9 @@
export function showNotification(message: string) {
const element = document.createElement('div');
element.classList.add('bottom-notification');
element.innerText = message;
document.body.appendChild(element);
setTimeout(() => {
element.remove();
}, 3000);
}

1149
iconoir.com/icons.csv Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,8 @@
import fs from 'fs';
export function getHeaderProps() {
const packageJson = JSON.parse(fs.readFileSync('../package.json').toString());
return {
currentVersion: `v${packageJson.version}`,
};
}

View file

@ -0,0 +1,42 @@
import { Icon } from '../components/IconList';
import csv from 'csvtojson';
import { incompatibleNames } from '../../constants';
import Case from 'case';
import * as AllIcons from 'iconoir-react';
const ICONS_PATH = 'icons.csv';
const TAG_SEPARATOR = '|';
const typedIncompatibleNames = incompatibleNames as Record<string, string>;
function getIconComponentName(filename: string) {
const dstFileName =
filename in typedIncompatibleNames
? typedIncompatibleNames[filename]
: filename;
return Case.pascal(dstFileName);
}
export async function getAllIcons(): Promise<Icon[]> {
const rows = await csv().fromFile(ICONS_PATH);
return rows.map<Icon>((row) => {
const iconComponentName = getIconComponentName(row.filename);
// Convert to lowercase to solve for differences in how the names are calculated.
const matchingKey = Object.keys(AllIcons).find(
(k) =>
k.toLowerCase() === iconComponentName.toLowerCase() ||
k.toLowerCase() === `svg${iconComponentName.toLowerCase()}`
);
if (!matchingKey)
throw new Error(
`Cannot find icon '${iconComponentName}' in iconoir-react.`
);
return {
filename: row.filename,
category: row.category,
tags:
row.tags?.split(TAG_SEPARATOR).map((item: string) => item.trim()) || [],
iconComponentName: matchingKey,
};
});
}

5
iconoir.com/next-env.d.ts vendored Executable file
View file

@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

20
iconoir.com/next.config.js Executable file
View file

@ -0,0 +1,20 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
compiler: {
styledComponents: true,
},
experimental: {
images: {
unoptimized: true,
},
},
async redirects() {
return [
{ source: '/docs', destination: '/docs/introduction', permanent: true }
]
}
};
module.exports = nextConfig;

52
iconoir.com/package.json Normal file
View file

@ -0,0 +1,52 @@
{
"name": "iconoir.com",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"export": "next export",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@react-aria/focus": "^3.6.1",
"@react-aria/i18n": "^3.4.1",
"@react-aria/slider": "^3.1.1",
"@react-aria/utils": "^3.13.1",
"@react-aria/visually-hidden": "^3.3.1",
"@react-stately/slider": "^3.1.1",
"animejs": "^3.2.1",
"case": "^1.6.3",
"iconoir-react": "workspace:*",
"lodash": "^4.17.21",
"moment": "^2.29.4",
"next": "12.2.2",
"next-mdx-remote": "^4.1.0",
"numbro": "^2.3.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-window": "^1.8.7",
"remark-gfm": "^3.0.1",
"remark-prism": "^1.3.6",
"styled-components": "^5.3.5",
"use-resize-observer": "^9.0.2"
},
"devDependencies": {
"@internationalized/number": "^3.1.1",
"@react-types/slider": "^3.1.1",
"@types/animejs": "^3.1.5",
"@types/lodash": "^4.14.182",
"@types/node": "18.0.5",
"@types/react": "^17.0.29",
"@types/react-dom": "^18.0.6",
"@types/react-window": "^1.8.5",
"@types/styled-components": "^5.1.25",
"axios": "^0.27.2",
"csvtojson": "^2.0.10",
"download-stats": "^0.3.4",
"eslint": "^8.0.0",
"eslint-config-next": "12.2.2",
"typescript": "^4.4.4"
}
}

9
iconoir.com/pages/_app.tsx Executable file
View file

@ -0,0 +1,9 @@
import type { AppProps } from 'next/app';
import '../styles/prism-theme.css';
import '../styles/theme.css';
function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}
export default MyApp;

View file

@ -0,0 +1,61 @@
import Document, {
Html,
Head,
Main,
NextScript,
DocumentContext,
DocumentInitialProps,
} from 'next/document';
import { ServerStyleSheet } from 'styled-components';
export default class IconoirDocument extends Document {
static async getInitialProps(
ctx: DocumentContext
): Promise<DocumentInitialProps> {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
};
} finally {
sheet.seal();
}
}
render() {
return (
<Html>
<Head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
rel="preconnect"
href="https://fonts.gstatic.com"
crossOrigin={'anonymous'}
/>
<link
href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&family=IBM+Plex+Mono:wght@400;700&display=swap"
rel="stylesheet"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}

View file

@ -0,0 +1,265 @@
import { GetStaticPathsResult, GetStaticPropsContext } from 'next';
import { serialize } from 'next-mdx-remote/serialize';
import { MDXRemoteSerializeResult } from 'next-mdx-remote';
import { MDXRemote } from '../../components/MDXRemote';
import fs from 'fs';
import { Layout } from '../../components/Layout';
import { SEO } from '../../components/SEO';
import { Header, HeaderProps } from '../../components/Header';
import { getHeaderProps } from '../../lib/getHeaderProps';
import styled from 'styled-components';
import { media } from '../../components/responsive';
import { Footer } from '../../components/Footer';
import path from 'path';
import { ParsedUrlQuery } from 'querystring';
import {
DocumentationNavigation,
DocumentationNavigationProps,
} from '../../components/DocumentationNavigation';
import remarkGfm from 'remark-gfm';
import { ReadOnGitHub } from '../../components/ReadOnGitHub';
interface DocumentationPageProps extends HeaderProps {
source: MDXRemoteSerializeResult;
title: string;
navigationItem: DocumentationItem;
navigationProps: DocumentationNavigationProps;
}
export default function DocumentationPage({
source,
title,
navigationItem,
navigationProps,
...headerProps
}: DocumentationPageProps) {
return (
<Layout>
<SEO title={title} />
<Header {...headerProps} currentVersionColor={'var(--green)'} />
<Container>
<NavigationContainer>
<DocumentationNavigation {...navigationProps} />
</NavigationContainer>
<ContentContainer>
<InnerContentContainer>
<MDXRemote {...source} />
{navigationItem.noReadOnGithub ||
!navigationItem.filepath ? null : (
<ReadOnGitHub path={navigationItem.filepath} />
)}
</InnerContentContainer>
</ContentContainer>
</Container>
<Footer />
</Layout>
);
}
export const Container = styled.div`
display: flex;
align-items: stretch;
flex-direction: column;
margin: 30px -30px 0 -30px;
${media.lg} {
flex-direction: row;
margin: 30px 0 0 -50px;
}
`;
export const NavigationContainer = styled.div`
${media.lg} {
width: 286px;
margin-right: 30px;
border-right: solid 2px var(--light-gray);
}
`;
export const ContentContainer = styled.div`
flex: 1;
`;
export const InnerContentContainer = styled.div`
margin: 0 30px;
max-width: 1168px;
${media.lg} {
margin: 0 auto;
}
img {
max-width: 100%;
}
> div {
margin: 24px 0;
> a img {
margin: 0 5px;
}
}
`;
export interface DocumentationItem {
path: string;
filepath?: string;
children?: DocumentationItem[];
title: string;
label?: string;
noReadOnGithub?: boolean;
skip?: boolean;
}
export function getDocumentationStructure(): DocumentationItem[] {
return [
{
path: '',
title: 'To Start',
children: [
{ path: 'introduction', filepath: 'README.md', title: 'Introduction' },
{
path: 'contributing',
filepath: 'CONTRIBUTING.md',
title: 'How to contribute',
},
],
},
{
path: 'libraries',
title: 'Libraries',
children: [
{
path: 'iconoir-react',
filepath: 'packages/iconoir-react/README.md',
title: 'React',
noReadOnGithub: true,
},
{
path: 'iconoir-react-native',
filepath: 'packages/iconoir-react-native/README.md',
title: 'React Native',
noReadOnGithub: true,
},
{
path: 'flutter',
filepath: 'packages/iconoir-flutter/README.md',
title: 'Flutter',
label: 'New',
noReadOnGithub: true,
},
{
path: 'framer',
filepath: 'docs/framer.md',
title: 'Framer',
noReadOnGithub: true,
},
{
path: 'css',
filepath: 'css/README.md',
title: 'CSS',
noReadOnGithub: true,
},
],
},
{
path: '',
title: 'Releases',
children: [
{
path: 'changelog',
filepath: 'CHANGELOG.md',
title: 'Changelog',
skip: true,
},
],
},
];
}
function structureItemsToPaths(
items: DocumentationItem[],
slugPrefix?: string[]
): ParsedUrlQuery[] {
const result: ParsedUrlQuery[] = [];
const filteredItems = items.filter((item) => !item.skip);
for (const item of filteredItems) {
if (item.filepath && item.path) {
result.push({ slug: [...(slugPrefix || []), item.path].filter(Boolean) });
}
if (item.children?.length) {
result.push(
...structureItemsToPaths(
item.children,
[...(slugPrefix || []), item.path].filter(Boolean)
)
);
}
}
return result;
}
function flattenItems(
items: DocumentationItem[],
prefix?: string[]
): DocumentationItem[] {
return items.reduce<DocumentationItem[]>((acc, item) => {
return [
...acc,
...(item.filepath
? [
{
...item,
path: [...(prefix || []), item.path].filter(Boolean).join('/'),
},
]
: []),
...(item.children?.length
? flattenItems(
item.children,
[...(prefix || []), item.path].filter(Boolean)
)
: []),
];
}, []);
}
function itemFromSlug(
items: DocumentationItem[],
slug: string[]
): DocumentationItem {
const flatItems = flattenItems(items);
const joinedSlug = slug.filter(Boolean).join('/');
const item = flatItems.find((flatItem) => flatItem.path === joinedSlug);
if (!item)
throw new Error(`Cannot find item matching slug '${slug.join('/')}'`);
return item;
}
export async function getStaticPaths(): Promise<GetStaticPathsResult> {
const structure = getDocumentationStructure();
return {
paths: structureItemsToPaths(structure).map((p) => ({ params: p })),
fallback: false,
};
}
function cleanSource(source: string): string {
return source
.replace(/src="/g, 'src="/')
.replace(/src="\/http/g, 'src="http');
}
export async function getStaticProps(context: GetStaticPropsContext) {
const items = getDocumentationStructure();
const navigationItem = itemFromSlug(items, context.params!.slug as string[]);
const repositoryRoot = path.join(process.cwd(), '..');
const filepath = path.join(repositoryRoot, navigationItem.filepath!);
const source = cleanSource(fs.readFileSync(filepath).toString());
const mdxSource = await serialize(source, {
parseFrontmatter: true,
mdxOptions: {
remarkPlugins: [require('remark-prism'), remarkGfm],
},
});
return {
props: {
source: mdxSource,
...getHeaderProps(),
title: context.params!.title?.toString() || null,
navigationItem,
navigationProps: { documentationItems: items },
},
};
}

View file

@ -0,0 +1,85 @@
import axios from 'axios';
import React from 'react';
import {
ChangelogEntry,
ChangelogEntryProps,
} from '../../components/ChangelogEntry';
import { REPO } from '../../components/constants';
import {
DocumentationNavigation,
DocumentationNavigationProps,
} from '../../components/DocumentationNavigation';
import { Footer } from '../../components/Footer';
import { Header, HeaderProps } from '../../components/Header';
import { Layout } from '../../components/Layout';
import { SEO } from '../../components/SEO';
import { H1 } from '../../components/Typography';
import remarkGfm from 'remark-gfm';
import { getHeaderProps } from '../../lib/getHeaderProps';
import {
Container,
ContentContainer,
getDocumentationStructure,
InnerContentContainer,
NavigationContainer,
} from './[...slug]';
import { serialize } from 'next-mdx-remote/serialize';
import { ReadOnGitHub } from '../../components/ReadOnGitHub';
export interface ChangelogProps extends HeaderProps {
documentationProps: DocumentationNavigationProps;
entries: ChangelogEntryProps[];
}
export default function Changelog({
documentationProps,
entries,
...headerProps
}: ChangelogProps) {
return (
<Layout>
<SEO title={'Changelog'} />
<Header {...headerProps} currentVersionColor={'var(--green)'} />
<Container>
<NavigationContainer>
<DocumentationNavigation {...documentationProps} />
</NavigationContainer>
<ContentContainer>
<InnerContentContainer>
<H1 style={{ marginBottom: 72 }}>Changelog</H1>
{entries.map((entry) => (
<ChangelogEntry key={entry.name} {...entry} />
))}
<ReadOnGitHub path={'../../releases'} />
</InnerContentContainer>
</ContentContainer>
</Container>
<Footer />
</Layout>
);
}
export async function getStaticProps() {
const apiResult = await axios.get(
`https://api.github.com/repos/${REPO}/releases`
);
const entryData = apiResult.data || [];
const entries: ChangelogEntryProps[] = [];
for (const entry of entryData) {
entries.push({
...entry,
body: await serialize(entry.body, {
mdxOptions: {
remarkPlugins: [require('remark-prism'), remarkGfm],
},
}),
});
}
const items = getDocumentationStructure();
return {
props: {
entries,
...getHeaderProps(),
documentationProps: { documentationItems: items },
},
};
}

View file

@ -0,0 +1,10 @@
import React from 'react';
import { useRouter } from 'next/router';
export default function Index() {
const router = useRouter();
React.useEffect(() => {
router.replace('/docs/introduction');
}, []);
return null;
}

175
iconoir.com/pages/index.tsx Executable file
View file

@ -0,0 +1,175 @@
import { PeaceHand } from 'iconoir-react';
import type { NextPage } from 'next';
import styled from 'styled-components';
import { AvailableFor } from '../components/AvailableFor';
import { LargeButton } from '../components/Button';
import { REPO, SUPPORT_LINK } from '../components/constants';
import { Explore } from '../components/Explore';
import { Header } from '../components/Header';
import { HeaderBackground } from '../components/HeaderBackground';
import { Icon } from '../components/IconList';
import { SEO } from '../components/SEO';
import { Stat, StatsContainer } from '../components/Stats';
import { Text18 } from '../components/Typography';
import { getAllIcons } from '../lib/getIcons';
import axios from 'axios';
import numbro from 'numbro';
// @ts-ignore no types
import * as downloadStats from 'download-stats';
import { media } from '../components/responsive';
import { Praise } from '../components/Praise';
import { Footer } from '../components/Footer';
import { getHeaderProps } from '../lib/getHeaderProps';
import { Layout } from '../components/Layout';
interface HomeProps {
allIcons: Icon[];
currentVersion: string;
numStars: number;
numDownloads: number;
}
const Home: NextPage<HomeProps> = ({
allIcons,
currentVersion,
numStars,
numDownloads,
}) => {
return (
<Layout>
<SEO />
<Header currentVersion={currentVersion} />
<HeaderBackground src={'/home-background.svg'}>
<HeroText>Your new default library.</HeroText>
</HeaderBackground>
<HeroDescription>
Iconoir is one of the biggest open source icons libraries. No premium
icons, no email sign-up, no newsletters. Icons available in SVG format,
Font, React and React Native libraries, Figma and Framer.
</HeroDescription>
<StatsContainer>
<Stat
value={allIcons.length.toString()}
description={
'icons available in this very moment, and theyre growing fast!'
}
/>
<Stat
value={'100%'}
description={
'free icons. Iconoir is open source and were ready for your help.'
}
/>
<Stat
value={numbro(numDownloads).format({
average: true,
mantissa: 1,
})}
description={
'downloads/month on React only. Iconoir also supports React Native, Flutter and CSS.'
}
/>
<Stat
value={numbro(numStars).format({
average: true,
mantissa: 1,
})}
description={
'people who starred the project on Github. Show your support and be one of them.'
}
/>
</StatsContainer>
<AvailableFor />
<SupportContainer>
<LargeButton
as={'a'}
href={SUPPORT_LINK}
target={'_blank'}
rel={'noreferrer'}
>
<PeaceHand width={'1em'} height={'1em'} />
<span>Support the project</span>
</LargeButton>
<Text18>
Iconoir is an open source project built with consistent passion and
dedication. If you consider this library valuable for you and your
projects, support Iconoir with a donation to help us sustain costs and
development time.
</Text18>
</SupportContainer>
<PraiseContainer>
<Praise />
</PraiseContainer>
<Explore allIcons={allIcons} />
<Footer />
</Layout>
);
};
export const HeroText = styled.h1`
font-size: 50px;
font-weight: 700;
letter-spacing: -0.05em;
line-height: 52px;
text-align: center;
margin: 60px auto 40px auto;
${media.md} {
font-size: 90px;
line-height: 1;
margin: 200px auto 80px auto;
}
`;
export const HeroDescription = styled(Text18)<{ topMargin?: number }>`
display: block;
max-width: 750px;
margin: 0 auto;
text-align: center;
${media.lg} {
margin-top: ${(props) => props.topMargin || 0}px;
}
`;
const SupportContainer = styled.div`
text-align: center;
> * {
margin: 0 auto;
max-width: 750px;
}
> :not(:last-child) {
margin-bottom: 30px;
}
margin-bottom: 50px;
${media.sm} {
margin-bottom: 150px;
}
`;
const PraiseContainer = styled.div`
margin: 50px 0 64px 0;
${media.md} {
margin: 140px 0 150px 0;
}
width: 100%;
`;
export default Home;
export async function getStaticProps() {
const headerProps = getHeaderProps();
const apiResult = await axios.get(`https://api.github.com/repos/${REPO}`);
const stars = apiResult.data?.stargazers_count;
if (!stars) throw new Error('Could not find GitHub stars');
const numDownloads = await new Promise<number>((resolve, reject) => {
downloadStats.get.lastMonth('iconoir-react', (err: any, results: any) => {
if (err) return reject(err);
resolve(results.downloads);
});
});
if (!numDownloads) throw new Error('Could not find NPM downloads');
return {
props: {
...headerProps,
allIcons: await getAllIcons(),
numStars: stars,
numDownloads,
},
};
}

View file

@ -0,0 +1,163 @@
import { NextPage } from 'next';
import styled from 'styled-components';
import { HeroText } from '.';
import { LargeButton } from '../components/Button';
import { Header, HeaderProps } from '../components/Header';
import { HeaderBackground } from '../components/HeaderBackground';
import { Layout } from '../components/Layout';
import { SEO } from '../components/SEO';
import { Code, Heading2, Text18 } from '../components/Typography';
import { getHeaderProps } from '../lib/getHeaderProps';
import Image from 'next/image';
import { ArrowRight } from 'iconoir-react';
import { SUPPORT_LINK } from '../components/constants';
import { Footer } from '../components/Footer';
import { media } from '../components/responsive';
interface SupportProps extends HeaderProps {}
const Support: NextPage<SupportProps> = ({ ...headerProps }) => {
return (
<Layout>
<SEO title={'Donate - Our Mission'} />
<Header {...headerProps} currentVersionColor={'var(--blue)'} />
<HeaderBackground src={'/support-background.svg'}>
<HeroText>Support our open library.</HeroText>
</HeaderBackground>
<PageContainer>
<Text18>
Iconoir is an open source project built with consistent passion and
dedication. There are no pro subscriptions, forced sign-ups, free
trials or limitations. <br />
Building an open library is what makes us happy.
</Text18>
<Heading2>Our mission pillars</Heading2>
<Code>
<strong>Free forever.</strong> We want to give to developers and users
high-quality free icons. Hassle-free.
</Code>
<Code>
<strong>Full coverage.</strong> We're aiming at having as many unique
icons as possible.
</Code>
<Code>
<strong>Used everywhere.</strong> We'd like to see Iconoir as a point
of reference for any new and exciting project out there.
</Code>
<Text18>
If you consider this library valuable for you and your projects,
support Iconoir with a donation to help us sustain costs and
development time.
</Text18>
<DonateSeparator />
<DonateContainer>
<DonateLeft>
<DonateHeader>Donate with OpenCollective</DonateHeader>
<Text18>We collect donations with transparency.</Text18>
</DonateLeft>
<DonateRight>
<DonateImage>
<Image
src={'/opencollective.png'}
alt={'OpenCollective Logo'}
width={65}
height={65}
style={{ mixBlendMode: 'multiply' }}
/>
</DonateImage>
<DonateButton
as={'a'}
href={SUPPORT_LINK}
target={'_blank'}
rel={'noreferrer'}
>
<span>Donate</span> <ArrowRight />
</DonateButton>
</DonateRight>
</DonateContainer>
<DonateContainer>
<DonateLeft>
<DonateHeader>Join us on Discord</DonateHeader>
<Text18>
Tristique lobortis risus donec aliquam mattis nibh nisi congue
cursus sollicitudin erat posuere consequat gravida rhoncus.
</Text18>
</DonateLeft>
<DonateRight>
<DonateButton>
<span>Join</span> <ArrowRight />
</DonateButton>
</DonateRight>
</DonateContainer>
</PageContainer>
<Footer />
</Layout>
);
};
const PageContainer = styled.div`
max-width: 1336px;
margin: 75px auto 0 auto;
${media.lg} {
margin: 150px auto 0 auto;
}
`;
const DonateSeparator = styled.div`
margin-top: 88px;
`;
const DonateLeft = styled.div`
max-width: 800px;
`;
const DonateImage = styled.div`
display: none;
${media.lg} {
display: block;
}
`;
export const DonateRight = styled.div`
display: flex;
align-items: center;
margin-top: 30px;
> :not(:last-child) {
margin-right: 30px !important;
}
${media.md} {
margin-top: 0;
margin-left: 30px;
}
`;
export const DonateButton = styled(LargeButton)`
padding: 0 24px;
height: 50px;
`;
export const DonateContainer = styled.div`
display: flex;
flex-direction: column;
align-items: stretch;
background: var(--lighter-gray);
padding: 30px;
margin-bottom: 24px;
${media.md} {
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 40px 56px;
}
`;
export const DonateHeader = styled.div`
font-size: 18px;
line-height: 27px;
letter-spacing: -0.02em;
font-weight: 700;
margin-bottom: 16px;
${media.md} {
font-size: 30px;
}
`;
export default Support;
export async function getStaticProps() {
return {
props: getHeaderProps(),
};
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -0,0 +1,3 @@
<svg width="1100" height="232" viewBox="0 0 1100 232" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M41 156C80.398 156 117.332 118.418 143.289 83.2093C162.222 57.5279 202.927 54.8611 221.285 80.9563L271.649 152.544C285.819 172.686 314.198 176.37 333.041 160.514L431.941 77.2936C448.857 63.0593 473.912 64.3758 489.243 80.3044L583.254 177.978C600.686 196.089 630.016 194.934 645.97 175.509L743.534 56.7178C760.914 35.5563 793.571 36.4191 809.809 58.4688L862.343 129.804C886.688 162.861 949.508 144.175 969.529 108.335C987.532 76.1075 1015.82 52 1059 52" stroke="#FFEBE4" stroke-width="82" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 KiB

View file

@ -0,0 +1,11 @@
<svg width="117" height="35" viewBox="0 0 117 35" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.58984 29.533C3.58984 30.0552 4.01322 30.4786 4.53549 30.4786H6.86127C7.38353 30.4786 7.80691 30.0552 7.80691 29.533V12.2558C7.80691 11.7335 7.38353 11.3101 6.86127 11.3101H4.53549C4.01322 11.3101 3.58984 11.7335 3.58984 12.2558V29.533Z" fill="black"/>
<path d="M29.1474 26.3741C29.4274 25.9413 29.2504 25.3748 28.7972 25.1293L26.813 24.0546C26.3353 23.7958 25.7437 23.9974 25.4371 24.4459C24.4163 25.9391 22.7539 26.9516 20.681 26.9516C17.269 26.9516 14.8537 24.3064 14.8537 20.8944C14.8537 17.4824 17.269 14.8371 20.681 14.8371C22.7539 14.8371 24.4163 15.8496 25.4371 17.3428C25.7437 17.7913 26.3353 17.9929 26.813 17.7342L28.7972 16.6594C29.2504 16.4139 29.4274 15.8474 29.1474 15.4146C27.3821 12.6856 24.2874 10.9268 20.681 10.9268C14.9304 10.9268 10.56 15.2205 10.56 20.8944C10.56 26.5683 14.9304 30.862 20.681 30.862C24.2874 30.862 27.3821 29.1032 29.1474 26.3741Z" fill="black"/>
<path d="M50.6422 20.8944C50.6422 15.3355 46.2718 10.9268 40.5979 10.9268C34.924 10.9268 30.5536 15.3355 30.5536 20.8944C30.5536 26.4532 34.924 30.862 40.5979 30.862C46.2718 30.862 50.6422 26.4532 50.6422 20.8944ZM34.809 20.8944C34.809 17.4824 37.1859 14.8371 40.5979 14.8371C43.9716 14.8371 46.3485 17.4824 46.3485 20.8944C46.3485 24.3064 43.9716 26.9516 40.5979 26.9516C37.1859 26.9516 34.809 24.3064 34.809 20.8944Z" fill="black"/>
<path d="M53.3501 29.533C53.3501 30.0552 53.7734 30.4786 54.2957 30.4786H56.6215C57.1437 30.4786 57.5671 30.0552 57.5671 29.533V21.0477C57.5671 16.9457 59.714 14.7605 62.5126 14.7605C64.9662 14.7605 66.4996 16.6006 66.4996 19.6293V29.533C66.4996 30.0552 66.923 30.4786 67.4453 30.4786H69.7711C70.2933 30.4786 70.7167 30.0552 70.7167 29.533V18.9392C70.7167 14.0704 67.8414 10.9268 63.3943 10.9268C60.9024 10.9268 58.7172 12.0002 57.5671 13.917V12.2558C57.5671 11.7335 57.1437 11.3101 56.6215 11.3101H54.2957C53.7734 11.3101 53.3501 11.7335 53.3501 12.2558V29.533Z" fill="black"/>
<path d="M93.2876 20.8944C93.2876 15.3355 88.9172 10.9268 83.2433 10.9268C77.5695 10.9268 73.199 15.3355 73.199 20.8944C73.199 26.4532 77.5695 30.862 83.2433 30.862C88.9172 30.862 93.2876 26.4532 93.2876 20.8944ZM77.4544 20.8944C77.4544 17.4824 79.8313 14.8371 83.2433 14.8371C86.617 14.8371 88.9939 17.4824 88.9939 20.8944C88.9939 24.3064 86.617 26.9516 83.2433 26.9516C79.8313 26.9516 77.4544 24.3064 77.4544 20.8944Z" fill="black"/>
<path d="M95.9955 29.533C95.9955 30.0552 96.4189 30.4786 96.9411 30.4786H99.2669C99.7892 30.4786 100.213 30.0552 100.213 29.533V12.2558C100.213 11.7335 99.7892 11.3101 99.2669 11.3101H96.9411C96.4189 11.3101 95.9955 11.7335 95.9955 12.2558V29.533Z" fill="black"/>
<path d="M104.346 29.533C104.346 30.0552 104.769 30.4786 105.291 30.4786H107.617C108.139 30.4786 108.563 30.0552 108.563 29.533V21.1244C108.563 17.2907 110.748 15.2588 113.163 15.2588C113.466 15.2588 113.745 15.2768 114.017 15.3127C114.65 15.3964 115.272 14.9527 115.272 14.3136V12.047C115.272 11.5963 114.954 11.2021 114.505 11.1586C114.207 11.1298 113.915 11.1184 113.623 11.1184C111.438 11.1184 109.291 12.5369 108.563 14.7221V12.2558C108.563 11.7335 108.139 11.3101 107.617 11.3101H105.291C104.769 11.3101 104.346 11.7335 104.346 12.2558V29.533Z" fill="black"/>
<circle cx="98.1297" cy="6.36411" r="2.36411" fill="black"/>
<circle cx="5.69224" cy="6.36411" r="2.36411" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

@ -0,0 +1,7 @@
<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15 15.75C15 14.4902 15.5004 13.282 16.3912 12.3912C17.282 11.5004 18.4902 11 19.75 11H24.5V20.5H19.75C18.4902 20.5 17.282 19.9996 16.3912 19.1088C15.5004 18.218 15 17.0098 15 15.75Z" stroke="black" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M24.5 11H29.25C29.8738 11 30.4914 11.1229 31.0677 11.3616C31.644 11.6003 32.1677 11.9502 32.6088 12.3912C33.0498 12.8323 33.3997 13.356 33.6384 13.9323C33.8771 14.5085 34 15.1262 34 15.75C34 16.3738 33.8771 16.9914 33.6384 17.5677C33.3997 18.144 33.0498 18.6677 32.6088 19.1088C32.1677 19.5498 31.644 19.8997 31.0677 20.1384C30.4914 20.3771 29.8738 20.5 29.25 20.5H24.5V11Z" stroke="black" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M24.5 25.25C24.5 24.6262 24.6229 24.0086 24.8616 23.4323C25.1003 22.856 25.4502 22.3323 25.8912 21.8912C26.3323 21.4502 26.856 21.1003 27.4323 20.8616C28.0086 20.6229 28.6262 20.5 29.25 20.5C29.8738 20.5 30.4914 20.6229 31.0677 20.8616C31.644 21.1003 32.1677 21.4502 32.6088 21.8912C33.0498 22.3323 33.3997 22.856 33.6384 23.4323C33.8771 24.0086 34 24.6262 34 25.25C34 25.8738 33.8771 26.4914 33.6384 27.0677C33.3997 27.644 33.0498 28.1677 32.6088 28.6088C32.1677 29.0498 31.644 29.3997 31.0677 29.6384C30.4914 29.8771 29.8738 30 29.25 30C28.6262 30 28.0086 29.8771 27.4323 29.6384C26.856 29.3997 26.3323 29.0498 25.8912 28.6088C25.4502 28.1677 25.1003 27.644 24.8616 27.0677C24.6229 26.4914 24.5 25.8738 24.5 25.25V25.25Z" stroke="black" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M15 34.75C15 33.4902 15.5004 32.282 16.3912 31.3912C17.282 30.5004 18.4902 30 19.75 30H24.5V34.75C24.5 36.0098 23.9996 37.218 23.1088 38.1088C22.218 38.9996 21.0098 39.5 19.75 39.5C18.4902 39.5 17.282 38.9996 16.3912 38.1088C15.5004 37.218 15 36.0098 15 34.75Z" stroke="black" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M15 25.25C15 23.9902 15.5004 22.782 16.3912 21.8912C17.282 21.0004 18.4902 20.5 19.75 20.5H24.5V30H19.75C18.4902 30 17.282 29.4996 16.3912 28.6088C15.5004 27.718 15 26.5098 15 25.25Z" stroke="black" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1,19 @@
<svg width="100" height="50" viewBox="0 0 100 50" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M38.1594 19.0264H45.9501V20.5533H39.7407V25.1652H45.3483V26.6703H39.7407V32.5117H38.1594V19.0264Z" fill="black"/>
<path d="M47.5964 19.0264H49.1967V32.5021H47.5964V19.0264Z" fill="black"/>
<path d="M51.9178 31.7872C51.3092 31.1098 51.0049 30.162 51.0049 28.9439V22.9043H52.6038V28.7008C52.6038 29.6182 52.8112 30.2897 53.2259 30.7154C53.6407 31.141 54.1986 31.3543 54.8995 31.3552C55.4122 31.3634 55.9148 31.2125 56.3381 30.9232C56.7564 30.6374 57.0934 30.248 57.3162 29.793C57.5475 29.3381 57.6671 28.8348 57.6653 28.3245V22.9043H59.2656V32.5031H57.7401V31.1161H57.6653C57.3658 31.628 56.9285 32.0454 56.4033 32.321C55.8419 32.643 55.2057 32.8116 54.5586 32.8101C53.4084 32.8056 52.5282 32.4646 51.9178 31.7872Z" fill="black"/>
<path d="M63.9561 32.6105C63.6449 32.4965 63.3598 32.321 63.118 32.0943C62.871 31.8574 62.6787 31.5695 62.5542 31.2507C62.4188 30.876 62.3548 30.4793 62.3654 30.0811V24.3539H60.6904V22.9044H62.3654V20.1943H63.9643V22.9044H66.2981V24.3539H63.9643V29.7021C63.9643 30.2455 64.0648 30.6381 64.2659 30.8948C64.504 31.1774 64.8491 31.3186 65.301 31.3186C65.6759 31.3153 66.0419 31.204 66.3551 30.9981V32.5603C66.1718 32.6459 65.9789 32.7092 65.7805 32.7491C65.535 32.7902 65.2863 32.8088 65.0375 32.8048C64.6681 32.8069 64.3016 32.7411 63.9561 32.6105V32.6105Z" fill="black"/>
<path d="M70.569 32.6105C70.2584 32.4962 69.9738 32.3207 69.7322 32.0943C69.4849 31.8574 69.2921 31.5696 69.1671 31.2507C69.0322 30.8759 68.9687 30.4793 68.9797 30.0811V24.3539H67.3047V22.9044H68.9797V20.1943H70.5785V22.9044H72.9123V24.3539H70.5785V29.7021C70.5785 30.2455 70.6791 30.6381 70.8788 30.8948C71.1178 31.1774 71.4633 31.3186 71.9153 31.3186C72.2897 31.3154 72.6553 31.2041 72.968 30.9981V32.5603C72.7853 32.646 72.5928 32.7094 72.3948 32.7491C72.1488 32.7901 71.8997 32.8088 71.6504 32.8048C71.2811 32.8064 70.9146 32.7406 70.569 32.6105V32.6105Z" fill="black"/>
<path d="M75.9798 32.1447C75.2556 31.7081 74.6664 31.0795 74.2776 30.3285C73.8613 29.523 73.6508 28.6269 73.665 27.7203C73.6559 26.8344 73.8521 25.9584 74.2382 25.161C74.6033 24.4043 75.1667 23.7606 75.8684 23.2985C76.5959 22.8282 77.4474 22.586 78.3136 22.603C79.1712 22.5815 80.0179 22.7993 80.7588 23.232C81.446 23.6478 81.9987 24.253 82.3509 24.9749C82.7325 25.7669 82.9228 26.6375 82.9065 27.5165C82.9092 27.6813 82.89 27.8458 82.8494 28.0056H75.2652C75.2803 28.6556 75.4622 29.2909 75.7937 29.8503C76.081 30.3287 76.494 30.7191 76.9877 30.9792C77.4542 31.2266 77.9743 31.3558 78.5024 31.3555C79.7322 31.3555 80.6795 30.7786 81.3443 29.6248L82.7027 30.2837C82.3129 31.0362 81.732 31.6729 81.0182 32.1298C80.3091 32.5826 79.4529 32.809 78.4494 32.809C77.5806 32.8204 76.7257 32.5904 75.9798 32.1447ZM81.2111 26.6865C81.1804 26.2661 81.0649 25.8562 80.8715 25.4816C80.6536 25.0622 80.3246 24.7107 79.9206 24.4654C79.4284 24.1735 78.8624 24.0296 78.2905 24.0511C77.5834 24.0336 76.8977 24.2941 76.3805 24.7765C75.8442 25.2879 75.4849 25.9569 75.3549 26.6865H81.2111Z" fill="black"/>
<path d="M83.9417 22.9043H85.4672V24.4475H85.5419C85.746 23.8988 86.1288 23.4346 86.6287 23.1298C87.1286 22.79 87.7181 22.6061 88.3226 22.6014C88.7092 22.5921 89.0936 22.6629 89.4515 22.8092V24.5222C89.0285 24.317 88.5631 24.2142 88.0931 24.222C87.6361 24.22 87.1896 24.358 86.8134 24.6173C86.4217 24.89 86.1043 25.2561 85.8897 25.6823C85.6578 26.1282 85.5384 26.6239 85.5419 27.1264V32.5031H83.943V22.9043H83.9417Z" fill="black"/>
<path d="M28.6528 24.6602H21.5468L15.3306 30.8778L18.8829 34.4301L28.6528 24.6602Z" fill="black"/>
<path d="M13.5537 29.1005L10 25.5468L21.5468 14H28.6528L13.5537 29.1005Z" fill="black"/>
<path d="M18.8828 34.4303L21.5467 37.0942H28.6528L22.4338 30.8779L18.8828 34.4303Z" fill="black"/>
<path d="M28.6528 24.6597L22.4366 30.876L28.6528 37.0936H21.5468L18.8829 34.4297L15.3292 30.876L21.5468 24.6597H28.6528ZM21.5468 14L10 25.5468L13.5537 29.1005L28.6528 14H21.5468Z" fill="url(#paint0_radial_2514_13416)"/>
<defs>
<radialGradient id="paint0_radial_2514_13416" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(11.7914 16.2265) scale(28.4295)">
<stop stop-color="white" stop-opacity="0.1"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</radialGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View file

@ -0,0 +1,3 @@
<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.291 20.0364H34.2V11.8H16.7812L25.291 20.0364ZM24.709 21.3818H15.8V30.014L24.3 38.2408V29.6182H33.2188L24.709 21.3818ZM15.1 10H34.9C35.1917 10 35.4715 10.1188 35.6778 10.3302C35.8841 10.5416 36 10.8283 36 11.1273V20.7091C36 21.0081 35.8841 21.2948 35.6778 21.5062C35.4715 21.7176 35.1917 21.8364 34.9 21.8364H27.7668L35.6553 29.4714C35.8154 29.6264 35.9264 29.827 35.974 30.0474C36.0216 30.2677 36.0035 30.4977 35.9222 30.7074C35.8408 30.9171 35.6999 31.097 35.5176 31.2237C35.3354 31.3504 35.1202 31.4182 34.9 31.4182H26.1V39.8727C26.1 40.0929 26.0371 40.3083 25.919 40.4923C25.8008 40.6763 25.6327 40.8208 25.4354 40.9079C25.2381 40.9951 25.0201 41.0211 24.8085 40.9828C24.5969 40.9445 24.4009 40.8435 24.2447 40.6922L14.3447 31.1104C14.2358 31.0051 14.1491 30.878 14.0898 30.7371C14.0306 30.5962 14 30.4444 14 30.2909V20.7091C14 20.4101 14.1159 20.1234 14.3222 19.912C14.5285 19.7006 14.8083 19.5818 15.1 19.5818H22.2332L14.3447 11.9468C14.1846 11.7918 14.0736 11.5912 14.026 11.3708C13.9784 11.1504 13.9965 10.9205 14.0778 10.7108C14.1592 10.5011 14.3001 10.3212 14.4824 10.1945C14.6646 10.0677 14.8798 10 15.1 10Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,7 @@
<svg width="167" height="50" viewBox="0 0 167 50" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M25.4946 27.6059C27.079 27.6059 28.3634 26.3214 28.3634 24.737C28.3634 23.1526 27.079 21.8682 25.4946 21.8682C23.9102 21.8682 22.6257 23.1526 22.6257 24.737C22.6257 26.3214 23.9102 27.6059 25.4946 27.6059Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.4946 18.9043C29.3464 18.9043 32.9247 19.457 35.6226 20.3858C38.8733 21.5049 40.872 23.2013 40.872 24.7373C40.872 26.3379 38.7538 28.14 35.263 29.2966C32.6239 30.171 29.151 30.6274 25.4946 30.6274C21.7458 30.6274 18.1959 30.199 15.5271 29.287C12.1505 28.1329 10.1167 26.3074 10.1167 24.7373C10.1167 23.2137 12.025 21.5305 15.23 20.4131C17.938 19.469 21.6046 18.9043 25.4941 18.9043H25.4946Z" stroke="black" stroke-width="1.37246"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.417 21.8362C22.3413 18.4995 24.6076 15.6759 26.7601 13.8027C29.3535 11.5458 31.8214 10.6619 33.152 11.4292C34.5386 12.2288 35.0415 14.9641 34.2995 18.5658C33.7391 21.2889 32.399 24.5253 30.5724 27.6927C28.6995 30.9402 26.5551 33.8013 24.4319 35.6571C21.7453 38.0056 19.1479 38.8555 17.7878 38.0711C16.4679 37.3105 15.9632 34.8161 16.5963 31.4814C17.1313 28.6638 18.4739 25.2055 20.4164 21.8361L20.417 21.8362Z" stroke="black" stroke-width="1.37246"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.4216 27.7013C18.4918 24.3684 17.178 20.9941 16.6296 18.1939C15.9694 14.8199 16.4362 12.2402 17.7655 11.4707C19.1508 10.6687 21.7717 11.599 24.5215 14.0407C26.6005 15.8866 28.7354 18.6634 30.5674 21.8279C32.4456 25.0722 33.8533 28.3591 34.4009 31.1253C35.0938 34.6258 34.533 37.3005 33.1741 38.0871C31.8555 38.8504 29.4427 38.0423 26.8699 35.8283C24.6961 33.9577 22.3704 31.0673 20.4216 27.7013V27.7013Z" stroke="black" stroke-width="1.37246"/>
<path d="M48.692 31.3809H50.1035V25.252H51.6079L56.8267 31.3809H58.6653L53.4651 25.2706C55.6009 25.1034 56.901 23.7477 56.901 21.7419C56.901 19.5875 55.4152 18.1946 53.0008 18.1946H48.692V31.3809ZM50.1035 23.952V19.5132H52.8336C54.5051 19.5132 55.4709 20.2747 55.4709 21.7419C55.4709 23.2091 54.5051 23.952 52.8336 23.952H50.1035ZM63.5352 31.5666C65.4667 31.5666 66.841 30.6937 67.6582 29.3008L66.5996 28.6136C65.9681 29.7094 65.0581 30.3594 63.5352 30.3594C61.5108 30.3594 60.1179 28.8736 60.0993 26.9607H67.9368V26.6078C67.9182 23.6362 65.9867 21.909 63.5723 21.909C60.7493 21.909 58.7621 24.0448 58.7621 26.7378C58.7621 29.4122 60.6193 31.5666 63.5352 31.5666ZM63.5166 23.1162C65.0767 23.1162 66.3581 24.1377 66.581 25.8092H60.2107C60.5265 24.1191 61.9751 23.1162 63.5166 23.1162ZM73.8062 31.5666C75.2363 31.5666 76.5735 30.8237 77.2421 29.8208V31.3809H78.5421V22.0947H77.2421V23.6548C76.5735 22.6519 75.2363 21.909 73.8062 21.909C71.1689 21.909 69.1817 24.082 69.1817 26.7378C69.1817 29.3936 71.1689 31.5666 73.8062 31.5666ZM73.9362 30.3594C71.9118 30.3594 70.5189 28.7436 70.5189 26.7378C70.5189 24.732 71.9118 23.1162 73.9362 23.1162C75.9606 23.1162 77.3535 24.732 77.3535 26.7378C77.3535 28.7436 75.9606 30.3594 73.9362 30.3594ZM85.4285 31.5666C87.3415 31.5666 88.8644 30.5823 89.6816 29.1336L88.5487 28.4836C87.9915 29.5979 86.8772 30.3594 85.4285 30.3594C83.3113 30.3594 81.9184 28.725 81.9184 26.7378C81.9184 24.7506 83.3113 23.1162 85.4285 23.1162C86.8772 23.1162 87.9915 23.8777 88.5487 24.992L89.6816 24.342C88.8644 22.8933 87.3415 21.909 85.4285 21.909C82.6055 21.909 80.5812 24.082 80.5812 26.7378C80.5812 29.3936 82.6055 31.5666 85.4285 31.5666ZM94.8518 31.4737C95.3718 31.4737 95.8361 31.3809 96.2261 31.2323V30.0622C95.9289 30.1737 95.4275 30.2665 95.0561 30.2665C94.0717 30.2665 93.5146 29.8765 93.5146 28.6879V23.3205H96.2261V22.0947H93.5146V19.5132H92.2145V22.0947H90.2273V23.3205H92.2145V28.7993C92.2145 30.6194 93.3474 31.4737 94.8518 31.4737ZM113.467 31.3809V18.1946H112.055V28.985L103.642 18.1946H102.398V31.3809H103.809V20.5904L112.222 31.3809H113.467ZM120.23 31.5666C121.66 31.5666 122.997 30.8237 123.665 29.8208V31.3809H124.966V22.0947H123.665V23.6548C122.997 22.6519 121.66 21.909 120.23 21.909C117.592 21.909 115.605 24.082 115.605 26.7378C115.605 29.3936 117.592 31.5666 120.23 31.5666ZM120.36 30.3594C118.335 30.3594 116.942 28.7436 116.942 26.7378C116.942 24.732 118.335 23.1162 120.36 23.1162C122.384 23.1162 123.777 24.732 123.777 26.7378C123.777 28.7436 122.384 30.3594 120.36 30.3594ZM131.109 31.4737C131.629 31.4737 132.093 31.3809 132.483 31.2323V30.0622C132.186 30.1737 131.685 30.2665 131.313 30.2665C130.329 30.2665 129.772 29.8765 129.772 28.6879V23.3205H132.483V22.0947H129.772V19.5132H128.472V22.0947H126.485V23.3205H128.472V28.7993C128.472 30.6194 129.605 31.4737 131.109 31.4737ZM134.901 19.3089C135.44 19.3089 135.867 18.8632 135.867 18.3246C135.867 17.786 135.44 17.3402 134.901 17.3402C134.344 17.3402 133.898 17.786 133.898 18.3246C133.898 18.8632 134.344 19.3089 134.901 19.3089ZM134.233 31.3809H135.533V22.0947H134.233V31.3809ZM141.256 31.3809H142.278L146.624 22.0947H145.157L141.758 29.4679L138.359 22.0947H136.911L141.256 31.3809ZM151.848 31.5666C153.779 31.5666 155.154 30.6937 155.971 29.3008L154.912 28.6136C154.281 29.7094 153.371 30.3594 151.848 30.3594C149.823 30.3594 148.43 28.8736 148.412 26.9607H156.249V26.6078C156.231 23.6362 154.299 21.909 151.885 21.909C149.062 21.909 147.075 24.0448 147.075 26.7378C147.075 29.4122 148.932 31.5666 151.848 31.5666ZM151.829 23.1162C153.389 23.1162 154.671 24.1377 154.894 25.8092H148.523C148.839 24.1191 150.288 23.1162 151.829 23.1162Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

View file

@ -0,0 +1,7 @@
<svg width="100" height="50" viewBox="0 0 100 50" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M22.8325 27.7552C24.3681 27.7552 25.6129 26.5104 25.6129 24.9748C25.6129 23.4392 24.3681 22.1943 22.8325 22.1943C21.2969 22.1943 20.052 23.4392 20.052 24.9748C20.052 26.5104 21.2969 27.7552 22.8325 27.7552Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M22.8323 19.3223C26.5654 19.3223 30.0334 19.8579 32.6483 20.7581C35.7988 21.8428 37.7359 23.4868 37.7359 24.9755C37.7359 26.5268 35.683 28.2734 32.2997 29.3944C29.7419 30.2418 26.376 30.6842 22.8323 30.6842C19.199 30.6842 15.7585 30.2689 13.1719 29.385C9.89932 28.2665 7.92822 26.4973 7.92822 24.9755C7.92822 23.4989 9.77772 21.8676 12.884 20.7846C15.5086 19.8695 19.0622 19.3223 22.8318 19.3223H22.8323Z" stroke="black" stroke-width="1.33018"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.9113 22.1644C19.7763 18.9305 21.9727 16.1939 24.0589 14.3785C26.5724 12.1911 28.9643 11.3345 30.2539 12.0781C31.5978 12.8531 32.0852 15.5041 31.3661 18.9948C30.8229 21.634 29.5241 24.7706 27.7537 27.8404C25.9386 30.9879 23.8602 33.7608 21.8025 35.5595C19.1987 37.8356 16.6813 38.6593 15.363 37.8991C14.0839 37.1619 13.5947 34.7443 14.2083 31.5124C14.7268 28.7817 16.028 25.4299 17.9107 22.1644L17.9113 22.1644Z" stroke="black" stroke-width="1.33018"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.9157 27.8488C16.0454 24.6186 14.7721 21.3483 14.2405 18.6343C13.6007 15.3643 14.0531 12.8641 15.3415 12.1183C16.6841 11.341 19.2242 12.2427 21.8893 14.6091C23.9043 16.3981 25.9734 19.0894 27.7489 22.1563C29.5692 25.3007 30.9335 28.4863 31.4643 31.1673C32.1358 34.5599 31.5923 37.1522 30.2753 37.9146C28.9973 38.6544 26.6588 37.8711 24.1653 35.7253C22.0585 33.9124 19.8044 31.1111 17.9157 27.8487V27.8488Z" stroke="black" stroke-width="1.33018"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M53.696 32.0779L50.288 26.9291C52.0104 26.7771 53.5624 25.5291 53.5624 23.2491C53.5624 20.9291 51.916 19.4443 49.6064 19.4443H44.5344V32.0683H46.1056V27.0523H48.6232L51.8408 32.0683L53.696 32.0779ZM49.4184 25.6803H46.1064V20.8539H49.4184C50.9144 20.8539 51.936 21.8379 51.936 23.2579C51.936 24.6779 50.9136 25.6819 49.4184 25.6819V25.6803ZM59.5448 32.3043C61.0408 32.3043 62.2704 31.8123 63.1792 30.9043L62.4976 29.9763C61.7776 30.7147 60.7184 31.1307 59.6776 31.1307C57.7096 31.1307 56.4976 29.6923 56.384 27.9707H63.784V27.6107C63.784 24.8667 62.156 22.7083 59.392 22.7083C56.78 22.7083 54.888 24.8475 54.888 27.4971C54.888 30.3555 56.8376 32.2971 59.544 32.2971L59.5448 32.3043ZM62.3848 26.9283H56.3848C56.4608 25.5283 57.4248 23.8803 59.3752 23.8803C61.4576 23.8803 62.3656 25.5651 62.3848 26.9283ZM73.212 32.0771V25.7931C73.212 23.5787 71.604 22.7075 69.6728 22.7075C68.1768 22.7075 67.004 23.1995 66.02 24.2219L66.6824 25.2059C67.496 24.3355 68.3864 23.9379 69.484 23.9379C70.8088 23.9379 71.7936 24.6379 71.7936 25.8683V27.5147C71.0552 26.6627 70.0144 26.2651 68.7648 26.2651C67.2128 26.2651 65.5648 27.2307 65.5648 29.2747C65.5648 31.2619 67.2112 32.3035 68.7648 32.3035C69.9952 32.3035 71.0368 31.8683 71.7936 31.0355V32.0755L73.212 32.0771ZM69.276 31.2819C67.9512 31.2819 67.0232 30.4499 67.0232 29.2947C67.0232 28.1211 67.9512 27.2883 69.276 27.2883C70.2792 27.2883 71.244 27.6667 71.7936 28.4243V30.1467C71.2448 30.9035 70.2792 31.2827 69.276 31.2827V31.2819ZM80.076 32.3043C81.7416 32.3043 82.7256 31.6227 83.4264 30.7147L82.48 29.8443C81.8744 30.6579 81.0984 31.0363 80.16 31.0363C78.2104 31.0363 77 29.5219 77 27.4971C77 25.4723 78.2112 23.9771 80.16 23.9771C81.1064 23.9771 81.8824 24.3371 82.48 25.1691L83.4264 24.2987C82.7264 23.3899 81.7416 22.7091 80.076 22.7091C77.3504 22.7091 75.5144 24.7915 75.5144 27.4979C75.5144 30.2235 77.3504 32.2979 80.076 32.2979V32.3043ZM87.724 32.3043C88.556 32.3043 89.068 32.0579 89.428 31.7179L89.012 30.6579C88.8224 30.8659 88.452 31.0363 88.0464 31.0363C87.4224 31.0363 87.1184 30.5443 87.1184 29.8627V24.1843H88.9744V22.9355H87.1184V20.4371H85.6984V22.9355H84.184V24.1851H85.6984V30.1667C85.6984 31.5299 86.38 32.3059 87.724 32.3059V32.3043Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View file

@ -0,0 +1,5 @@
<svg width="471" height="343" viewBox="0 0 471 343" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M246.001 301C181.014 258.244 118.14 255 41.001 255" stroke="#E4F9FF" stroke-width="82" stroke-linecap="round"/>
<path d="M429.001 254.512C306.001 155.512 187 148 41.0004 148" stroke="#E4F9FF" stroke-width="82" stroke-linecap="round"/>
<path d="M41.001 147.512C164.001 48.5117 283.002 41 429.002 41" stroke="#E4F9FF" stroke-width="82" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 475 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 855 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7 KiB

4
iconoir.com/public/vercel.svg Executable file
View file

@ -0,0 +1,4 @@
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,430 @@
/**
* One Light theme for prism.js
* Based on Atom's One Light theme: https://github.com/atom/atom/tree/master/packages/one-light-syntax
*/
/**
* One Light colours (accurate as of commit eb064bf on 19 Feb 2021)
* From colors.less
* --mono-1: hsl(230, 8%, 24%);
* --mono-2: hsl(230, 6%, 44%);
* --mono-3: hsl(230, 4%, 64%)
* --hue-1: hsl(198, 99%, 37%);
* --hue-2: hsl(221, 87%, 60%);
* --hue-3: hsl(301, 63%, 40%);
* --hue-4: hsl(119, 34%, 47%);
* --hue-5: hsl(5, 74%, 59%);
* --hue-5-2: hsl(344, 84%, 43%);
* --hue-6: hsl(35, 99%, 36%);
* --hue-6-2: hsl(35, 99%, 40%);
* --syntax-fg: hsl(230, 8%, 24%);
* --syntax-bg: hsl(230, 1%, 98%);
* --syntax-gutter: hsl(230, 1%, 62%);
* --syntax-guide: hsla(230, 8%, 24%, 0.2);
* --syntax-accent: hsl(230, 100%, 66%);
* From syntax-variables.less
* --syntax-selection-color: hsl(230, 1%, 90%);
* --syntax-gutter-background-color-selected: hsl(230, 1%, 90%);
* --syntax-cursor-line: hsla(230, 8%, 24%, 0.05);
*/
code[class*="language-"],
pre[class*="language-"] {
background: hsl(230, 1%, 98%);
color: hsl(230, 8%, 24%);
font-family: "Fira Code", "Fira Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
line-height: 1.5;
-moz-tab-size: 2;
-o-tab-size: 2;
tab-size: 2;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
/* Selection */
code[class*="language-"]::-moz-selection,
code[class*="language-"] *::-moz-selection,
pre[class*="language-"] *::-moz-selection {
background: hsl(230, 1%, 90%);
color: inherit;
}
code[class*="language-"]::selection,
code[class*="language-"] *::selection,
pre[class*="language-"] *::selection {
background: hsl(230, 1%, 90%);
color: inherit;
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: 0.5em 0;
overflow: auto;
border-radius: 0.3em;
}
/* Inline code */
:not(pre)>code[class*="language-"] {
padding: 0.2em 0.3em;
border-radius: 0.3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.cdata {
color: hsl(230, 4%, 64%);
}
.token.doctype,
.token.punctuation,
.token.entity {
color: hsl(230, 8%, 24%);
}
.token.attr-name,
.token.class-name,
.token.boolean,
.token.constant,
.token.number,
.token.atrule {
color: hsl(35, 99%, 36%);
}
.token.keyword {
color: hsl(301, 63%, 40%);
}
.token.property,
.token.tag,
.token.symbol,
.token.deleted,
.token.important {
color: hsl(5, 74%, 59%);
}
.token.selector,
.token.string,
.token.char,
.token.builtin,
.token.inserted,
.token.regex,
.token.attr-value,
.token.attr-value>.token.punctuation {
color: hsl(119, 34%, 47%);
}
.token.variable,
.token.operator,
.token.function {
color: hsl(221, 87%, 60%);
}
.token.url {
color: hsl(198, 99%, 37%);
}
/* HTML overrides */
.token.attr-value>.token.punctuation.attr-equals,
.token.special-attr>.token.attr-value>.token.value.css {
color: hsl(230, 8%, 24%);
}
/* CSS overrides */
.language-css .token.selector {
color: hsl(5, 74%, 59%);
}
.language-css .token.property {
color: hsl(230, 8%, 24%);
}
.language-css .token.function,
.language-css .token.url>.token.function {
color: hsl(198, 99%, 37%);
}
.language-css .token.url>.token.string.url {
color: hsl(119, 34%, 47%);
}
.language-css .token.important,
.language-css .token.atrule .token.rule {
color: hsl(301, 63%, 40%);
}
/* JS overrides */
.language-javascript .token.operator {
color: hsl(301, 63%, 40%);
}
.language-javascript .token.template-string>.token.interpolation>.token.interpolation-punctuation.punctuation {
color: hsl(344, 84%, 43%);
}
/* JSON overrides */
.language-json .token.operator {
color: hsl(230, 8%, 24%);
}
.language-json .token.null.keyword {
color: hsl(35, 99%, 36%);
}
/* MD overrides */
.language-markdown .token.url,
.language-markdown .token.url>.token.operator,
.language-markdown .token.url-reference.url>.token.string {
color: hsl(230, 8%, 24%);
}
.language-markdown .token.url>.token.content {
color: hsl(221, 87%, 60%);
}
.language-markdown .token.url>.token.url,
.language-markdown .token.url-reference.url {
color: hsl(198, 99%, 37%);
}
.language-markdown .token.blockquote.punctuation,
.language-markdown .token.hr.punctuation {
color: hsl(230, 4%, 64%);
font-style: italic;
}
.language-markdown .token.code-snippet {
color: hsl(119, 34%, 47%);
}
.language-markdown .token.bold .token.content {
color: hsl(35, 99%, 36%);
}
.language-markdown .token.italic .token.content {
color: hsl(301, 63%, 40%);
}
.language-markdown .token.strike .token.content,
.language-markdown .token.strike .token.punctuation,
.language-markdown .token.list.punctuation,
.language-markdown .token.title.important>.token.punctuation {
color: hsl(5, 74%, 59%);
}
/* General */
.token.bold {
font-weight: bold;
}
.token.comment,
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
.token.namespace {
opacity: 0.8;
}
/* Plugin overrides */
/* Selectors should have higher specificity than those in the plugins' default stylesheets */
/* Show Invisibles plugin overrides */
.token.token.tab:not(:empty):before,
.token.token.cr:before,
.token.token.lf:before,
.token.token.space:before {
color: hsla(230, 8%, 24%, 0.2);
}
/* Toolbar plugin overrides */
/* Space out all buttons and move them away from the right edge of the code block */
div.code-toolbar>.toolbar.toolbar>.toolbar-item {
margin-right: 0.4em;
}
/* Styling the buttons */
div.code-toolbar>.toolbar.toolbar>.toolbar-item>button,
div.code-toolbar>.toolbar.toolbar>.toolbar-item>a,
div.code-toolbar>.toolbar.toolbar>.toolbar-item>span {
background: hsl(230, 1%, 90%);
color: hsl(230, 6%, 44%);
padding: 0.1em 0.4em;
border-radius: 0.3em;
}
div.code-toolbar>.toolbar.toolbar>.toolbar-item>button:hover,
div.code-toolbar>.toolbar.toolbar>.toolbar-item>button:focus,
div.code-toolbar>.toolbar.toolbar>.toolbar-item>a:hover,
div.code-toolbar>.toolbar.toolbar>.toolbar-item>a:focus,
div.code-toolbar>.toolbar.toolbar>.toolbar-item>span:hover,
div.code-toolbar>.toolbar.toolbar>.toolbar-item>span:focus {
background: hsl(230, 1%, 78%);
/* custom: darken(--syntax-bg, 20%) */
color: hsl(230, 8%, 24%);
}
/* Line Highlight plugin overrides */
/* The highlighted line itself */
.line-highlight.line-highlight {
background: hsla(230, 8%, 24%, 0.05);
}
/* Default line numbers in Line Highlight plugin */
.line-highlight.line-highlight:before,
.line-highlight.line-highlight[data-end]:after {
background: hsl(230, 1%, 90%);
color: hsl(230, 8%, 24%);
padding: 0.1em 0.6em;
border-radius: 0.3em;
box-shadow: 0 2px 0 0 rgba(0, 0, 0, 0.2);
/* same as Toolbar plugin default */
}
/* Hovering over a linkable line number (in the gutter area) */
/* Requires Line Numbers plugin as well */
pre[id].linkable-line-numbers.linkable-line-numbers span.line-numbers-rows>span:hover:before {
background-color: hsla(230, 8%, 24%, 0.05);
}
/* Line Numbers and Command Line plugins overrides */
/* Line separating gutter from coding area */
.line-numbers.line-numbers .line-numbers-rows,
.command-line .command-line-prompt {
border-right-color: hsla(230, 8%, 24%, 0.2);
}
/* Stuff in the gutter */
.line-numbers .line-numbers-rows>span:before,
.command-line .command-line-prompt>span:before {
color: hsl(230, 1%, 62%);
}
/* Match Braces plugin overrides */
/* Note: Outline colour is inherited from the braces */
.rainbow-braces .token.token.punctuation.brace-level-1,
.rainbow-braces .token.token.punctuation.brace-level-5,
.rainbow-braces .token.token.punctuation.brace-level-9 {
color: hsl(5, 74%, 59%);
}
.rainbow-braces .token.token.punctuation.brace-level-2,
.rainbow-braces .token.token.punctuation.brace-level-6,
.rainbow-braces .token.token.punctuation.brace-level-10 {
color: hsl(119, 34%, 47%);
}
.rainbow-braces .token.token.punctuation.brace-level-3,
.rainbow-braces .token.token.punctuation.brace-level-7,
.rainbow-braces .token.token.punctuation.brace-level-11 {
color: hsl(221, 87%, 60%);
}
.rainbow-braces .token.token.punctuation.brace-level-4,
.rainbow-braces .token.token.punctuation.brace-level-8,
.rainbow-braces .token.token.punctuation.brace-level-12 {
color: hsl(301, 63%, 40%);
}
/* Diff Highlight plugin overrides */
/* Taken from https://github.com/atom/github/blob/master/styles/variables.less */
pre.diff-highlight>code .token.token.deleted:not(.prefix),
pre>code.diff-highlight .token.token.deleted:not(.prefix) {
background-color: hsla(353, 100%, 66%, 0.15);
}
pre.diff-highlight>code .token.token.deleted:not(.prefix)::-moz-selection,
pre.diff-highlight>code .token.token.deleted:not(.prefix) *::-moz-selection,
pre>code.diff-highlight .token.token.deleted:not(.prefix)::-moz-selection,
pre>code.diff-highlight .token.token.deleted:not(.prefix) *::-moz-selection {
background-color: hsla(353, 95%, 66%, 0.25);
}
pre.diff-highlight>code .token.token.deleted:not(.prefix)::selection,
pre.diff-highlight>code .token.token.deleted:not(.prefix) *::selection,
pre>code.diff-highlight .token.token.deleted:not(.prefix)::selection,
pre>code.diff-highlight .token.token.deleted:not(.prefix) *::selection {
background-color: hsla(353, 95%, 66%, 0.25);
}
pre.diff-highlight>code .token.token.inserted:not(.prefix),
pre>code.diff-highlight .token.token.inserted:not(.prefix) {
background-color: hsla(137, 100%, 55%, 0.15);
}
pre.diff-highlight>code .token.token.inserted:not(.prefix)::-moz-selection,
pre.diff-highlight>code .token.token.inserted:not(.prefix) *::-moz-selection,
pre>code.diff-highlight .token.token.inserted:not(.prefix)::-moz-selection,
pre>code.diff-highlight .token.token.inserted:not(.prefix) *::-moz-selection {
background-color: hsla(135, 73%, 55%, 0.25);
}
pre.diff-highlight>code .token.token.inserted:not(.prefix)::selection,
pre.diff-highlight>code .token.token.inserted:not(.prefix) *::selection,
pre>code.diff-highlight .token.token.inserted:not(.prefix)::selection,
pre>code.diff-highlight .token.token.inserted:not(.prefix) *::selection {
background-color: hsla(135, 73%, 55%, 0.25);
}
/* Previewers plugin overrides */
/* Based on https://github.com/atom-community/atom-ide-datatip/blob/master/styles/atom-ide-datatips.less and https://github.com/atom/atom/blob/master/packages/one-light-ui */
/* Border around popup */
.prism-previewer.prism-previewer:before,
.prism-previewer-gradient.prism-previewer-gradient div {
border-color: hsl(0, 0, 95%);
}
/* Angle and time should remain as circles and are hence not included */
.prism-previewer-color.prism-previewer-color:before,
.prism-previewer-gradient.prism-previewer-gradient div,
.prism-previewer-easing.prism-previewer-easing:before {
border-radius: 0.3em;
}
/* Triangles pointing to the code */
.prism-previewer.prism-previewer:after {
border-top-color: hsl(0, 0, 95%);
}
.prism-previewer-flipped.prism-previewer-flipped.after {
border-bottom-color: hsl(0, 0, 95%);
}
/* Background colour within the popup */
.prism-previewer-angle.prism-previewer-angle:before,
.prism-previewer-time.prism-previewer-time:before,
.prism-previewer-easing.prism-previewer-easing {
background: hsl(0, 0%, 100%);
}
/* For angle, this is the positive area (eg. 90deg will display one quadrant in this colour) */
/* For time, this is the alternate colour */
.prism-previewer-angle.prism-previewer-angle circle,
.prism-previewer-time.prism-previewer-time circle {
stroke: hsl(230, 8%, 24%);
stroke-opacity: 1;
}
/* Stroke colours of the handle, direction point, and vector itself */
.prism-previewer-easing.prism-previewer-easing circle,
.prism-previewer-easing.prism-previewer-easing path,
.prism-previewer-easing.prism-previewer-easing line {
stroke: hsl(230, 8%, 24%);
}
/* Fill colour of the handle */
.prism-previewer-easing.prism-previewer-easing circle {
fill: transparent;
}

View file

@ -0,0 +1,82 @@
:root {
/* Colors */
--black-40: rgba(0, 0, 0, 0.4);
--black-60: rgba(0, 0, 0, 0.6);
--black-80: rgba(0, 0, 0, 0.8);
--black: #000000;
--darker-gray: #484848;
--dark-gray: #626262;
--pink: #FFEBE4;
--blue: #E4F9FF;
--green: #E4FFE4;
--gray: #E7E7E7;
--light-gray: #F2F2F2;
--lighter-gray: #F9F9F9;
--super-light-gray: #FBFBFB;
--white-80: rgba(255, 255, 255, 0.8);
--white: #FFFFFF;
--accent: #1E5AF6;
--code-family: 'IBM Plex Mono', monospace;
--font-family: 'DM Sans', sans-serif;
}
body {
margin: 0;
padding: 50px 30px;
background: #FFFFFF;
overflow-x: hidden;
}
a {
color: var(--black);
text-decoration: underline;
}
* {
font-weight: 400;
font-family: var(--font-family);
letter-spacing: -0.02em;
}
@keyframes bottomAnimation {
0% {
transform: translateX(-50%) translateY(200px);
opacity: 0;
}
10% {
transform: translateX(-50%) translateY(0);
opacity: 1;
}
90% {
opacity: 1;
}
100% {
opacity: 0;
}
}
.bottom-notification {
position: fixed;
bottom: 50px;
left: 50%;
transform: translateX(-50%);
text-align: center;
background: var(--black);
color: var(--white);
border-radius: 10px;
padding: 15px 30px;
text-align: center;
font-size: 15px;
font-weight: 700;
animation: 3s cubic-bezier(0.16, 1, 0.3, 1) bottomAnimation;
z-index: 2000;
}
@media (min-width: 1100px) {
body {
padding: 50px;
}
}

29
iconoir.com/tsconfig.json Executable file
View file

@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"typeRoots": [
"node_modules/@types",
"types"
]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"],
"references": [
{
"path": "../packages/iconoir-react"
}
]
}

View file

@ -40,5 +40,10 @@
"listr2": "^3.12.2",
"semver": "^7.3.5",
"prettier": "^2.4.1"
},
"pnpm": {
"overrides": {
"@types/react": "^17.0.29"
}
}
}
}

View file

@ -1,4 +1,4 @@
## Flutter Iconoir Icons
# Flutter Iconoir Icons
![PUB Version](https://img.shields.io/pub/v/iconoir_flutter?style=flat-square)
![PUB Monthly Downloads](https://img.shields.io/pub/dm/iconoir_flutter?style=flat-square)
@ -8,13 +8,13 @@ Iconoir is an open source library with 900+ SVG Icons. No premium icons, no emai
`iconoir_flutter` is an open source package that exports these icons as Flutter widgets (`flutter_svg`) that can be used in all of your Flutter projects.
### Installation
## Installation
```
flutter pub add iconoir_flutter
```
### Usage
## Usage
```dart
import 'package:flutter/material.dart';
@ -58,7 +58,7 @@ Default values for the most common props are given below:
| height | "1.5em" |
### Icon names
## Icon names
For the most part, the Flutter widgets are named as PascalCase variations of their reference names (i.e. `add-circle-outline` becomes `AddCircleOutline`). However, some names have been altered slightly either because they start with numerical digits, which would lead to invalid Flutter widgets names, or because they are organisations which use PascalCase in their brand names, such as `GitHub`. The altered names are as follows:

File diff suppressed because it is too large Load diff

View file

@ -17,6 +17,14 @@
"elements":["Svg"],
"attributes":["xmlns"]
}
],
[
"@svgr/babel-plugin-remove-jsx-attribute",
{
"elements": ["Path"],
"attributes": ["strokeWidth"]
},
"remove-stroke-width"
]
]
}

View file

@ -1,4 +1,4 @@
## React Native Iconoir Icons
# React Native Iconoir Icons
![NPM Version](https://img.shields.io/npm/v/iconoir-react-native?style=flat-square)
![NPM Monthly Downloads](https://img.shields.io/npm/dm/iconoir-react-native?style=flat-square)
@ -8,7 +8,7 @@ Iconoir is an open source library with 900+ SVG Icons. No premium icons, no emai
`iconoir-react-native` is an open source package that exports these icons as React Native components (`react-native-svg`) that can be used in all of your React Native projects.
### Installation
## Installation
```
yarn add iconoir-react-native react-native-svg
@ -18,7 +18,7 @@ or
npm i iconoir-react-native react-native-svg
```
### Usage
## Usage
```javascript
import React from 'react';
@ -50,8 +50,30 @@ Default values for the most common props are given below:
| strokeWidth | 1.5 |
| fill | "none" |
## IconoirProvider
### Icon names
Tired of specifying the same props for every single icon, every time you use them? So were we. Use IconoirProvider to set the default icon props for everything inside IconoirProvider.
```tsx
import { IconoirProvider, Check } from 'iconoir-react-native'
return (
<IconoirProvider
iconProps={{
color: '#AAAAAA',
strokeWidth: 1,
width: '1em',
height: '1em',
}}
>
<SomeOtherContainer>
<Check />
</SomeOtherContainer>
</IconoirProvider>
)
```
## Icon names
For the most part, the React components are named as PascalCase variations of their reference names (i.e. `add-circle-outline` becomes `AddCircleOutline`). However, some names have been altered slightly either because they start with numerical digits, which would lead to invalid React component names, or because they are organisations which use PascalCase in their brand names, such as `GitHub`. The altered names are as follows:

View file

@ -36,8 +36,9 @@
"devDependencies": {
"@babel/runtime": "^7.15.4",
"@types/react": "^17.0.29",
"@types/react-native": "^0.50.0",
"react": "^17.0.2",
"react-native-svg": "^12.1.1",
"typescript": "^4.4.4"
}
}
}

View file

@ -1,10 +1,13 @@
import * as React from 'react';
import Svg, { SvgProps, Path } from 'react-native-svg';
import * as React from "react";
import Svg, { SvgProps, Path } from "react-native-svg";
import { IconoirContext } from "./IconoirContext";
function Svg3DAddHole(
props: SvgProps,
passedProps: SvgProps,
svgRef?: React.Ref<React.Component<SvgProps>>
) {
const context = React.useContext(IconoirContext);
const props = { ...context, ...passedProps };
return (
<Svg
width="1.5em"

View file

@ -1,10 +1,13 @@
import * as React from 'react';
import Svg, { SvgProps, Path } from 'react-native-svg';
import * as React from "react";
import Svg, { SvgProps, Path } from "react-native-svg";
import { IconoirContext } from "./IconoirContext";
function Svg3DArc(
props: SvgProps,
passedProps: SvgProps,
svgRef?: React.Ref<React.Component<SvgProps>>
) {
const context = React.useContext(IconoirContext);
const props = { ...context, ...passedProps };
return (
<Svg
width="1.5em"

View file

@ -1,10 +1,13 @@
import * as React from 'react';
import Svg, { SvgProps, Path } from 'react-native-svg';
import * as React from "react";
import Svg, { SvgProps, Path } from "react-native-svg";
import { IconoirContext } from "./IconoirContext";
function Svg3DArcCenterPt(
props: SvgProps,
passedProps: SvgProps,
svgRef?: React.Ref<React.Component<SvgProps>>
) {
const context = React.useContext(IconoirContext);
const props = { ...context, ...passedProps };
return (
<Svg
width="1.5em"

View file

@ -1,10 +1,13 @@
import * as React from 'react';
import Svg, { SvgProps, Path } from 'react-native-svg';
import * as React from "react";
import Svg, { SvgProps, Path } from "react-native-svg";
import { IconoirContext } from "./IconoirContext";
function Svg3DBridge(
props: SvgProps,
passedProps: SvgProps,
svgRef?: React.Ref<React.Component<SvgProps>>
) {
const context = React.useContext(IconoirContext);
const props = { ...context, ...passedProps };
return (
<Svg
width="1.5em"

View file

@ -1,10 +1,13 @@
import * as React from 'react';
import Svg, { SvgProps, Path } from 'react-native-svg';
import * as React from "react";
import Svg, { SvgProps, Path } from "react-native-svg";
import { IconoirContext } from "./IconoirContext";
function Svg3DCenterBox(
props: SvgProps,
passedProps: SvgProps,
svgRef?: React.Ref<React.Component<SvgProps>>
) {
const context = React.useContext(IconoirContext);
const props = { ...context, ...passedProps };
return (
<Svg
width="1.5em"

View file

@ -1,10 +1,13 @@
import * as React from 'react';
import Svg, { SvgProps, Path } from 'react-native-svg';
import * as React from "react";
import Svg, { SvgProps, Path } from "react-native-svg";
import { IconoirContext } from "./IconoirContext";
function Svg3DEllipse(
props: SvgProps,
passedProps: SvgProps,
svgRef?: React.Ref<React.Component<SvgProps>>
) {
const context = React.useContext(IconoirContext);
const props = { ...context, ...passedProps };
return (
<Svg
width="1.5em"

View file

@ -1,10 +1,13 @@
import * as React from 'react';
import Svg, { SvgProps, Path } from 'react-native-svg';
import * as React from "react";
import Svg, { SvgProps, Path } from "react-native-svg";
import { IconoirContext } from "./IconoirContext";
function Svg3DEllipseThreePts(
props: SvgProps,
passedProps: SvgProps,
svgRef?: React.Ref<React.Component<SvgProps>>
) {
const context = React.useContext(IconoirContext);
const props = { ...context, ...passedProps };
return (
<Svg
width="1.5em"

View file

@ -1,10 +1,13 @@
import * as React from 'react';
import Svg, { SvgProps, Path } from 'react-native-svg';
import * as React from "react";
import Svg, { SvgProps, Path } from "react-native-svg";
import { IconoirContext } from "./IconoirContext";
function Svg3DPtBox(
props: SvgProps,
passedProps: SvgProps,
svgRef?: React.Ref<React.Component<SvgProps>>
) {
const context = React.useContext(IconoirContext);
const props = { ...context, ...passedProps };
return (
<Svg
width="1.5em"

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