From 851843b0d0f9b528272f17e3bd5e077f910647b3 Mon Sep 17 00:00:00 2001 From: graphemecluster Date: Thu, 3 Oct 2024 15:16:18 +0800 Subject: [PATCH] Fix some ESLint problems --- src/Classes/CustomElement.tsx | 6 ++--- src/Components/App.tsx | 7 +++--- src/Components/CreateSchemaDialog.tsx | 23 +++++++++--------- src/Components/Main.tsx | 3 ++- src/Components/SchemaEditor.tsx | 35 +++++++++++++-------------- src/Components/Tooltip.tsx | 11 +++++---- src/Components/TooltipChar.tsx | 8 +++--- src/Yitizi.d.ts | 2 +- src/actions.ts | 2 +- src/consts.ts | 7 +++--- src/evaluate.ts | 9 ++++--- src/options.tsx | 3 +-- src/utils.tsx | 4 +-- 13 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/Classes/CustomElement.tsx b/src/Classes/CustomElement.tsx index 4fa2157..4259182 100644 --- a/src/Classes/CustomElement.tsx +++ b/src/Classes/CustomElement.tsx @@ -1,10 +1,10 @@ import { createElement, Fragment } from "react"; -import type { HTMLAttributes, ReactElement } from "react"; import styled from "@emotion/styled"; import type { ReactNode } from "../consts"; import type { Property } from "csstype"; +import type { HTMLAttributes, ReactElement } from "react"; const Missing = styled.span` &:after { @@ -12,7 +12,7 @@ const Missing = styled.span` } `; -type TagToProp = { +interface TagToProp { f: undefined; b: undefined; i: undefined; @@ -23,7 +23,7 @@ type TagToProp = { fg: Property.Color; bg: Property.BackgroundColor; size: Property.FontSize; -}; +} type AllTags = keyof TagToProp; type Tag = { diff --git a/src/Components/App.tsx b/src/Components/App.tsx index 3a6c004..10663d4 100644 --- a/src/Components/App.tsx +++ b/src/Components/App.tsx @@ -1,6 +1,7 @@ import "purecss/build/pure.css"; - import { useCallback, useRef } from "react"; +// NOTE sweetalert2's ESM export does not setup styles properly, manually importing +import "sweetalert2/dist/sweetalert2.css"; import { injectGlobal, css as stylesheet } from "@emotion/css"; import styled from "@emotion/styled"; @@ -11,9 +12,7 @@ import Main from "./Main"; import Swal from "../Classes/SwalReact"; import { codeFontFamily, noop } from "../consts"; -// NOTE sweetalert2's ESM export does not setup styles properly, manually importing -import "sweetalert2/dist/sweetalert2.css"; - +// eslint-disable-next-line @typescript-eslint/no-unused-expressions injectGlobal` html, body { diff --git a/src/Components/CreateSchemaDialog.tsx b/src/Components/CreateSchemaDialog.tsx index 17e0fe5..237d13f 100644 --- a/src/Components/CreateSchemaDialog.tsx +++ b/src/Components/CreateSchemaDialog.tsx @@ -1,4 +1,4 @@ -import { ChangeEventHandler, forwardRef, RefObject, useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { css } from "@emotion/react"; @@ -10,11 +10,12 @@ import ExplorerFolder from "./ExplorerFolder"; import Spinner from "./Spinner"; import actions from "../actions"; import Swal from "../Classes/SwalReact"; -import { invalidCharsRegex, newFileTemplate, tshetUinhExamplesURLPrefix, UseMainState } from "../consts"; +import { invalidCharsRegex, newFileTemplate, tshetUinhExamplesURLPrefix } from "../consts"; import samples from "../samples"; import { fetchFile, normalizeFileName } from "../utils"; -import type { Folder, Sample, SchemaState } from "../consts"; +import type { Folder, Sample, SchemaState, UseMainState } from "../consts"; +import type { ChangeEventHandler, RefObject } from "react"; const Container = styled.dialog` transform: scale(0.9); @@ -183,12 +184,12 @@ const CreateSchemaDialog = forwardRef getDefaultFileName("") + ".js"); + const [createSchemaName, setCreateSchemaName] = useState(() => `${getDefaultFileName("")}.js`); const [createSchemaSample, setCreateSchemaSample] = useState(""); const [loading, setLoading] = useState(false); const resetDialog = useCallback(() => { - setCreateSchemaName(getDefaultFileName("") + ".js"); + setCreateSchemaName(`${getDefaultFileName("")}.js`); setCreateSchemaSample(""); setLoading(false); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -199,7 +200,7 @@ const CreateSchemaDialog = forwardRef { const name = normalizeFileName(createSchemaName); if (!name || name === getDefaultFileName(createSchemaSample)) - setCreateSchemaName(getDefaultFileName(sample) + ".js"); + setCreateSchemaName(`${getDefaultFileName(sample)}.js`); setCreateSchemaSample(sample); }}>
@@ -236,9 +237,9 @@ const CreateSchemaDialog = forwardRef { const name = normalizeFileName(createSchemaName); if (!name || name === getDefaultFileName(createSchemaSample)) - setCreateSchemaName(getDefaultFileName("") + ".js"); + setCreateSchemaName(`${getDefaultFileName("")}.js`); setCreateSchemaSample(""); }}>
diff --git a/src/Components/Main.tsx b/src/Components/Main.tsx index a21c944..ade7aa5 100644 --- a/src/Components/Main.tsx +++ b/src/Components/Main.tsx @@ -1,4 +1,4 @@ -import { MutableRefObject, useCallback, useEffect, useReducer, useRef, useState } from "react"; +import { useCallback, useEffect, useReducer, useRef, useState } from "react"; import { createPortal } from "react-dom"; import styled from "@emotion/styled"; @@ -16,6 +16,7 @@ import initialState, { stateStorageLocation } from "../state"; import { copy, notifyError } from "../utils"; import type { MainState, Option, ReactNode } from "../consts"; +import type { MutableRefObject } from "react"; const dummyOutput = document.createElement("output"); diff --git a/src/Components/SchemaEditor.tsx b/src/Components/SchemaEditor.tsx index 9f37192..c2a554d 100644 --- a/src/Components/SchemaEditor.tsx +++ b/src/Components/SchemaEditor.tsx @@ -1,5 +1,4 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import type { MouseEvent, MutableRefObject } from "react"; import { createPortal } from "react-dom"; import { css } from "@emotion/react"; @@ -18,6 +17,7 @@ import "../editor/setup"; import { memoize, normalizeFileName, notifyError } from "../utils"; import type { UseMainState, ReactNode } from "../consts"; +import type { MouseEvent, MutableRefObject } from "react"; const TabBar = styled.div` display: flex; @@ -234,10 +234,10 @@ export default function SchemaEditor({ state, setState, commonOptions, evaluateH sample ||= "untitled"; const indices = schemaNames .map(name => { - if (name === sample + ".js") return 0; - if (!name.startsWith(sample + "-") || !name.endsWith(".js")) return -1; + if (name === `${sample}.js`) return 0; + if (!name.startsWith(`${sample}-`) || !name.endsWith(".js")) return -1; const start = sample.length + 1; - for (let i = start; i < name.length - 3; i++) if (name[i] < +(i === start) + "" || name[i] > "9") return -1; + for (let i = start; i < name.length - 3; i++) if (name[i] < `${+(i === start)}` || name[i] > "9") return -1; return +name.slice(start, -3); }) .sort((a, b) => a - b); @@ -268,10 +268,9 @@ export default function SchemaEditor({ state, setState, commonOptions, evaluateH for (const file of files) { // POSIX allows all characters other than `\0` and `/` in file names, // this is necessary to ensure that the file name is valid on all platforms. - const name = - getDefaultFileNameWithSchemaNames(currSchemaNames)( - normalizeFileName(file.name).replace(invalidCharsRegex, "_"), - ) + ".js"; + const name = `${getDefaultFileNameWithSchemaNames(currSchemaNames)( + normalizeFileName(file.name).replace(invalidCharsRegex, "_"), + )}.js`; currSchemaNames.push(name); newState = actions.addSchema({ name, input: contents[i++] })(newState); } @@ -408,7 +407,7 @@ export default function SchemaEditor({ state, setState, commonOptions, evaluateH const index = schemas.findIndex(schema => schema.name === name); const children = [].slice.call(tabBarRef.current.children, 0, -1) as HTMLElement[]; const widths = children.map(element => element.getBoundingClientRect().width); - const currentWidth = widths[index] + "px"; + const currentWidth = `${widths[index]}px`; const threshold: number[] = []; threshold[index] = 0; @@ -424,9 +423,9 @@ export default function SchemaEditor({ state, setState, commonOptions, evaluateH let clientX = startX; function move(event: { clientX: number } | TouchEvent) { - clientX = "clientX" in event ? event.clientX : (event.touches?.[0]?.clientX ?? clientX); + clientX = "clientX" in event ? event.clientX : (event.touches[0].clientX ?? clientX); let value = clientX - startX; - children[index].style.left = value + "px"; + children[index].style.left = `${value}px`; if (value < 0) { value = -value; for (let i = 0; i < index; i++) children[i].style.left = value >= threshold[i] ? currentWidth : ""; @@ -434,12 +433,12 @@ export default function SchemaEditor({ state, setState, commonOptions, evaluateH } else { for (let i = 0; i < index; i++) children[i].style.left = ""; for (let i = length - 1; i > index; i--) - children[i].style.left = value >= threshold[i] ? "-" + currentWidth : ""; + children[i].style.left = value >= threshold[i] ? `-${currentWidth}` : ""; } } function end(event: { clientX: number } | TouchEvent) { - clientX = "clientX" in event ? event.clientX : (event.touches?.[0]?.clientX ?? clientX); + clientX = "clientX" in event ? event.clientX : (event.touches[0].clientX ?? clientX); let value = clientX - startX; children.forEach(element => (element.style.left = "")); let i: number; @@ -475,7 +474,7 @@ export default function SchemaEditor({ state, setState, commonOptions, evaluateH const hasSchemaName = (name: string) => schemas.find(schema => schema.name === name); if (!name) return "檔案名稱為空"; if (invalidCharsRegex.test(name)) return "檔案名稱含有特殊字元"; - if (hasSchemaName(name + ".js")) return "檔案名稱與現有檔案重複"; + if (hasSchemaName(`${name}.js`)) return "檔案名稱與現有檔案重複"; return ""; } @@ -497,15 +496,15 @@ export default function SchemaEditor({ state, setState, commonOptions, evaluateH confirmButtonText: "確定", cancelButtonText: "取消", }); - const confirmButton = Swal.getConfirmButton() as HTMLButtonElement; + const confirmButton = Swal.getConfirmButton()!; confirmButton.disabled = true; confirmButton.style.pointerEvents = "none"; - const input = Swal.getInput() as HTMLInputElement; + const input = Swal.getInput()!; input.addEventListener("input", () => { const newName = normalizeFileName(input.value); const validation = validateFileName(newName); if (validation) { - if (newName + ".js" !== name) { + if (`${newName}.js` !== name) { const { selectionStart, selectionEnd, selectionDirection } = input; Swal.showValidationMessage(validation); input.setSelectionRange(selectionStart, selectionEnd, selectionDirection || undefined); @@ -521,7 +520,7 @@ export default function SchemaEditor({ state, setState, commonOptions, evaluateH const { isConfirmed, value } = await promise; if (isConfirmed) { const newName = normalizeFileName(value); - if (!validateFileName(newName)) setState(actions.renameSchema(name, newName + ".js")); + if (!validateFileName(newName)) setState(actions.renameSchema(name, `${newName}.js`)); } } } diff --git a/src/Components/Tooltip.tsx b/src/Components/Tooltip.tsx index 1a5624b..e51f1de 100644 --- a/src/Components/Tooltip.tsx +++ b/src/Components/Tooltip.tsx @@ -1,9 +1,10 @@ -import { cloneElement, ReactElement, useCallback, useEffect, useRef } from "react"; -import type { SyntheticEvent } from "react"; +import { cloneElement, useCallback, useEffect, useRef } from "react"; import { createRoot } from "react-dom/client"; import { css as stylesheet } from "@emotion/css"; +import type { ReactElement, SyntheticEvent } from "react"; + function getPageWidth() { return Math.max( document.body.scrollWidth, @@ -68,9 +69,9 @@ function TooltipAnchor({ targetLeft = Math.min(getPageWidth() - oneRemSize - divInnerBox.width, Math.max(oneRemSize, targetLeft)); targetLeft += window.scrollX; - div.className = tooltipStyle + (fixedWidth ? " " + fixedWidthStyle : ""); - div.style.top = targetTop + "px"; - div.style.left = targetLeft + "px"; + div.className = tooltipStyle + (fixedWidth ? ` ${fixedWidthStyle}` : ""); + div.style.top = `${targetTop}px`; + div.style.left = `${targetLeft}px`; div.style.visibility = "visible"; }); diff --git a/src/Components/TooltipChar.tsx b/src/Components/TooltipChar.tsx index 445d984..e3cfd6d 100644 --- a/src/Components/TooltipChar.tsx +++ b/src/Components/TooltipChar.tsx @@ -1,5 +1,4 @@ import { Fragment, useState } from "react"; - import { 資料 } from "tshet-uinh"; import { css } from "@emotion/react"; @@ -112,10 +111,9 @@ export default function TooltipChar({ } } 各反切.反 = new Set(Array.from(各反切.反).filter(反切 => !各反切.切.has(反切))); - const 反切text = - (["反", "切"] as const) - .flatMap(x => (各反切[x].size ? [[...各反切[x]].join("/") + x] : [])) - .join(" ") + " "; + const 反切text = `${(["反", "切"] as const) + .flatMap(x => (各反切[x].size ? [[...各反切[x]].join("/") + x] : [])) + .join(" ")} `; const 出處text = 來源 && ["廣韻", "王三"].includes(來源.文獻) ? `[${來源.文獻} ${來源.韻目}韻]` : ""; return ( diff --git a/src/Yitizi.d.ts b/src/Yitizi.d.ts index 84abfad..18e801d 100644 --- a/src/Yitizi.d.ts +++ b/src/Yitizi.d.ts @@ -1,4 +1,4 @@ declare module "yitizi" { - export const yitiziData: { [c: string]: string }; + export const yitiziData: Record; export function get(c: string): string[]; } diff --git a/src/actions.ts b/src/actions.ts index ca8b44b..841b907 100644 --- a/src/actions.ts +++ b/src/actions.ts @@ -58,7 +58,7 @@ export default { const schemas = [...state.schemas]; const index = schemas.findIndex(schema => schema.name === name); const newState = { ...schemas[index], input }; - newState.parameters = newState.parameters?.refresh(input) || ParameterSet.from(input); + newState.parameters = newState.parameters.refresh(input) || ParameterSet.from(input); schemas[index] = newState; return { ...state, schemas }; }, diff --git a/src/consts.ts b/src/consts.ts index 3b7f0a0..978f7df 100644 --- a/src/consts.ts +++ b/src/consts.ts @@ -1,8 +1,7 @@ -import type { Dispatch, DispatchWithoutAction, ReactChild, ReactPortal, SetStateAction } from "react"; - import type { CustomNode } from "./Classes/CustomElement"; import type ParameterSet from "./Classes/ParameterSet"; import type samples from "./samples"; +import type { Dispatch, DispatchWithoutAction, ReactChild, ReactPortal, SetStateAction } from "react"; import type { 資料 } from "tshet-uinh"; export const tshetUinhExamplesURLPrefix = "https://cdn.jsdelivr.net/gh/nk2028/tshet-uinh-examples@main/"; @@ -85,7 +84,9 @@ export type Query = Readonly = T extends Record ? Values : T; export type Sample = Values; -export type Folder = { [name: string]: Folder | Sample }; +export interface Folder { + [name: string]: Folder | Sample; +} type UseGet = { [P in K]: T }; type UseSet = { [P in `set${Capitalize}`]: Dispatch> }; diff --git a/src/evaluate.ts b/src/evaluate.ts index 34001ef..acd1cad 100644 --- a/src/evaluate.ts +++ b/src/evaluate.ts @@ -1,10 +1,11 @@ import { 推導方案 } from "tshet-uinh-deriver-tools"; -import { CustomNode, Formatter } from "./Classes/CustomElement"; +import { Formatter } from "./Classes/CustomElement"; import { tshetUinhTextLabelURLPrefix } from "./consts"; import { evaluateOption, getArticle, setArticle } from "./options"; import { fetchFile, normalizeFileName, notifyError } from "./utils"; +import type { CustomNode } from "./Classes/CustomElement"; import type { MainState, ReactNode } from "./consts"; import type { 音韻地位 } from "tshet-uinh"; import type { 原始推導函數, 推導函數 } from "tshet-uinh-deriver-tools"; @@ -26,7 +27,7 @@ export default async function evaluate(state: MainState): Promise { const { schemas, option } = state; if (option === "convertPresetArticle" && !getArticle()) - setArticle(await fetchFile(tshetUinhTextLabelURLPrefix + "index.txt")); + setArticle(await fetchFile(`${tshetUinhTextLabelURLPrefix}index.txt`)); else if (option === "compareSchemas" && schemas.length < 2) throw notifyError("此選項需要兩個或以上方案"); else await new Promise(resolve => setTimeout(resolve)); @@ -47,9 +48,9 @@ export default async function evaluate(state: MainState): Promise { function require(current: string, references: string[] = []): Require { const newReferences = references.concat(current); - if (references.includes(current)) throw notifyError("Circular reference detected: " + newReferences.join(" -> ")); + if (references.includes(current)) throw notifyError(`Circular reference detected: ${newReferences.join(" -> ")}`); return (音韻地位, 字頭) => sample => { - const schema = schemas.find(({ name }) => name === normalizeFileName(sample) + ".js"); + const schema = schemas.find(({ name }) => name === `${normalizeFileName(sample)}.js`); if (!schema) throw notifyError("Schema not found"); return new SchemaFromRequire(rawDeriverFrom(schema.input), require(sample, newReferences), 音韻地位, 字頭); }; diff --git a/src/options.tsx b/src/options.tsx index 6540dbb..c996420 100644 --- a/src/options.tsx +++ b/src/options.tsx @@ -1,5 +1,4 @@ import { Fragment } from "react"; - import { 資料, 音韻地位 } from "tshet-uinh"; import Yitizi from "yitizi"; @@ -192,7 +191,7 @@ export const evaluateOption: Record = { return ( b[2] - a[2]).map(([, 擬音陣列, count]) => [...wrap(擬音陣列), count + ""])} + body={result.sort((a, b) => b[2] - a[2]).map(([, 擬音陣列, count]) => [...wrap(擬音陣列), `${count}`])} /> ); }, diff --git a/src/utils.tsx b/src/utils.tsx index 13488fe..32ba010 100644 --- a/src/utils.tsx +++ b/src/utils.tsx @@ -26,10 +26,10 @@ export function notifyError(msg: string, err?: unknown) { let curErr: Error = err; while (curErr.cause instanceof Error) { curErr = curErr.cause; - technical += "\n" + curErr.message; + technical += `\n${curErr.message}`; } if (curErr.stack) { - technical += "\n\n" + curErr.stack; + technical += `\n\n${curErr.stack}`; } } const config: SweetAlertOptions = {