Created
May 13, 2025 09:23
-
-
Save manabuyasuda/b7671e8d5515d49490ad0c679502d3d5 to your computer and use it in GitHub Desktop.
SVGファイルをSVGRでコンポーネント化して共通Iconコンポーネントで管理する
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module.exports = { | |
typescript: true, // .tsxファイルを生成する | |
memo: true, // Reactのメモ化を有効にする | |
jsxRuntime: 'automatic', // Reactの明示的なインポートをしない | |
filenameCase: 'kebab', // ファイル名をkebab-caseにする | |
index: false, // index.tsxファイルを生成しない | |
svgoConfig: { | |
plugins: [ | |
{ | |
name: 'preset-default', // 標準的なプリセットを使用する | |
}, | |
'removeXMLNS', // xmlnsとxmlns:xlinkを削除する | |
{ | |
name: 'removeAttrs', | |
params: { | |
attrs: ['data-name'], // デザインデータから出力される不要な属性を削除する | |
}, | |
}, | |
], | |
}, | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// scripts/generate-icons.sh | |
#!/usr/bin/env bash | |
# ============================================================================= | |
# SVGアイコンをReactコンポーネントに変換するスクリプト | |
# ============================================================================= | |
# エラーが発生したら即座に終了し、未定義の変数を使用した場合もエラーにする | |
set -eu | |
# 出力先ディレクトリが存在しない場合は作成する | |
echo "出力先ディレクトリを準備しています..." | |
mkdir -p src/components/Icon/generated | |
# 出力先ディレクトリの中身を全て削除して初期化する | |
echo "前回出力したファイルを削除しています..." | |
rm -rf src/components/Icon/generated/* | |
# SVGRはSVGをReactコンポーネントに変換するツール | |
# 設定ファイルを使用: | |
# .svgrrc.js: SVGRの設定(typescript, memo, svgoConfig等) | |
# .prettierrc: コード整形の設定 | |
echo "SVGファイルからReactコンポーネントを生成しています..." | |
npx svgr \ | |
--out-dir src/components/Icon/generated src/components/Icon/icons | |
echo "--------------------------------------------------------------" | |
echo "SVGRによるコンポーネント生成が完了しました" | |
echo "--------------------------------------------------------------" | |
echo "生成されたコンポーネント:" | |
find src/components/Icon/generated -name "*.tsx" | sort | |
# 手動で更新が必要なファイルの案内 | |
echo "" | |
echo "--------------------------------------------------------------" | |
echo "手動で更新が必要なファイル:" | |
echo "--------------------------------------------------------------" | |
echo "1. src/components/Icon/types.ts" | |
echo " - IconName型にアイコン名を追加または削除" | |
echo "" | |
echo "2. src/components/Icon/index.tsx" | |
echo " - 追加したアイコンをインポート" | |
echo " - iconComponentsマッピングに追加" | |
echo "--------------------------------------------------------------" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// . | |
src/components/Icon/index.module.scss | |
.icon { | |
display: inline-flex; | |
align-items: center; | |
justify-content: center; | |
width: 1em; | |
min-width: 1em; | |
height: 1em; | |
min-height: 1em; | |
fill: currentcolor; | |
stroke: currentcolor; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//src/components/Icon/index.tsx | |
import { ComponentPropsWithoutRef } from 'react'; | |
import { IconProps, IconName } from './types'; | |
import styles from './index.module.scss'; | |
// アイコンが増減したら変更してください | |
import ArrowRight from './generated/arrow-right'; | |
import Circle from './generated/circle'; | |
import Close from './generated/close'; | |
import Menu from './generated/menu'; | |
// アイコンが増減したら変更してください | |
const iconComponents: Record<IconName, React.FC<ComponentPropsWithoutRef<'svg'>>> = { | |
'arrow-right': ArrowRight, | |
circle: Circle, | |
close: Close, | |
menu: Menu, | |
}; | |
/** | |
* SVGアイコンを表示するコンポーネントです。 | |
* アイコンを追加する場合は、src/components/Icon/iconsにSVGファイルを追加して、npm run build:iconsを実行してください。 | |
* | |
* @example | |
* // 基本的な使用法 | |
* <Icon name="arrow-right" className={styles.arrowRightIcon} /> | |
* | |
* @example | |
* // アクセシビリティ対応(スクリーンリーダー向け - JSXスタイル推奨) | |
* <Icon name="close" className={styles.closeIcon} ariaLabel="閉じる" /> | |
* | |
* @example | |
* // アクセシビリティ対応(スクリーンリーダー向け - HTML互換スタイル) | |
* <Icon name="close" className={styles.closeIcon} aria-label="閉じる" /> | |
*/ | |
export function Icon({ | |
name, | |
className, | |
ariaLabel, | |
'aria-label': ariaLabelHtml, | |
...props | |
}: IconProps) { | |
// 対応するアイコンコンポーネントを取得する | |
const IconComponent = iconComponents[name]; | |
if (!IconComponent) { | |
console.warn(`Icon "${name}" not found`); | |
return null; | |
} | |
// ariaLabelとaria-labelの両方が指定された場合はariaLabelを優先 | |
const accessibilityLabel = ariaLabel || ariaLabelHtml; | |
// クラス名を結合する | |
const combinedClassName = className ? `${styles.icon} ${className}` : styles.icon; | |
// アクセシビリティ属性を設定する | |
const a11yProps = { | |
'aria-hidden': !accessibilityLabel ? true : undefined, | |
role: accessibilityLabel ? 'img' : undefined, | |
'aria-label': accessibilityLabel, | |
}; | |
return <IconComponent className={combinedClassName} {...a11yProps} {...props} />; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//src/components/Icon/types.ts | |
import { ComponentPropsWithoutRef } from 'react'; | |
// アイコンが増減したら変更してください | |
export type IconName = 'arrow-right' | 'circle' | 'close' | 'menu'; | |
export type IconProps = ComponentPropsWithoutRef<'svg'> & { | |
name: IconName; | |
ariaLabel?: string; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment