Skip to content

Commit

Permalink
feat: front template
Browse files Browse the repository at this point in the history
  • Loading branch information
ikkz committed Dec 7, 2024
1 parent 05bb7a6 commit 459fd5f
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 69 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
12 changes: 12 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions release.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "<ul><li>T: All sub-questions should be in an unordered list format</li><li>T: Each sub-question must begin with \"T:\" or \"F:\", indicating whether the sub-question is true or false<br></li><li>T: Pay special attention to ensuring \"T/F\" is followed by an English half-width colon<br></li></ul>",
"note": "note"
}
}
]
}
}
112 changes: 43 additions & 69 deletions src/components/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,53 @@ export const blurOptionsAtom = atomWithLocalStorage<boolean>(
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 (
<>
<Checkbox
title={t('biggerText')}
checked={biggerText}
onChange={setBiggerText}
/>
<Checkbox
title={t('noScroll')}
checked={noScorll}
onChange={setNoScorll}
/>
<Checkbox
title={t('selMenu')}
subtitle={t('selMenuDetail')}
checked={selectionMenu}
onChange={setSelectionMenu}
/>
<Checkbox
title={t('hideTimer')}
checked={hideTimer}
onChange={setHideTimer}
/>
<Checkbox
title={t('hideAbout')}
checked={hideAbout}
onChange={setHideAbout}
/>
</>
);
};

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 (
Expand All @@ -53,86 +87,26 @@ if (id === 'mcq') {
onChange={setHideQuestionType}
subtitle={t('hideQuestionTypeDetail')}
/>
<Checkbox
title={t('biggerText')}
checked={biggerText}
onChange={setBiggerText}
/>
<Checkbox
title={t('randomOption')}
subtitle={t('randomOptionDetail')}
checked={randomOptions}
onChange={setRandomOptions}
/>
<Checkbox
title={t('noScroll')}
checked={noScorll}
onChange={setNoScorll}
/>
<Checkbox
title={t('selMenu')}
subtitle={t('selMenuDetail')}
checked={selectionMenu}
onChange={setSelectionMenu}
/>
<Checkbox
title={t('blurOptions')}
subtitle={t('blurOptionsDetail')}
checked={blurOptions}
onChange={setBlurOptions}
/>
<Checkbox
title={t('hideTimer')}
checked={hideTimer}
onChange={setHideTimer}
/>
<Checkbox
title={t('hideAbout')}
checked={hideAbout}
onChange={setHideAbout}
/>
<CommonOptions />
</>
);
};
} 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 (
<>
<Checkbox
title={t('biggerText')}
checked={biggerText}
onChange={setBiggerText}
/>
<Checkbox
title={t('noScroll')}
checked={noScorll}
onChange={setNoScorll}
/>
<Checkbox
title={t('selMenu')}
subtitle={t('selMenuDetail')}
checked={selectionMenu}
onChange={setSelectionMenu}
/>
<Checkbox
title={t('hideTimer')}
checked={hideTimer}
onChange={setHideTimer}
/>
<Checkbox
title={t('hideAbout')}
checked={hideAbout}
onChange={setHideAbout}
/>
</>
);
};
OptionList = CommonOptions;
} else if (id === 'tof') {
OptionList = CommonOptions;
} else {
OptionList = () => null;
}
Expand Down
129 changes: 129 additions & 0 deletions src/entries/tof.tsx
Original file line number Diff line number Diff line change
@@ -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) => {

Check failure on line 19 in src/entries/tof.tsx

View workflow job for this annotation

GitHub Actions / lint

'answer' is defined but never used

Check failure on line 19 in src/entries/tof.tsx

View workflow job for this annotation

GitHub Actions / lint

'answer' is defined but never used
const attachNode = useCallback(
(ref: HTMLDivElement) => {
if (node && ref) {
node.remove();
ref.appendChild(node);
}
},
[node],
);

const back = useBack();

Check failure on line 30 in src/entries/tof.tsx

View workflow job for this annotation

GitHub Actions / lint

'back' is assigned a value but never used

Check failure on line 30 in src/entries/tof.tsx

View workflow job for this annotation

GitHub Actions / lint

'back' is assigned a value but never used
const [status, setStatus] = useCrossState<boolean | undefined>(
`status-${index}`,
undefined,
);

return (
<div className="rounded-xl bg-indigo-50 px-4 py-2 mt-4 flex items-center justify-between">
<div
ref={attachNode}
className={clsx(
'prose prose-neutral dark:prose-invert',
'flex-grow mr-2',
)}
/>
<div className="flex space-x-2">
<div
className={clsx(
'p-2 rounded-full cursor-pointer',
'transition-transform hover:scale-105 active:scale-95',
status === true
? 'bg-green-500 text-white'
: 'bg-indigo-100 text-gray-600',
)}
onClick={() => setStatus(true)}
>
<CheckCircle size={24} />
</div>
<div
className={clsx(
'p-2 rounded-full cursor-pointer',
'transition-transform hover:scale-105 active:scale-95',
status === false
? 'bg-red-500 text-white'
: 'bg-indigo-100 text-gray-600',
)}
onClick={() => setStatus(false)}
>
<XCircle size={24} />
</div>
</div>
</div>
);
};

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 <Item index={idx} key={idx} node={container} answer={answer} />;
});
}, []);
const hasNote = !isFieldEmpty(FIELD_ID('note'));

return (
<CardShell
title={t('question')}
questionExtra={items}
answer={
hasNote ? (
<AnkiField
name="note"
className={clsx('prose prose-sm mt-3', 'dark:prose-invert')}
/>
) : null
}
/>
);
};
5 changes: 5 additions & 0 deletions templates.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
}
}

0 comments on commit 459fd5f

Please sign in to comment.