From 75fd9a855c1a37ca3b3b8b5afb663801e34daf2e Mon Sep 17 00:00:00 2001 From: IRBorisov <8611739+IRBorisov@users.noreply.github.com> Date: Thu, 7 Sep 2023 16:30:43 +0300 Subject: [PATCH] Refactor Checkbox and add Tristate --- .../src/components/Common/Checkbox.tsx | 54 ++++++++-------- .../components/Common/DropdownCheckbox.tsx | 10 +-- .../src/components/Common/FileInput.tsx | 27 ++++---- .../src/components/Common/SubmitButton.tsx | 2 +- .../src/components/Common/TextInput.tsx | 4 +- .../src/components/Common/Tristate.tsx | 64 +++++++++++++++++++ rsconcept/frontend/src/components/Icons.tsx | 24 +++++++ rsconcept/frontend/src/index.css | 4 +- .../frontend/src/pages/CreateRSFormPage.tsx | 2 +- .../src/pages/LibraryPage/PickerStrategy.tsx | 12 ++-- .../src/pages/RSFormPage/DlgCloneRSForm.tsx | 2 +- .../src/pages/RSFormPage/DlgDeleteCst.tsx | 2 +- .../src/pages/RSFormPage/DlgGraphOptions.tsx | 24 +++---- .../src/pages/RSFormPage/DlgUploadRSForm.tsx | 23 +++---- .../src/pages/RSFormPage/EditorRSForm.tsx | 4 +- .../src/pages/RSFormPage/EditorTermGraph.tsx | 6 +- .../src/pages/RSFormPage/RSTabsMenu.tsx | 4 +- 17 files changed, 180 insertions(+), 88 deletions(-) create mode 100644 rsconcept/frontend/src/components/Common/Tristate.tsx diff --git a/rsconcept/frontend/src/components/Common/Checkbox.tsx b/rsconcept/frontend/src/components/Common/Checkbox.tsx index 6c8b11dc..5d2f3f7a 100644 --- a/rsconcept/frontend/src/components/Common/Checkbox.tsx +++ b/rsconcept/frontend/src/components/Common/Checkbox.tsx @@ -1,5 +1,6 @@ -import { useRef } from 'react'; +import { useMemo } from 'react'; +import { CheckboxChecked } from '../Icons'; import Label from './Label'; export interface CheckboxProps { @@ -9,51 +10,52 @@ export interface CheckboxProps { disabled?: boolean widthClass?: string tooltip?: string - value?: boolean - onChange?: (event: React.ChangeEvent) => void + + value: boolean + setValue?: (newValue: boolean) => void } -function Checkbox({ id, required, disabled, tooltip, label, widthClass = 'w-fit', value, onChange }: CheckboxProps) { - const inputRef = useRef(null); - - const cursor = disabled ? 'cursor-not-allowed' : 'cursor-pointer'; +function Checkbox({ id, required, disabled, tooltip, label, widthClass = 'w-fit', value, setValue }: CheckboxProps) { + const cursor = useMemo( + () => { + if (disabled) { + return 'cursor-not-allowed'; + } else if (setValue) { + return 'cursor-pointer'; + } else { + return '' + } + }, [disabled, setValue]); + const bgColor = useMemo( + () => { + return value !== false ? 'clr-primary' : 'clr-app' + }, [value]); function handleClick(event: React.MouseEvent): void { event.preventDefault(); - if (!disabled) { - inputRef.current?.click(); + if (disabled || !setValue) { + return; } + setValue(!value); } return ( ); } diff --git a/rsconcept/frontend/src/components/Common/DropdownCheckbox.tsx b/rsconcept/frontend/src/components/Common/DropdownCheckbox.tsx index 02bb94e2..cd2db6c5 100644 --- a/rsconcept/frontend/src/components/Common/DropdownCheckbox.tsx +++ b/rsconcept/frontend/src/components/Common/DropdownCheckbox.tsx @@ -1,15 +1,15 @@ import Checkbox from './Checkbox'; interface DropdownCheckboxProps { + value: boolean label?: string tooltip?: string disabled?: boolean - value?: boolean - onChange?: (event: React.ChangeEvent) => void + setValue?: (newValue: boolean) => void } -function DropdownCheckbox({ tooltip, onChange, disabled, ...props }: DropdownCheckboxProps) { - const behavior = (onChange && !disabled ? 'clr-hover' : ''); +function DropdownCheckbox({ tooltip, setValue, disabled, ...props }: DropdownCheckboxProps) { + const behavior = (setValue && !disabled ? 'clr-hover' : ''); return (
diff --git a/rsconcept/frontend/src/components/Common/FileInput.tsx b/rsconcept/frontend/src/components/Common/FileInput.tsx index c8c2927b..da805c1e 100644 --- a/rsconcept/frontend/src/components/Common/FileInput.tsx +++ b/rsconcept/frontend/src/components/Common/FileInput.tsx @@ -4,18 +4,22 @@ import { UploadIcon } from '../Icons'; import Button from './Button'; import Label from './Label'; -interface FileInputProps { - id?: string - required?: boolean +interface FileInputProps +extends Omit, 'className' | 'title' | 'style' | 'accept' | 'type'> { label: string + tooltip?: string acceptType?: string widthClass?: string onChange?: (event: React.ChangeEvent) => void } -function FileInput({ id, required, label, acceptType, widthClass = 'w-full', onChange }: FileInputProps) { +function FileInput({ + label, acceptType, tooltip, + widthClass = 'w-fit', onChange, + ...props +}: FileInputProps) { const inputRef = useRef(null); - const [labelText, setLabelText] = useState(''); + const [fileName, setFileName] = useState(''); const handleUploadClick = () => { inputRef.current?.click(); @@ -23,9 +27,9 @@ function FileInput({ id, required, label, acceptType, widthClass = 'w-full', onC const handleFileChange = (event: React.ChangeEvent) => { if (event.target.files && event.target.files.length > 0) { - setLabelText(event.target.files[0].name) + setFileName(event.target.files[0].name) } else { - setLabelText('') + setFileName('') } if (onChange) { onChange(event); @@ -33,21 +37,22 @@ function FileInput({ id, required, label, acceptType, widthClass = 'w-full', onC }; return ( -
- +
); diff --git a/rsconcept/frontend/src/components/Common/SubmitButton.tsx b/rsconcept/frontend/src/components/Common/SubmitButton.tsx index 18c780a4..0d7335cd 100644 --- a/rsconcept/frontend/src/components/Common/SubmitButton.tsx +++ b/rsconcept/frontend/src/components/Common/SubmitButton.tsx @@ -14,7 +14,7 @@ function SubmitButton({ return ( + ); +} + +export default Checkbox; diff --git a/rsconcept/frontend/src/components/Icons.tsx b/rsconcept/frontend/src/components/Icons.tsx index da38c72c..a4345260 100644 --- a/rsconcept/frontend/src/components/Icons.tsx +++ b/rsconcept/frontend/src/components/Icons.tsx @@ -317,3 +317,27 @@ export function InDoor(props: IconProps) { ); } + +export function CheckboxChecked() { + return ( + + + + ); +} + +export function CheckboxNull() { + return ( + + + + ); +} diff --git a/rsconcept/frontend/src/index.css b/rsconcept/frontend/src/index.css index ffed6e75..ef21b2a0 100644 --- a/rsconcept/frontend/src/index.css +++ b/rsconcept/frontend/src/index.css @@ -140,7 +140,6 @@ .clr-footer, .clr-modal-backdrop, .clr-btn-nav, - .clr-checkbox, .clr-input:disabled ) { background-color: var(--cl-bg-100); @@ -168,8 +167,7 @@ :is(.clr-primary, .clr-btn-primary:hover, - .clr-btn-primary:focus, - .clr-checkbox:checked + .clr-btn-primary:focus ) { color: var(--cl-prim-fg-100); background-color: var(--cl-prim-bg-100); diff --git a/rsconcept/frontend/src/pages/CreateRSFormPage.tsx b/rsconcept/frontend/src/pages/CreateRSFormPage.tsx index 8f8099fc..1cb8147b 100644 --- a/rsconcept/frontend/src/pages/CreateRSFormPage.tsx +++ b/rsconcept/frontend/src/pages/CreateRSFormPage.tsx @@ -93,7 +93,7 @@ function CreateRSFormPage() { /> setCommon(event.target.checked)} + setValue={value => setCommon(value ?? false)} /> handleChange(LibraryFilterStrategy.MANUAL)} + setValue={() => handleChange(LibraryFilterStrategy.MANUAL)} value={value === LibraryFilterStrategy.MANUAL} label='Отображать все' /> handleChange(LibraryFilterStrategy.COMMON)} + setValue={() => handleChange(LibraryFilterStrategy.COMMON)} value={value === LibraryFilterStrategy.COMMON} label='Общедоступные' tooltip='Отображать только общедоступные схемы' /> handleChange(LibraryFilterStrategy.CANONICAL)} + setValue={() => handleChange(LibraryFilterStrategy.CANONICAL)} value={value === LibraryFilterStrategy.CANONICAL} label='Неизменные' tooltip='Отображать только стандартные схемы' /> handleChange(LibraryFilterStrategy.PERSONAL)} + setValue={() => handleChange(LibraryFilterStrategy.PERSONAL)} value={value === LibraryFilterStrategy.PERSONAL} label='Личные' disabled={!user} tooltip='Отображать только подписки и владеемые схемы' /> handleChange(LibraryFilterStrategy.SUBSCRIBE)} + setValue={() => handleChange(LibraryFilterStrategy.SUBSCRIBE)} value={value === LibraryFilterStrategy.SUBSCRIBE} label='Подписки' disabled={!user} tooltip='Отображать только подписки' /> handleChange(LibraryFilterStrategy.OWNED)} + setValue={() => handleChange(LibraryFilterStrategy.OWNED)} value={value === LibraryFilterStrategy.OWNED} disabled={!user} label='Я - Владелец!' diff --git a/rsconcept/frontend/src/pages/RSFormPage/DlgCloneRSForm.tsx b/rsconcept/frontend/src/pages/RSFormPage/DlgCloneRSForm.tsx index 12a1e040..4d81ee33 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/DlgCloneRSForm.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/DlgCloneRSForm.tsx @@ -79,7 +79,7 @@ function DlgCloneRSForm({ hideWindow }: DlgCloneRSFormProps) { /> setCommon(event.target.checked)} + setValue={value => setCommon(value)} /> ); diff --git a/rsconcept/frontend/src/pages/RSFormPage/DlgDeleteCst.tsx b/rsconcept/frontend/src/pages/RSFormPage/DlgDeleteCst.tsx index e04144f1..e80a20df 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/DlgDeleteCst.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/DlgDeleteCst.tsx @@ -52,7 +52,7 @@ function DlgDeleteCst({ hideWindow, selected, onDelete }: DlgDeleteCstProps) { setExpandOut(data.target.checked)} + setValue={value => setExpandOut(value)} /> diff --git a/rsconcept/frontend/src/pages/RSFormPage/DlgGraphOptions.tsx b/rsconcept/frontend/src/pages/RSFormPage/DlgGraphOptions.tsx index e946e815..96142bd9 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/DlgGraphOptions.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/DlgGraphOptions.tsx @@ -81,25 +81,25 @@ function DlgGraphOptions({ hideWindow, initial, onConfirm }:DlgGraphOptionsProps label='Скрыть текст' tooltip='Не отображать термины' value={noTerms} - onChange={ event => setNoTerms(event.target.checked) } + setValue={ value => setNoTerms(value) } /> setNoHermits(event.target.checked) } + setValue={ value => setNoHermits(value) } /> setNoTemplates(event.target.checked) } + setValue={ value => setNoTemplates(value) } /> setNoTransitive(event.target.checked) } + setValue={ value => setNoTransitive(value) } />
@@ -107,42 +107,42 @@ function DlgGraphOptions({ hideWindow, initial, onConfirm }:DlgGraphOptionsProps setAllowBase(event.target.checked) } + setValue={ value => setAllowBase(value) } /> setAllowStruct(event.target.checked) } + setValue={ value => setAllowStruct(value) } /> setAllowTerm(event.target.checked) } + setValue={ value => setAllowTerm(value) } /> setAllowAxiom(event.target.checked) } + setValue={ value => setAllowAxiom(value) } /> setAllowFunction(event.target.checked) } + setValue={ value => setAllowFunction(value) } /> setAllowPredicate(event.target.checked) } + setValue={ value => setAllowPredicate(value) } /> setAllowConstant(event.target.checked) } + setValue={ value => setAllowConstant(value) } /> setAllowTheorem(event.target.checked) } + setValue ={ value => setAllowTheorem(value) } />
diff --git a/rsconcept/frontend/src/pages/RSFormPage/DlgUploadRSForm.tsx b/rsconcept/frontend/src/pages/RSFormPage/DlgUploadRSForm.tsx index 36bef8bc..aa2381db 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/DlgUploadRSForm.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/DlgUploadRSForm.tsx @@ -44,17 +44,18 @@ function DlgUploadRSForm({ hideWindow }: DlgUploadRSFormProps) { onSubmit={handleSubmit} submitText='Загрузить' > -
- - setLoadMetadata(event.target.checked)} - /> +
+ + setLoadMetadata(value)} + widthClass='w-fit pb-2' + />
); diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSForm.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSForm.tsx index 7edeaac7..d41902a3 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSForm.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSForm.tsx @@ -136,14 +136,14 @@ function EditorRSForm({ onDestroy, onClaim, onShare, isModified, setIsModified, value={common} widthClass='w-fit mt-3' disabled={!isEditable} - onChange={event => setCommon(event.target.checked)} + setValue={value => setCommon(value)} /> setCanonical(event.target.checked)} + setValue={value => setCanonical(value)} />
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx index 8227a929..de6bd17d 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx @@ -415,18 +415,18 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra setNoTerms(event.target.checked) } + setValue={ value => setNoTerms(value) } /> setNoTransitive(event.target.checked) } + setValue={ value => setNoTransitive(value) } /> setOrbit(event.target.checked) } + setValue={ value => setOrbit(value) } /> diff --git a/rsconcept/frontend/src/pages/RSFormPage/RSTabsMenu.tsx b/rsconcept/frontend/src/pages/RSFormPage/RSTabsMenu.tsx index 5227c9ba..ab817a21 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/RSTabsMenu.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/RSTabsMenu.tsx @@ -146,14 +146,14 @@ function RSTabsMenu({ {(isOwned || user?.is_staff) && } {user?.is_staff && }