Replace popup with React
This commit is contained in:
parent
4028acda0c
commit
a73359094a
6
src/common/openUrl.js
Normal file
6
src/common/openUrl.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import browser from "webextension-polyfill";
|
||||||
|
|
||||||
|
export default async url => {
|
||||||
|
const activeTab = (await browser.tabs.query({ currentWindow: true, active: true }))[0];
|
||||||
|
browser.tabs.create({ url: url, index: activeTab.index + 1 });
|
||||||
|
};
|
|
@ -65,6 +65,15 @@ const formatResult = result => {
|
||||||
|
|
||||||
export default async (sourceWord, sourceLang = "auto", targetLang) => {
|
export default async (sourceWord, sourceLang = "auto", targetLang) => {
|
||||||
sourceWord = sourceWord.trim();
|
sourceWord = sourceWord.trim();
|
||||||
|
if (sourceWord === "")
|
||||||
|
return {
|
||||||
|
resultText: "",
|
||||||
|
candidateText: "",
|
||||||
|
sourceLanguage: "en",
|
||||||
|
percentage: 0,
|
||||||
|
statusText: "OK"
|
||||||
|
};
|
||||||
|
|
||||||
const history = getHistory(sourceWord, sourceLang, targetLang);
|
const history = getHistory(sourceWord, sourceLang, targetLang);
|
||||||
if (history) return history.result;
|
if (history) return history.result;
|
||||||
|
|
||||||
|
|
50
src/popup/components/Footer.js
Normal file
50
src/popup/components/Footer.js
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import React, { Component } from "react";
|
||||||
|
import browser from "webextension-polyfill";
|
||||||
|
import genelateLangOptions from "src/common/genelateLangOptions";
|
||||||
|
import openUrl from "src/common/openUrl";
|
||||||
|
import "../styles/Footer.scss";
|
||||||
|
|
||||||
|
export default class Footer extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.langList = genelateLangOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLinkClick = async () => {
|
||||||
|
const { tabUrl, targetLang } = this.props;
|
||||||
|
const encodedUrl = encodeURIComponent(tabUrl);
|
||||||
|
const translateUrl = `https://translate.google.com/translate?hl=${targetLang}&sl=auto&u=${encodedUrl}`;
|
||||||
|
openUrl(translateUrl);
|
||||||
|
};
|
||||||
|
|
||||||
|
handleChange = e => {
|
||||||
|
const lang = e.target.value;
|
||||||
|
this.props.handleLangChange(lang);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { tabUrl, targetLang } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div id="footer">
|
||||||
|
<div className="translateLink">
|
||||||
|
{tabUrl && <a onClick={this.handleLinkClick}>{browser.i18n.getMessage("showLink")}</a>}
|
||||||
|
</div>
|
||||||
|
<div className="selectWrap">
|
||||||
|
<select
|
||||||
|
id="langList"
|
||||||
|
value={targetLang}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
title={browser.i18n.getMessage("targetLangLabel")}
|
||||||
|
>
|
||||||
|
{this.langList.map(option => (
|
||||||
|
<option value={option.value} key={option.value}>
|
||||||
|
{option.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
42
src/popup/components/Header.js
Normal file
42
src/popup/components/Header.js
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import React from "react";
|
||||||
|
import browser from "webextension-polyfill";
|
||||||
|
import browserInfo from "browser-info";
|
||||||
|
import openUrl from "src/common/openUrl";
|
||||||
|
import HeartIcon from "../icons/heart.svg";
|
||||||
|
import SettingsIcon from "../icons/settings.svg";
|
||||||
|
import "../styles/header.scss";
|
||||||
|
|
||||||
|
//TODO: 次のタブで開く
|
||||||
|
const openPayPal = () => {
|
||||||
|
const isChrome = browserInfo().name === "Chrome";
|
||||||
|
const url = `https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&no_shipping=1&business=sienori.firefox@gmail.com&item_name=SimpleTranslate ${
|
||||||
|
isChrome ? "for Chrome " : ""
|
||||||
|
}- Donation`;
|
||||||
|
openUrl(url);
|
||||||
|
};
|
||||||
|
const openSettings = () => {
|
||||||
|
const url = "../options/index.html#settings";
|
||||||
|
openUrl(url);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default () => (
|
||||||
|
<div id="header">
|
||||||
|
<div className="title">Simple Translate</div>
|
||||||
|
<div className="rightButtons">
|
||||||
|
<button
|
||||||
|
className="heartButton"
|
||||||
|
onClick={openPayPal}
|
||||||
|
title={browser.i18n.getMessage("donateWithPaypalLabel")}
|
||||||
|
>
|
||||||
|
<HeartIcon />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={"settingsButton"}
|
||||||
|
onClick={openSettings}
|
||||||
|
title={browser.i18n.getMessage("settingsLabel")}
|
||||||
|
>
|
||||||
|
<SettingsIcon />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
33
src/popup/components/InputArea.js
Normal file
33
src/popup/components/InputArea.js
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import React, { Component } from "react";
|
||||||
|
import ReactDOM from "react-dom";
|
||||||
|
import browser from "webextension-polyfill";
|
||||||
|
import "../styles/inputArea.scss";
|
||||||
|
|
||||||
|
export default class InputArea extends Component {
|
||||||
|
resizeTextArea = () => {
|
||||||
|
const textarea = ReactDOM.findDOMNode(this.refs.textarea);
|
||||||
|
textarea.style.height = "1px";
|
||||||
|
textarea.style.height = `${textarea.scrollHeight + 2}px`;
|
||||||
|
};
|
||||||
|
|
||||||
|
handleInputText = e => {
|
||||||
|
const inputText = e.target.value;
|
||||||
|
this.props.handleInputText(inputText);
|
||||||
|
this.resizeTextArea();
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div id="inputArea">
|
||||||
|
<textarea
|
||||||
|
value={this.props.inputText}
|
||||||
|
ref="textarea"
|
||||||
|
placeholder={browser.i18n.getMessage("initialTextArea")}
|
||||||
|
onChange={this.handleInputText}
|
||||||
|
autoFocus
|
||||||
|
spellCheck={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
123
src/popup/components/PopupPage.js
Normal file
123
src/popup/components/PopupPage.js
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
import React, { Component } from "react";
|
||||||
|
import browser from "webextension-polyfill";
|
||||||
|
import { initSettings, getSettings } from "src/settings/settings";
|
||||||
|
import translate from "src/common/translate";
|
||||||
|
import Header from "./Header";
|
||||||
|
import InputArea from "./InputArea";
|
||||||
|
import ResultArea from "./ResultArea";
|
||||||
|
import Footer from "./Footer";
|
||||||
|
import "../styles/PopupPage.scss";
|
||||||
|
|
||||||
|
const getTabInfo = async () => {
|
||||||
|
try {
|
||||||
|
const tab = (await browser.tabs.query({ currentWindow: true, active: true }))[0];
|
||||||
|
const tabInfo = await browser.tabs.sendMessage(tab.id, { message: "getTabInfo" });
|
||||||
|
return tabInfo;
|
||||||
|
} catch (e) {
|
||||||
|
return { url: "", selectedText: "" };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class PopupPage extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
targetLang: "",
|
||||||
|
inputText: "",
|
||||||
|
resultText: "",
|
||||||
|
candidateText: "",
|
||||||
|
statusText: "OK",
|
||||||
|
tabUrl: ""
|
||||||
|
};
|
||||||
|
this.isSwitchedSecondLang = false;
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init = async () => {
|
||||||
|
await initSettings();
|
||||||
|
|
||||||
|
const targetLang = getSettings("targetLang");
|
||||||
|
this.setState({
|
||||||
|
targetLang: targetLang
|
||||||
|
});
|
||||||
|
|
||||||
|
const tabInfo = await getTabInfo();
|
||||||
|
this.setState({
|
||||||
|
inputText: tabInfo.selectedText,
|
||||||
|
tabUrl: tabInfo.url
|
||||||
|
});
|
||||||
|
if (tabInfo.selectedText !== "") this.translateText(tabInfo.selectedText, targetLang);
|
||||||
|
};
|
||||||
|
|
||||||
|
handleInputText = inputText => {
|
||||||
|
this.setState({ inputText: inputText });
|
||||||
|
|
||||||
|
const waitTime = getSettings("waitTime");
|
||||||
|
clearTimeout(this.inputTimer);
|
||||||
|
this.inputTimer = setTimeout(
|
||||||
|
() => this.translateText(inputText, this.state.targetLang),
|
||||||
|
waitTime
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
handleLangChange = lang => {
|
||||||
|
this.setState({ targetLang: lang });
|
||||||
|
const inputText = this.state.inputText;
|
||||||
|
if (inputText !== "") this.translateText(inputText, lang);
|
||||||
|
};
|
||||||
|
|
||||||
|
translateText = async (text, targetLang) => {
|
||||||
|
const result = await translate(text, "auto", targetLang);
|
||||||
|
this.setState({
|
||||||
|
resultText: result.resultText,
|
||||||
|
candidateText: result.candidateText,
|
||||||
|
statusText: result.statusText
|
||||||
|
});
|
||||||
|
this.switchSecondLang(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
switchSecondLang = result => {
|
||||||
|
if (!getSettings("ifChangeSecondLang")) return;
|
||||||
|
|
||||||
|
const defaultTargetLang = getSettings("targetLang");
|
||||||
|
const secondLang = getSettings("secondTargetLang");
|
||||||
|
if (defaultTargetLang === secondLang) return;
|
||||||
|
|
||||||
|
const equalsSourceAndTarget =
|
||||||
|
result.sourceLanguage === this.state.targetLang && result.percentage > 0;
|
||||||
|
const equalsSourceAndDefault =
|
||||||
|
result.sourceLanguage === defaultTargetLang && result.percentage > 0;
|
||||||
|
|
||||||
|
if (!this.isSwitchedSecondLang) {
|
||||||
|
if (equalsSourceAndTarget && equalsSourceAndDefault) {
|
||||||
|
this.handleLangChange(secondLang);
|
||||||
|
this.isSwitchedSecondLang = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!equalsSourceAndDefault) {
|
||||||
|
this.handleLangChange(defaultTargetLang);
|
||||||
|
this.isSwitchedSecondLang = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Header />
|
||||||
|
<InputArea inputText={this.state.inputText} handleInputText={this.handleInputText} />
|
||||||
|
<hr />
|
||||||
|
<ResultArea
|
||||||
|
resultText={this.state.resultText}
|
||||||
|
candidateText={this.state.candidateText}
|
||||||
|
statusText={this.state.statusText}
|
||||||
|
/>
|
||||||
|
<Footer
|
||||||
|
tabUrl={this.state.tabUrl}
|
||||||
|
targetLang={this.state.targetLang}
|
||||||
|
handleLangChange={this.handleLangChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
38
src/popup/components/ResultArea.js
Normal file
38
src/popup/components/ResultArea.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import React from "react";
|
||||||
|
import browser from "webextension-polyfill";
|
||||||
|
import "../styles/ResultArea.scss";
|
||||||
|
|
||||||
|
const getErrorMessage = statusText => {
|
||||||
|
let errorMessage = "";
|
||||||
|
switch (statusText) {
|
||||||
|
case "":
|
||||||
|
errorMessage = browser.i18n.getMessage("networkError");
|
||||||
|
break;
|
||||||
|
case "Service Unavailable":
|
||||||
|
errorMessage = browser.i18n.getMessage("unavailableError");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
errorMessage = `${browser.i18n.getMessage("unknownError")} [${statusText}]`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return errorMessage;
|
||||||
|
};
|
||||||
|
|
||||||
|
const splitLine = text => {
|
||||||
|
const regex = /(\n)/g;
|
||||||
|
return text.split(regex).map((line, i) => (line.match(regex) ? <br key={i} /> : line));
|
||||||
|
};
|
||||||
|
|
||||||
|
export default props => {
|
||||||
|
const { resultText, candidateText, statusText } = props;
|
||||||
|
const isError = statusText !== "OK";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div id="resultArea">
|
||||||
|
<p className="resultText">{splitLine(resultText)}</p>
|
||||||
|
<p className="candidateText">
|
||||||
|
{isError ? getErrorMessage(statusText) : splitLine(candidateText)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
6
src/popup/icons/heart.svg
Normal file
6
src/popup/icons/heart.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 492.719 492.719">
|
||||||
|
<path d="M492.719,166.008c0-73.486-59.573-133.056-133.059-133.056c-47.985,0-89.891,25.484-113.302,63.569
|
||||||
|
c-23.408-38.085-65.332-63.569-113.316-63.569C59.556,32.952,0,92.522,0,166.008c0,40.009,17.729,75.803,45.671,100.178
|
||||||
|
l188.545,188.553c3.22,3.22,7.587,5.029,12.142,5.029c4.555,0,8.922-1.809,12.142-5.029l188.545-188.553
|
||||||
|
C474.988,241.811,492.719,206.017,492.719,166.008z" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 467 B |
20
src/popup/icons/settings.svg
Normal file
20
src/popup/icons/settings.svg
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||||
|
<path class="st0" d="M499.453,210.004l-55.851-2.58c-5.102-0.23-9.608-3.395-11.546-8.103l-11.508-27.695
|
||||||
|
c-1.937-4.728-0.997-10.145,2.455-13.914l37.668-41.332c4.718-5.188,4.546-13.205-0.421-18.182l-46.434-46.443
|
||||||
|
c-4.986-4.967-13.003-5.159-18.2-0.412l-41.312,37.668c-3.778,3.443-9.206,4.402-13.924,2.436l-27.694-11.488
|
||||||
|
c-4.718-1.946-7.864-6.454-8.094-11.565l-2.589-55.831C301.675,5.534,295.883,0,288.864,0h-65.708
|
||||||
|
c-7.02,0-12.831,5.534-13.156,12.562l-2.571,55.831c-0.23,5.111-3.376,9.618-8.094,11.565L171.64,91.447
|
||||||
|
c-4.737,1.966-10.165,1.007-13.924-2.436l-41.331-37.668c-5.198-4.746-13.215-4.564-18.201,0.412L51.769,98.198
|
||||||
|
c-4.986,4.977-5.158,12.994-0.422,18.182l37.668,41.332c3.452,3.769,4.373,9.186,2.416,13.914l-11.469,27.695
|
||||||
|
c-1.956,4.708-6.444,7.873-11.564,8.103l-55.832,2.58c-7.019,0.316-12.562,6.118-12.562,13.147v65.699
|
||||||
|
c0,7.019,5.543,12.83,12.562,13.148l55.832,2.579c5.12,0.229,9.608,3.394,11.564,8.103l11.469,27.694
|
||||||
|
c1.957,4.728,1.036,10.146-2.416,13.914l-37.668,41.313c-4.756,5.217-4.564,13.224,0.403,18.201l46.471,46.443
|
||||||
|
c4.967,4.977,12.965,5.15,18.182,0.422l41.312-37.677c3.759-3.443,9.207-4.392,13.924-2.435l27.694,11.478
|
||||||
|
c4.719,1.956,7.864,6.464,8.094,11.575l2.571,55.831c0.325,7.02,6.136,12.562,13.156,12.562h65.708
|
||||||
|
c7.02,0,12.812-5.542,13.138-12.562l2.589-55.831c0.23-5.111,3.376-9.619,8.094-11.575l27.694-11.478
|
||||||
|
c4.718-1.957,10.146-1.008,13.924,2.435l41.312,37.677c5.198,4.728,13.215,4.555,18.2-0.422l46.434-46.443
|
||||||
|
c4.967-4.977,5.139-12.984,0.421-18.201l-37.668-41.313c-3.452-3.768-4.412-9.186-2.455-13.914l11.508-27.694
|
||||||
|
c1.937-4.709,6.444-7.874,11.546-8.103l55.851-2.579c7.019-0.318,12.542-6.129,12.542-13.148v-65.699
|
||||||
|
C511.995,216.122,506.472,210.32,499.453,210.004z M256.01,339.618c-46.164,0-83.622-37.438-83.622-83.612
|
||||||
|
c0-46.184,37.458-83.622,83.622-83.622s83.602,37.438,83.602,83.622C339.612,302.179,302.174,339.618,256.01,339.618z" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -1,81 +1,12 @@
|
||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
|
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="stylesheet" type="text/css" href="popup.css">
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<svg class=hidden>
|
<div id=root />
|
||||||
<defs>
|
|
||||||
<symbol id="settingSvg" viewBox="0 0 512 512">
|
|
||||||
<path class="st0" d="M499.453,210.004l-55.851-2.58c-5.102-0.23-9.608-3.395-11.546-8.103l-11.508-27.695
|
|
||||||
c-1.937-4.728-0.997-10.145,2.455-13.914l37.668-41.332c4.718-5.188,4.546-13.205-0.421-18.182l-46.434-46.443
|
|
||||||
c-4.986-4.967-13.003-5.159-18.2-0.412l-41.312,37.668c-3.778,3.443-9.206,4.402-13.924,2.436l-27.694-11.488
|
|
||||||
c-4.718-1.946-7.864-6.454-8.094-11.565l-2.589-55.831C301.675,5.534,295.883,0,288.864,0h-65.708
|
|
||||||
c-7.02,0-12.831,5.534-13.156,12.562l-2.571,55.831c-0.23,5.111-3.376,9.618-8.094,11.565L171.64,91.447
|
|
||||||
c-4.737,1.966-10.165,1.007-13.924-2.436l-41.331-37.668c-5.198-4.746-13.215-4.564-18.201,0.412L51.769,98.198
|
|
||||||
c-4.986,4.977-5.158,12.994-0.422,18.182l37.668,41.332c3.452,3.769,4.373,9.186,2.416,13.914l-11.469,27.695
|
|
||||||
c-1.956,4.708-6.444,7.873-11.564,8.103l-55.832,2.58c-7.019,0.316-12.562,6.118-12.562,13.147v65.699
|
|
||||||
c0,7.019,5.543,12.83,12.562,13.148l55.832,2.579c5.12,0.229,9.608,3.394,11.564,8.103l11.469,27.694
|
|
||||||
c1.957,4.728,1.036,10.146-2.416,13.914l-37.668,41.313c-4.756,5.217-4.564,13.224,0.403,18.201l46.471,46.443
|
|
||||||
c4.967,4.977,12.965,5.15,18.182,0.422l41.312-37.677c3.759-3.443,9.207-4.392,13.924-2.435l27.694,11.478
|
|
||||||
c4.719,1.956,7.864,6.464,8.094,11.575l2.571,55.831c0.325,7.02,6.136,12.562,13.156,12.562h65.708
|
|
||||||
c7.02,0,12.812-5.542,13.138-12.562l2.589-55.831c0.23-5.111,3.376-9.619,8.094-11.575l27.694-11.478
|
|
||||||
c4.718-1.957,10.146-1.008,13.924,2.435l41.312,37.677c5.198,4.728,13.215,4.555,18.2-0.422l46.434-46.443
|
|
||||||
c4.967-4.977,5.139-12.984,0.421-18.201l-37.668-41.313c-3.452-3.768-4.412-9.186-2.455-13.914l11.508-27.694
|
|
||||||
c1.937-4.709,6.444-7.874,11.546-8.103l55.851-2.579c7.019-0.318,12.542-6.129,12.542-13.148v-65.699
|
|
||||||
C511.995,216.122,506.472,210.32,499.453,210.004z M256.01,339.618c-46.164,0-83.622-37.438-83.622-83.612
|
|
||||||
c0-46.184,37.458-83.622,83.622-83.622s83.602,37.438,83.602,83.622C339.612,302.179,302.174,339.618,256.01,339.618z"></path>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="heartSvg" viewBox="0 0 492.719 492.719">
|
|
||||||
<path d="M492.719,166.008c0-73.486-59.573-133.056-133.059-133.056c-47.985,0-89.891,25.484-113.302,63.569
|
|
||||||
c-23.408-38.085-65.332-63.569-113.316-63.569C59.556,32.952,0,92.522,0,166.008c0,40.009,17.729,75.803,45.671,100.178
|
|
||||||
l188.545,188.553c3.22,3.22,7.587,5.029,12.142,5.029c4.555,0,8.922-1.809,12.142-5.029l188.545-188.553
|
|
||||||
C474.988,241.811,492.719,206.017,492.719,166.008z"></path>
|
|
||||||
</symbol>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
||||||
<div id=header>
|
|
||||||
<div id=title>Simple Translate</div>
|
|
||||||
<div class="rightButtons">
|
|
||||||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&no_shipping=1&business=sienori.firefox@gmail.com&item_name=SimpleTranslate - Donation"
|
|
||||||
target="_blank">
|
|
||||||
<div id="donate" title="Donate with PayPal">
|
|
||||||
<svg>
|
|
||||||
<use xlink:href="#heartSvg"></use>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<a href="../options/options.html" target="_blank">
|
|
||||||
<div id="setting" title="Setting">
|
|
||||||
<svg>
|
|
||||||
<use xlink:href="#settingSvg"></use>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id=main>
|
|
||||||
<textarea id=textarea spellcheck=false contenteditable=true autofocus></textarea>
|
|
||||||
<hr>
|
|
||||||
<div id=target>
|
|
||||||
<p class=result></p>
|
|
||||||
<p class=candidate></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id=footer>
|
|
||||||
<div id=link></div>
|
|
||||||
<div class=selectWrap>
|
|
||||||
<select id="langList" title="Target language"></select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="../Settings.js"></script>
|
|
||||||
<script src="../translate.js"></script>
|
|
||||||
<script src="popup.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -1,231 +1,5 @@
|
||||||
/* Copyright (c) 2017-2018 Sienori All rights reserved.
|
import React from "react";
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
import ReactDOM from "react-dom";
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
import PopupPage from "./components/PopupPage";
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
const S = new settingsObj();
|
ReactDOM.render(<PopupPage />, document.getElementById("root"));
|
||||||
const T = new Translate();
|
|
||||||
|
|
||||||
//設定を読み出し
|
|
||||||
S.init().then(function(value) {
|
|
||||||
defaultTargetLang = value.targetLang;
|
|
||||||
secondTargetLang = value.secondTargetLang;
|
|
||||||
langList.value = value.targetLang; //リスト初期値をセット
|
|
||||||
langList.addEventListener("change", changeLang);
|
|
||||||
|
|
||||||
document.body.style.fontSize = value.fontSize;
|
|
||||||
});
|
|
||||||
|
|
||||||
let target = document.getElementById("target");
|
|
||||||
let langList = document.getElementById("langList");
|
|
||||||
let textarea = document.getElementById("textarea");
|
|
||||||
|
|
||||||
const initialText = browser.i18n.getMessage("initialTextArea");
|
|
||||||
textarea.placeholder = initialText;
|
|
||||||
|
|
||||||
let secondTargetLang;
|
|
||||||
let defaultTargetLang;
|
|
||||||
let sourceWord = "";
|
|
||||||
|
|
||||||
setLangList();
|
|
||||||
|
|
||||||
function setLangList() {
|
|
||||||
let langListStr = browser.i18n.getMessage("langList");
|
|
||||||
langListStr = langListStr.split(", ");
|
|
||||||
|
|
||||||
for (let i in langListStr) {
|
|
||||||
langListStr[i] = langListStr[i].split(":");
|
|
||||||
}
|
|
||||||
langListStr = langListStr.sort(alphabeticallySort);
|
|
||||||
|
|
||||||
let langListHtml = "";
|
|
||||||
for (let i of langListStr) {
|
|
||||||
langListHtml += `<option value=${i[0]}>${i[1]}</option>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
langList.innerHTML = langListHtml;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTitles();
|
|
||||||
function setTitles() {
|
|
||||||
document.getElementById("donate").title = browser.i18n.getMessage("donateWithPaypalLabel");
|
|
||||||
document.getElementById("setting").title = browser.i18n.getMessage("settingsLabel");
|
|
||||||
document.getElementById("langList").title = browser.i18n.getMessage("targetLangLabel");
|
|
||||||
}
|
|
||||||
|
|
||||||
function alphabeticallySort(a, b) {
|
|
||||||
if (a[1].toString() > b[1].toString()) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//翻訳先言語変更時に更新
|
|
||||||
async function changeLang() {
|
|
||||||
if (typeof url != "undefined") showLink();
|
|
||||||
|
|
||||||
if (sourceWord !== "") {
|
|
||||||
const resultData = await T.translate(sourceWord, undefined, langList.value);
|
|
||||||
showResult(resultData.resultText, resultData.candidateText, resultData.statusText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//アクティブなタブを取得して渡す
|
|
||||||
browser.tabs
|
|
||||||
.query({
|
|
||||||
currentWindow: true,
|
|
||||||
active: true
|
|
||||||
})
|
|
||||||
.then(function(tabs) {
|
|
||||||
getSelectionWord(tabs);
|
|
||||||
});
|
|
||||||
|
|
||||||
//アクティブタブから選択文字列とurlを取得
|
|
||||||
function getSelectionWord(tabs) {
|
|
||||||
for (let tab of tabs) {
|
|
||||||
browser.tabs
|
|
||||||
.sendMessage(tab.id, {
|
|
||||||
message: "fromPopup"
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
sourceWord = response.word || "";
|
|
||||||
url = response.url;
|
|
||||||
refleshSource();
|
|
||||||
showLink();
|
|
||||||
})
|
|
||||||
.catch(() => {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//ページ翻訳へのリンクを表示
|
|
||||||
function showLink() {
|
|
||||||
document.getElementById("link").innerHTML =
|
|
||||||
"<a href=https://translate.google.com/translate?hl=" +
|
|
||||||
langList.value +
|
|
||||||
"&sl=auto&u=" +
|
|
||||||
encodeURIComponent(url) +
|
|
||||||
">" +
|
|
||||||
browser.i18n.getMessage("showLink") +
|
|
||||||
"</a>";
|
|
||||||
}
|
|
||||||
|
|
||||||
//翻訳元テキストを表示
|
|
||||||
function refleshSource() {
|
|
||||||
if (sourceWord !== "") {
|
|
||||||
textarea.innerHTML = sourceWord;
|
|
||||||
resize();
|
|
||||||
inputText();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea.addEventListener("paste", () => {
|
|
||||||
resize();
|
|
||||||
inputText();
|
|
||||||
});
|
|
||||||
|
|
||||||
textarea.addEventListener("keydown", resize);
|
|
||||||
|
|
||||||
textarea.addEventListener("keyup", function(event) {
|
|
||||||
if (sourceWord == textarea.value) return;
|
|
||||||
|
|
||||||
resize();
|
|
||||||
inputText();
|
|
||||||
});
|
|
||||||
|
|
||||||
//テキストボックスをリサイズ
|
|
||||||
function resize() {
|
|
||||||
setTimeout(function() {
|
|
||||||
textarea.style.height = "0px";
|
|
||||||
textarea.style.height = parseInt(textarea.scrollHeight) + "px";
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea.addEventListener("click", textAreaClick, {
|
|
||||||
once: true
|
|
||||||
});
|
|
||||||
//テキストエリアクリック時の処理
|
|
||||||
function textAreaClick() {
|
|
||||||
textarea.select();
|
|
||||||
}
|
|
||||||
|
|
||||||
let inputTimer;
|
|
||||||
//文字入力時の処理
|
|
||||||
function inputText() {
|
|
||||||
sourceWord = textarea.value;
|
|
||||||
const waitTime = S.get().waitTime;
|
|
||||||
|
|
||||||
clearTimeout(inputTimer);
|
|
||||||
inputTimer = setTimeout(() => {
|
|
||||||
runTranslation();
|
|
||||||
}, waitTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function runTranslation() {
|
|
||||||
if (sourceWord == "") {
|
|
||||||
showResult("", "");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const resultData = await T.translate(sourceWord, "auto", langList.value);
|
|
||||||
changeSecondLang(defaultTargetLang, resultData.sourceLanguage, resultData.percentage);
|
|
||||||
showResult(resultData.resultText, resultData.candidateText, resultData.statusText);
|
|
||||||
}
|
|
||||||
|
|
||||||
function showResult(resultText, candidateText, statusText = "OK") {
|
|
||||||
const resultArea = target.getElementsByClassName("result")[0];
|
|
||||||
const candidateArea = target.getElementsByClassName("candidate")[0];
|
|
||||||
|
|
||||||
resultArea.innerText = resultText;
|
|
||||||
if (S.get().ifShowCandidate) candidateArea.innerText = candidateText;
|
|
||||||
|
|
||||||
if (statusText != "OK") showError(statusText);
|
|
||||||
}
|
|
||||||
|
|
||||||
function showError(statusText) {
|
|
||||||
let errorMessage = "";
|
|
||||||
switch (statusText) {
|
|
||||||
case "":
|
|
||||||
errorMessage = browser.i18n.getMessage("networkError");
|
|
||||||
break;
|
|
||||||
case "Service Unavailable":
|
|
||||||
errorMessage = browser.i18n.getMessage("unavailableError");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
errorMessage = `${browser.i18n.getMessage("unknownError")} [${statusText}]`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const candidateArea = target.getElementsByClassName("candidate")[0];
|
|
||||||
candidateArea.innerText = errorMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
let changeLangFlag = false;
|
|
||||||
|
|
||||||
function changeSecondLang(defaultTargetLang, sourceLang, percentage) {
|
|
||||||
if (!S.get().ifChangeSecondLang) return;
|
|
||||||
//検出された翻訳元言語がターゲット言語と一致
|
|
||||||
const equalsSourceAndTarget = sourceLang == langList.value && percentage > 0;
|
|
||||||
|
|
||||||
//検出された翻訳元言語がデフォルト言語と一致
|
|
||||||
const equalsSourceAndDefault = sourceLang == defaultTargetLang && percentage > 0;
|
|
||||||
|
|
||||||
if (!changeLangFlag) {
|
|
||||||
//通常時
|
|
||||||
if (equalsSourceAndTarget && equalsSourceAndDefault) {
|
|
||||||
//ソースとターゲットとデフォルトが一致する場合
|
|
||||||
//ターゲットを第2言語に変更
|
|
||||||
changeLangFlag = true;
|
|
||||||
langList.value = secondTargetLang;
|
|
||||||
changeLang();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//第2言語に切替した後
|
|
||||||
if (!equalsSourceAndDefault) {
|
|
||||||
//ソースとデフォルトが異なる場合
|
|
||||||
//ターゲットをデフォルトに戻す
|
|
||||||
changeLangFlag = false;
|
|
||||||
langList.value = defaultTargetLang;
|
|
||||||
changeLang();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,227 +0,0 @@
|
||||||
:root {
|
|
||||||
--main-text: #0c0c0d;
|
|
||||||
--sub-text: #737373;
|
|
||||||
--line: #ededf0;
|
|
||||||
--button: #d7d7db;
|
|
||||||
--highlight: #5595ff;
|
|
||||||
--main-bg: #ffffff;
|
|
||||||
--confirm: #ff4f4f;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: "Segoe UI", "San Francisco", "Ubuntu", "Fira Sans", "Roboto", "Arial", "Helvetica",
|
|
||||||
sans-serif;
|
|
||||||
text-align: left;
|
|
||||||
font-size: 13px;
|
|
||||||
width: 348px;
|
|
||||||
overflow: hidden;
|
|
||||||
background-color: var(--main-bg);
|
|
||||||
|
|
||||||
padding: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header {
|
|
||||||
padding: 10px;
|
|
||||||
background-color: var(--line);
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
-moz-user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#title {
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 400;
|
|
||||||
color: #666;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header .rightButtons {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
#donate {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#donate svg {
|
|
||||||
height: 18px;
|
|
||||||
width: 18px;
|
|
||||||
fill: var(--sub-text);
|
|
||||||
transition: all 100ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
#donate:hover svg {
|
|
||||||
fill: var(--confirm);
|
|
||||||
}
|
|
||||||
|
|
||||||
#setting {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#setting svg {
|
|
||||||
flex-shrink: 0;
|
|
||||||
height: 18px;
|
|
||||||
width: 18px;
|
|
||||||
fill: var(--sub-text);
|
|
||||||
transform: rotate(180deg);
|
|
||||||
transition: fill 100ms, transform 300ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
#setting:hover svg {
|
|
||||||
fill: var(--highlight);
|
|
||||||
transform: rotate(270deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
#main {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
font: inherit;
|
|
||||||
resize: none;
|
|
||||||
overflow: auto;
|
|
||||||
background-color: var(--main-bg);
|
|
||||||
|
|
||||||
max-height: 215px;
|
|
||||||
height: 37px;
|
|
||||||
|
|
||||||
/* 100% - padding*2 - border*2 */
|
|
||||||
width: calc(100% - 22px);
|
|
||||||
|
|
||||||
padding: 10px;
|
|
||||||
border: solid 1px var(--button);
|
|
||||||
border-radius: 2px;
|
|
||||||
transition: border-color 100ms ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea:hover,
|
|
||||||
textarea:focus {
|
|
||||||
border-color: var(--highlight);
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
border: none;
|
|
||||||
border-top: solid 1px var(--button);
|
|
||||||
height: 1px;
|
|
||||||
margin: 10px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#target {
|
|
||||||
max-height: 215px;
|
|
||||||
min-height: 30px;
|
|
||||||
overflow-y: auto;
|
|
||||||
word-wrap: break-word;
|
|
||||||
padding: 0px 5px 0px;
|
|
||||||
background-color: var(--main-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
#target p {
|
|
||||||
margin: 0;
|
|
||||||
background-color: var(--main-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
#target .result {
|
|
||||||
background-color: var(--main-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
#target .candidate {
|
|
||||||
color: var(--sub-text);
|
|
||||||
margin-top: 1em;
|
|
||||||
background-color: var(--main-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
#target .candidate:empty {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#footer {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 0px 10px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#link {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#link a {
|
|
||||||
font-style: normal;
|
|
||||||
text-decoration: none;
|
|
||||||
color: var(--highlight);
|
|
||||||
}
|
|
||||||
|
|
||||||
#link a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
-moz-appearance: none;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
border: var(--button) solid 1px;
|
|
||||||
border-radius: 2px;
|
|
||||||
padding: 3px 5px;
|
|
||||||
padding-right: 20px;
|
|
||||||
width: 100%;
|
|
||||||
transition: border-color 100ms ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
select:hover {
|
|
||||||
border: var(--highlight) solid 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectWrap {
|
|
||||||
position: relative;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectWrap:before {
|
|
||||||
pointer-events: none;
|
|
||||||
content: "";
|
|
||||||
z-index: 1;
|
|
||||||
position: absolute;
|
|
||||||
top: 40%;
|
|
||||||
right: 7px;
|
|
||||||
width: 5px;
|
|
||||||
height: 5px;
|
|
||||||
|
|
||||||
transform: rotate(45deg);
|
|
||||||
border-bottom: 2px solid var(--sub-text);
|
|
||||||
border-right: 2px solid var(--sub-text);
|
|
||||||
|
|
||||||
transition: border-color 100ms ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectWrap:hover::before {
|
|
||||||
border-bottom: 2px solid var(--highlight);
|
|
||||||
border-right: 2px solid var(--highlight);
|
|
||||||
}
|
|
||||||
|
|
||||||
::-moz-selection {
|
|
||||||
background: var(--line);
|
|
||||||
}
|
|
57
src/popup/styles/Footer.scss
Normal file
57
src/popup/styles/Footer.scss
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
#footer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0px 10px 10px;
|
||||||
|
|
||||||
|
.translateLink {
|
||||||
|
flex-shrink: 0;
|
||||||
|
a {
|
||||||
|
font-style: normal;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--highlight);
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.selectWrap {
|
||||||
|
position: relative;
|
||||||
|
margin-left: 5px;
|
||||||
|
&:before {
|
||||||
|
pointer-events: none;
|
||||||
|
content: "";
|
||||||
|
z-index: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 40%;
|
||||||
|
right: 7px;
|
||||||
|
width: 5px;
|
||||||
|
height: 5px;
|
||||||
|
|
||||||
|
transform: rotate(45deg);
|
||||||
|
border-bottom: 2px solid var(--sub-text);
|
||||||
|
border-right: 2px solid var(--sub-text);
|
||||||
|
|
||||||
|
transition: border-color 100ms ease-out;
|
||||||
|
}
|
||||||
|
&:hover::before {
|
||||||
|
border-bottom: 2px solid var(--highlight);
|
||||||
|
border-right: 2px solid var(--highlight);
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
-moz-appearance: none;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
background-color: var(--main-bg);
|
||||||
|
border: var(--button) solid 1px;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 3px 5px;
|
||||||
|
padding-right: 20px;
|
||||||
|
width: 100%;
|
||||||
|
transition: border-color 100ms ease-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
61
src/popup/styles/Header.scss
Normal file
61
src/popup/styles/Header.scss
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#header {
|
||||||
|
padding: 10px;
|
||||||
|
background-color: var(--line);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #666;
|
||||||
|
cursor: default;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rightButtons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
height: 18px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
display: block;
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heartButton {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 10px;
|
||||||
|
&:hover svg {
|
||||||
|
fill: var(--confirm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.settingsButton {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
&:hover svg {
|
||||||
|
fill: var(--highlight);
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
height: 18px;
|
||||||
|
width: 18px;
|
||||||
|
fill: var(--sub-text);
|
||||||
|
transition: fill 100ms, transform 300ms ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
src/popup/styles/InputArea.scss
Normal file
26
src/popup/styles/InputArea.scss
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#inputArea {
|
||||||
|
margin: 10px;
|
||||||
|
textarea {
|
||||||
|
font: inherit;
|
||||||
|
resize: none;
|
||||||
|
overflow: auto;
|
||||||
|
background-color: var(--main-bg);
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
height: 60px;
|
||||||
|
max-height: 240px;
|
||||||
|
min-height: 60px;
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px;
|
||||||
|
border: solid 1px var(--button);
|
||||||
|
border-radius: 2px;
|
||||||
|
transition: border-color 100ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea:hover,
|
||||||
|
textarea:focus {
|
||||||
|
border-color: var(--highlight);
|
||||||
|
}
|
||||||
|
}
|
38
src/popup/styles/PopupPage.scss
Normal file
38
src/popup/styles/PopupPage.scss
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: "Segoe UI", "San Francisco", "Ubuntu", "Fira Sans", "Roboto", "Arial", "Helvetica",
|
||||||
|
sans-serif;
|
||||||
|
font-size: 13px;
|
||||||
|
width: 348px;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: var(--main-bg);
|
||||||
|
|
||||||
|
#root {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
border-top: solid 1px var(--button);
|
||||||
|
height: 1px;
|
||||||
|
margin: 0px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-moz-selection {
|
||||||
|
background: var(--line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--main-text: #0c0c0d;
|
||||||
|
--sub-text: #737373;
|
||||||
|
--line: #ededf0;
|
||||||
|
--button: #d7d7db;
|
||||||
|
--highlight: #5595ff;
|
||||||
|
--main-bg: #ffffff;
|
||||||
|
--confirm: #ff4f4f;
|
||||||
|
--error: #d70022;
|
||||||
|
--warn: #ff8f00;
|
||||||
|
--success: #058b00;
|
||||||
|
--info: #0a84ff;
|
||||||
|
}
|
25
src/popup/styles/ResultArea.scss
Normal file
25
src/popup/styles/ResultArea.scss
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#resultArea {
|
||||||
|
max-height: 240px;
|
||||||
|
min-height: 30px;
|
||||||
|
overflow-y: auto;
|
||||||
|
word-wrap: break-word;
|
||||||
|
background-color: var(--main-bg);
|
||||||
|
margin: 10px;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0px 5px;
|
||||||
|
background-color: var(--main-bg);
|
||||||
|
|
||||||
|
&.resultText {
|
||||||
|
color: var(--main-text);
|
||||||
|
}
|
||||||
|
&.candidateText {
|
||||||
|
color: var(--sub-text);
|
||||||
|
margin-top: 1em;
|
||||||
|
&:empty {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue