Skip to content

Commit 7e1cffd

Browse files
authored
feat(icons): add DatabaseIcon (#613)
1 parent a598267 commit 7e1cffd

File tree

5 files changed

+170
-0
lines changed

5 files changed

+170
-0
lines changed

.changeset/cold-drinks-breathe.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@cube-dev/ui-kit': patch
3+
---
4+
5+
Add DatabaseIcon.

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,5 @@ module.exports = /** @type {import('eslint').Linter.Config} */ ({
107107
},
108108
},
109109
],
110+
ignorePatterns: ['*.js'],
110111
});

src/icons/DatabaseIcon.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { wrapIcon } from './wrap-icon';
2+
3+
export const DatabaseIcon = wrapIcon(
4+
'DatabaseIcon',
5+
<svg
6+
viewBox="0 0 16 16"
7+
xmlns="http://www.w3.org/2000/svg"
8+
width="16"
9+
height="16"
10+
fill="none"
11+
>
12+
<path
13+
fill="currentColor"
14+
fillRule="evenodd"
15+
d="M3.75 2.085c-1.111.505-1.49 1.07-1.49 1.462s.379.957 1.49 1.462c1.055.478 2.556.79 4.25.79s3.195-.312 4.25-.79c1.111-.505 1.49-1.07 1.49-1.462s-.379-.957-1.49-1.462c-1.055-.478-2.556-.791-4.25-.791s-3.195.313-4.25.791M3.212.908C4.471.336 6.164 0 8 0s3.53.336 4.789.908c1.2.545 2.252 1.43 2.252 2.639l-.003.105q.003.03.003.059v8.578c0 1.236-1.024 2.167-2.24 2.75C11.54 15.644 9.841 16 8 16c-1.84 0-3.539-.356-4.801-.961-1.216-.583-2.24-1.514-2.24-2.75V3.711l.003-.06-.003-.104c0-1.21 1.051-2.094 2.252-2.64M2.26 5.65v2.352c.002.446.405 1.04 1.502 1.562 1.051.5 2.55.827 4.239.827s3.188-.327 4.239-.827c1.098-.522 1.501-1.117 1.502-1.563V5.65a6 6 0 0 1-.952.536c-1.26.571-2.953.908-4.789.908s-3.53-.337-4.789-.908a6 6 0 0 1-.952-.536m11.482 4.528a6 6 0 0 1-.942.554c-1.262.6-2.96.953-4.799.953s-3.537-.353-4.8-.953a6 6 0 0 1-.94-.554v2.11c0 .457.406 1.059 1.503 1.585 1.05.504 2.548.833 4.237.833s3.186-.33 4.237-.833c1.097-.526 1.504-1.127 1.504-1.584z"
16+
clipRule="evenodd"
17+
/>
18+
</svg>,
19+
);

