Skip to content

Commit

Permalink
Merge pull request #92 from inkonchain/feat/new-icons
Browse files Browse the repository at this point in the history
feat: support icon subfolders, add new ones
  • Loading branch information
fran-ink authored Feb 10, 2025
2 parents 321bf3d + 724b297 commit fdf1391
Show file tree
Hide file tree
Showing 69 changed files with 381 additions and 145 deletions.
107 changes: 75 additions & 32 deletions scripts/import-svgs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,85 @@
import fs from "fs/promises";
import path from "path";

const currentDir = import.meta.dirname;
const iconsDir = path.join(currentDir, "../src/icons");

const svgs = await fs.readdir(iconsDir);
await Promise.all(
svgs
.filter((svg) => svg.endsWith(".svg"))
.map(async (svg) => {
const svgContent = await fs.readFile(path.join(iconsDir, svg), "utf8");
const result = svgContent
.replace(/stroke="#160F1F"/g, 'stroke="currentColor"')
.replace(/fill="#160F1F"/g, 'fill="currentColor"')
.replace(/width="24"/g, 'width="100%"')
.replace(/height="24"/g, 'height="100%"');
await fs.writeFile(path.join(iconsDir, svg), result, "utf8");
})
);

/** Use this to map an invalid name temporarily (until it is fixed in the Figma) */
const iconNameMapping = {};

function getIconName(svg) {
const result = svg.replace(".svg", "").replace("Type=", "");
const result = svg.replace(".svg", "");
return iconNameMapping[result] ?? result;
}

const header = `/**\n * This file is auto-generated by the \`import-svgs.mjs\` script.\n */`;
const content = svgs
.filter((svg) => svg.endsWith(".svg"))
.map(
(svg) => `export { default as ${getIconName(svg)} } from "./${svg}?react";`
)
.join("\n");

await fs.writeFile(
path.join(iconsDir, "index.ts"),
`${header}\n\n${content}\n`,
"utf8"
);
async function processSvgsInFolder(folder) {
// Start by renaming the files to remove the "Type=" or "Propery 1=" prefix
await Promise.all(
(await fs.readdir(folder, { recursive: true })).map(async (name) => {
if (name.includes("=")) {
const currentPath = name.split("/").slice(0, -1).join("/");
const newName = currentPath + "/" + name.split("=")[1];
if (await fs.access(path.join(folder, newName)).catch(() => false)) {
await fs.unlink(path.join(folder, newName));
}
await fs.rename(path.join(folder, name), path.join(folder, newName));
}
})
);

const svgs = await fs.readdir(folder, { recursive: true });
await Promise.all(
svgs
.filter((svg) => svg.endsWith(".svg"))
.map(async (svg) => {
console.log(path.join(folder, svg));
const svgContent = await fs.readFile(path.join(folder, svg), "utf8");
const result = svgContent
.replace(/stroke="#160F1F"/g, 'stroke="currentColor"')
.replace(/fill="#160F1F"/g, 'fill="currentColor"')
.replace(/width="24"/g, 'width="100%"')
.replace(/height="24"/g, 'height="100%"');
await fs.writeFile(path.join(folder, svg), result, "utf8");
})
);
}

async function createIndexFile(folder) {
const header = `/**\n * This file is auto-generated by the \`import-svgs.mjs\` script.\n */`;
const stuffInDir = await fs.readdir(folder);
const foundSvgs = [];
const foundFolders = [];
await Promise.all(
stuffInDir.map(async (stuff) => {
if (stuff.endsWith(".svg")) {
foundSvgs.push(stuff);
} else if ((await fs.stat(path.join(folder, stuff))).isDirectory()) {
foundFolders.push(stuff);
}
})
);
await Promise.all(
foundFolders.map(async (f) => {
await createIndexFile(path.join(folder, f));
})
);
const content = foundSvgs
.map(
(svg) =>
`export { default as ${getIconName(svg)} } from "./${svg}?react";`
)
.concat(
foundFolders.map(
(folder) => `export * as ${folder} from "./${folder}/index.ts";`
)
)
.join("\n");

await fs.writeFile(
path.join(folder, "index.ts"),
`${header}\n\n${content}\n`,
"utf8"
);
}

