diff --git a/.changeset/cold-drinks-breathe.md b/.changeset/cold-drinks-breathe.md new file mode 100644 index 000000000..2601f9c39 --- /dev/null +++ b/.changeset/cold-drinks-breathe.md @@ -0,0 +1,5 @@ +--- +'@cube-dev/ui-kit': patch +--- + +Add DatabaseIcon. diff --git a/.eslintrc.js b/.eslintrc.js index 12b7f2085..f77902af9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -107,4 +107,5 @@ module.exports = /** @type {import('eslint').Linter.Config} */ ({ }, }, ], + ignorePatterns: ['*.js'], }); diff --git a/src/icons/DatabaseIcon.tsx b/src/icons/DatabaseIcon.tsx new file mode 100644 index 000000000..1b5e9193a --- /dev/null +++ b/src/icons/DatabaseIcon.tsx @@ -0,0 +1,19 @@ +import { wrapIcon } from './wrap-icon'; + +export const DatabaseIcon = wrapIcon( + 'DatabaseIcon', + + + , +); diff --git a/src/icons/add-new-icon.js b/src/icons/add-new-icon.js new file mode 100644 index 000000000..00e2af166 --- /dev/null +++ b/src/icons/add-new-icon.js @@ -0,0 +1,144 @@ +#!/usr/bin/env node + +const { execSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); +const prettier = require('prettier'); + +(async function main() { + // Run svgo on all svg files in this folder + try { + console.log('Optimizing SVGs with svgo...'); + execSync('svgo *.svg', { stdio: 'inherit' }); + } catch (err) { + console.error('Error running svgo:', err); + process.exit(1); + } + + // Get all .svg files in the current directory + const allFiles = fs.readdirSync(process.cwd()); + const svgFiles = allFiles.filter((file) => file.endsWith('.svg')); + + for (const svgFile of svgFiles) { + const name = path.basename(svgFile, '.svg'); + const tsxFileName = `${name}Icon.tsx`; + + // Read the original SVG file + let svgContent = fs.readFileSync(svgFile, 'utf8'); + + // Replace '#43436B' with 'currentColor' + svgContent = svgContent.replace(/#43436B/g, 'currentColor'); + + // Ensure the tag has a viewBox attribute; if not, add one. + const svgTagMatch = svgContent.match(/]*)>/); + if (svgTagMatch) { + const svgTag = svgTagMatch[0]; + if (!/viewBox=/.test(svgTag)) { + // Insert viewBox attribute right after tag found in ${svgFile}. Skipping file.`); + continue; + } + + // Convert dash-case attributes to camelCase (e.g. fill-rule -> fillRule) + svgContent = svgContent.replace( + /(\s+)([a-z]+-[a-z0-9-]+)(\s*=)/gi, + (match, pre, attrName, post) => { + const camelAttr = attrName.replace(/-([a-z])/g, (_, letter) => + letter.toUpperCase(), + ); + return pre + camelAttr + post; + }, + ); + + // Build the TSX content using the template. + const tsxTemplate = ` +import { wrapIcon } from './wrap-icon'; + +export const ${name}Icon = wrapIcon( + '${name}Icon', + ( + ${svgContent.trim()} + ), +); +`; + + // Format the content with local Prettier configuration (using babel-ts parser) + let formattedTSX; + try { + formattedTSX = await prettier.format(tsxTemplate, { parser: 'babel-ts' }); + } catch (err) { + console.error(`Error formatting ${tsxFileName} with Prettier:`, err); + formattedTSX = tsxTemplate; // Fallback to unformatted version + } + + // Write the new TSX file + fs.writeFileSync(tsxFileName, formattedTSX, 'utf8'); + console.log(`Created ${tsxFileName}`); + } + + // Update index.ts to add new exports and sort them + const indexPath = path.join(process.cwd(), 'index.ts'); + if (!fs.existsSync(indexPath)) { + console.error('index.ts not found in the current directory.'); + process.exit(1); + } + + let indexContent = fs.readFileSync(indexPath, 'utf8'); + const lines = indexContent.split('\n'); + + // Locate the line with "export { wrapIcon } from './wrap-icon';" + const wrapIconLineIndex = lines.findIndex((line) => + line.includes("export { wrapIcon } from './wrap-icon';"), + ); + if (wrapIconLineIndex === -1) { + console.error("Couldn't find the export { wrapIcon } line in index.ts."); + process.exit(1); + } + + // Get the export block above the wrapIcon export + let headerLines = lines.slice(0, wrapIconLineIndex); + const footerLines = lines.slice(wrapIconLineIndex); + + // Ensure that for each SVG file there is an export line + svgFiles.forEach((svgFile) => { + const name = path.basename(svgFile, '.svg'); + const exportLine = `export { ${name}Icon } from './${name}Icon';`; + // Add if not already present (ignoring extra whitespace) + if (!headerLines.some((line) => line.trim() === exportLine)) { + headerLines.push(exportLine); + } + }); + + // Remove empty lines and sort the export lines alphabetically + headerLines = headerLines + .filter((line) => line.trim() !== '') + .sort((a, b) => a.localeCompare(b)); + + // Reassemble the file content + const newIndexContent = [...headerLines, ...footerLines].join('\n'); + + // Format the updated index.ts with Prettier + let formattedIndex; + try { + formattedIndex = await prettier.format(newIndexContent, { + parser: 'babel-ts', + }); + } catch (err) { + console.error('Error formatting index.ts with Prettier:', err); + formattedIndex = newIndexContent; + } + + // Write the updated index.ts + fs.writeFileSync(indexPath, formattedIndex, 'utf8'); + console.log('Updated index.ts'); + + // Remove all initial SVG files + for (const svgFile of svgFiles) { + fs.unlinkSync(svgFile); + console.log(`Removed ${svgFile}`); + } +})(); diff --git a/src/icons/index.ts b/src/icons/index.ts index 9b1eae4eb..8527eec4f 100644 --- a/src/icons/index.ts +++ b/src/icons/index.ts @@ -23,6 +23,7 @@ export { CountIcon } from './CountIcon'; export { CubeIcon } from './CubeIcon'; export { DangerIcon } from './DangerIcon'; export { DashboardIcon } from './DashboardIcon'; +export { DatabaseIcon } from './DatabaseIcon'; export { DirectionIcon } from './DirectionIcon'; export { DonutIcon } from './DonutIcon'; export { DownIcon } from './DownIcon';