From 384ddeda4a6d0a3d477511757a2dbd9c58ad6ac7 Mon Sep 17 00:00:00 2001 From: Donghyun Hwang Date: Thu, 10 Oct 2024 01:04:07 +0900 Subject: [PATCH 1/2] feat: implement cotato icons --- package.json | 4 +- scripts/generateIconTypes.js | 23 +++++ src/components/CotatoIcon.tsx | 64 ++++++++++++ src/types/regularIconNames.ts | 181 ++++++++++++++++++++++++++++++++++ src/types/solidIconNames.ts | 181 ++++++++++++++++++++++++++++++++++ src/utils/iconLoader.js | 52 ++++++++++ yarn.lock | 36 ++++++- 7 files changed, 537 insertions(+), 4 deletions(-) create mode 100644 scripts/generateIconTypes.js create mode 100644 src/components/CotatoIcon.tsx create mode 100644 src/types/regularIconNames.ts create mode 100644 src/types/solidIconNames.ts create mode 100644 src/utils/iconLoader.js diff --git a/package.json b/package.json index 88c7add9..410d260d 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "@craco/craco": "^7.1.0", "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", + "@hackernoon/pixel-icon-library": "^1.0.2", "@mui/icons-material": "^5.15.20", "@mui/lab": "^6.0.0-beta.9", "@mui/material": "^6.1.2", @@ -109,7 +110,8 @@ "start": "craco start", "build": "craco build", "test": "craco test", - "lint": "eslint src --ext .js,.jsx,.ts,.tsx" + "lint": "eslint src --ext .js,.jsx,.ts,.tsx", + "generate-icon-types": "node scripts/generateIconTypes.js" }, "browserslist": { "production": [ diff --git a/scripts/generateIconTypes.js b/scripts/generateIconTypes.js new file mode 100644 index 00000000..5c7d4a18 --- /dev/null +++ b/scripts/generateIconTypes.js @@ -0,0 +1,23 @@ +const fs = require('fs'); +const path = require('path'); + +// 경로 설정 +const iconDir = path.resolve(__dirname, '../node_modules/@hackernoon/pixel-icon-library/icons/SVG'); +const styles = ['regular', 'solid']; + +// 아이콘 이름 추출 및 타입 생성 +styles.forEach((style) => { + const iconsPath = path.join(iconDir, style); + const iconFiles = fs.readdirSync(iconsPath); + + const iconNames = iconFiles + .filter((file) => file.endsWith('.svg')) + .map((file) => file.replace('.svg', '')); + + const typeDef = `export type ${style.charAt(0).toUpperCase() + style.slice(1)}IconName = ${iconNames + .map((name) => `'${name}'`) + .join(' | ')};`; + + const outputPath = path.resolve(__dirname, `../src/types/${style}IconNames.ts`); + fs.writeFileSync(outputPath, typeDef); +}); diff --git a/src/components/CotatoIcon.tsx b/src/components/CotatoIcon.tsx new file mode 100644 index 00000000..579b1599 --- /dev/null +++ b/src/components/CotatoIcon.tsx @@ -0,0 +1,64 @@ +import React, { ObjectHTMLAttributes } from 'react'; +import { regularIcons, solidIcons } from '../utils/iconLoader'; +import { RegularIconName } from '../types/regularIconNames'; +import { SolidIconName } from '../types/solidIconNames'; +import { useTheme } from 'styled-components'; + +// +// +// + +interface CotatoIconProps extends ObjectHTMLAttributes { + icon: RegularIconName | SolidIconName; + size?: string; + color?: string; +} + +// +// +// + +/** + * Common Icon Component for Cotato, from Pixel Icon Library + * ref: https://www.npmjs.com/package/@hackernoon/pixel-icon-library?activeTab=readme + */ +const CotatoIcon: React.FC = ({ icon, color, size, ...rest }) => { + const theme = useTheme(); + + const icons = icon in solidIcons ? solidIcons : regularIcons; + const Icon = icons[icon as keyof typeof icons]; + + const defaultStyle = { + backgroundColor: color || theme.colors.common.black, + width: '24px', + height: '24px', + }; + + if (!Icon) { + return
X
; + } + + // + // + // + + return ( +
+ ); +}; + +export default CotatoIcon; diff --git a/src/types/regularIconNames.ts b/src/types/regularIconNames.ts new file mode 100644 index 00000000..889df67d --- /dev/null +++ b/src/types/regularIconNames.ts @@ -0,0 +1,181 @@ +export type RegularIconName = + | 'ad' + | 'analytics' + | 'angle-down' + | 'angle-left' + | 'angle-right' + | 'angle-up' + | 'arrow-alt-circle-down' + | 'arrow-alt-circle-left' + | 'arrow-alt-circle-right' + | 'arrow-alt-circle-up' + | 'arrow-circle-down' + | 'arrow-circle-left' + | 'arrow-circle-right' + | 'arrow-circle-up' + | 'arrow-down' + | 'arrow-left' + | 'arrow-right' + | 'arrow-up' + | 'at' + | 'badge-check' + | 'bank' + | 'bars' + | 'bell-exclaimation' + | 'bell-mute' + | 'bell' + | 'bold' + | 'bolt' + | 'book-heart' + | 'bookmark' + | 'box-usd' + | 'brightness-high' + | 'brightness-low' + | 'bullet-list' + | 'bullhorn' + | 'calender' + | 'cc' + | 'chart-line' + | 'chart-network' + | 'check-box' + | 'check-circle' + | 'check-list' + | 'check' + | 'chevron-down' + | 'chevron-up' + | 'circle-notch' + | 'clipboard' + | 'clock' + | 'cloud-download-alt' + | 'cloud-upload' + | 'code' + | 'cog' + | 'comment-dots' + | 'comment-quote' + | 'comment' + | 'comments' + | 'copy' + | 'credit-card' + | 'crown' + | 'divider' + | 'download-alt' + | 'download' + | 'edit' + | 'ellipses-horizontal-circle' + | 'ellipses-horizontal' + | 'ellipses-vertical-circle' + | 'ellipses-vertical' + | 'envelope' + | 'exclaimation' + | 'exclamation-triangle' + | 'expand' + | 'external-link' + | 'eye-cross' + | 'eye' + | 'face-thinking' + | 'file-import' + | 'filter-alt-circle' + | 'filter' + | 'fire' + | 'flag-checkered' + | 'flag' + | 'folder-open' + | 'folder' + | 'globe-americas' + | 'globe' + | 'grid' + | 'h1' + | 'h2' + | 'h3' + | 'headphones' + | 'heart' + | 'highlight' + | 'hockey-mask' + | 'home' + | 'image' + | 'info-circle' + | 'italics' + | 'lightbulb' + | 'link' + | 'location-pin' + | 'lock-alt' + | 'lock-open' + | 'lock' + | 'login' + | 'logout' + | 'message-dots' + | 'message' + | 'minus' + | 'moon' + | 'music' + | 'newspaper' + | 'numbered-list' + | 'octagon-check' + | 'octagon-times' + | 'page-break' + | 'paperclip' + | 'paragraph' + | 'pause' + | 'pen-nib' + | 'pen' + | 'pencil-ruler' + | 'pencil' + | 'people-carry' + | 'phone-ringing-high' + | 'phone-ringing-low' + | 'plane-departure' + | 'plane' + | 'play' + | 'playlist' + | 'plus' + | 'print' + | 'pro' + | 'question' + | 'quote-left' + | 'quote-right' + | 'receipt' + | 'refresh' + | 'retro-camera' + | 'robot' + | 'save' + | 'search' + | 'seedlings' + | 'share' + | 'shop' + | 'shopping-cart' + | 'shuffle' + | 'sort' + | 'sound-mute' + | 'sound-on' + | 'spinner-third' + | 'spinner' + | 'star-crescent' + | 'star' + | 'strike-through' + | 'sun' + | 'table' + | 'tag' + | 'themes' + | 'thumbsdown' + | 'thumbsup' + | 'thumbtack' + | 'times-circle' + | 'times' + | 'translate' + | 'trash-alt' + | 'trash' + | 'trending' + | 'trophy' + | 'underline' + | 'unlock-alt' + | 'unlock' + | 'upload-alt' + | 'upload' + | 'user-check' + | 'user-headset' + | 'user' + | 'users-crown' + | 'users' + | 'vote-yeah' + | 'wallet' + | 'window-close'; diff --git a/src/types/solidIconNames.ts b/src/types/solidIconNames.ts new file mode 100644 index 00000000..0bf042e4 --- /dev/null +++ b/src/types/solidIconNames.ts @@ -0,0 +1,181 @@ +export type SolidIconName = + | 'ad-solid' + | 'analytics-solid' + | 'angle-down-solid' + | 'angle-left-solid' + | 'angle-right-solid' + | 'angle-up-solid' + | 'arrow-alt-circle-down-solid' + | 'arrow-alt-circle-left-solid' + | 'arrow-alt-circle-right-solid' + | 'arrow-alt-circle-up-solid' + | 'arrow-circle-down-solid' + | 'arrow-circle-left-solid' + | 'arrow-circle-right-solid' + | 'arrow-circle-up-solid' + | 'arrow-down-solid' + | 'arrow-left-solid' + | 'arrow-right-solid' + | 'arrow-up-solid' + | 'at-solid' + | 'badge-check-solid' + | 'bank-solid' + | 'bars-solid' + | 'bell-exclaimation-solid' + | 'bell-mute-solid' + | 'bell-solid' + | 'bold-solid' + | 'bolt-solid' + | 'book-heart-solid' + | 'bookmark-solid' + | 'box-usd-solid' + | 'brightness-high-solid' + | 'brightness-low-solid' + | 'bullet-list-solid' + | 'bullhorn-solid' + | 'calender-solid' + | 'cc-solid' + | 'chart-line-solid' + | 'chart-network-solid' + | 'check-box-solid' + | 'check-circle-solid' + | 'check-list-solid' + | 'check-solid' + | 'chevron-down-solid' + | 'chevron-up-solid' + | 'circle-notch-solid' + | 'clipboard-solid' + | 'clock-solid' + | 'cloud-download-solid' + | 'cloud-upload-solid' + | 'code-solid' + | 'cog-solid' + | 'comment-dots-solid' + | 'comment-quote-solid' + | 'comment-solid' + | 'comments-solid' + | 'copy-solid' + | 'credit-card-solid' + | 'crown-solid' + | 'divider-solid' + | 'download-alt-solid' + | 'download-solid' + | 'edit-solid' + | 'ellipses-horizontal-circle-solid' + | 'ellipses-horizontal-solid' + | 'ellipses-vertical-circle-solid' + | 'ellipses-vertical-solid' + | 'envelope-solid' + | 'exclaimation-solid' + | 'exclamation-triangle-solid' + | 'expand-solid' + | 'external-link-solid' + | 'eye-cross-solid' + | 'eye-solid' + | 'face-thinking-solid' + | 'file-import-solid' + | 'filter-alt-circle-solid' + | 'filter-solid' + | 'fire-solid' + | 'flag-checkered-solid' + | 'flag-solid' + | 'folder-open-solid' + | 'folder-solid' + | 'globe-americas-solid' + | 'globe-solid' + | 'grid-solid' + | 'heading-1-solid' + | 'heading-2-solid' + | 'heading-3-solid' + | 'headphones-solid' + | 'heart-solid' + | 'highlight-solid' + | 'hockey-mask-solid' + | 'home-solid' + | 'image-solid' + | 'info-circle-solid' + | 'italics-solid' + | 'lightbulb-solid' + | 'link-solid' + | 'location-pin-solid' + | 'lock-alt-solid' + | 'lock-open-solid' + | 'lock-solid' + | 'login-solid' + | 'logout-solid' + | 'message-dots-solid' + | 'message-solid' + | 'minus-solid' + | 'moon-solid' + | 'music-solid' + | 'newspaper-solid' + | 'numbered-list-solid' + | 'octagon-check-solid' + | 'octagon-times-solid' + | 'page-break-solid' + | 'paperclip-solid' + | 'paragraph-solid' + | 'pause-solid' + | 'pen-nib-solid' + | 'pen-solid' + | 'pencil-ruler-solid' + | 'pencil-solid' + | 'people-carry-solid' + | 'phone-ringing-high-solid' + | 'phone-ringing-low-solid' + | 'plane-departure-solid' + | 'plane-solid' + | 'play-solid' + | 'playlist-solid' + | 'plus-solid' + | 'print-solid' + | 'pro-solid' + | 'question-solid' + | 'quote-left-solid' + | 'quote-right-solid' + | 'receipt-solid' + | 'refresh-solid' + | 'retro-camera-solid' + | 'robot-solid' + | 'save-solid' + | 'search-solid' + | 'seedlings-solid' + | 'share-solid' + | 'shop-solid' + | 'shopping-cart-solid' + | 'shuffle-solid' + | 'sort-solid' + | 'sound-mute-solid' + | 'sound-on-solid' + | 'spinner-solid' + | 'spinner-third-solid' + | 'star-crescent-solid' + | 'star-solid' + | 'strike-through-solid' + | 'sun-solid' + | 'table-solid' + | 'tag-solid' + | 'themes-solid' + | 'thumbsdown-solid' + | 'thumbsup-solid' + | 'thumbtack-solid' + | 'times-circle-solid' + | 'times-solid' + | 'translate-solid' + | 'trash-alt-solid' + | 'trash-solid' + | 'trending-solid' + | 'trophy-solid' + | 'underline-solid' + | 'unlock-alt-solid' + | 'unlock-solid' + | 'upload-alt-solid' + | 'upload-solid' + | 'user-check-solid' + | 'user-headset-solid' + | 'user-solid' + | 'users-crown-solid' + | 'users-solid' + | 'vote-yeah-solid' + | 'wallet-solid' + | 'window-close-solid'; diff --git a/src/utils/iconLoader.js b/src/utils/iconLoader.js new file mode 100644 index 00000000..8d0a73c2 --- /dev/null +++ b/src/utils/iconLoader.js @@ -0,0 +1,52 @@ +/** + * load all icons from the pixel-icon-library + * require.context has issue that use dynamic path, so separate the function + * + */ + +const loadRegularSvgIcons = () => { + const regularContext = require.context( + '@hackernoon/pixel-icon-library/icons/SVG/regular', + false, + /\.svg$/, + ); + + const icons = {}; + + regularContext.keys().forEach((key) => { + const iconName = key.match(/\.\/(.*)\.svg/)?.[1]; + + if (!iconName) { + return; + } + + icons[iconName] = regularContext(key); + }); + + return icons; +}; + +const loadSolidSvgIcons = () => { + const solidContext = require.context( + '@hackernoon/pixel-icon-library/icons/SVG/solid', + false, + /\.svg$/, + ); + + const icons = {}; + + solidContext.keys().forEach((key) => { + const iconName = key.match(/\.\/(.*)\.svg/)?.[1]; + + if (!iconName) { + return; + } + + icons[iconName] = solidContext(key); + }); + + return icons; +}; + +export const regularIcons = loadRegularSvgIcons('regular'); +export const solidIcons = loadSolidSvgIcons('solid'); diff --git a/yarn.lock b/yarn.lock index 6ab76b68..bd9e81e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1674,6 +1674,11 @@ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.8.tgz#21a907684723bbbaa5f0974cf7730bd797eb8e62" integrity sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig== +"@hackernoon/pixel-icon-library@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@hackernoon/pixel-icon-library/-/pixel-icon-library-1.0.2.tgz#7c95e903c3a8cff775cddd60dd452c4e7b103b4b" + integrity sha512-ETR4/3pb4ffL62xPkEhymM8xnhN4v5thS/yrs1pqeVcsSpBgOKjXuGZyAevqvZ5LlqB5szYmkoX9FG6JH5mllw== + "@humanwhocodes/config-array@^0.11.14": version "0.11.14" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" @@ -10519,7 +10524,16 @@ string-natural-compare@^3.0.1: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0, string-width@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -10622,7 +10636,14 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -11856,7 +11877,7 @@ workbox-window@6.6.1: "@types/trusted-types" "^2.0.2" workbox-core "6.6.1" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -11874,6 +11895,15 @@ wrap-ansi@^6.0.1: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 9f29f3b5d4d12720c6f4138d4f0f65ee1adbc812 Mon Sep 17 00:00:00 2001 From: Donghyun Hwang Date: Thu, 10 Oct 2024 01:05:10 +0900 Subject: [PATCH 2/2] chore: add ref comment --- src/utils/iconLoader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/iconLoader.js b/src/utils/iconLoader.js index 8d0a73c2..f939a70b 100644 --- a/src/utils/iconLoader.js +++ b/src/utils/iconLoader.js @@ -1,7 +1,7 @@ /** * load all icons from the pixel-icon-library * require.context has issue that use dynamic path, so separate the function - * + * ref: https://github.com/webpack/webpack/issues/9300 */ const loadRegularSvgIcons = () => {