From 459fd5f17bc288c632f01047aa57c822474b51fd Mon Sep 17 00:00:00 2001 From: ikkz Date: Sat, 7 Dec 2024 22:53:58 +0800 Subject: [PATCH] feat: front template --- package.json | 1 + pnpm-lock.yaml | 12 ++++ release.json | 16 +++++ src/components/settings.tsx | 112 ++++++++++++------------------- src/entries/tof.tsx | 129 ++++++++++++++++++++++++++++++++++++ templates.json | 5 ++ 6 files changed, 206 insertions(+), 69 deletions(-) create mode 100644 src/entries/tof.tsx diff --git a/package.json b/package.json index e4ce5d9..58e9d21 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "ahooks": "^3.8.1", "clsx": "^2.1.1", "jotai": "^2.9.3", + "lucide-react": "^0.468.0", "preact": "^10.23.2", "react-error-boundary": "^4.1.1", "react-use": "^17.5.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6bf7a08..01513e9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: jotai: specifier: ^2.9.3 version: 2.9.3(@types/react@18.3.5)(react@18.3.1) + lucide-react: + specifier: ^0.468.0 + version: 0.468.0(react@18.3.1) preact: specifier: ^10.23.2 version: 10.23.2 @@ -1913,6 +1916,11 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lucide-react@0.468.0: + resolution: {integrity: sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc + magic-string@0.30.11: resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} @@ -5228,6 +5236,10 @@ snapshots: lru-cache@10.4.3: {} + lucide-react@0.468.0(react@18.3.1): + dependencies: + react: 18.3.1 + magic-string@0.30.11: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 diff --git a/release.json b/release.json index 9fec568..cf37db7 100644 --- a/release.json +++ b/release.json @@ -33,5 +33,21 @@ } } ] + }, + "tof": { + "id": { + "zh": 1356462000, + "en": 1693362483 + }, + "deck": 1598429921, + "notes": [ + { + "fields": { + "question": "This is the stem of the question. It supports various content formats in Anki, including bold, formulas, etc.", + "items": "", + "note": "note" + } + } + ] } } diff --git a/src/components/settings.tsx b/src/components/settings.tsx index 017fc89..fde6aa7 100644 --- a/src/components/settings.tsx +++ b/src/components/settings.tsx @@ -30,19 +30,53 @@ export const blurOptionsAtom = atomWithLocalStorage( false, ); +const CommonOptions: FC = () => { + const [selectionMenu, setSelectionMenu] = useAtom(selectionMenuAtom); + const [hideAbout, setHideAbout] = useAtom(hideAboutAtom); + const [biggerText, setBiggerText] = useAtom(biggerTextAtom); + const [hideTimer, setHideTimer] = useAtom(hideTimerAtom); + const [noScorll, setNoScorll] = useAtom(noScorllAtom); + + return ( + <> + + + + + + + ); +}; + let OptionList: FC; // these branches can be treeshaken by rollup if (id === 'mcq') { OptionList = () => { const [randomOptions, setRandomOptions] = useAtom(randomOptionsAtom); - const [selectionMenu, setSelectionMenu] = useAtom(selectionMenuAtom); - const [hideAbout, setHideAbout] = useAtom(hideAboutAtom); - const [biggerText, setBiggerText] = useAtom(biggerTextAtom); - const [hideTimer, setHideTimer] = useAtom(hideTimerAtom); const [hideQuestionType, setHideQuestionType] = useAtom(hideQuestionTypeAtom); - const [noScorll, setNoScorll] = useAtom(noScorllAtom); const [blurOptions, setBlurOptions] = useAtom(blurOptionsAtom); return ( @@ -53,86 +87,26 @@ if (id === 'mcq') { onChange={setHideQuestionType} subtitle={t('hideQuestionTypeDetail')} /> - - - - - + ); }; } else if (id === 'basic') { - OptionList = () => { - const [selectionMenu, setSelectionMenu] = useAtom(selectionMenuAtom); - const [hideAbout, setHideAbout] = useAtom(hideAboutAtom); - const [biggerText, setBiggerText] = useAtom(biggerTextAtom); - const [hideTimer, setHideTimer] = useAtom(hideTimerAtom); - const [noScorll, setNoScorll] = useAtom(noScorllAtom); - - return ( - <> - - - - - - - ); - }; + OptionList = CommonOptions; +} else if (id === 'tof') { + OptionList = CommonOptions; } else { OptionList = () => null; } diff --git a/src/entries/tof.tsx b/src/entries/tof.tsx new file mode 100644 index 0000000..1959f6d --- /dev/null +++ b/src/entries/tof.tsx @@ -0,0 +1,129 @@ +import { CardShell } from '@/components/card-shell'; +import { AnkiField } from '@/components/field'; +import { useBack } from '@/hooks/use-back'; +import { useCrossState } from '@/hooks/use-cross-state'; +import { FIELD_ID } from '@/utils/const'; +import { isFieldEmpty } from '@/utils/field'; +import { t } from '@/utils/locale'; +import useCreation from 'ahooks/es/useCreation'; +import clsx from 'clsx'; +import { CheckCircle, XCircle } from 'lucide-react'; +import { useCallback } from 'react'; + +interface ItemProp { + index: number; + node: HTMLDivElement; + answer?: boolean; +} + +const Item = ({ node, answer, index }: ItemProp) => { + const attachNode = useCallback( + (ref: HTMLDivElement) => { + if (node && ref) { + node.remove(); + ref.appendChild(node); + } + }, + [node], + ); + + const back = useBack(); + const [status, setStatus] = useCrossState( + `status-${index}`, + undefined, + ); + + return ( +
+
+
+
setStatus(true)} + > + +
+
setStatus(false)} + > + +
+
+
+ ); +}; + +export default () => { + const items = useCreation(() => { + const field = document.getElementById(FIELD_ID('items')); + if (!field) { + return null; + } + const itemNodes = field + .querySelector('ul') + ?.querySelectorAll(':scope > li'); + if (!itemNodes?.length) { + return null; + } + return Array.from(itemNodes).map((node, idx) => { + const answer = !node.textContent?.startsWith('F:'); + const container = document.createElement('div'); + container.append(...Array.from(node.childNodes)); + + let firstTextNode: Node | null = container; + while (firstTextNode && firstTextNode.nodeType !== Node.TEXT_NODE) { + firstTextNode = firstTextNode.firstChild; + } + do { + if (!firstTextNode) { + break; + } + const match = firstTextNode.textContent?.match(/^(T|F):\s*/); + if (!match) { + break; + } + const range = document.createRange(); + range.setStart(firstTextNode, 0); + range.setEnd(firstTextNode, match[0].length); + range.deleteContents(); + // eslint-disable-next-line no-constant-condition + } while (false); + return ; + }); + }, []); + const hasNote = !isFieldEmpty(FIELD_ID('note')); + + return ( + + ) : null + } + /> + ); +}; diff --git a/templates.json b/templates.json index 0a22216..1ef3e22 100644 --- a/templates.json +++ b/templates.json @@ -19,5 +19,10 @@ "id": "basic", "name": "Basic", "fields": ["question", "answer", "note", "Tags"] + }, + "tof": { + "id": "tof", + "name": "True or False", + "fields": ["question", "items", "note", "Tags"] } }