Chrome拡張機能をReactで作ってみた
はじめに
こんにちは。
師走ですね。12月に入ってから一段と寒さと乾燥がひどくなったように感じます。
厳しい冬の寒さにさらされると、必然、物思いに耽る方も多くなることでしょう。私もその1人です。
私は普段、Javaでバックエンドのロジックを担当しています。逆にフロント側を構築することはありません。
なのでこの厳しい寒さの中、夜、布団に入るとふと思うのです。
「...ああ🥶」
「...フロント、触ってみたいな🙃」
「...React
って」
「なんだか響きだけでかっこいいな🎵」
ミーハー心のままネットを漁っていたところ、こちらの記事を発見。
なんと!ReactでChromeの拡張機能が作れるそうです。
しかも出来合いのスターターキットがあるではないですか!
これなら気軽にできそう🎵ということで。
今回はこれらを参考に、Chrome拡張機能を作ってみました。
セットアップ
簡単です。
- nodeやyarnをイントールして
- テンプレートをクローンして
-
yarn dev
!
これだけです。お手軽ですね〜
※詳細な環境構築については参考記事をご覧ください
作ったもの
あとはテンプレートを自分の作りたいものに改造していく作業です。
私はコードレビューなどでよく見る略語(LGTM、IMO…)のリストを表示する機能を実装しました。
開発中、突然イカした略語が飛んでくること、ありませんか?
不意の3文字に目が回ること、ありますよね?
もう大丈夫。これで完璧に対応できます。
プルダウンから選ぶと...
なるほど!(略語のコピーもできます)
実装
主に実装したのはPopup.tsx
とTranslator.tsx
。
Popup.tsx
に略語の全データを保持しておき、プルダウンで選択されたデータをTranslator.tsx
に渡して意味や語源が表示されるようにしました。
import { getBucket } from '@extend-chrome/storage';
import { Container, Select } from '@mantine/core';
import { useEffect, useState } from 'react';
import { Translator } from '../app/features/translator/Translator';
interface MyBucket {
targetLang: string | null;
}
const bucket = getBucket<MyBucket>('my_bucket', 'sync');
const Popup = () => {
document.body.style.width = '20rem';
document.body.style.height = '20rem';
const [lang, setLang] = useState<string | null>(null);
const translations: Record<string, { original: string; japanese: string }> = {
Q: { original: 'Question', japanese: '質問' },
WIP: { original: 'Work In Progress', japanese: '作業中です' },
FYI: { original: 'For Your Information', japanese: '参考までに' },
IMO: { original: 'In My Opinion', japanese: '私が思うに、私の意見としては' },
LGTM: { original: 'Looks Good To Me', japanese: '良いと思います' },
SSIA: { original: 'Subject Says It All', japanese: '件名の通り' },
ASAP: { original: 'As Soon As Possible', japanese: 'できるだけ早く' },
IMHO: { original: 'In My Humble Opinion', japanese: '私のつたない意見では' },
NITS: { original: 'Nitpick', japanese: '細かい指摘' },
'TL;DR': { original: 'Too Long; Didn’t Read', japanese: '長文なので要約を載せます' },
AFAIK: { original: 'As Far As I Know', japanese: '私の知る限りでは' },
GOTCHA: { original: 'I have got you', japanese: 'わかりました' },
};
useEffect(() => {
(async () => {
const value = await bucket.get();
if (value.targetLang) {
setLang(value.targetLang);
}
})();
}, []);
const saveLang = (lang: string | null) => {
bucket.set({ targetLang: lang });
setLang(lang);
};
return (
<Container p="xl">
<Select
label="List of abbreviations"
value={lang}
onChange={saveLang}
data={Object.keys(translations).map((key) => ({ value: key, label: key }))}
clearable
/>
{lang && (
<Translator
targetText={lang}
originalText={translations[lang].original}
japaneseText={translations[lang].japanese}
/>
)}
</Container>
);
};
export default Popup;
import { ActionIcon, CopyButton, Tooltip, Text, Box, Divider, Stack } from '@mantine/core';
import { MdDone, MdOutlineContentCopy } from 'react-icons/md';
export interface TranslatorProps {
targetText: string;
originalText: string;
japaneseText: string;
}
export const Translator: React.FC<TranslatorProps> = ({
targetText,
originalText,
japaneseText,
}) => {
return (
<Box
sx={{
padding: '16px',
backgroundColor: '#ffffff',
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)',
borderRadius: '12px',
maxWidth: '400px',
margin: '0 auto',
}}
>
<Stack spacing="xs">
<Text weight={700} size="lg" style={{ color: '#343a40' }}>
語源:
<Text component="span" color="blue" inherit ml="xs">
{originalText}
</Text>
</Text>
<Text weight={700} size="lg" style={{ color: '#343a40' }}>
意味:
<Text component="span" color="teal" inherit ml="xs">
{japaneseText}
</Text>
</Text>
</Stack>
<Divider my="sm" />
<CopyButton value={targetText}>
{({ copied, copy }) => (
<Tooltip label={copied ? 'コピーしました' : 'クリップボードにコピー'} withArrow>
<ActionIcon
onClick={copy}
size="lg"
variant="filled"
style={{
backgroundColor: copied ? '#38c172' : '#f0f0f0',
color: copied ? '#ffffff' : '#343a40',
transition: 'background-color 0.3s ease',
}}
>
{copied ? <MdDone /> : <MdOutlineContentCopy />}
</ActionIcon>
</Tooltip>
)}
</CopyButton>
</Box>
);
};
まとめ
出来合いのものがある分、かなりとっつきやすかったです。
- Reactで手を動かしてみたい方
- Chrome拡張機能をサッと作ってみたい方
にはおすすめのテンプレートです。
ただその分Reactとは何か
はほとんど理解しないまま作業していました。
自学を進めたいと思います。。
Discussion