R: Unify components API for user inputs

This commit is contained in:
Ivan 2025-02-04 20:35:18 +03:00
parent 4cf24d0200
commit e0abbe6534
55 changed files with 193 additions and 190 deletions

View File

@ -17,6 +17,9 @@ import { describeConstituenta } from '@/utils/labels';
interface PickConstituentaProps extends CProps.Styling { interface PickConstituentaProps extends CProps.Styling {
id?: string; id?: string;
value?: IConstituenta;
onChange: (newValue: IConstituenta) => void;
prefixID: string; prefixID: string;
data?: IConstituenta[]; data?: IConstituenta[];
rows?: number; rows?: number;
@ -25,9 +28,6 @@ interface PickConstituentaProps extends CProps.Styling {
onBeginFilter?: (cst: IConstituenta) => boolean; onBeginFilter?: (cst: IConstituenta) => boolean;
describeFunc?: (cst: IConstituenta) => string; describeFunc?: (cst: IConstituenta) => string;
matchFunc?: (cst: IConstituenta, filter: string) => boolean; matchFunc?: (cst: IConstituenta, filter: string) => boolean;
value?: IConstituenta;
onSelectValue: (newValue: IConstituenta) => void;
} }
const columnHelper = createColumnHelper<IConstituenta>(); const columnHelper = createColumnHelper<IConstituenta>();
@ -42,7 +42,7 @@ function PickConstituenta({
describeFunc = describeConstituenta, describeFunc = describeConstituenta,
matchFunc = (cst, filter) => matchConstituenta(cst, filter, CstMatchMode.ALL), matchFunc = (cst, filter) => matchConstituenta(cst, filter, CstMatchMode.ALL),
onBeginFilter, onBeginFilter,
onSelectValue, onChange,
className, className,
...restProps ...restProps
}: PickConstituentaProps) { }: PickConstituentaProps) {
@ -110,7 +110,7 @@ function PickConstituenta({
<p>Измените параметры фильтра</p> <p>Измените параметры фильтра</p>
</NoData> </NoData>
} }
onRowClicked={onSelectValue} onRowClicked={onChange}
/> />
</div> </div>
); );

View File