const currentDir = import.meta.dirname;
const dirToProcess = path.join(currentDir, "../src/icons");
await processSvgsInFolder(dirToProcess);
await createIndexFile(dirToProcess);
27 changes: 27 additions & 0 deletions src/icons/AllIcons.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.tooltip-on-hover {
position: relative;
}

.tooltip-on-hover:before {
display: block;
content: attr(data-title);
position: absolute;
top: 0;
transform: translateX(-50%) translateY(-125%);
background-color: var(--ink-button-primary);
color: var(--ink-text-on-primary);
padding: 4px 8px;
border-radius: 8px;
opacity: 0;
z-index: 1;
pointer-events: none;
width: 0;
left: 0;
}

.tooltip-on-hover:hover:before {
left: 50%;
transition: opacity 0.1s ease-in-out;
opacity: 1;
width: auto;
}
76 changes: 68 additions & 8 deletions src/icons/AllIcons.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { classNames } from "../util/classes";
import * as Icons from "./index";
import "./AllIcons.css";

const icons = Object.values(Icons);
type IconType = (typeof Icons)[keyof typeof Icons];

interface IconsOrFolder {
[key: string]: IconType | IconsOrFolder;
}

export const AllIcons: React.FC<{
containerClassName?: string;
Expand All @@ -10,16 +15,71 @@ export const AllIcons: React.FC<{
return (
<div
className={classNames(
"ink:flex ink:flex-wrap ink:gap-2",
"ink:flex ink:flex-wrap ink:gap-2 ink:text-text-default",
containerClassName
)}
>
{icons.map((Icon) => (
<Icon
className={classNames("ink:size-4", iconClassName)}
key={Icon.name}
/>
))}
<div className="ink:text-body-2-bold">Click to copy icon name</div>
<IconsOrFolder
title="InkIcon"
iconsOrFolder={Icons}
iconClassName={classNames("tooltip-on-hover", iconClassName)}
/>
</div>
);
};

function IconsOrFolder({
title,
iconsOrFolder,
iconClassName,
}: {
title: string;
iconsOrFolder: IconsOrFolder;
iconClassName?: string;
}) {
return (
<div className={classNames("ink:flex ink:flex-col ink:gap-2 ink:pl-2")}>
<div className="ink:text-caption-1-bold ink:text-text-muted">{title}</div>

<div className="ink:flex ink:flex-wrap ink:gap-2">
{Object.entries(iconsOrFolder).map(([name, IconOrFolder]) => {
if (!isIconFolder(IconOrFolder)) {
return (
<div
className="tooltip-on-hover ink:cursor-pointer ink:whitespace-nowrap"
data-title={`<${title}.${name} />`}
onClick={() =>
navigator.clipboard.writeText(`<${title}.${name} />`)
}
>
<IconOrFolder
className={classNames("ink:size-4", iconClassName)}
title={`${title}.${name}`}
key={name}
/>
</div>
);
}
})}
</div>

{Object.entries(iconsOrFolder).map(([name, IconOrFolder]) => {
if (isIconFolder(IconOrFolder)) {
return (
<IconsOrFolder
key={name}
title={`${title}.${name}`}
iconsOrFolder={IconOrFolder}
iconClassName={iconClassName}
/>
);
}
})}
</div>
);
}

function isIconFolder(icon: IconType | IconsOrFolder): icon is IconsOrFolder {
return typeof icon === "object";
}
3 changes: 3 additions & 0 deletions src/icons/Apps.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/icons/Arrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/icons/Bridge.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/icons/Check.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/icons/CheckBadge.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
3 changes: 3 additions & 0 deletions src/icons/Chevron.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/icons/Close.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/icons/CloseSmall.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit fdf1391

Please sign in to comment.