src/icons/add-new-icon.js

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
#!/usr/bin/env node
2+
3+
const { execSync } = require('child_process');
4+
const fs = require('fs');
5+
const path = require('path');
6+
const prettier = require('prettier');
7+
8+
(async function main() {
9+
// Run svgo on all svg files in this folder
10+
try {
11+
console.log('Optimizing SVGs with svgo...');
12+
execSync('svgo *.svg', { stdio: 'inherit' });
13+
} catch (err) {
14+
console.error('Error running svgo:', err);
15+
process.exit(1);
16+
}
17+
18+
// Get all .svg files in the current directory
19+
const allFiles = fs.readdirSync(process.cwd());
20+
const svgFiles = allFiles.filter((file) => file.endsWith('.svg'));
21+
22+
for (const svgFile of svgFiles) {
23+
const name = path.basename(svgFile, '.svg');
24+
const tsxFileName = `${name}Icon.tsx`;
25+
26+
// Read the original SVG file
27+
let svgContent = fs.readFileSync(svgFile, 'utf8');
28+
29+
// Replace '#43436B' with 'currentColor'
30+
svgContent = svgContent.replace(/#43436B/g, 'currentColor');
31+
32+
// Ensure the <svg> tag has a viewBox attribute; if not, add one.
33+
const svgTagMatch = svgContent.match(/<svg\b([^>]*)>/);
34+
if (svgTagMatch) {
35+
const svgTag = svgTagMatch[0];
36+
if (!/viewBox=/.test(svgTag)) {
37+
// Insert viewBox attribute right after <svg
38+
const newSvgTag = svgTag.replace('<svg', '<svg viewBox="0 0 16 16"');
39+
svgContent = svgContent.replace(svgTag, newSvgTag);
40+
}
41+
} else {
42+
console.warn(`No <svg> tag found in ${svgFile}. Skipping file.`);
43+
continue;
44+
}
45+
46+
// Convert dash-case attributes to camelCase (e.g. fill-rule -> fillRule)
47+
svgContent = svgContent.replace(
48+
/(\s+)([a-z]+-[a-z0-9-]+)(\s*=)/gi,
49+
(match, pre, attrName, post) => {
50+
const camelAttr = attrName.replace(/-([a-z])/g, (_, letter) =>
51+
letter.toUpperCase(),
52+
);
53+
return pre + camelAttr + post;
54+
},
55+
);
56+
57+
// Build the TSX content using the template.
58+
const tsxTemplate = `
59+
import { wrapIcon } from './wrap-icon';
60+
61+
export const ${name}Icon = wrapIcon(
62+
'${name}Icon',
63+
(
64+
${svgContent.trim()}
65+
),
66+
);
67+
`;
68+
69+
// Format the content with local Prettier configuration (using babel-ts parser)
70+
let formattedTSX;
71+
try {
72+
formattedTSX = await prettier.format(tsxTemplate, { parser: 'babel-ts' });
73+
} catch (err) {
74+
console.error(`Error formatting ${tsxFileName} with Prettier:`, err);
75+
formattedTSX = tsxTemplate; // Fallback to unformatted version
76+
}
77+
78+
// Write the new TSX file
79+
fs.writeFileSync(tsxFileName, formattedTSX, 'utf8');
80+
console.log(`Created ${tsxFileName}`);
81+
}
82+
83+
// Update index.ts to add new exports and sort them
84+
const indexPath = path.join(process.cwd(), 'index.ts');
85+
if (!fs.existsSync(indexPath)) {
86+
console.error('index.ts not found in the current directory.');
87+
process.exit(1);
88+
}
89+
90+
let indexContent = fs.readFileSync(indexPath, 'utf8');
91+
const lines = indexContent.split('\n');
92+
93+
// Locate the line with "export { wrapIcon } from './wrap-icon';"
94+
const wrapIconLineIndex = lines.findIndex((line) =>
95+
line.includes("export { wrapIcon } from './wrap-icon';"),
96+
);
97+
if (wrapIconLineIndex === -1) {
98+
console.error("Couldn't find the export { wrapIcon } line in index.ts.");
99+
process.exit(1);
100+
}
101+
102+
// Get the export block above the wrapIcon export
103+
let headerLines = lines.slice(0, wrapIconLineIndex);
104+
const footerLines = lines.slice(wrapIconLineIndex);
105+
106+
// Ensure that for each SVG file there is an export line
107+
svgFiles.forEach((svgFile) => {
108+
const name = path.basename(svgFile, '.svg');
109+
const exportLine = `export { ${name}Icon } from './${name}Icon';`;
110+
// Add if not already present (ignoring extra whitespace)
111+
if (!headerLines.some((line) => line.trim() === exportLine)) {
112+
headerLines.push(exportLine);
113+
}
114+
});
115+
116+
// Remove empty lines and sort the export lines alphabetically
117+
headerLines = headerLines
118+
.filter((line) => line.trim() !== '')
119+
.sort((a, b) => a.localeCompare(b));
120+
121+
// Reassemble the file content
122+
const newIndexContent = [...headerLines, ...footerLines].join('\n');
123+
124+
// Format the updated index.ts with Prettier
125+
let formattedIndex;
126+
try {
127+
formattedIndex = await prettier.format(newIndexContent, {
128+
parser: 'babel-ts',
129+
});
130+
} catch (err) {
131+
console.error('Error formatting index.ts with Prettier:', err);
132+
formattedIndex = newIndexContent;
133+
}
134+
135+
// Write the updated index.ts
136+
fs.writeFileSync(indexPath, formattedIndex, 'utf8');
137+
console.log('Updated index.ts');
138+
139+
// Remove all initial SVG files
140+
for (const svgFile of svgFiles) {
141+
fs.unlinkSync(svgFile);
142+
console.log(`Removed ${svgFile}`);
143+
}
144+
})();

src/icons/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export { CountIcon } from './CountIcon';
2323
export { CubeIcon } from './CubeIcon';
2424
export { DangerIcon } from './DangerIcon';
2525
export { DashboardIcon } from './DashboardIcon';
26+
export { DatabaseIcon } from './DatabaseIcon';
2627
export { DirectionIcon } from './DirectionIcon';
2728
export { DonutIcon } from './DonutIcon';
2829
export { DownIcon } from './DownIcon';

0 commit comments

Comments
 (0)