@ -18,15 +18,15 @@ import ToolbarGraphSelection from './ToolbarGraphSelection';
interface PickMultiConstituentaProps extends CProps.Styling { interface PickMultiConstituentaProps extends CProps.Styling {
id?: string; id?: string;
value: ConstituentaID[];
onChange: React.Dispatch<React.SetStateAction<ConstituentaID[]>>;
schema: IRSForm; schema: IRSForm;
data: IConstituenta[]; data: IConstituenta[];
prefixID: string; prefixID: string;
rows?: number; rows?: number;
noBorder?: boolean; noBorder?: boolean;
selected: ConstituentaID[];
setSelected: React.Dispatch<React.SetStateAction<ConstituentaID[]>>;
} }
const columnHelper = createColumnHelper<IConstituenta>(); const columnHelper = createColumnHelper<IConstituenta>();
@ -38,8 +38,8 @@ function PickMultiConstituenta({
prefixID, prefixID,
rows, rows,
noBorder, noBorder,
selected, value,
setSelected, onChange,
className, className,
...restProps ...restProps
}: PickMultiConstituentaProps) { }: PickMultiConstituentaProps) {
@ -74,10 +74,10 @@ function PickMultiConstituenta({
} }
const newRowSelection: RowSelectionState = {}; const newRowSelection: RowSelectionState = {};
filtered.forEach((cst, index) => { filtered.forEach((cst, index) => {
newRowSelection[String(index)] = selected.includes(cst.id); newRowSelection[String(index)] = value.includes(cst.id);
}); });
setRowSelection(newRowSelection); setRowSelection(newRowSelection);
}, [filtered, setRowSelection, selected]); }, [filtered, setRowSelection, value]);
useEffect(() => { useEffect(() => {
if (data.length === 0) { if (data.length === 0) {
@ -91,7 +91,7 @@ function PickMultiConstituenta({
function handleRowSelection(updater: React.SetStateAction<RowSelectionState>) { function handleRowSelection(updater: React.SetStateAction<RowSelectionState>) {
if (!data) { if (!data) {
setSelected([]); onChange([]);
} else { } else {
const newRowSelection = typeof updater === 'function' ? updater(rowSelection) : updater; const newRowSelection = typeof updater === 'function' ? updater(rowSelection) : updater;
const newSelection: ConstituentaID[] = []; const newSelection: ConstituentaID[] = [];
@ -100,7 +100,7 @@ function PickMultiConstituenta({
newSelection.push(cst.id); newSelection.push(cst.id);
} }
}); });
setSelected(prev => [...prev.filter(cst_id => !filtered.find(cst => cst.id === cst_id)), ...newSelection]); onChange(prev => [...prev.filter(cst_id => !filtered.find(cst => cst.id === cst_id)), ...newSelection]);
} }
} }
@ -122,7 +122,7 @@ function PickMultiConstituenta({
<div className={clsx(noBorder ? '' : 'border', className)} {...restProps}> <div className={clsx(noBorder ? '' : 'border', className)} {...restProps}>
<div className={clsx('px-3 flex justify-between items-center', 'clr-input', 'border-b', 'rounded-t-md')}> <div className={clsx('px-3 flex justify-between items-center', 'clr-input', 'border-b', 'rounded-t-md')}>
<div className='w-[24ch] select-none whitespace-nowrap'> <div className='w-[24ch] select-none whitespace-nowrap'>
{data.length > 0 ? `Выбраны ${selected.length} из ${data.length}` : 'Конституенты'} {data.length > 0 ? `Выбраны ${value.length} из ${data.length}` : 'Конституенты'}
</div> </div>
<SearchBar <SearchBar
id='dlg_constituents_search' id='dlg_constituents_search'
@ -135,9 +135,9 @@ function PickMultiConstituenta({
graph={foldedGraph} graph={foldedGraph}
isCore={cstID => isBasicConcept(schema.cstByID.get(cstID)?.cst_type)} isCore={cstID => isBasicConcept(schema.cstByID.get(cstID)?.cst_type)}
isOwned={cstID => !schema.cstByID.get(cstID)?.is_inherited} isOwned={cstID => !schema.cstByID.get(cstID)?.is_inherited}
selected={selected} value={value}
setSelected={setSelected} onChange={onChange}
emptySelection={selected.length === 0} emptySelection={value.length === 0}
className='w-fit' className='w-fit'
/> />
</div> </div>

View File

@ -12,36 +12,36 @@ import NoData from '@/components/ui/NoData';
import { IOperation, OperationID } from '@/models/oss'; import { IOperation, OperationID } from '@/models/oss';
interface PickMultiOperationProps extends CProps.Styling { interface PickMultiOperationProps extends CProps.Styling {
rows?: number; value: OperationID[];
onChange: React.Dispatch<React.SetStateAction<OperationID[]>>;
items: IOperation[]; items: IOperation[];
selected: OperationID[]; rows?: number;
setSelected: React.Dispatch<React.SetStateAction<OperationID[]>>;
} }
const columnHelper = createColumnHelper<IOperation>(); const columnHelper = createColumnHelper<IOperation>();
function PickMultiOperation({ rows, items, selected, setSelected, className, ...restProps }: PickMultiOperationProps) { function PickMultiOperation({ rows, items, value, onChange, className, ...restProps }: PickMultiOperationProps) {
const selectedItems = selected.map(itemID => items.find(item => item.id === itemID)!); const selectedItems = value.map(itemID => items.find(item => item.id === itemID)!);
const nonSelectedItems = items.filter(item => !selected.includes(item.id)); const nonSelectedItems = items.filter(item => !value.includes(item.id));
const [lastSelected, setLastSelected] = useState<IOperation | undefined>(undefined); const [lastSelected, setLastSelected] = useState<IOperation | undefined>(undefined);
function handleDelete(operation: OperationID) { function handleDelete(operation: OperationID) {
setSelected(prev => prev.filter(item => item !== operation)); onChange(prev => prev.filter(item => item !== operation));
} }
function handleSelect(operation?: IOperation) { function handleSelect(operation?: IOperation) {
if (operation) { if (operation) {
setLastSelected(operation); setLastSelected(operation);
setSelected(prev => [...prev, operation.id]); onChange(prev => [...prev, operation.id]);
setTimeout(() => setLastSelected(undefined), 1000); setTimeout(() => setLastSelected(undefined), 1000);
} }
} }
function handleMoveUp(operation: OperationID) { function handleMoveUp(operation: OperationID) {
const index = selected.indexOf(operation); const index = value.indexOf(operation);
if (index > 0) { if (index > 0) {
setSelected(prev => { onChange(prev => {
const newSelected = [...prev]; const newSelected = [...prev];
newSelected[index] = newSelected[index - 1]; newSelected[index] = newSelected[index - 1];
newSelected[index - 1] = operation; newSelected[index - 1] = operation;
@ -51,9 +51,9 @@ function PickMultiOperation({ rows, items, selected, setSelected, className, ...
} }
function handleMoveDown(operation: OperationID) { function handleMoveDown(operation: OperationID) {
const index = selected.indexOf(operation); const index = value.indexOf(operation);
if (index < selected.length - 1) { if (index < value.length - 1) {
setSelected(prev => { onChange(prev => {
const newSelected = [...prev]; const newSelected = [...prev];
newSelected[index] = newSelected[index + 1]; newSelected[index] = newSelected[index + 1];
newSelected[index + 1] = operation; newSelected[index + 1] = operation;
@ -118,7 +118,7 @@ function PickMultiOperation({ rows, items, selected, setSelected, className, ...
noBorder noBorder
items={nonSelectedItems} // prettier: split-line items={nonSelectedItems} // prettier: split-line
value={lastSelected} value={lastSelected}
onSelectValue={handleSelect} onChange={handleSelect}
/> />
<DataTable <DataTable
dense dense

View File

@ -19,14 +19,15 @@ import SelectLocation from './SelectLocation';
interface PickSchemaProps extends CProps.Styling { interface PickSchemaProps extends CProps.Styling {
id?: string; id?: string;
value?: LibraryItemID;
onChange: (newValue: LibraryItemID) => void;
initialFilter?: string; initialFilter?: string;
rows?: number; rows?: number;
items: ILibraryItem[]; items: ILibraryItem[];
itemType: LibraryItemType; itemType: LibraryItemType;
value?: LibraryItemID;
baseFilter?: (target: ILibraryItem) => boolean; baseFilter?: (target: ILibraryItem) => boolean;
onSelectValue: (newValue: LibraryItemID) => void;
} }
const columnHelper = createColumnHelper<ILibraryItem>(); const columnHelper = createColumnHelper<ILibraryItem>();
@ -38,7 +39,7 @@ function PickSchema({
items, items,
itemType, itemType,
value, value,
onSelectValue, onChange,
baseFilter, baseFilter,
className, className,
...restProps ...restProps
@ -156,7 +157,7 @@ function PickSchema({
<p>Измените параметры фильтра</p> <p>Измените параметры фильтра</p>
</FlexColumn> </FlexColumn>
} }
onRowClicked={rowData => onSelectValue(rowData.id)} onRowClicked={rowData => onChange(rowData.id)}
/> />
</div> </div>
); );

View File

@ -20,8 +20,9 @@ import { errors } from '@/utils/labels';
import SelectLibraryItem from './SelectLibraryItem'; import SelectLibraryItem from './SelectLibraryItem';
interface PickSubstitutionsProps extends CProps.Styling { interface PickSubstitutionsProps extends CProps.Styling {
substitutions: ICstSubstitute[]; value: ICstSubstitute[];
setSubstitutions: React.Dispatch<React.SetStateAction<ICstSubstitute[]>>; onChange: React.Dispatch<React.SetStateAction<ICstSubstitute[]>>;
suggestions?: ICstSubstitute[]; suggestions?: ICstSubstitute[];
prefixID: string; prefixID: string;
@ -35,8 +36,8 @@ interface PickSubstitutionsProps extends CProps.Styling {
const columnHelper = createColumnHelper<IMultiSubstitution>(); const columnHelper = createColumnHelper<IMultiSubstitution>();
function PickSubstitutions({ function PickSubstitutions({
substitutions, value,
setSubstitutions, onChange,
suggestions, suggestions,
prefixID, prefixID,
rows, rows,
@ -66,7 +67,7 @@ function PickSubstitutions({
) ?? []; ) ?? [];
const substitutionData: IMultiSubstitution[] = [ const substitutionData: IMultiSubstitution[] = [
...substitutions.map(item => ({ ...value.map(item => ({
original_source: getSchemaByCst(item.original)!, original_source: getSchemaByCst(item.original)!,
original: getConstituenta(item.original)!, original: getConstituenta(item.original)!,
substitution: getConstituenta(item.substitution)!, substitution: getConstituenta(item.substitution)!,
@ -110,8 +111,8 @@ function PickSubstitutions({
original: deleteRight ? rightCst.id : leftCst.id, original: deleteRight ? rightCst.id : leftCst.id,
substitution: deleteRight ? leftCst.id : rightCst.id substitution: deleteRight ? leftCst.id : rightCst.id
}; };
const toDelete = substitutions.map(item => item.original); const toDelete = value.map(item => item.original);
const replacements = substitutions.map(item => item.substitution); const replacements = value.map(item => item.substitution);
if ( if (
toDelete.includes(newSubstitution.original) || toDelete.includes(newSubstitution.original) ||
toDelete.includes(newSubstitution.substitution) || toDelete.includes(newSubstitution.substitution) ||
@ -126,7 +127,7 @@ function PickSubstitutions({
return; return;
} }
} }
setSubstitutions(prev => [...prev, newSubstitution]); onChange(prev => [...prev, newSubstitution]);
setLeftCst(undefined); setLeftCst(undefined);
setRightCst(undefined); setRightCst(undefined);
} }
@ -136,12 +137,12 @@ function PickSubstitutions({
} }
function handleAcceptSuggestion(item: IMultiSubstitution) { function handleAcceptSuggestion(item: IMultiSubstitution) {
setSubstitutions(prev => [...prev, { original: item.original.id, substitution: item.substitution.id }]); onChange(prev => [...prev, { original: item.original.id, substitution: item.substitution.id }]);
} }
function handleDeleteSubstitution(target: IMultiSubstitution) { function handleDeleteSubstitution(target: IMultiSubstitution) {
handleDeclineSuggestion(target); handleDeclineSuggestion(target);
setSubstitutions(prev => { onChange(prev => {
const newItems: ICstSubstitute[] = []; const newItems: ICstSubstitute[] = [];
prev.forEach(item => { prev.forEach(item => {
if (item.original !== target.original.id || item.substitution !== target.substitution.id) { if (item.original !== target.original.id || item.substitution !== target.substitution.id) {
@ -230,15 +231,15 @@ function PickSubstitutions({
placeholder='Выберите аргумент' placeholder='Выберите аргумент'
items={allowSelfSubstitution ? schemas : schemas.filter(item => item.id !== rightArgument?.id)} items={allowSelfSubstitution ? schemas : schemas.filter(item => item.id !== rightArgument?.id)}
value={leftArgument} value={leftArgument}
onSelectValue={setLeftArgument} onChange={setLeftArgument}
/> />
<SelectConstituenta <SelectConstituenta
noBorder noBorder
items={(leftArgument as IRSForm)?.items.filter( items={(leftArgument as IRSForm)?.items.filter(
cst => !substitutions.find(item => item.original === cst.id) && (!filter || filter(cst)) cst => !value.find(item => item.original === cst.id) && (!filter || filter(cst))
)} )}
value={leftCst} value={leftCst}
onSelectValue={setLeftCst} onChange={setLeftCst}
/> />
</div> </div>
<div className='flex flex-col gap-1'> <div className='flex flex-col gap-1'>
@ -268,15 +269,15 @@ function PickSubstitutions({
placeholder='Выберите аргумент' placeholder='Выберите аргумент'
items={allowSelfSubstitution ? schemas : schemas.filter(item => item.id !== leftArgument?.id)} items={allowSelfSubstitution ? schemas : schemas.filter(item => item.id !== leftArgument?.id)}
value={rightArgument} value={rightArgument}
onSelectValue={setRightArgument} onChange={setRightArgument}
/> />
<SelectConstituenta <SelectConstituenta
noBorder noBorder
items={(rightArgument as IRSForm)?.items.filter( items={(rightArgument as IRSForm)?.items.filter(
cst => !substitutions.find(item => item.original === cst.id) && (!filter || filter(cst)) cst => !value.find(item => item.original === cst.id) && (!filter || filter(cst))
)} )}
value={rightCst} value={rightCst}
onSelectValue={setRightCst} onChange={setRightCst}
/> />
</div> </div>
</div> </div>

View File

@ -15,6 +15,7 @@ import { describeAccessPolicy, labelAccessPolicy } from '@/utils/labels';
interface SelectAccessPolicyProps extends CProps.Styling { interface SelectAccessPolicyProps extends CProps.Styling {
value: AccessPolicy; value: AccessPolicy;
onChange: (value: AccessPolicy) => void; onChange: (value: AccessPolicy) => void;
disabled?: boolean; disabled?: boolean;
stretchLeft?: boolean; stretchLeft?: boolean;
} }

View File

@ -10,10 +10,10 @@ import { matchConstituenta } from '@/models/rsformAPI';
import { describeConstituenta, describeConstituentaTerm } from '@/utils/labels'; import { describeConstituenta, describeConstituentaTerm } from '@/utils/labels';
interface SelectConstituentaProps extends CProps.Styling { interface SelectConstituentaProps extends CProps.Styling {
items?: IConstituenta[];
value?: IConstituenta; value?: IConstituenta;
onSelectValue: (newValue?: IConstituenta) => void; onChange: (newValue?: IConstituenta) => void;
items?: IConstituenta[];
placeholder?: string; placeholder?: string;
noBorder?: boolean; noBorder?: boolean;
} }
@ -22,7 +22,7 @@ function SelectConstituenta({
className, className,
items, items,
value, value,
onSelectValue, onChange,
placeholder = 'Выберите конституенту', placeholder = 'Выберите конституенту',
...restProps ...restProps
}: SelectConstituentaProps) { }: SelectConstituentaProps) {
@ -42,7 +42,7 @@ function SelectConstituenta({
className={clsx('text-ellipsis', className)} className={clsx('text-ellipsis', className)}
options={options} options={options}
value={value ? { value: value.id, label: `${value.alias}: ${describeConstituentaTerm(value)}` } : null} value={value ? { value: value.id, label: `${value.alias}: ${describeConstituentaTerm(value)}` } : null}
onChange={data => onSelectValue(items?.find(cst => cst.id === data?.value))} onChange={data => onChange(items?.find(cst => cst.id === data?.value))}
// @ts-expect-error: TODO: use type definitions from react-select in filter object // @ts-expect-error: TODO: use type definitions from react-select in filter object
filterOption={filter} filterOption={filter}
placeholder={placeholder} placeholder={placeholder}

View File

@ -15,8 +15,8 @@ import { describeCstSource, labelCstSource } from '@/utils/labels';
interface SelectGraphFilterProps extends CProps.Styling { interface SelectGraphFilterProps extends CProps.Styling {
value: DependencyMode; value: DependencyMode;
dense?: boolean;
onChange: (value: DependencyMode) => void; onChange: (value: DependencyMode) => void;
dense?: boolean;
} }
function SelectGraphFilter({ value, dense, onChange, ...restProps }: SelectGraphFilterProps) { function SelectGraphFilter({ value, dense, onChange, ...restProps }: SelectGraphFilterProps) {

View File

@ -10,7 +10,7 @@ import { matchLibraryItem } from '@/models/libraryAPI';
interface SelectLibraryItemProps extends CProps.Styling { interface SelectLibraryItemProps extends CProps.Styling {
items?: ILibraryItem[]; items?: ILibraryItem[];
value?: ILibraryItem; value?: ILibraryItem;
onSelectValue: (newValue?: ILibraryItem) => void; onChange: (newValue?: ILibraryItem) => void;
placeholder?: string; placeholder?: string;
noBorder?: boolean; noBorder?: boolean;
@ -20,7 +20,7 @@ function SelectLibraryItem({
className, className,
items, items,
value, value,
onSelectValue, onChange,
placeholder = 'Выберите схему', placeholder = 'Выберите схему',
...restProps ...restProps
}: SelectLibraryItemProps) { }: SelectLibraryItemProps) {
@ -40,7 +40,7 @@ function SelectLibraryItem({
className={clsx('text-ellipsis', className)} className={clsx('text-ellipsis', className)}
options={options} options={options}
value={value ? { value: value.id, label: `${value.alias}: ${value.title}` } : null} value={value ? { value: value.id, label: `${value.alias}: ${value.title}` } : null}
onChange={data => onSelectValue(items?.find(cst => cst.id === data?.value))} onChange={data => onChange(items?.find(cst => cst.id === data?.value))}
// @ts-expect-error: TODO: use type definitions from react-select in filter object // @ts-expect-error: TODO: use type definitions from react-select in filter object
filterOption={filter} filterOption={filter}
placeholder={placeholder} placeholder={placeholder}

View File

@ -12,9 +12,10 @@ import { labelFolderNode } from '@/utils/labels';
interface SelectLocationProps extends CProps.Styling { interface SelectLocationProps extends CProps.Styling {
value: string; value: string;
onClick: (event: CProps.EventMouse, target: FolderNode) => void;
prefix: string; prefix: string;
dense?: boolean; dense?: boolean;
onClick: (event: CProps.EventMouse, target: FolderNode) => void;
} }
function SelectLocation({ value, dense, prefix, onClick, className, style }: SelectLocationProps) { function SelectLocation({ value, dense, prefix, onClick, className, style }: SelectLocationProps) {

View File

@ -14,10 +14,9 @@ import SelectLocation from './SelectLocation';
interface SelectLocationContextProps extends CProps.Styling { interface SelectLocationContextProps extends CProps.Styling {
value: string; value: string;
onChange: (newValue: string) => void;
title?: string; title?: string;
stretchTop?: boolean; stretchTop?: boolean;
onChange: (newValue: string) => void;
} }
function SelectLocationContext({ function SelectLocationContext({

View File

@ -15,8 +15,8 @@ import { describeCstMatchMode, labelCstMatchMode } from '@/utils/labels';
interface SelectMatchModeProps extends CProps.Styling { interface SelectMatchModeProps extends CProps.Styling {
value: CstMatchMode; value: CstMatchMode;
dense?: boolean;
onChange: (value: CstMatchMode) => void; onChange: (value: CstMatchMode) => void;
dense?: boolean;
} }
function SelectMatchMode({ value, dense, onChange, ...restProps }: SelectMatchModeProps) { function SelectMatchMode({ value, dense, onChange, ...restProps }: SelectMatchModeProps) {

View File

@ -10,11 +10,11 @@ interface SelectMultiGrammemeProps
extends Omit<SelectMultiProps<IGrammemeOption>, 'value' | 'onChange'>, extends Omit<SelectMultiProps<IGrammemeOption>, 'value' | 'onChange'>,
CProps.Styling { CProps.Styling {
value: IGrammemeOption[]; value: IGrammemeOption[];
onChangeValue: (newValue: IGrammemeOption[]) => void; onChange: (newValue: IGrammemeOption[]) => void;
placeholder?: string; placeholder?: string;
} }
function SelectMultiGrammeme({ value, onChangeValue, ...restProps }: SelectMultiGrammemeProps) { function SelectMultiGrammeme({ value, onChange, ...restProps }: SelectMultiGrammemeProps) {
const [options, setOptions] = useState<IGrammemeOption[]>([]); const [options, setOptions] = useState<IGrammemeOption[]>([]);
useEffect(() => { useEffect(() => {
@ -28,7 +28,7 @@ function SelectMultiGrammeme({ value, onChangeValue, ...restProps }: SelectMulti
<SelectMulti <SelectMulti
options={options} options={options}
value={value} value={value}
onChange={newValue => onChangeValue([...newValue].sort(compareGrammemeOptions))} onChange={newValue => onChange([...newValue].sort(compareGrammemeOptions))}
{...restProps} {...restProps}
/> />
); );

View File

@ -10,7 +10,7 @@ import { matchOperation } from '@/models/ossAPI';
interface SelectOperationProps extends CProps.Styling { interface SelectOperationProps extends CProps.Styling {
items?: IOperation[]; items?: IOperation[];
value?: IOperation; value?: IOperation;
onSelectValue: (newValue?: IOperation) => void; onChange: (newValue?: IOperation) => void;
placeholder?: string; placeholder?: string;
noBorder?: boolean; noBorder?: boolean;
@ -20,7 +20,7 @@ function SelectOperation({
className, className,
items, items,
value, value,
onSelectValue, onChange,
placeholder = 'Выберите операцию', placeholder = 'Выберите операцию',
...restProps ...restProps
}: SelectOperationProps) { }: SelectOperationProps) {
@ -40,7 +40,7 @@ function SelectOperation({
className={clsx('text-ellipsis', className)} className={clsx('text-ellipsis', className)}
options={options} options={options}
value={value ? { value: value.id, label: `${value.alias}: ${value.title}` } : null} value={value ? { value: value.id, label: `${value.alias}: ${value.title}` } : null}
onChange={data => onSelectValue(items?.find(cst => cst.id === data?.value))} onChange={data => onChange(items?.find(cst => cst.id === data?.value))}
// @ts-expect-error: TODO: use type definitions from react-select in filter object // @ts-expect-error: TODO: use type definitions from react-select in filter object
filterOption={filter} filterOption={filter}
placeholder={placeholder} placeholder={placeholder}

View File

@ -11,7 +11,7 @@ import { matchUser } from '@/models/userAPI';
interface SelectUserProps extends CProps.Styling { interface SelectUserProps extends CProps.Styling {
value?: UserID; value?: UserID;
onSelectValue: (newValue: UserID) => void; onChange: (newValue: UserID) => void;
filter?: (userID: UserID) => boolean; filter?: (userID: UserID) => boolean;
placeholder?: string; placeholder?: string;
@ -22,7 +22,7 @@ function SelectUser({
className, className,
filter, filter,
value, value,
onSelectValue, onChange,
placeholder = 'Выберите пользователя', placeholder = 'Выберите пользователя',
...restProps ...restProps
}: SelectUserProps) { }: SelectUserProps) {
@ -46,7 +46,7 @@ function SelectUser({
options={options} options={options}
value={value ? { value: value, label: getUserLabel(value) } : null} value={value ? { value: value, label: getUserLabel(value) } : null}
onChange={data => { onChange={data => {
if (data?.value !== undefined) onSelectValue(data.value); if (data?.value !== undefined) onChange(data.value);
}} }}
// @ts-expect-error: TODO: use type definitions from react-select in filter object // @ts-expect-error: TODO: use type definitions from react-select in filter object
filterOption={filterLabel} filterOption={filterLabel}

View File

@ -11,13 +11,13 @@ interface SelectVersionProps extends CProps.Styling {
id?: string; id?: string;
items?: IVersionInfo[]; items?: IVersionInfo[];
value?: VersionID; value?: VersionID;
onSelectValue: (newValue?: VersionID) => void; onChange: (newValue?: VersionID) => void;
placeholder?: string; placeholder?: string;
noBorder?: boolean; noBorder?: boolean;
} }
function SelectVersion({ id, className, items, value, onSelectValue, ...restProps }: SelectVersionProps) { function SelectVersion({ id, className, items, value, onChange, ...restProps }: SelectVersionProps) {
const options = [ const options = [
{ {
value: undefined, value: undefined,
@ -40,7 +40,7 @@ function SelectVersion({ id, className, items, value, onSelectValue, ...restProp
className={clsx('min-w-[12rem] text-ellipsis', className)} className={clsx('min-w-[12rem] text-ellipsis', className)}
options={options} options={options}
value={{ value: value, label: valueLabel }} value={{ value: value, label: valueLabel }}
onChange={data => onSelectValue(data?.value)} onChange={data => onChange(data?.value)}
{...restProps} {...restProps}
/> />
); );

View File

@ -10,16 +10,16 @@ import { prefixes } from '@/utils/constants';
import { DefaultWordForms, IGrammemeOption, SelectorGrammemes } from '@/utils/selectors'; import { DefaultWordForms, IGrammemeOption, SelectorGrammemes } from '@/utils/selectors';
interface SelectWordFormProps extends CProps.Styling { interface SelectWordFormProps extends CProps.Styling {
selected: IGrammemeOption[]; value: IGrammemeOption[];
setSelected: React.Dispatch<React.SetStateAction<IGrammemeOption[]>>; onChange: React.Dispatch<React.SetStateAction<IGrammemeOption[]>>;
} }
function SelectWordForm({ selected, setSelected, className, ...restProps }: SelectWordFormProps) { function SelectWordForm({ value, onChange, className, ...restProps }: SelectWordFormProps) {
const handleSelect = useCallback( const handleSelect = useCallback(
(grams: Grammeme[]) => { (grams: Grammeme[]) => {
setSelected(SelectorGrammemes.filter(({ value }) => grams.includes(value as Grammeme))); onChange(SelectorGrammemes.filter(({ value }) => grams.includes(value as Grammeme)));
}, },
[setSelected] [onChange]
); );
return ( return (
@ -30,7 +30,7 @@ function SelectWordForm({ selected, setSelected, className, ...restProps }: Sele
text={data.text} text={data.text}
example={data.example} example={data.example}
grams={data.grams} grams={data.grams}
isSelected={data.grams.every(gram => selected.find(item => (item.value as Grammeme) === gram))} isSelected={data.grams.every(gram => value.find(item => (item.value as Grammeme) === gram))}
onSelectGrams={handleSelect} onSelectGrams={handleSelect}
/> />
))} ))}

View File

@ -17,37 +17,37 @@ import MiniButton from '@/components/ui/MiniButton';
import { Graph } from '@/models/Graph'; import { Graph } from '@/models/Graph';
interface ToolbarGraphSelectionProps extends CProps.Styling { interface ToolbarGraphSelectionProps extends CProps.Styling {
value: number[];
onChange: (newSelection: number[]) => void;
graph: Graph; graph: Graph;
selected: number[];
isCore: (item: number) => boolean; isCore: (item: number) => boolean;
isOwned?: (item: number) => boolean; isOwned?: (item: number) => boolean;
setSelected: (newSelection: number[]) => void;
emptySelection?: boolean; emptySelection?: boolean;
} }
function ToolbarGraphSelection({ function ToolbarGraphSelection({
className, className,
graph, graph,
selected, value: selected,
isCore, isCore,
isOwned, isOwned,
setSelected, onChange,
emptySelection, emptySelection,
...restProps ...restProps
}: ToolbarGraphSelectionProps) { }: ToolbarGraphSelectionProps) {
const handleSelectCore = useCallback(() => { const handleSelectCore = useCallback(() => {
const core = [...graph.nodes.keys()].filter(isCore); const core = [...graph.nodes.keys()].filter(isCore);
setSelected([...core, ...graph.expandInputs(core)]); onChange([...core, ...graph.expandInputs(core)]);
}, [setSelected, graph, isCore]); }, [onChange, graph, isCore]);
const handleSelectOwned = useCallback( const handleSelectOwned = useCallback(
() => (isOwned ? setSelected([...graph.nodes.keys()].filter(isOwned)) : undefined), () => (isOwned ? onChange([...graph.nodes.keys()].filter(isOwned)) : undefined),
[setSelected, graph, isOwned] [onChange, graph, isOwned]
); );
const handleInvertSelection = useCallback( const handleInvertSelection = useCallback(
() => setSelected([...graph.nodes.keys()].filter(item => !selected.includes(item))), () => onChange([...graph.nodes.keys()].filter(item => !selected.includes(item))),
[setSelected, selected, graph] [onChange, selected, graph]
); );
return ( return (
@ -55,37 +55,37 @@ function ToolbarGraphSelection({
<MiniButton <MiniButton
titleHtml='Сбросить выделение' titleHtml='Сбросить выделение'
icon={<IconReset size='1.25rem' className='icon-primary' />} icon={<IconReset size='1.25rem' className='icon-primary' />}
onClick={() => setSelected([])} onClick={() => onChange([])}
disabled={emptySelection} disabled={emptySelection}
/> />
<MiniButton <MiniButton
titleHtml='Выделить все влияющие' titleHtml='Выделить все влияющие'
icon={<IconGraphCollapse size='1.25rem' className='icon-primary' />} icon={<IconGraphCollapse size='1.25rem' className='icon-primary' />}
onClick={() => setSelected([...selected, ...graph.expandAllInputs(selected)])} onClick={() => onChange([...selected, ...graph.expandAllInputs(selected)])}
disabled={emptySelection} disabled={emptySelection}
/> />
<MiniButton <MiniButton
titleHtml='Выделить все зависимые' titleHtml='Выделить все зависимые'
icon={<IconGraphExpand size='1.25rem' className='icon-primary' />} icon={<IconGraphExpand size='1.25rem' className='icon-primary' />}
onClick={() => setSelected([...selected, ...graph.expandAllOutputs(selected)])} onClick={() => onChange([...selected, ...graph.expandAllOutputs(selected)])}
disabled={emptySelection} disabled={emptySelection}
/> />
<MiniButton <MiniButton
titleHtml='<b>Максимизация</b> <br/>дополнение выделения конституентами, <br/>зависимыми только от выделенных' titleHtml='<b>Максимизация</b> <br/>дополнение выделения конституентами, <br/>зависимыми только от выделенных'
icon={<IconGraphMaximize size='1.25rem' className='icon-primary' />} icon={<IconGraphMaximize size='1.25rem' className='icon-primary' />}
onClick={() => setSelected(graph.maximizePart(selected))} onClick={() => onChange(graph.maximizePart(selected))}
disabled={emptySelection} disabled={emptySelection}
/> />
<MiniButton <MiniButton
titleHtml='Выделить поставщиков' titleHtml='Выделить поставщиков'
icon={<IconGraphInputs size='1.25rem' className='icon-primary' />} icon={<IconGraphInputs size='1.25rem' className='icon-primary' />}
onClick={() => setSelected([...selected, ...graph.expandInputs(selected)])} onClick={() => onChange([...selected, ...graph.expandInputs(selected)])}
disabled={emptySelection} disabled={emptySelection}
/> />
<MiniButton <MiniButton
titleHtml='Выделить потребителей' titleHtml='Выделить потребителей'
icon={<IconGraphOutputs size='1.25rem' className='icon-primary' />} icon={<IconGraphOutputs size='1.25rem' className='icon-primary' />}
onClick={() => setSelected([...selected, ...graph.expandOutputs(selected)])} onClick={() => onChange([...selected, ...graph.expandOutputs(selected)])}
disabled={emptySelection} disabled={emptySelection}
/> />
<MiniButton <MiniButton

View File

@ -4,7 +4,7 @@ import { CheckboxChecked } from '@/components/Icons';
import { CProps } from '@/components/props'; import { CProps } from '@/components/props';
import { globals } from '@/utils/constants'; import { globals } from '@/utils/constants';
export interface CheckboxProps extends Omit<CProps.Button, 'value' | 'onClick'> { export interface CheckboxProps extends Omit<CProps.Button, 'value' | 'onClick' | 'onChange'> {
/** Label to display next to the checkbox. */ /** Label to display next to the checkbox. */
label?: string; label?: string;
@ -15,7 +15,7 @@ export interface CheckboxProps extends Omit<CProps.Button, 'value' | 'onClick'>
value?: boolean; value?: boolean;
/** Callback to set the `value`. */ /** Callback to set the `value`. */
setValue?: (newValue: boolean) => void; onChange?: (newValue: boolean) => void;
} }
/** /**
@ -29,18 +29,18 @@ function Checkbox({
hideTitle, hideTitle,
className, className,
value, value,
setValue, onChange,
...restProps ...restProps
}: CheckboxProps) { }: CheckboxProps) {
const cursor = disabled ? 'cursor-arrow' : setValue ? 'cursor-pointer' : ''; const cursor = disabled ? 'cursor-arrow' : onChange ? 'cursor-pointer' : '';
function handleClick(event: CProps.EventMouse): void { function handleClick(event: CProps.EventMouse): void {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
if (disabled || !setValue) { if (disabled || !onChange) {
return; return;
} }
setValue(!value); onChange(!value);
} }
return ( return (

View File

@ -6,12 +6,12 @@ import { globals } from '@/utils/constants';
import { CheckboxProps } from './Checkbox'; import { CheckboxProps } from './Checkbox';
export interface CheckboxTristateProps extends Omit<CheckboxProps, 'value' | 'setValue'> { export interface CheckboxTristateProps extends Omit<CheckboxProps, 'value' | 'onChange'> {
/** Current value - `null`, `true` or `false`. */ /** Current value - `null`, `true` or `false`. */
value: boolean | null; value: boolean | null;
/** Callback to set the `value`. */ /** Callback to set the `value`. */
setValue?: (newValue: boolean | null) => void; onChange?: (newValue: boolean | null) => void;
} }
/** /**
@ -25,23 +25,23 @@ function CheckboxTristate({
hideTitle, hideTitle,
className, className,
value, value,
setValue, onChange,
...restProps ...restProps
}: CheckboxTristateProps) { }: CheckboxTristateProps) {
const cursor = disabled ? 'cursor-arrow' : setValue ? 'cursor-pointer' : ''; const cursor = disabled ? 'cursor-arrow' : onChange ? 'cursor-pointer' : '';
function handleClick(event: CProps.EventMouse): void { function handleClick(event: CProps.EventMouse): void {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
if (disabled || !setValue) { if (disabled || !onChange) {
return; return;
} }
if (value === false) { if (value === false) {
setValue(null); onChange(null);
} else if (value === null) { } else if (value === null) {
setValue(true); onChange(true);
} else { } else {
setValue(false); onChange(false);
} }
} }

View File

@ -22,7 +22,7 @@ function SelectAll<TData>({ table, resetLastSelected }: SelectAllProps<TData>) {
value={ value={
!table.getIsAllPageRowsSelected() && table.getIsSomePageRowsSelected() ? null : table.getIsAllPageRowsSelected() !table.getIsAllPageRowsSelected() && table.getIsSomePageRowsSelected() ? null : table.getIsAllPageRowsSelected()
} }
setValue={handleChange} onChange={handleChange}
/> />
); );
} }

View File

@ -15,7 +15,7 @@ function SelectRow<TData>({ row, onChangeLastSelected }: SelectRowProps<TData>)
row.toggleSelected(value); row.toggleSelected(value);
} }
return <Checkbox tabIndex={-1} value={row.getIsSelected()} setValue={handleChange} />; return <Checkbox tabIndex={-1} value={row.getIsSelected()} onChange={handleChange} />;
} }
export default SelectRow; export default SelectRow;

View File

@ -3,7 +3,7 @@ import clsx from 'clsx';
import Checkbox, { CheckboxProps } from './Checkbox'; import Checkbox, { CheckboxProps } from './Checkbox';
/** Animated {@link Checkbox} inside a {@link Dropdown} item. */ /** Animated {@link Checkbox} inside a {@link Dropdown} item. */
function DropdownCheckbox({ setValue, disabled, ...restProps }: CheckboxProps) { function DropdownCheckbox({ onChange: setValue, disabled, ...restProps }: CheckboxProps) {
return ( return (
<div <div
className={clsx( className={clsx(
@ -13,7 +13,7 @@ function DropdownCheckbox({ setValue, disabled, ...restProps }: CheckboxProps) {
!!setValue && !disabled && 'clr-hover' !!setValue && !disabled && 'clr-hover'
)} )}
> >
<Checkbox tabIndex={-1} disabled={disabled} setValue={setValue} {...restProps} /> <Checkbox tabIndex={-1} disabled={disabled} onChange={setValue} {...restProps} />
</div> </div>
); );
} }

View File

@ -19,7 +19,7 @@ interface SelectTreeProps<ItemType> extends CProps.Styling {
prefix: string; prefix: string;
/** Callback to be called when the value changes. */ /** Callback to be called when the value changes. */
onChangeValue: (newItem: ItemType) => void; onChange: (newItem: ItemType) => void;
/** Callback providing the parent of the item. */ /** Callback providing the parent of the item. */
getParent: (item: ItemType) => ItemType; getParent: (item: ItemType) => ItemType;
@ -40,7 +40,7 @@ function SelectTree<ItemType>({
getParent, getParent,
getLabel, getLabel,
getDescription, getDescription,
onChangeValue, onChange,
prefix, prefix,
...restProps ...restProps
}: SelectTreeProps<ItemType>) { }: SelectTreeProps<ItemType>) {
@ -75,7 +75,7 @@ function SelectTree<ItemType>({
function handleSetValue(event: CProps.EventMouse, target: ItemType) { function handleSetValue(event: CProps.EventMouse, target: ItemType) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
onChangeValue(target); onChange(target);
} }
return ( return (

View File

@ -61,7 +61,7 @@ function DlgChangeInputSchema() {
items={sortedItems} items={sortedItems}
itemType={LibraryItemType.RSFORM} itemType={LibraryItemType.RSFORM}
value={selected} // prettier: split-line value={selected} // prettier: split-line
onSelectValue={handleSelectLocation} onChange={handleSelectLocation}
rows={14} rows={14}
baseFilter={baseFilter} baseFilter={baseFilter}
/> />

View File

@ -6,7 +6,7 @@ import { useState } from 'react';
import { useConceptNavigation } from '@/app/Navigation/NavigationContext'; import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
import { urls } from '@/app/urls'; import { urls } from '@/app/urls';
import { useAuthSuspense } from '@/backend/auth/useAuth'; import { useAuthSuspense } from '@/backend/auth/useAuth';
import { IRSFormCloneDTO } from '@/backend/library/api'; import { IRCloneLibraryItemDTO } from '@/backend/library/api';
import { useCloneItem } from '@/backend/library/useCloneItem'; import { useCloneItem } from '@/backend/library/useCloneItem';
import { VisibilityIcon } from '@/components/DomainIcons'; import { VisibilityIcon } from '@/components/DomainIcons';
import SelectAccessPolicy from '@/components/select/SelectAccessPolicy'; import SelectAccessPolicy from '@/components/select/SelectAccessPolicy';
@ -59,7 +59,7 @@ function DlgCloneLibraryItem() {
} }
function handleSubmit() { function handleSubmit() {
const data: IRSFormCloneDTO = { const data: IRCloneLibraryItemDTO = {
id: base.id, id: base.id,
item_type: base.item_type, item_type: base.item_type,
title: title, title: title,
@ -137,7 +137,7 @@ function DlgCloneLibraryItem() {
id='dlg_only_selected' id='dlg_only_selected'
label={`Только выбранные конституенты [${selected.length} из ${totalCount}]`} label={`Только выбранные конституенты [${selected.length} из ${totalCount}]`}
value={onlySelected} value={onlySelected}
setValue={value => setOnlySelected(value)} onChange={value => setOnlySelected(value)}
/> />
</Modal> </Modal>
); );

View File

@ -98,7 +98,7 @@ function TabInputOperation({
</div> </div>
<Checkbox <Checkbox
value={createSchema} value={createSchema}
setValue={onChangeCreateSchema} onChange={onChangeCreateSchema}
label='Создать новую схему' label='Создать новую схему'
titleHtml='Создать пустую схему для загрузки' titleHtml='Создать пустую схему для загрузки'
/> />
@ -108,7 +108,7 @@ function TabInputOperation({
items={sortedItems} items={sortedItems}
value={attachedID} value={attachedID}
itemType={LibraryItemType.RSFORM} itemType={LibraryItemType.RSFORM}
onSelectValue={onChangeAttachedID} onChange={onChangeAttachedID}
rows={8} rows={8}
baseFilter={baseFilter} baseFilter={baseFilter}
/> />

View File

@ -57,7 +57,7 @@ function TabSynthesisOperation({
<FlexColumn> <FlexColumn>
<Label text={`Выбор аргументов: [ ${inputs.length} ]`} /> <Label text={`Выбор аргументов: [ ${inputs.length} ]`} />
<PickMultiOperation items={oss.items} selected={inputs} setSelected={setInputs} rows={6} /> <PickMultiOperation items={oss.items} value={inputs} onChange={setInputs} rows={6} />
</FlexColumn> </FlexColumn>
</div> </div>
); );

View File

@ -64,7 +64,7 @@ function DlgCreateVersion() {
id='dlg_only_selected' id='dlg_only_selected'
label={`Только выбранные конституенты [${selected.length} из ${totalCount}]`} label={`Только выбранные конституенты [${selected.length} из ${totalCount}]`}
value={onlySelected} value={onlySelected}
setValue={value => setOnlySelected(value)} onChange={value => setOnlySelected(value)}
/> />
</Modal> </Modal>
); );

View File

@ -190,7 +190,7 @@ function TabArguments({ state, schema, partialUpdate }: TabArgumentsProps) {
id='dlg_argument_picker' id='dlg_argument_picker'
value={selectedCst} value={selectedCst}
data={schema.items} data={schema.items}
onSelectValue={handleSelectConstituenta} onChange={handleSelectConstituenta}
prefixID={prefixes.cst_modal_list} prefixID={prefixes.cst_modal_list}
rows={7} rows={7}
/> />

View File

@ -102,7 +102,7 @@ function TabTemplate({ state, partialUpdate, templateSchema }: TabTemplateProps)
id='dlg_template_picker' id='dlg_template_picker'
value={state.prototype} value={state.prototype}
data={filteredData} data={filteredData}
onSelectValue={cst => partialUpdate({ prototype: cst })} onChange={cst => partialUpdate({ prototype: cst })}
prefixID={prefixes.cst_template_ist} prefixID={prefixes.cst_template_ist}
className='rounded-t-none' className='rounded-t-none'
rows={8} rows={8}

View File

@ -55,7 +55,7 @@ function DlgDeleteCst() {
label='Удалить зависимые конституенты' label='Удалить зависимые конституенты'
className='mb-2' className='mb-2'
value={expandOut} value={expandOut}
setValue={value => setExpandOut(value)} onChange={value => setExpandOut(value)}
/> />
{hasInherited ? ( {hasInherited ? (
<p className='text-sm clr-text-red'>Внимание! Выбранные конституенты имеют наследников в ОСС</p> <p className='text-sm clr-text-red'>Внимание! Выбранные конституенты имеют наследников в ОСС</p>

View File

@ -39,7 +39,7 @@ function DlgDeleteOperation() {
label='Сохранить наследованные конституенты' label='Сохранить наследованные конституенты'
titleHtml='Наследованные конституенты <br/>превратятся в дописанные' titleHtml='Наследованные конституенты <br/>превратятся в дописанные'
value={keepConstituents} value={keepConstituents}
setValue={setKeepConstituents} onChange={setKeepConstituents}
disabled={target.result === null} disabled={target.result === null}
/> />
<Checkbox <Checkbox
@ -50,7 +50,7 @@ function DlgDeleteOperation() {
: 'Удалить схему вместе с операцией' : 'Удалить схему вместе с операцией'
} }
value={deleteSchema} value={deleteSchema}
setValue={setDeleteSchema} onChange={setDeleteSchema}
disabled={!target.is_owned || target.result === null} disabled={!target.is_owned || target.result === null}
/> />
</Modal> </Modal>

View File

@ -63,7 +63,7 @@ function DlgEditEditors() {
<SelectUser <SelectUser
filter={id => !selected.includes(id)} filter={id => !selected.includes(id)}
value={undefined} value={undefined}
onSelectValue={onAddEditor} onChange={onAddEditor}
className='w-[25rem]' className='w-[25rem]'
/> />
</div> </div>

View File

@ -19,7 +19,7 @@ function TabArguments({ oss, inputs, target, setInputs }: TabArgumentsProps) {
<div className='cc-fade-in cc-column'> <div className='cc-fade-in cc-column'>
<FlexColumn> <FlexColumn>
<Label text={`Выбор аргументов: [ ${inputs.length} ]`} /> <Label text={`Выбор аргументов: [ ${inputs.length} ]`} />
<PickMultiOperation items={filtered} selected={inputs} setSelected={setInputs} rows={8} /> <PickMultiOperation items={filtered} value={inputs} onChange={setInputs} rows={8} />
</FlexColumn> </FlexColumn>
</div> </div>
); );

View File

@ -29,8 +29,8 @@ function TabSynthesis({
schemas={schemas} schemas={schemas}
prefixID={prefixes.dlg_cst_substitutes_list} prefixID={prefixes.dlg_cst_substitutes_list}
rows={8} rows={8}
substitutions={substitutions} value={substitutions}
setSubstitutions={setSubstitutions} onChange={setSubstitutions}
suggestions={suggestions} suggestions={suggestions}
/> />
<TextArea <TextArea

View File

@ -64,7 +64,7 @@ function TabEntityReference({ initial, schema, onChangeValid, onChangeReference
initialFilter={initial.text} initialFilter={initial.text}
value={selectedCst} value={selectedCst}
data={schema.items} data={schema.items}
onSelectValue={handleSelectConstituenta} onChange={handleSelectConstituenta}
prefixID={prefixes.cst_modal_list} prefixID={prefixes.cst_modal_list}
describeFunc={cst => cst.term_resolved} describeFunc={cst => cst.term_resolved}
matchFunc={(cst, filter) => matchConstituenta(cst, filter, CstMatchMode.TERM)} matchFunc={(cst, filter) => matchConstituenta(cst, filter, CstMatchMode.TERM)}
@ -94,7 +94,7 @@ function TabEntityReference({ initial, schema, onChangeValid, onChangeReference
/> />
</div> </div>
<SelectWordForm selected={selectedGrams} setSelected={setSelectedGrams} /> <SelectWordForm value={selectedGrams} onChange={setSelectedGrams} />
<div className='flex items-center gap-4'> <div className='flex items-center gap-4'>
<Label text='Словоформа' /> <Label text='Словоформа' />
@ -104,7 +104,7 @@ function TabEntityReference({ initial, schema, onChangeValid, onChangeReference
className='flex-grow' className='flex-grow'
menuPlacement='top' menuPlacement='top'
value={selectedGrams} value={selectedGrams}
onChangeValue={setSelectedGrams} onChange={setSelectedGrams}
/> />
</div> </div>
</div> </div>

View File

@ -172,7 +172,7 @@ function DlgEditWordForms() {
placeholder='Выберите граммемы' placeholder='Выберите граммемы'
className='min-w-[15rem] h-fit' className='min-w-[15rem] h-fit'
value={inputGrams} value={inputGrams}
onChangeValue={setInputGrams} onChange={setInputGrams}
/> />
</div> </div>

View File

@ -31,31 +31,31 @@ function DlgGraphParams() {
label='Скрыть текст' label='Скрыть текст'
title='Не отображать термины' title='Не отображать термины'
value={params.noText} value={params.noText}
setValue={value => updateParams({ noText: value })} onChange={value => updateParams({ noText: value })}
/> />
<Checkbox <Checkbox
label='Скрыть несвязанные' label='Скрыть несвязанные'
title='Неиспользуемые конституенты' title='Неиспользуемые конституенты'
value={params.noHermits} value={params.noHermits}
setValue={value => updateParams({ noHermits: value })} onChange={value => updateParams({ noHermits: value })}
/> />
<Checkbox <Checkbox
label='Скрыть шаблоны' label='Скрыть шаблоны'
titleHtml='Терм-функции и предикат-функции <br/>с параметризованными аргументами' titleHtml='Терм-функции и предикат-функции <br/>с параметризованными аргументами'
value={params.noTemplates} value={params.noTemplates}
setValue={value => updateParams({ noTemplates: value })} onChange={value => updateParams({ noTemplates: value })}
/> />
<Checkbox <Checkbox
label='Транзитивная редукция' label='Транзитивная редукция'
titleHtml='Удалить связи, образующие <br/>транзитивные пути в графе' titleHtml='Удалить связи, образующие <br/>транзитивные пути в графе'
value={params.noTransitive} value={params.noTransitive}
setValue={value => updateParams({ noTransitive: value })} onChange={value => updateParams({ noTransitive: value })}
/> />
<Checkbox <Checkbox
label='Свернуть порожденные' label='Свернуть порожденные'
title='Не отображать порожденные понятия' title='Не отображать порожденные понятия'
value={params.foldDerived} value={params.foldDerived}
setValue={value => updateParams({ foldDerived: value })} onChange={value => updateParams({ foldDerived: value })}
/> />
</div> </div>
<div className='flex flex-col gap-1'> <div className='flex flex-col gap-1'>
@ -63,42 +63,42 @@ function DlgGraphParams() {
<Checkbox <Checkbox
label={labelCstType(CstType.BASE)} label={labelCstType(CstType.BASE)}
value={params.allowBase} value={params.allowBase}
setValue={value => updateParams({ allowBase: value })} onChange={value => updateParams({ allowBase: value })}
/> />
<Checkbox <Checkbox
label={labelCstType(CstType.STRUCTURED)} label={labelCstType(CstType.STRUCTURED)}
value={params.allowStruct} value={params.allowStruct}
setValue={value => updateParams({ allowStruct: value })} onChange={value => updateParams({ allowStruct: value })}
/> />
<Checkbox <Checkbox
label={labelCstType(CstType.TERM)} label={labelCstType(CstType.TERM)}
value={params.allowTerm} value={params.allowTerm}
setValue={value => updateParams({ allowTerm: value })} onChange={value => updateParams({ allowTerm: value })}
/> />
<Checkbox <Checkbox
label={labelCstType(CstType.AXIOM)} label={labelCstType(CstType.AXIOM)}
value={params.allowAxiom} value={params.allowAxiom}
setValue={value => updateParams({ allowAxiom: value })} onChange={value => updateParams({ allowAxiom: value })}
/> />
<Checkbox <Checkbox
label={labelCstType(CstType.FUNCTION)} label={labelCstType(CstType.FUNCTION)}
value={params.allowFunction} value={params.allowFunction}
setValue={value => updateParams({ allowFunction: value })} onChange={value => updateParams({ allowFunction: value })}
/> />
<Checkbox <Checkbox
label={labelCstType(CstType.PREDICATE)} label={labelCstType(CstType.PREDICATE)}
value={params.allowPredicate} value={params.allowPredicate}
setValue={value => updateParams({ allowPredicate: value })} onChange={value => updateParams({ allowPredicate: value })}
/> />
<Checkbox <Checkbox
label={labelCstType(CstType.CONSTANT)} label={labelCstType(CstType.CONSTANT)}
value={params.allowConstant} value={params.allowConstant}
setValue={value => updateParams({ allowConstant: value })} onChange={value => updateParams({ allowConstant: value })}
/> />
<Checkbox <Checkbox
label={labelCstType(CstType.THEOREM)} label={labelCstType(CstType.THEOREM)}
value={params.allowTheorem} value={params.allowTheorem}
setValue={value => updateParams({ allowTheorem: value })} onChange={value => updateParams({ allowTheorem: value })}
/> />
</div> </div>
</Modal> </Modal>

View File

@ -21,8 +21,8 @@ function TabConstituents({ itemID, selected, setSelected }: TabConstituentsProps
data={schema.items} data={schema.items}
rows={13} rows={13}
prefixID={prefixes.cst_inline_synth_list} prefixID={prefixes.cst_inline_synth_list}
selected={selected} value={selected}
setSelected={setSelected} onChange={setSelected}
/> />
); );
} }

View File

@ -25,7 +25,7 @@ function TabSource({ selected, receiver, setSelected }: TabSourceProps) {
itemType={LibraryItemType.RSFORM} itemType={LibraryItemType.RSFORM}
rows={14} rows={14}
value={selected} value={selected}
onSelectValue={setSelected} onChange={setSelected}
/> />
<div className='flex items-center gap-6 '> <div className='flex items-center gap-6 '>
<span className='select-none'>Выбрана</span> <span className='select-none'>Выбрана</span>

View File

@ -22,8 +22,8 @@ function TabSubstitutions({ sourceID, receiver, selected, substitutions, setSubs
return ( return (
<PickSubstitutions <PickSubstitutions
substitutions={substitutions} value={substitutions}
setSubstitutions={setSubstitutions} onChange={setSubstitutions}
rows={10} rows={10}
prefixID={prefixes.cst_inline_synth_substitutes} prefixID={prefixes.cst_inline_synth_substitutes}
schemas={schemas} schemas={schemas}

View File

@ -103,7 +103,7 @@ function DlgRelocateConstituents() {
placeholder='Выберите исходную схему' placeholder='Выберите исходную схему'
items={sourceSchemas} items={sourceSchemas}
value={source} value={source}
onSelectValue={handleSelectSource} onChange={handleSelectSource}
/> />
<MiniButton <MiniButton
title='Направление перемещения' title='Направление перемещения'
@ -116,7 +116,7 @@ function DlgRelocateConstituents() {
placeholder='Выберите целевую схему' placeholder='Выберите целевую схему'
items={destinationSchemas} items={destinationSchemas}
value={destination} value={destination}
onSelectValue={handleSelectDestination} onChange={handleSelectDestination}
/> />
</div> </div>
{sourceData.isLoading ? <Loader /> : null} {sourceData.isLoading ? <Loader /> : null}
@ -127,8 +127,8 @@ function DlgRelocateConstituents() {
data={filteredConstituents} data={filteredConstituents}
rows={12} rows={12}
prefixID={prefixes.dlg_cst_constituents_list} prefixID={prefixes.dlg_cst_constituents_list}
selected={selected} value={selected}
setSelected={setSelected} onChange={setSelected}
/> />
) : null} ) : null}
</div> </div>

View File

@ -40,8 +40,8 @@ function DlgSubstituteCst() {
> >
<PickSubstitutions <PickSubstitutions
allowSelfSubstitution allowSelfSubstitution
substitutions={substitutions} value={substitutions}
setSubstitutions={setSubstitutions} onChange={setSubstitutions}
rows={6} rows={6}
prefixID={prefixes.dlg_cst_substitutes_list} prefixID={prefixes.dlg_cst_substitutes_list}
schemas={[schema]} schemas={[schema]}

View File

@ -53,7 +53,7 @@ function DlgUploadRSForm() {
label='Загружать название и комментарий' label='Загружать название и комментарий'
className='py-2' className='py-2'
value={loadMetadata} value={loadMetadata}
setValue={value => setLoadMetadata(value)} onChange={value => setLoadMetadata(value)}
/> />
</Modal> </Modal>
); );

View File

@ -127,7 +127,7 @@ function ToolbarSearch({ total, filtered }: ToolbarSearchProps) {
placeholder='Выберите владельца' placeholder='Выберите владельца'
className='min-w-[15rem] text-sm mx-1 mb-1' className='min-w-[15rem] text-sm mx-1 mb-1'
value={filterUser} value={filterUser}
onSelectValue={setFilterUser} onChange={setFilterUser}
/> />
</Dropdown> </Dropdown>
</div> </div>

View File

@ -57,7 +57,7 @@ function TopicsDropdown({ activeTopic, onChangeTopic }: TopicsDropdownProps) {
<SelectTree <SelectTree
items={Object.values(HelpTopic).map(item => item as HelpTopic)} items={Object.values(HelpTopic).map(item => item as HelpTopic)}
value={activeTopic} value={activeTopic}
onChangeValue={handleSelectTopic} onChange={handleSelectTopic}
prefix={prefixes.topic_list} prefix={prefixes.topic_list}
getParent={item => topicParent.get(item) ?? item} getParent={item => topicParent.get(item) ?? item}
getLabel={labelHelpTopic} getLabel={labelHelpTopic}

View File

@ -17,7 +17,7 @@ function TopicsStatic({ activeTopic, onChangeTopic }: TopicsStaticProps) {
<SelectTree <SelectTree
items={Object.values(HelpTopic).map(item => item as HelpTopic)} items={Object.values(HelpTopic).map(item => item as HelpTopic)}
value={activeTopic} value={activeTopic}
onChangeValue={onChangeTopic} onChange={onChangeTopic}
prefix={prefixes.topic_list} prefix={prefixes.topic_list}
getParent={item => topicParent.get(item) ?? item} getParent={item => topicParent.get(item) ?? item}
getLabel={labelHelpTopic} getLabel={labelHelpTopic}

View File

@ -3,7 +3,7 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { ILibraryUpdateDTO } from '@/backend/library/api'; import { IUpdateLibraryItemDTO } from '@/backend/library/api';
import { useUpdateItem } from '@/backend/library/useUpdateItem'; import { useUpdateItem } from '@/backend/library/useUpdateItem';
import { useMutatingOss } from '@/backend/oss/useMutatingOss'; import { useMutatingOss } from '@/backend/oss/useMutatingOss';
import { IconSave } from '@/components/Icons'; import { IconSave } from '@/components/Icons';
@ -73,7 +73,7 @@ function FormOSS({ id }: FormOSSProps) {
if (!schema) { if (!schema) {
return; return;
} }
const data: ILibraryUpdateDTO = { const data: IUpdateLibraryItemDTO = {
id: schema.id, id: schema.id,
item_type: LibraryItemType.RSFORM, item_type: LibraryItemType.RSFORM,
title: title, title: title,

View File

@ -17,9 +17,9 @@ export function Component() {
const router = useConceptNavigation(); const router = useConceptNavigation();
const token = useQueryStrings().get('token') ?? ''; const token = useQueryStrings().get('token') ?? '';
const { validateToken, resetPassword, isPending, error } = useResetPassword(); const { validateToken, resetPassword, isPending, error: serverError } = useResetPassword();
const [isValidated, setIsValidated] = useState(false); const [isTokenValidated, setIsTokenValidated] = useState(false);
const [newPassword, setNewPassword] = useState(''); const [newPassword, setNewPassword] = useState('');
const [newPasswordRepeat, setNewPasswordRepeat] = useState(''); const [newPasswordRepeat, setNewPasswordRepeat] = useState('');
@ -45,11 +45,11 @@ export function Component() {
} }
useEffect(() => { useEffect(() => {
if (!isValidated && !isPending) { if (!isTokenValidated && !isPending) {
validateToken({ token: token }); validateToken({ token: token });
setIsValidated(true); setIsTokenValidated(true);
} }
}, [token, validateToken, isValidated, isPending]); }, [token, validateToken, isTokenValidated, isPending]);
if (isPending) { if (isPending) {
return <Loader />; return <Loader />;
@ -88,13 +88,13 @@ export function Component() {
loading={isPending} loading={isPending}
disabled={!canSubmit} disabled={!canSubmit}
/> />
{error ? <ProcessError error={error} /> : null} {serverError ? <ServerError error={serverError} /> : null}
</form> </form>
); );
} }
// ====== Internals ========= // ====== Internals =========
function ProcessError({ error }: { error: ErrorData }): React.ReactElement { function ServerError({ error }: { error: ErrorData }): React.ReactElement {
if (axios.isAxiosError(error) && error.response && error.response.status === 404) { if (axios.isAxiosError(error) && error.response && error.response.status === 404) {
return <div className='mx-auto mt-6 text-sm select-text text-warn-600'>Данная ссылка не действительна</div>; return <div className='mx-auto mt-6 text-sm select-text text-warn-600'>Данная ссылка не действительна</div>;
} }

View File

@ -112,7 +112,7 @@ function EditorLibraryItem({ controller }: EditorLibraryItemProps) {
<SelectUser <SelectUser
className='w-[25rem] sm:w-[26rem] text-sm' className='w-[25rem] sm:w-[26rem] text-sm'
value={controller.schema.owner ?? undefined} value={controller.schema.owner ?? undefined}
onSelectValue={onSelectUser} onChange={onSelectUser}
/> />
) : null} ) : null}
</Overlay> </Overlay>

View File

@ -5,7 +5,7 @@ import { useEffect, useState } from 'react';
import { useConceptNavigation } from '@/app/Navigation/NavigationContext'; import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
import { urls } from '@/app/urls'; import { urls } from '@/app/urls';
import { ILibraryUpdateDTO } from '@/backend/library/api'; import { IUpdateLibraryItemDTO } from '@/backend/library/api';
import { useUpdateItem } from '@/backend/library/useUpdateItem'; import { useUpdateItem } from '@/backend/library/useUpdateItem';
import { useMutatingRSForm } from '@/backend/rsform/useMutatingRSForm'; import { useMutatingRSForm } from '@/backend/rsform/useMutatingRSForm';
import { IconSave } from '@/components/Icons'; import { IconSave } from '@/components/Icons';
@ -83,7 +83,7 @@ function FormRSForm({ id }: FormRSFormProps) {
if (!schema) { if (!schema) {
return; return;
} }
const data: ILibraryUpdateDTO = { const data: IUpdateLibraryItemDTO = {
id: schema.id, id: schema.id,
item_type: LibraryItemType.RSFORM, item_type: LibraryItemType.RSFORM,
title: title, title: title,
@ -131,7 +131,7 @@ function FormRSForm({ id }: FormRSFormProps) {
className='select-none' className='select-none'
value={schema.version} // value={schema.version} //
items={schema.versions} items={schema.versions}
onSelectValue={handleSelectVersion} onChange={handleSelectVersion}
/> />
</div> </div>
</div> </div>

View File

@ -311,8 +311,8 @@ function TGFlow() {
? cstID => !controller.schema.cstByID.get(cstID)?.is_inherited ? cstID => !controller.schema.cstByID.get(cstID)?.is_inherited
: undefined : undefined
} }
selected={controller.selected} value={controller.selected}
setSelected={handleSetSelected} onChange={handleSetSelected}
emptySelection={controller.selected.length === 0} emptySelection={controller.selected.length === 0}
/> />
) : null} ) : null}

View File

@ -141,11 +141,11 @@ function FormSignup() {
</div> </div>
<div className='flex gap-1 text-sm'> <div className='flex gap-1 text-sm'>
<Checkbox id='accept_terms' label='Принимаю условия' value={acceptPrivacy} setValue={setAcceptPrivacy} /> <Checkbox id='accept_terms' label='Принимаю условия' value={acceptPrivacy} onChange={setAcceptPrivacy} />
<TextURL text='обработки персональных данных...' href={urls.help_topic(HelpTopic.INFO_PRIVACY)} /> <TextURL text='обработки персональных данных...' href={urls.help_topic(HelpTopic.INFO_PRIVACY)} />
</div> </div>
<div className='flex gap-1 text-sm'> <div className='flex gap-1 text-sm'>
<Checkbox id='accept_rules' label='Принимаю ' value={acceptRules} setValue={setAcceptRules} /> <Checkbox id='accept_rules' label='Принимаю ' value={acceptRules} onChange={setAcceptRules} />
<TextURL text='правила поведения на Портале...' href={urls.help_topic(HelpTopic.INFO_RULES)} /> <TextURL text='правила поведения на Портале...' href={urls.help_topic(HelpTopic.INFO_RULES)} />
</div> </div>

View File

@ -11,7 +11,7 @@ import TextInput from '@/components/ui/TextInput';
import TextURL from '@/components/ui/TextURL'; import TextURL from '@/components/ui/TextURL';
export function Component() { export function Component() {
const { requestPasswordReset, isPending, error, reset } = useRequestPasswordReset(); const { requestPasswordReset, isPending, error: serverError, reset: clearServerError } = useRequestPasswordReset();
const [isCompleted, setIsCompleted] = useState(false); const [isCompleted, setIsCompleted] = useState(false);
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
@ -24,8 +24,8 @@ export function Component() {
} }
useEffect(() => { useEffect(() => {
reset(); clearServerError();
}, [email, reset]); }, [email, clearServerError]);
if (isCompleted) { if (isCompleted) {
return ( return (
@ -54,14 +54,14 @@ export function Component() {
loading={isPending} loading={isPending}
disabled={!email} disabled={!email}
/> />
{error ? <ProcessError error={error} /> : null} {serverError ? <ServerError error={serverError} /> : null}
</form> </form>
); );
} }
} }
// ====== Internals ========= // ====== Internals =========
function ProcessError({ error }: { error: ErrorData }): React.ReactElement { function ServerError({ error }: { error: ErrorData }): React.ReactElement {
if (axios.isAxiosError(error) && error.response && error.response.status === 400) { if (axios.isAxiosError(error) && error.response && error.response.status === 400) {
return ( return (
<div className='mx-auto mt-6 text-sm select-text text-warn-600'>Данный email не используется на Портале.</div> <div className='mx-auto mt-6 text-sm select-text text-warn-600'>Данный email не используется на Портале.</div>