Refactoring: attribute names + introducing namespace

This commit is contained in:
IRBorisov 2023-12-18 19:42:27 +03:00
parent 6965e83e19
commit db1d5077c7
74 changed files with 458 additions and 381 deletions

View File

@ -1,9 +1,9 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { IColorsProps, IControlProps } from './commonInterfaces'; import { CProps } from '../props';
interface ButtonProps interface ButtonProps
extends IControlProps, IColorsProps, Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'title'| 'type'> { extends CProps.Control, CProps.Colors, CProps.Button {
text?: string text?: string
icon?: React.ReactNode icon?: React.ReactNode
@ -12,7 +12,7 @@ extends IControlProps, IColorsProps, Omit<React.ButtonHTMLAttributes<HTMLButtonE
} }
function Button({ function Button({
text, icon, tooltip, loading, text, icon, loading,
dense, disabled, noBorder, noOutline, dense, disabled, noBorder, noOutline,
colors = 'clr-btn-default', colors = 'clr-btn-default',
className, className,
@ -21,7 +21,6 @@ function Button({
return ( return (
<button type='button' <button type='button'
disabled={disabled ?? loading} disabled={disabled ?? loading}
title={tooltip}
className={clsx( className={clsx(
'inline-flex gap-2 items-center justify-center', 'inline-flex gap-2 items-center justify-center',
'select-none disabled:cursor-not-allowed', 'select-none disabled:cursor-not-allowed',
@ -34,8 +33,8 @@ function Button({
'outline-none': noOutline, 'outline-none': noOutline,
'clr-outline': !noOutline, 'clr-outline': !noOutline,
}, },
colors, className,
className colors
)} )}
{...restProps} {...restProps}
> >

View File

@ -2,20 +2,20 @@ import clsx from 'clsx';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { CheckboxCheckedIcon } from '../Icons'; import { CheckboxCheckedIcon } from '../Icons';
import { CProps } from '../props';
import Label from './Label'; import Label from './Label';
export interface CheckboxProps export interface CheckboxProps
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'title' | 'value' | 'onClick' > { extends Omit<CProps.Button, 'value' | 'onClick'> {
label?: string label?: string
disabled?: boolean disabled?: boolean
tooltip?: string
value: boolean value: boolean
setValue?: (newValue: boolean) => void setValue?: (newValue: boolean) => void
} }
function Checkbox({ function Checkbox({
id, disabled, tooltip, label, id, disabled, label,
className, value, setValue, ...restProps className, value, setValue, ...restProps
}: CheckboxProps) { }: CheckboxProps) {
const cursor = useMemo( const cursor = useMemo(
@ -46,7 +46,6 @@ function Checkbox({
cursor, cursor,
className className
)} )}
title={tooltip}
disabled={disabled} disabled={disabled}
onClick={handleClick} onClick={handleClick}
{...restProps} {...restProps}

View File

@ -1,18 +1,19 @@
import { BiSearchAlt2 } from 'react-icons/bi'; import { BiSearchAlt2 } from 'react-icons/bi';
import { CProps } from '../props';
import Overlay from './Overlay'; import Overlay from './Overlay';
import TextInput from './TextInput'; import TextInput from './TextInput';
interface ConceptSearchProps { interface ConceptSearchProps
extends CProps.Styling {
value: string value: string
onChange?: (newValue: string) => void onChange?: (newValue: string) => void
noBorder?: boolean noBorder?: boolean
dimensions?: string
} }
function ConceptSearch({ value, onChange, noBorder, dimensions }: ConceptSearchProps) { function ConceptSearch({ value, onChange, noBorder, ...restProps }: ConceptSearchProps) {
return ( return (
<div className={dimensions}> <div {...restProps}>
<Overlay <Overlay
position='top-[-0.125rem] left-3 translate-y-1/2' position='top-[-0.125rem] left-3 translate-y-1/2'
className='pointer-events-none clr-text-controls' className='pointer-events-none clr-text-controls'

View File

@ -3,13 +3,11 @@ import type { TabProps } from 'react-tabs';
import { Tab } from 'react-tabs'; import { Tab } from 'react-tabs';
interface ConceptTabProps interface ConceptTabProps
extends Omit<TabProps, 'title' | 'children'> { extends Omit<TabProps, 'children'> {
className?: string
tooltip?: string
label?: string label?: string
} }
function ConceptTab({ label, tooltip, className, ...otherProps }: ConceptTabProps) { function ConceptTab({ label, className, ...otherProps }: ConceptTabProps) {
return ( return (
<Tab <Tab
className={clsx( className={clsx(
@ -20,7 +18,6 @@ function ConceptTab({ label, tooltip, className, ...otherProps }: ConceptTabProp
'select-none hover:cursor-pointer', 'select-none hover:cursor-pointer',
className className
)} )}
title={tooltip}
{...otherProps} {...otherProps}
> >
{label} {label}

View File

@ -12,9 +12,9 @@ extends Omit<ITooltip, 'variant'> {
} }
function ConceptTooltip({ function ConceptTooltip({
className,
layer='z-tooltip', layer='z-tooltip',
place='bottom', place='bottom',
className,
style, style,
...restProps ...restProps
}: ConceptTooltipProps) { }: ConceptTooltipProps) {

View File

@ -1,17 +1,19 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { CProps } from '../props';
import Overlay from './Overlay'; import Overlay from './Overlay';
interface DropdownProps { interface DropdownProps
extends CProps.Styling {
stretchLeft?: boolean stretchLeft?: boolean
dimensions?: string
children: React.ReactNode children: React.ReactNode
} }
function Dropdown({ function Dropdown({
dimensions = 'w-fit', className,
stretchLeft, stretchLeft,
children children,
...restProps
}: DropdownProps) { }: DropdownProps) {
return ( return (
<Overlay <Overlay
@ -26,8 +28,9 @@ function Dropdown({
'right-0': stretchLeft, 'right-0': stretchLeft,
'left-0': !stretchLeft 'left-0': !stretchLeft
}, },
dimensions className
)} )}
{...restProps}
> >
{children} {children}
</Overlay>); </Overlay>);

View File

@ -1,27 +1,22 @@
import clsx from 'clsx'; import clsx from 'clsx';
interface DropdownButtonProps { import { CProps } from '../props';
interface DropdownButtonProps
extends CProps.Button {
text?: string text?: string
icon?: React.ReactNode icon?: React.ReactNode
className?: string
tooltip?: string | undefined
onClick?: () => void
disabled?: boolean
children?: React.ReactNode children?: React.ReactNode
} }
function DropdownButton({ function DropdownButton({
text, icon, children, text, icon, className, onClick,
tooltip, className, children,
disabled, ...restProps
onClick
}: DropdownButtonProps) { }: DropdownButtonProps) {
return ( return (
<button type='button' <button type='button'
disabled={disabled}
title={tooltip}
onClick={onClick} onClick={onClick}
className={clsx( className={clsx(
'px-3 py-1 inline-flex items-center gap-2', 'px-3 py-1 inline-flex items-center gap-2',
@ -34,6 +29,7 @@ function DropdownButton({
}, },
className className
)} )}
{...restProps}
> >
{children ? children : null} {children ? children : null}
{!children && icon ? icon : null} {!children && icon ? icon : null}

View File

@ -5,15 +5,15 @@ import Checkbox from './Checkbox';
interface DropdownCheckboxProps { interface DropdownCheckboxProps {
value: boolean value: boolean
label?: string label?: string
tooltip?: string title?: string
disabled?: boolean disabled?: boolean
setValue?: (newValue: boolean) => void setValue?: (newValue: boolean) => void
} }
function DropdownCheckbox({ tooltip, setValue, disabled, ...restProps }: DropdownCheckboxProps) { function DropdownCheckbox({ title, setValue, disabled, ...restProps }: DropdownCheckboxProps) {
return ( return (
<div <div
title={tooltip} title={title}
className={clsx( className={clsx(
'px-3 py-1', 'px-3 py-1',
'text-left overflow-ellipsis whitespace-nowrap', 'text-left overflow-ellipsis whitespace-nowrap',

View File

@ -4,22 +4,22 @@ import clsx from 'clsx';
import { useRef, useState } from 'react'; import { useRef, useState } from 'react';
import { BiUpload } from 'react-icons/bi'; import { BiUpload } from 'react-icons/bi';
import { CProps } from '../props';
import Button from './Button'; import Button from './Button';
import Label from './Label'; import Label from './Label';
interface FileInputProps interface FileInputProps
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'className' | 'title' | 'style' | 'accept' | 'type'> { extends Omit<CProps.Input, 'accept' | 'type'> {
label: string label: string
tooltip?: string
dimensions?: string
acceptType?: string acceptType?: string
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
} }
function FileInput({ function FileInput({
label, acceptType, tooltip, label, acceptType, title,
dimensions = 'w-fit', onChange, className, style,
onChange,
...restProps ...restProps
}: FileInputProps) { }: FileInputProps) {
const inputRef = useRef<HTMLInputElement | null>(null); const inputRef = useRef<HTMLInputElement | null>(null);
@ -31,9 +31,9 @@ function FileInput({
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => { const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.files && event.target.files.length > 0) { if (event.target.files && event.target.files.length > 0) {
setFileName(event.target.files[0].name) setFileName(event.target.files[0].name);
} else { } else {
setFileName('') setFileName('');
} }
if (onChange) { if (onChange) {
onChange(event); onChange(event);
@ -41,11 +41,14 @@ function FileInput({
}; };
return ( return (
<div className={clsx( <div
className={clsx(
'py-2', 'py-2',
'flex flex-col gap-2 items-start', 'flex flex-col gap-2 items-center',
dimensions className
)}> )}
style={style}
>
<input type='file' <input type='file'
ref={inputRef} ref={inputRef}
style={{ display: 'none' }} style={{ display: 'none' }}
@ -57,7 +60,7 @@ function FileInput({
text={label} text={label}
icon={<BiUpload size='1.5rem' />} icon={<BiUpload size='1.5rem' />}
onClick={handleUploadClick} onClick={handleUploadClick}
tooltip={tooltip} title={title}
/> />
<Label text={fileName} /> <Label text={fileName} />
</div>); </div>);

View File

@ -0,0 +1,26 @@
import clsx from 'clsx';
import { classnames } from '@/utils/constants';
import { CProps } from '../props';
export interface FlexColumnProps
extends CProps.Div {}
function FlexColumn({
className, children,
...restProps
}: FlexColumnProps) {
return (
<div
className={clsx(
classnames.flex_col,
className
)}
{...restProps}
>
{children}
</div>);
}
export default FlexColumn;

View File

@ -1,13 +1,13 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { LabelHTMLAttributes } from 'react';
import { CProps } from '../props';
interface LabelProps interface LabelProps
extends Omit<React.DetailedHTMLProps<LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>, 'children' | 'title'> { extends CProps.Label {
text?: string text?: string
tooltip?: string
} }
function Label({ text, tooltip, className, ...restProps }: LabelProps) { function Label({ text, className, ...restProps }: LabelProps) {
if (!text) { if (!text) {
return null; return null;
} }
@ -17,7 +17,6 @@ function Label({ text, tooltip, className, ...restProps }: LabelProps) {
'text-sm font-semibold whitespace-nowrap', 'text-sm font-semibold whitespace-nowrap',
className className
)} )}
title={tooltip}
{...restProps} {...restProps}
> >
{text} {text}

View File

@ -2,15 +2,15 @@ interface LabeledValueProps {
id?: string id?: string
label: string label: string
text: string | number text: string | number
tooltip?: string title?: string
} }
function LabeledValue({ id, label, text, tooltip }: LabeledValueProps) { function LabeledValue({ id, label, text, title }: LabeledValueProps) {
return ( return (
<div className='flex justify-between gap-3'> <div className='flex justify-between gap-3'>
<label <label
className='font-semibold' className='font-semibold'
title={tooltip} title={title}
htmlFor={id} htmlFor={id}
> >
{label} {label}

View File

@ -1,21 +1,20 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { CProps } from '../props';
interface MiniButtonProps interface MiniButtonProps
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'className' | 'title' | 'children' > { extends CProps.Button {
icon: React.ReactNode icon: React.ReactNode
tooltip?: string
noHover?: boolean noHover?: boolean
dimensions?: string
} }
function MiniButton({ function MiniButton({
icon, tooltip, noHover, tabIndex, icon, noHover, tabIndex,
dimensions='w-fit h-fit', className,
...restProps ...restProps
}: MiniButtonProps) { }: MiniButtonProps) {
return ( return (
<button type='button' <button type='button'
title={tooltip}
tabIndex={tabIndex ?? -1} tabIndex={tabIndex ?? -1}
className={clsx( className={clsx(
'px-1 py-1', 'px-1 py-1',
@ -26,7 +25,7 @@ function MiniButton({
'outline-none': noHover, 'outline-none': noHover,
'clr-hover': !noHover 'clr-hover': !noHover
}, },
dimensions className
)} )}
{...restProps} {...restProps}
> >

View File

@ -6,29 +6,34 @@ import { BiX } from 'react-icons/bi';
import useEscapeKey from '@/hooks/useEscapeKey'; import useEscapeKey from '@/hooks/useEscapeKey';
import { CProps } from '../props';
import Button from './Button'; import Button from './Button';
import MiniButton from './MiniButton'; import MiniButton from './MiniButton';
import Overlay from './Overlay'; import Overlay from './Overlay';
export interface ModalProps { export interface ModalProps
title?: string extends CProps.Styling {
header?: string
submitText?: string submitText?: string
submitInvalidTooltip?: string submitInvalidTooltip?: string
readonly?: boolean readonly?: boolean
canSubmit?: boolean canSubmit?: boolean
hideWindow: () => void hideWindow: () => void
onSubmit?: () => void onSubmit?: () => void
onCancel?: () => void onCancel?: () => void
children: React.ReactNode children: React.ReactNode
className?: string
} }
function Modal({ function Modal({
title, hideWindow, onSubmit, header, hideWindow, onSubmit,
readonly, onCancel, canSubmit, readonly, onCancel, canSubmit,
submitInvalidTooltip, className, submitInvalidTooltip, className,
children, children,
submitText = 'Продолжить' submitText = 'Продолжить',
...restProps
}: ModalProps) { }: ModalProps) {
const ref = useRef(null); const ref = useRef(null);
useEscapeKey(hideWindow); useEscapeKey(hideWindow);
@ -58,16 +63,17 @@ function Modal({
'border shadow-md', 'border shadow-md',
'clr-app' 'clr-app'
)} )}
{...restProps}
> >
<Overlay position='right-[0.3rem] top-2'> <Overlay position='right-[0.3rem] top-2'>
<MiniButton <MiniButton
tooltip='Закрыть диалоговое окно [ESC]' title='Закрыть диалоговое окно [ESC]'
icon={<BiX size='1.25rem'/>} icon={<BiX size='1.25rem'/>}
onClick={handleCancel} onClick={handleCancel}
/> />
</Overlay> </Overlay>
{title ? <h1 className='px-12 py-2 select-none'>{title}</h1> : null} {header ? <h1 className='px-12 py-2 select-none'>{header}</h1> : null}
<div <div
className={clsx( className={clsx(
@ -89,7 +95,7 @@ function Modal({
{!readonly ? {!readonly ?
<Button autoFocus <Button autoFocus
text={submitText} text={submitText}
tooltip={!canSubmit ? submitInvalidTooltip: ''} title={!canSubmit ? submitInvalidTooltip: ''}
className='min-w-[8rem] min-h-[2.6rem]' className='min-w-[8rem] min-h-[2.6rem]'
colors='clr-btn-primary' colors='clr-btn-primary'
disabled={!canSubmit} disabled={!canSubmit}

View File

@ -1,19 +1,31 @@
interface OverlayProps { import clsx from 'clsx'
import { CProps } from '../props'
interface OverlayProps extends CProps.Styling {
id?: string id?: string
children: React.ReactNode children: React.ReactNode
position?: string position?: string
className?: string
layer?: string layer?: string
} }
function Overlay({ function Overlay({
id, children, className, children, className,
position='top-0 right-0', position='top-0 right-0',
layer='z-pop' layer='z-pop',
...restProps
}: OverlayProps) { }: OverlayProps) {
return ( return (
<div className='relative'> <div className='relative'>
<div id={id} className={`absolute ${className} ${position} ${layer}`}> <div
className={clsx(
'absolute',
className,
position,
layer
)}
{...restProps}
>
{children} {children}
</div> </div>
</div>); </div>);

View File

@ -6,9 +6,7 @@ interface PageControlsProps {
setPageNumber: React.Dispatch<React.SetStateAction<number>> setPageNumber: React.Dispatch<React.SetStateAction<number>>
} }
function PageControls({ function PageControls({ pageNumber, pageCount, setPageNumber }: PageControlsProps) {
pageNumber, pageCount, setPageNumber
}: PageControlsProps) {
return ( return (
<> <>
<button type='button' <button type='button'

View File

@ -1,20 +1,20 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { CProps } from '../props';
interface SelectorButtonProps interface SelectorButtonProps
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'className' | 'children' | 'title' | 'type'> { extends CProps.Button {
text?: string text?: string
icon?: React.ReactNode icon?: React.ReactNode
tooltip?: string
dimensions?: string
borderClass?: string
colors?: string colors?: string
transparent?: boolean transparent?: boolean
} }
function SelectorButton({ function SelectorButton({
text, icon, tooltip, text, icon,
colors = 'clr-btn-default', colors = 'clr-btn-default',
dimensions = 'w-fit h-fit', className,
transparent, transparent,
...restProps ...restProps
}: SelectorButtonProps) { }: SelectorButtonProps) {
@ -29,10 +29,9 @@ function SelectorButton({
'clr-hover': transparent, 'clr-hover': transparent,
'border': !transparent, 'border': !transparent,
}, },
!transparent && colors, className,
dimensions !transparent && colors
)} )}
title={tooltip}
{...restProps} {...restProps}
> >
{icon ? icon : null} {icon ? icon : null}

View File

@ -1,22 +1,22 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { CProps } from '../props';
interface SubmitButtonProps interface SubmitButtonProps
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'title'> { extends CProps.Button {
text?: string text?: string
tooltip?: string
loading?: boolean loading?: boolean
icon?: React.ReactNode icon?: React.ReactNode
} }
function SubmitButton({ function SubmitButton({
text = 'ОК', text = 'ОК',
icon, disabled, tooltip, loading, icon, disabled, loading,
className, className,
...restProps ...restProps
}: SubmitButtonProps) { }: SubmitButtonProps) {
return ( return (
<button type='submit' <button type='submit'
title={tooltip}
className={clsx( className={clsx(
'px-3 py-2 inline-flex items-center gap-2 align-middle justify-center', 'px-3 py-2 inline-flex items-center gap-2 align-middle justify-center',
'border', 'border',

View File

@ -1,25 +1,25 @@
import clsx from 'clsx'; import clsx from 'clsx';
interface SwitchButtonProps<ValueType> { import { CProps } from '../props';
interface SwitchButtonProps<ValueType>
extends CProps.Styling {
id?: string id?: string
value: ValueType value: ValueType
label?: string label?: string
icon?: React.ReactNode icon?: React.ReactNode
tooltip?: string title?: string
dimensions?: string
isSelected?: boolean isSelected?: boolean
onSelect: (value: ValueType) => void onSelect: (value: ValueType) => void
} }
function SwitchButton<ValueType>({ function SwitchButton<ValueType>({
value, icon, label, tooltip, value, icon, label, className,
dimensions='w-fit h-fit',
isSelected, onSelect, ...restProps isSelected, onSelect, ...restProps
}: SwitchButtonProps<ValueType>) { }: SwitchButtonProps<ValueType>) {
return ( return (
<button type='button' tabIndex={-1} <button type='button' tabIndex={-1}
title={tooltip}
onClick={() => onSelect(value)} onClick={() => onSelect(value)}
className={clsx( className={clsx(
'px-2 py-1', 'px-2 py-1',
@ -28,7 +28,7 @@ function SwitchButton<ValueType>({
'clr-btn-clear clr-hover', 'clr-btn-clear clr-hover',
'cursor-pointer', 'cursor-pointer',
isSelected && 'clr-selected', isSelected && 'clr-selected',
dimensions className
)} )}
{...restProps} {...restProps}
> >

View File

@ -1,16 +1,15 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { TextareaHTMLAttributes } from 'react';
import { IColorsProps, IEditorProps } from './commonInterfaces'; import { CProps } from '../props';
import Label from './Label'; import Label from './Label';
export interface TextAreaProps export interface TextAreaProps
extends IEditorProps, IColorsProps, Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'title'> { extends CProps.Editor, CProps.Colors, CProps.TextArea {
dense?: boolean dense?: boolean
} }
function TextArea({ function TextArea({
id, label, required, tooltip, rows, id, label, required, rows,
dense, noBorder, noOutline, dense, noBorder, noOutline,
className, className,
colors = 'clr-input', colors = 'clr-input',
@ -26,7 +25,6 @@ function TextArea({
)}> )}>
<Label text={label} htmlFor={id} /> <Label text={label} htmlFor={id} />
<textarea id={id} <textarea id={id}
title={tooltip}
className={clsx( className={clsx(
'px-3 py-2', 'px-3 py-2',
'leading-tight', 'leading-tight',

View File

@ -1,10 +1,10 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { IColorsProps, IEditorProps } from './commonInterfaces'; import { CProps } from '../props';
import Label from './Label'; import Label from './Label';
interface TextInputProps interface TextInputProps
extends IEditorProps, IColorsProps, Omit<React.InputHTMLAttributes<HTMLInputElement>, 'title'> { extends CProps.Editor, CProps.Colors, CProps.Input {
dense?: boolean dense?: boolean
allowEnter?: boolean allowEnter?: boolean
} }
@ -16,7 +16,7 @@ function preventEnterCapture(event: React.KeyboardEvent<HTMLInputElement>) {
} }
function TextInput({ function TextInput({
id, label, dense, tooltip, noBorder, noOutline, allowEnter, disabled, id, label, dense, noBorder, noOutline, allowEnter, disabled,
className, className,
colors = 'clr-input', colors = 'clr-input',
onKeyDown, onKeyDown,
@ -32,7 +32,6 @@ function TextInput({
)}> )}>
<Label text={label} htmlFor={id} /> <Label text={label} htmlFor={id} />
<input id={id} <input id={id}
title={tooltip}
className={clsx( className={clsx(
'py-2', 'py-2',
'leading-tight truncate hover:text-clip', 'leading-tight truncate hover:text-clip',

View File

@ -3,31 +3,29 @@ import { Link } from 'react-router-dom';
interface TextURLProps { interface TextURLProps {
text: string text: string
tooltip?: string title?: string
href?: string href?: string
color?: string color?: string
onClick?: () => void onClick?: () => void
} }
function TextURL({ text, href, tooltip, color='clr-text-url', onClick }: TextURLProps) { function TextURL({ text, href, title, color='clr-text-url', onClick }: TextURLProps) {
const design = `cursor-pointer hover:underline ${color}`; const design = `cursor-pointer hover:underline ${color}`;
if (href) { if (href) {
return ( return (
<Link <Link tabIndex={-1}
className={design} className={design}
title={tooltip} title={title}
to={href} to={href}
tabIndex={-1}
> >
{text} {text}
</Link> </Link>
); );
} else if (onClick) { } else if (onClick) {
return ( return (
<span <span tabIndex={-1}
className={design} className={design}
onClick={onClick} onClick={onClick}
tabIndex={-1}
> >
{text} {text}
</span>); </span>);

View File

@ -13,7 +13,7 @@ extends Omit<CheckboxProps, 'value' | 'setValue'> {
} }
function Tristate({ function Tristate({
id, disabled, tooltip, label, id, disabled, label,
className, className,
value, setValue, value, setValue,
...restProps ...restProps
@ -51,7 +51,6 @@ function Tristate({
cursor, cursor,
className className
)} )}
title={tooltip}
disabled={disabled} disabled={disabled}
onClick={handleClick} onClick={handleClick}
{...restProps} {...restProps}

View File

@ -1,20 +0,0 @@
// =========== Module contains interfaces for common UI elements. ==========
export interface IControlProps {
tooltip?: string
disabled?: boolean
noBorder?: boolean
noOutline?: boolean
}
export interface IStylingProps {
style?: React.CSSProperties
className?: string
}
export interface IEditorProps extends IControlProps {
label?: string
}
export interface IColorsProps {
colors?: string
}

View File

@ -10,7 +10,7 @@ import {
import clsx from 'clsx'; import clsx from 'clsx';
import { useState } from 'react'; import { useState } from 'react';
import { IStylingProps } from '../Common/commonInterfaces'; import { CProps } from '../props';
import DefaultNoData from './DefaultNoData'; import DefaultNoData from './DefaultNoData';
import PaginationTools from './PaginationTools'; import PaginationTools from './PaginationTools';
import TableBody from './TableBody'; import TableBody from './TableBody';
@ -25,7 +25,7 @@ export interface IConditionalStyle<TData> {
} }
export interface DataTableProps<TData extends RowData> export interface DataTableProps<TData extends RowData>
extends IStylingProps, Pick<TableOptions<TData>, extends CProps.Styling, Pick<TableOptions<TData>,
'data' | 'columns' | 'data' | 'columns' |
'onRowSelectionChange' | 'onColumnVisibilityChange' 'onRowSelectionChange' | 'onColumnVisibilityChange'
> { > {

View File

@ -9,7 +9,7 @@ interface SelectAllProps<TData> {
function SelectAll<TData>({ table }: SelectAllProps<TData>) { function SelectAll<TData>({ table }: SelectAllProps<TData>) {
return ( return (
<Tristate tabIndex={-1} <Tristate tabIndex={-1}
tooltip='Выделить все' title='Выделить все'
value={ value={
(!table.getIsAllPageRowsSelected() && table.getIsSomePageRowsSelected()) (!table.getIsAllPageRowsSelected() && table.getIsSomePageRowsSelected())
? null ? null

View File

@ -11,7 +11,7 @@ function ConstituentaTooltip({ data, anchor }: ConstituentaTooltipProps) {
return ( return (
<ConceptTooltip clickable <ConceptTooltip clickable
anchorSelect={anchor} anchorSelect={anchor}
className='max-w-[25rem] min-w-[25rem]' className='w-[25rem]'
> >
<InfoConstituenta data={data} /> <InfoConstituenta data={data} />
</ConceptTooltip>); </ConceptTooltip>);

View File

@ -4,15 +4,16 @@ import ConceptTooltip from '@/components/Common/ConceptTooltip';
import TextURL from '@/components/Common/TextURL'; import TextURL from '@/components/Common/TextURL';
import { HelpTopic } from '@/models/miscelanious'; import { HelpTopic } from '@/models/miscelanious';
import { CProps } from '../props';
import InfoTopic from './InfoTopic'; import InfoTopic from './InfoTopic';
interface HelpButtonProps { interface HelpButtonProps
extends CProps.Styling {
topic: HelpTopic topic: HelpTopic
offset?: number offset?: number
dimensions?: string
} }
function HelpButton({ topic, offset, dimensions }: HelpButtonProps) { function HelpButton({ topic, ...restProps }: HelpButtonProps) {
return ( return (
<> <>
<div <div
@ -24,8 +25,7 @@ function HelpButton({ topic, offset, dimensions }: HelpButtonProps) {
<ConceptTooltip clickable <ConceptTooltip clickable
anchorSelect={`#help-${topic}`} anchorSelect={`#help-${topic}`}
layer='z-modal-tooltip' layer='z-modal-tooltip'
className={dimensions} {...restProps}
offset={offset}
> >
<div className='relative'> <div className='relative'>
<div className='absolute right-0 text-sm top-[0.4rem]'> <div className='absolute right-0 text-sm top-[0.4rem]'>

View File

@ -25,15 +25,15 @@ function UserDropdown({ hideDropdown }: UserDropdownProps) {
}; };
return ( return (
<Dropdown dimensions='w-36' stretchLeft> <Dropdown className='w-36' stretchLeft>
<DropdownButton <DropdownButton
text={user?.username} text={user?.username}
tooltip='Профиль пользователя' title='Профиль пользователя'
onClick={navigateProfile} onClick={navigateProfile}
/> />
<DropdownButton <DropdownButton
text={darkMode ? 'Светлая тема' : 'Темная тема'} text={darkMode ? 'Светлая тема' : 'Темная тема'}
tooltip='Переключение темы оформления' title='Переключение темы оформления'
onClick={toggleDarkMode} onClick={toggleDarkMode}
/> />
<DropdownButton <DropdownButton

View File

@ -47,10 +47,10 @@ const editorSetup: BasicSetupOptions = {
interface RSInputProps interface RSInputProps
extends Pick<ReactCodeMirrorProps, extends Pick<ReactCodeMirrorProps,
'id' | 'height' | 'minHeight' | 'maxHeight' | 'value' | 'className' | 'onFocus' | 'onBlur' | 'placeholder' 'id' | 'height' | 'minHeight' | 'maxHeight' | 'value' |
'onFocus' | 'onBlur' | 'placeholder' | 'style' | 'className'
> { > {
label?: string label?: string
dimensions?: string
disabled?: boolean disabled?: boolean
noTooltip?: boolean noTooltip?: boolean
innerref?: RefObject<ReactCodeMirrorRef> | undefined innerref?: RefObject<ReactCodeMirrorRef> | undefined
@ -61,7 +61,7 @@ extends Pick<ReactCodeMirrorProps,
function RSInput({ function RSInput({
id, label, innerref, onChange, onAnalyze, id, label, innerref, onChange, onAnalyze,
disabled, noTooltip, disabled, noTooltip,
dimensions = 'w-full', className, style,
...restProps ...restProps
}: RSInputProps) { }: RSInputProps) {
const { darkMode, colors } = useConceptTheme(); const { darkMode, colors } = useConceptTheme();
@ -129,11 +129,14 @@ function RSInput({
}, [thisRef, onAnalyze]); }, [thisRef, onAnalyze]);
return ( return (
<div className={clsx( <div
className={clsx(
'flex flex-col gap-2', 'flex flex-col gap-2',
dimensions, className,
cursor cursor
)}> )}
style={style}
>
<Label text={label} htmlFor={id}/> <Label text={label} htmlFor={id}/>
<CodeMirror id={id} <CodeMirror id={id}
ref={thisRef} ref={thisRef}

View File

@ -9,14 +9,12 @@ interface SelectGrammemeProps extends
Omit<SelectMultiProps<IGrammemeOption>, 'value' | 'onChange'> { Omit<SelectMultiProps<IGrammemeOption>, 'value' | 'onChange'> {
value: IGrammemeOption[] value: IGrammemeOption[]
setValue: React.Dispatch<React.SetStateAction<IGrammemeOption[]>> setValue: React.Dispatch<React.SetStateAction<IGrammemeOption[]>>
dimensions?: string
className?: string className?: string
placeholder?: string placeholder?: string
} }
function SelectGrammeme({ function SelectGrammeme({
value, setValue, value, setValue,
dimensions, className, placeholder,
...restProps ...restProps
}: SelectGrammemeProps) { }: SelectGrammemeProps) {
const [options, setOptions] = useState<IGrammemeOption[]>([]); const [options, setOptions] = useState<IGrammemeOption[]>([]);
@ -33,9 +31,7 @@ function SelectGrammeme({
return ( return (
<SelectMulti <SelectMulti
className={`${dimensions} ${className}`}
options={options} options={options}
placeholder={placeholder}
value={value} value={value}
onChange={newValue => setValue([...newValue].sort(compareGrammemeOptions))} onChange={newValue => setValue([...newValue].sort(compareGrammemeOptions))}
{...restProps} {...restProps}

View File

@ -0,0 +1,36 @@
// =========== Module contains interfaces for common UI elements. ==========
export namespace CProps {
export type Control = {
title?: string
disabled?: boolean
noBorder?: boolean
noOutline?: boolean
}
export type Styling = {
style?: React.CSSProperties
className?: string
}
export type Editor = Control & {
label?: string
}
export type Colors = {
colors?: string
}
export type Div = React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
export type Button = Omit<
React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>,
'children' | 'type'
>;
export type Label = Omit<
React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>,
'children'
>;
export type TextArea = React.DetailedHTMLProps<React.TextareaHTMLAttributes<HTMLTextAreaElement>, HTMLTextAreaElement>;
export type Input = React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>;
}

View File

@ -1,5 +1,6 @@
'use client'; 'use client';
import clsx from 'clsx';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
@ -11,6 +12,7 @@ import { useLibrary } from '@/context/LibraryContext';
import { useConceptNavigation } from '@/context/NagivationContext'; import { useConceptNavigation } from '@/context/NagivationContext';
import { ILibraryItem } from '@/models/library'; import { ILibraryItem } from '@/models/library';
import { IRSFormCreateData } from '@/models/rsform'; import { IRSFormCreateData } from '@/models/rsform';
import { classnames } from '@/utils/constants';
import { cloneTitle } from '@/utils/misc'; import { cloneTitle } from '@/utils/misc';
interface DlgCloneLibraryItemProps interface DlgCloneLibraryItemProps
@ -57,12 +59,12 @@ function DlgCloneLibraryItem({ hideWindow, base }: DlgCloneLibraryItemProps) {
return ( return (
<Modal <Modal
title='Создание копии концептуальной схемы' header='Создание копии концептуальной схемы'
hideWindow={hideWindow} hideWindow={hideWindow}
canSubmit={canSubmit} canSubmit={canSubmit}
submitText='Создать' submitText='Создать'
onSubmit={handleSubmit} onSubmit={handleSubmit}
className='flex flex-col gap-3 px-6 py-2' className={clsx('px-6 py-2', classnames.flex_col)}
> >
<TextInput <TextInput
label='Полное название' label='Полное название'

View File

@ -132,7 +132,7 @@ function ArgumentsTab({ state, schema, partialUpdate }: ArgumentsTabProps) {
<div className='max-h-[1.2rem]'> <div className='max-h-[1.2rem]'>
{props.row.original.value ? {props.row.original.value ?
<MiniButton <MiniButton
tooltip='Очистить значение' title='Очистить значение'
icon={<BiX size='0.75rem' className='clr-text-warning'/>} icon={<BiX size='0.75rem' className='clr-text-warning'/>}
noHover noHover
onClick={() => handleClearArgument(props.row.original)} onClick={() => handleClearArgument(props.row.original)}
@ -148,7 +148,7 @@ function ArgumentsTab({ state, schema, partialUpdate }: ArgumentsTabProps) {
}], [selectedArgument, colors]); }], [selectedArgument, colors]);
return ( return (
<div className='flex flex-col gap-3'> <>
<DataTable dense noFooter <DataTable dense noFooter
className={clsx( className={clsx(
'max-h-[5.8rem] min-h-[5.8rem]', 'max-h-[5.8rem] min-h-[5.8rem]',
@ -173,8 +173,8 @@ function ArgumentsTab({ state, schema, partialUpdate }: ArgumentsTabProps) {
/> />
<div className={clsx( <div className={clsx(
'py-1 flex gap-2 justify-center items-center', 'my-4',
'w-full', 'flex gap-2 justify-center items-center',
'select-none' 'select-none'
)}> )}>
<span title='Выберите аргумент из списка сверху и значение из списка снизу' <span title='Выберите аргумент из списка сверху и значение из списка снизу'
@ -184,25 +184,25 @@ function ArgumentsTab({ state, schema, partialUpdate }: ArgumentsTabProps) {
</span> </span>
<span>=</span> <span>=</span>
<RSInput noTooltip <RSInput noTooltip
dimensions='max-w-[12rem] w-full' className='w-[12rem]'
value={argumentValue} value={argumentValue}
onChange={newValue => setArgumentValue(newValue)} onChange={newValue => setArgumentValue(newValue)}
/> />
<div className='flex'> <div className='flex'>
<MiniButton <MiniButton
tooltip='Подставить значение аргумента' title='Подставить значение аргумента'
icon={<BiCheck size='1.25rem' className={!!argumentValue && !!selectedArgument ? 'clr-text-success' : ''} />} icon={<BiCheck size='1.25rem' className={!!argumentValue && !!selectedArgument ? 'clr-text-success' : ''} />}
disabled={!argumentValue || !selectedArgument} disabled={!argumentValue || !selectedArgument}
onClick={() => handleAssignArgument(selectedArgument!, argumentValue)} onClick={() => handleAssignArgument(selectedArgument!, argumentValue)}
/> />
<MiniButton <MiniButton
tooltip='Откатить значение' title='Откатить значение'
disabled={!isModified} disabled={!isModified}
onClick={handleReset} onClick={handleReset}
icon={<BiRefresh size='1.25rem' className={isModified ? 'clr-text-primary' : ''} />} icon={<BiRefresh size='1.25rem' className={isModified ? 'clr-text-primary' : ''} />}
/> />
<MiniButton <MiniButton
tooltip='Очистить значение аргумента' title='Очистить значение аргумента'
disabled={!selectedClearable} disabled={!selectedClearable}
icon={<BiX size='1.25rem' className={selectedClearable ? 'clr-text-warning': ''}/>} icon={<BiX size='1.25rem' className={selectedClearable ? 'clr-text-warning': ''}/>}
onClick={() => selectedArgument ? handleClearArgument(selectedArgument) : undefined} onClick={() => selectedArgument ? handleClearArgument(selectedArgument) : undefined}
@ -220,12 +220,12 @@ function ArgumentsTab({ state, schema, partialUpdate }: ArgumentsTabProps) {
<RSInput id='result' <RSInput id='result'
placeholder='Итоговое определение' placeholder='Итоговое определение'
className='mt-[1.2rem]'
height='5.1rem' height='5.1rem'
dimensions='w-full mt-[0.45rem]'
value={state.definition} value={state.definition}
disabled disabled
/> />
</div>); </>);
} }
export default ArgumentsTab; export default ArgumentsTab;

View File

@ -17,7 +17,7 @@ interface ConstituentaTabProps {
function ConstituentaTab({state, partialUpdate}: ConstituentaTabProps) { function ConstituentaTab({state, partialUpdate}: ConstituentaTabProps) {
return ( return (
<div className='flex flex-col gap-3'> <>
<div className='flex self-center gap-3 pr-2'> <div className='flex self-center gap-3 pr-2'>
<SelectSingle <SelectSingle
className='min-w-[14rem]' className='min-w-[14rem]'
@ -62,7 +62,7 @@ function ConstituentaTab({state, partialUpdate}: ConstituentaTabProps) {
value={state.convention} value={state.convention}
onChange={event => partialUpdate({ convention: event.target.value })} onChange={event => partialUpdate({ convention: event.target.value })}
/> />
</div>); </>);
} }
export default ConstituentaTab; export default ConstituentaTab;

View File

@ -12,6 +12,7 @@ import usePartialUpdate from '@/hooks/usePartialUpdate';
import { HelpTopic } from '@/models/miscelanious'; import { HelpTopic } from '@/models/miscelanious';
import { CstType, ICstCreateData, IRSForm } from '@/models/rsform'; import { CstType, ICstCreateData, IRSForm } from '@/models/rsform';
import { inferTemplatedType, substituteTemplateArgs } from '@/models/rslangAPI'; import { inferTemplatedType, substituteTemplateArgs } from '@/models/rslangAPI';
import { classnames } from '@/utils/constants';
import { createAliasFor, validateCstAlias } from '@/utils/misc'; import { createAliasFor, validateCstAlias } from '@/utils/misc';
import ArgumentsTab, { IArgumentsState } from './ArgumentsTab'; import ArgumentsTab, { IArgumentsState } from './ArgumentsTab';
@ -113,15 +114,15 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
return ( return (
<Modal <Modal
title='Создание конституенты из шаблона' header='Создание конституенты из шаблона'
submitText='Создать'
className='w-[43rem] h-[36rem] px-6'
hideWindow={hideWindow} hideWindow={hideWindow}
canSubmit={validated} canSubmit={validated}
onSubmit={handleSubmit} onSubmit={handleSubmit}
submitText='Создать'
className='max-w-[43rem] min-w-[43rem] min-h-[36rem] px-6'
> >
<Overlay position='top-0 right-[6rem]'> <Overlay position='top-0 right-[6rem]'>
<HelpButton topic={HelpTopic.RSTEMPLATES} dimensions='max-w-[35rem]' /> <HelpButton topic={HelpTopic.RSTEMPLATES} className='max-w-[35rem]' />
</Overlay> </Overlay>
<Tabs forceRenderTabPanel <Tabs forceRenderTabPanel
selectedTabClassName='clr-selected' selectedTabClassName='clr-selected'
@ -136,17 +137,17 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
)}> )}>
<ConceptTab <ConceptTab
label='Шаблон' label='Шаблон'
tooltip='Выбор шаблона выражения' title='Выбор шаблона выражения'
className='w-[8rem]' className='w-[8rem]'
/> />
<ConceptTab <ConceptTab
label='Аргументы' label='Аргументы'
tooltip='Подстановка аргументов шаблона' title='Подстановка аргументов шаблона'
className='w-[8rem]' className='w-[8rem]'
/> />
<ConceptTab <ConceptTab
label='Конституента' label='Конституента'
tooltip='Редактирование атрибутов конституенты' title='Редактирование атрибутов конституенты'
className='w-[8rem]' className='w-[8rem]'
/> />
</TabList> </TabList>
@ -166,7 +167,7 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
/> />
</TabPanel> </TabPanel>
<TabPanel style={{ display: activeTab === TabID.CONSTITUENTA ? '': 'none' }}> <TabPanel className={classnames.flex_col} style={{ display: activeTab === TabID.CONSTITUENTA ? '': 'none' }}>
<ConstituentaTab <ConstituentaTab
state={constituenta} state={constituenta}
partialUpdate={updateConstituenta} partialUpdate={updateConstituenta}

View File

@ -87,8 +87,7 @@ function TemplateTab({ state, partialUpdate }: TemplateTabProps) {
}, [state.filterCategory, selectedSchema]); }, [state.filterCategory, selectedSchema]);
return ( return (
<div className='flex flex-col gap-3'> <>
<div>
<div className='flex justify-stretch'> <div className='flex justify-stretch'>
<SelectSingle <SelectSingle
placeholder='Выберите категорию' placeholder='Выберите категорию'
@ -116,21 +115,18 @@ function TemplateTab({ state, partialUpdate }: TemplateTabProps) {
prefixID={prefixes.cst_template_ist} prefixID={prefixes.cst_template_ist}
rows={9} rows={9}
/> />
</div> <TextArea disabled spellCheck
<TextArea id='term'
rows={2}
disabled
placeholder='Шаблон конституенты не выбран' placeholder='Шаблон конституенты не выбран'
className='my-3'
rows={2}
value={prototypeInfo} value={prototypeInfo}
spellCheck
/> />
<RSInput id='expression' <RSInput disabled
height='5.1rem'
placeholder='Выберите шаблон из списка' placeholder='Выберите шаблон из списка'
disabled height='5.1rem'
value={state.prototype?.definition_formal} value={state.prototype?.definition_formal}
/> />
</div>); </>);
} }
export default TemplateTab; export default TemplateTab;

View File

@ -10,6 +10,7 @@ import TextInput from '@/components/Common/TextInput';
import RSInput from '@/components/RSInput'; import RSInput from '@/components/RSInput';
import usePartialUpdate from '@/hooks/usePartialUpdate'; import usePartialUpdate from '@/hooks/usePartialUpdate';
import { CstType,ICstCreateData, IRSForm } from '@/models/rsform'; import { CstType,ICstCreateData, IRSForm } from '@/models/rsform';
import { classnames } from '@/utils/constants';
import { labelCstType } from '@/utils/labels'; import { labelCstType } from '@/utils/labels';
import { createAliasFor, validateCstAlias } from '@/utils/misc'; import { createAliasFor, validateCstAlias } from '@/utils/misc';
import { SelectorCstType } from '@/utils/selectors'; import { SelectorCstType } from '@/utils/selectors';
@ -50,14 +51,15 @@ function DlgCreateCst({ hideWindow, initial, schema, onCreate }: DlgCreateCstPro
return ( return (
<Modal <Modal
title='Создание конституенты' header='Создание конституенты'
hideWindow={hideWindow} hideWindow={hideWindow}
canSubmit={validated} canSubmit={validated}
onSubmit={handleSubmit} onSubmit={handleSubmit}
submitText='Создать' submitText='Создать'
className={clsx( className={clsx(
'w-[35rem]', 'w-[35rem]',
'py-2 px-6 flex flex-col gap-3' 'py-2 px-6',
classnames.flex_col
)} )}
> >
<div className='flex self-center gap-6'> <div className='flex self-center gap-6'>

View File

@ -1,11 +1,12 @@
'use client'; 'use client';
import clsx from 'clsx';
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import Checkbox from '@/components/Common/Checkbox'; import Checkbox from '@/components/Common/Checkbox';
import Modal, { ModalProps } from '@/components/Common/Modal'; import Modal, { ModalProps } from '@/components/Common/Modal';
import { IRSForm } from '@/models/rsform'; import { IRSForm } from '@/models/rsform';
import { prefixes } from '@/utils/constants'; import { classnames, prefixes } from '@/utils/constants';
import ConstituentsList from './ConstituentsList'; import ConstituentsList from './ConstituentsList';
@ -31,11 +32,15 @@ function DlgDeleteCst({ hideWindow, selected, schema, onDelete }: DlgDeleteCstPr
return ( return (
<Modal canSubmit <Modal canSubmit
title='Удаление конституент' header='Удаление конституент'
submitText={expandOut ? 'Удалить с зависимыми' : 'Удалить'} submitText={expandOut ? 'Удалить с зависимыми' : 'Удалить'}
hideWindow={hideWindow} hideWindow={hideWindow}
onSubmit={handleSubmit} onSubmit={handleSubmit}
className='max-w-[60vw] min-w-[30rem] px-6 flex flex-col gap-3' className={clsx(
'max-w-[60vw] min-w-[30rem]',
'px-6',
classnames.flex_col
)}
> >
<ConstituentsList <ConstituentsList
title='Выбраны к удалению' title='Выбраны к удалению'

View File

@ -46,15 +46,15 @@ function DlgEditReference({ hideWindow, items, initial, onSave }: DlgEditReferen
return ( return (
<Modal <Modal
title='Редактирование ссылки' header='Редактирование ссылки'
submitText='Сохранить ссылку' submitText='Сохранить ссылку'
hideWindow={hideWindow} hideWindow={hideWindow}
canSubmit={isValid} canSubmit={isValid}
onSubmit={handleSubmit} onSubmit={handleSubmit}
className='min-w-[40rem] max-w-[40rem] px-6 min-h-[34rem]' className='w-[40rem] px-6 min-h-[34rem]'
> >
<Overlay position='top-0 right-[4rem]'> <Overlay position='top-0 right-[4rem]'>
<HelpButton topic={HelpTopic.TERM_CONTROL} dimensions='max-w-[35rem]' offset={14} /> <HelpButton topic={HelpTopic.TERM_CONTROL} className='max-w-[35rem]' offset={14} />
</Overlay> </Overlay>
<Tabs <Tabs
@ -69,12 +69,12 @@ function DlgEditReference({ hideWindow, items, initial, onSave }: DlgEditReferen
'border divide-x rounded-none' 'border divide-x rounded-none'
)}> )}>
<ConceptTab <ConceptTab
tooltip='Отсылка на термин в заданной словоформе' title='Отсылка на термин в заданной словоформе'
label={labelReferenceType(ReferenceType.ENTITY)} label={labelReferenceType(ReferenceType.ENTITY)}
className='w-[12rem]' className='w-[12rem]'
/> />
<ConceptTab <ConceptTab
tooltip='Установление синтаксической связи с отсылкой на термин' title='Установление синтаксической связи с отсылкой на термин'
label={labelReferenceType(ReferenceType.SYNTACTIC)} label={labelReferenceType(ReferenceType.SYNTACTIC)}
className='w-[12rem]' className='w-[12rem]'
/> />

View File

@ -2,6 +2,7 @@
import { useEffect, useLayoutEffect, useState } from 'react'; import { useEffect, useLayoutEffect, useState } from 'react';
import FlexColumn from '@/components/Common/FlexColumn';
import Label from '@/components/Common/Label'; import Label from '@/components/Common/Label';
import TextInput from '@/components/Common/TextInput'; import TextInput from '@/components/Common/TextInput';
import ConstituentaPicker from '@/components/Shared/ConstituentaPicker'; import ConstituentaPicker from '@/components/Shared/ConstituentaPicker';
@ -61,7 +62,7 @@ function EntityTab({ initial, items, setIsValid, setReference }: EntityTabProps)
} }
return ( return (
<div className='flex flex-col gap-3'> <FlexColumn>
<ConstituentaPicker <ConstituentaPicker
value={selectedCst} value={selectedCst}
data={items} data={items}
@ -77,7 +78,7 @@ function EntityTab({ initial, items, setIsValid, setReference }: EntityTabProps)
<TextInput dense <TextInput dense
label='Конституента' label='Конституента'
placeholder='Имя' placeholder='Имя'
className='max-w-[11rem] min-w-[11rem]' className='w-[11rem]'
value={alias} value={alias}
onChange={event => setAlias(event.target.value)} onChange={event => setAlias(event.target.value)}
/> />
@ -85,7 +86,7 @@ function EntityTab({ initial, items, setIsValid, setReference }: EntityTabProps)
label='Термин' label='Термин'
className='flex-grow text-sm' className='flex-grow text-sm'
value={term} value={term}
tooltip={term} title={term}
/> />
</div> </div>
@ -104,7 +105,7 @@ function EntityTab({ initial, items, setIsValid, setReference }: EntityTabProps)
setValue={setSelectedGrams} setValue={setSelectedGrams}
/> />
</div> </div>
</div>); </FlexColumn>);
} }
export default EntityTab; export default EntityTab;

View File

@ -123,14 +123,14 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
return ( return (
<Modal canSubmit <Modal canSubmit
title='Редактирование словоформ' header='Редактирование словоформ'
hideWindow={hideWindow} hideWindow={hideWindow}
submitText='Сохранить' submitText='Сохранить'
onSubmit={handleSubmit} onSubmit={handleSubmit}
className='flex flex-col min-w-[40rem] max-w-[40rem] px-6' className='flex flex-col w-[40rem] px-6'
> >
<Overlay position='top-[-0.2rem] left-[7.5rem]'> <Overlay position='top-[-0.2rem] left-[7.5rem]'>
<HelpButton topic={HelpTopic.TERM_CONTROL} dimensions='max-w-[38rem]' offset={3} /> <HelpButton topic={HelpTopic.TERM_CONTROL} className='max-w-[38rem]' offset={3} />
</Overlay> </Overlay>
<TextArea disabled spellCheck <TextArea disabled spellCheck
@ -154,7 +154,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
/> />
<div className='flex flex-col self-center gap-1'> <div className='flex flex-col self-center gap-1'>
<MiniButton noHover <MiniButton noHover
tooltip='Определить граммемы' title='Определить граммемы'
icon={<BiRightArrow icon={<BiRightArrow
size='1.25rem' size='1.25rem'
className={inputText ? 'clr-text-primary' : ''} className={inputText ? 'clr-text-primary' : ''}
@ -163,7 +163,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
onClick={handleParse} onClick={handleParse}
/> />
<MiniButton noHover <MiniButton noHover
tooltip='Генерировать словоформу' title='Генерировать словоформу'
icon={<BiLeftArrow size='1.25rem' className={inputGrams.length !== 0 ? 'clr-text-primary' : ''} />} icon={<BiLeftArrow size='1.25rem' className={inputGrams.length !== 0 ? 'clr-text-primary' : ''} />}
disabled={textProcessor.loading || inputGrams.length == 0} disabled={textProcessor.loading || inputGrams.length == 0}
onClick={handleInflect} onClick={handleInflect}
@ -171,7 +171,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
</div> </div>
<SelectGrammeme <SelectGrammeme
placeholder='Выберите граммемы' placeholder='Выберите граммемы'
dimensions='min-w-[15rem] max-w-[15rem]' className='w-[15rem]'
value={inputGrams} value={inputGrams}
setValue={setInputGrams} setValue={setInputGrams}
/> />
@ -179,7 +179,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
<Overlay position='top-2 left-0'> <Overlay position='top-2 left-0'>
<MiniButton noHover <MiniButton noHover
tooltip='Внести словоформу' title='Внести словоформу'
icon={<BiCheck icon={<BiCheck
size='1.25rem' size='1.25rem'
className={inputText && inputGrams.length !== 0 ? 'clr-text-success' : ''} className={inputText && inputGrams.length !== 0 ? 'clr-text-success' : ''}
@ -188,7 +188,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
onClick={handleAddForm} onClick={handleAddForm}
/> />
<MiniButton noHover <MiniButton noHover
tooltip='Генерировать стандартные словоформы' title='Генерировать стандартные словоформы'
icon={<BiChevronsDown size='1.25rem' className={inputText ? 'clr-text-primary' : ''} icon={<BiChevronsDown size='1.25rem' className={inputText ? 'clr-text-primary' : ''}
/>} />}
disabled={textProcessor.loading || !inputText} disabled={textProcessor.loading || !inputText}
@ -203,7 +203,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
)}> )}>
<span>Заданные вручную словоформы [{forms.length}]</span> <span>Заданные вручную словоформы [{forms.length}]</span>
<MiniButton noHover <MiniButton noHover
tooltip='Сбросить все словоформы' title='Сбросить все словоформы'
icon={<BiX size='1rem' className={forms.length !== 0 ? 'clr-text-warning' : ''} />} icon={<BiX size='1rem' className={forms.length !== 0 ? 'clr-text-warning' : ''} />}
disabled={textProcessor.loading || forms.length === 0} disabled={textProcessor.loading || forms.length === 0}
onClick={handleResetAll} onClick={handleResetAll}

View File

@ -62,7 +62,7 @@ function WordFormsTable({ forms, setForms, onFormSelect }: WordFormsTableProps)
maxSize: 50, maxSize: 50,
cell: props => cell: props =>
<MiniButton noHover <MiniButton noHover
tooltip='Удалить словоформу' title='Удалить словоформу'
icon={<BiX size='1rem' className='clr-text-warning'/>} icon={<BiX size='1rem' className='clr-text-warning'/>}
onClick={() => handleDeleteRow(props.row.index)} onClick={() => handleDeleteRow(props.row.index)}
/> />

View File

@ -24,7 +24,7 @@ function DlgGraphParams({ hideWindow, initial, onConfirm } : DlgGraphParamsProps
return ( return (
<Modal canSubmit <Modal canSubmit
hideWindow={hideWindow} hideWindow={hideWindow}
title='Настройки графа термов' header='Настройки графа термов'
onSubmit={handleSubmit} onSubmit={handleSubmit}
submitText='Применить' submitText='Применить'
className='flex gap-12 px-6 py-2' className='flex gap-12 px-6 py-2'
@ -33,25 +33,25 @@ function DlgGraphParams({ hideWindow, initial, onConfirm } : DlgGraphParamsProps
<h1 className='mb-2'>Преобразования</h1> <h1 className='mb-2'>Преобразования</h1>
<Checkbox <Checkbox
label='Скрыть текст' label='Скрыть текст'
tooltip='Не отображать термины' title='Не отображать термины'
value={params.noText} value={params.noText}
setValue={value => updateParams({noText: value})} setValue={value => updateParams({noText: value})}
/> />
<Checkbox <Checkbox
label='Скрыть несвязанные' label='Скрыть несвязанные'
tooltip='Неиспользуемые конституенты' title='Неиспользуемые конституенты'
value={params.noHermits} value={params.noHermits}
setValue={value => updateParams({ noHermits: value})} setValue={value => updateParams({ noHermits: value})}
/> />
<Checkbox <Checkbox
label='Скрыть шаблоны' label='Скрыть шаблоны'
tooltip='Терм-функции и предикат-функции с параметризованными аргументами' title='Терм-функции и предикат-функции с параметризованными аргументами'
value={params.noTemplates} value={params.noTemplates}
setValue={value => updateParams({ noTemplates: value})} setValue={value => updateParams({ noTemplates: value})}
/> />
<Checkbox <Checkbox
label='Транзитивная редукция' label='Транзитивная редукция'
tooltip='Удалить связи, образующие транзитивные пути в графе' title='Удалить связи, образующие транзитивные пути в графе'
value={params.noTransitive} value={params.noTransitive}
setValue={value => updateParams({ noTransitive: value})} setValue={value => updateParams({ noTransitive: value})}
/> />

View File

@ -44,7 +44,7 @@ function DlgRenameCst({ hideWindow, initial, onRename }: DlgRenameCstProps) {
return ( return (
<Modal <Modal
title='Переименование конституенты' header='Переименование конституенты'
submitText='Переименовать' submitText='Переименовать'
submitInvalidTooltip={'Введите незанятое имя, соответствующее типу'} submitInvalidTooltip={'Введите незанятое имя, соответствующее типу'}
hideWindow={hideWindow} hideWindow={hideWindow}

View File

@ -41,7 +41,7 @@ function DlgUploadRSForm({ hideWindow }: DlgUploadRSFormProps) {
return ( return (
<Modal <Modal
title='Импорт схемы из Экстеора' header='Импорт схемы из Экстеора'
hideWindow={hideWindow} hideWindow={hideWindow}
canSubmit={!!file} canSubmit={!!file}
onSubmit={handleSubmit} onSubmit={handleSubmit}
@ -50,7 +50,6 @@ function DlgUploadRSForm({ hideWindow }: DlgUploadRSFormProps) {
> >
<FileInput <FileInput
label='Выбрать файл' label='Выбрать файл'
dimensions='w-full flex items-center'
acceptType={EXTEOR_TRS_FILE} acceptType={EXTEOR_TRS_FILE}
onChange={handleFile} onChange={handleFile}
/> />

View File

@ -19,7 +19,7 @@ import { useLibrary } from '@/context/LibraryContext';
import { useConceptNavigation } from '@/context/NagivationContext'; import { useConceptNavigation } from '@/context/NagivationContext';
import { LibraryItemType } from '@/models/library'; import { LibraryItemType } from '@/models/library';
import { IRSFormCreateData } from '@/models/rsform'; import { IRSFormCreateData } from '@/models/rsform';
import { EXTEOR_TRS_FILE, limits, patterns } from '@/utils/constants'; import { classnames, EXTEOR_TRS_FILE, limits, patterns } from '@/utils/constants';
function CreateRSFormPage() { function CreateRSFormPage() {
const router = useConceptNavigation(); const router = useConceptNavigation();
@ -81,8 +81,8 @@ function CreateRSFormPage() {
<RequireAuth> <RequireAuth>
<form <form
className={clsx( className={clsx(
'w-full max-w-lg', 'px-6 py-3',
'px-6 py-3 flex flex-col gap-3' classnames.flex_col
)} )}
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
@ -94,7 +94,7 @@ function CreateRSFormPage() {
onChange={handleFileChange} onChange={handleFileChange}
/> />
<MiniButton <MiniButton
tooltip='Загрузить из Экстеор' title='Загрузить из Экстеор'
icon={<BiDownload size='1.25rem' className='clr-text-primary'/>} icon={<BiDownload size='1.25rem' className='clr-text-primary'/>}
onClick={() => inputRef.current?.click()} onClick={() => inputRef.current?.click()}
/> />
@ -112,7 +112,7 @@ function CreateRSFormPage() {
placeholder={file && 'Загрузить из файла'} placeholder={file && 'Загрузить из файла'}
className='w-[14rem]' className='w-[14rem]'
pattern={patterns.alias} pattern={patterns.alias}
tooltip={`не более ${limits.alias_len} символов`} title={`не более ${limits.alias_len} символов`}
value={alias} value={alias}
onChange={event => setAlias(event.target.value)} onChange={event => setAlias(event.target.value)}
/> />

View File

@ -42,8 +42,8 @@ function PickerStrategy({ value, onChange }: PickerStrategyProps) {
return ( return (
<div ref={strategyMenu.ref} className='h-full text-right'> <div ref={strategyMenu.ref} className='h-full text-right'>
<SelectorButton transparent tabIndex={-1} <SelectorButton transparent tabIndex={-1}
tooltip='Список фильтров' title='Список фильтров'
dimensions='w-fit h-full' className='h-full'
icon={<BiFilterAlt size='1.25rem' />} icon={<BiFilterAlt size='1.25rem' />}
text={labelLibraryFilter(value)} text={labelLibraryFilter(value)}
onClick={strategyMenu.toggle} onClick={strategyMenu.toggle}
@ -59,7 +59,7 @@ function PickerStrategy({ value, onChange }: PickerStrategyProps) {
value={value === strategy} value={value === strategy}
setValue={() => handleChange(strategy)} setValue={() => handleChange(strategy)}
label={labelLibraryFilter(strategy)} label={labelLibraryFilter(strategy)}
tooltip={describeLibraryFilter(strategy)} title={describeLibraryFilter(strategy)}
disabled={isStrategyDisabled(strategy)} disabled={isStrategyDisabled(strategy)}
/>); />);
})} })}

View File

@ -65,7 +65,7 @@ function SearchPanel({ total, filtered, query, setQuery, strategy, setFilter }:
'flex gap-1 justify-center items-center' 'flex gap-1 justify-center items-center'
)}> )}>
<ConceptSearch noBorder <ConceptSearch noBorder
dimensions='min-w-[10rem]' className='min-w-[10rem]'
value={query} value={query}
onChange={handleChangeQuery} onChange={handleChangeQuery}
/> />

View File

@ -101,7 +101,7 @@ function ViewLibrary({ items, resetQuery: cleanQuery }: ViewLibraryProps) {
)}> )}>
<HelpButton <HelpButton
topic={HelpTopic.LIBRARY} topic={HelpTopic.LIBRARY}
dimensions='max-w-[35rem]' className='max-w-[35rem]'
offset={0} offset={0}
/> />
</div> </div>

View File

@ -13,7 +13,7 @@ import { useAuth } from '@/context/AuthContext';
import { useConceptNavigation } from '@/context/NagivationContext'; import { useConceptNavigation } from '@/context/NagivationContext';
import useQueryStrings from '@/hooks/useQueryStrings'; import useQueryStrings from '@/hooks/useQueryStrings';
import { IUserLoginData } from '@/models/library'; import { IUserLoginData } from '@/models/library';
import { resources } from '@/utils/constants'; import { classnames, resources } from '@/utils/constants';
function ProcessError({error}: { error: ErrorData }): React.ReactElement { function ProcessError({error}: { error: ErrorData }): React.ReactElement {
@ -68,7 +68,8 @@ function LoginPage() {
<form <form
className={clsx( className={clsx(
'w-[24rem]', 'w-[24rem]',
'pt-12 pb-6 px-6 flex flex-col gap-3' 'pt-12 pb-6 px-6',
classnames.flex_col
)} )}
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >

View File

@ -13,8 +13,7 @@ function TopicsList({ activeTopic, onChangeTopic }: TopicsListProps) {
return ( return (
<div className={clsx( <div className={clsx(
'sticky top-0 left-0', 'sticky top-0 left-0',
'min-w-[13rem] h-fit', 'min-w-[13rem]',
'flex flex-col',
'border-x', 'border-x',
'clr-controls', 'clr-controls',
'small-caps', 'small-caps',

View File

@ -30,31 +30,31 @@ function ConstituentaToolbar({
return ( return (
<Overlay position='top-1 right-1/2 translate-x-1/2' className='flex'> <Overlay position='top-1 right-1/2 translate-x-1/2' className='flex'>
<MiniButton <MiniButton
tooltip='Сохранить изменения [Ctrl + S]' title='Сохранить изменения [Ctrl + S]'
disabled={!canSave} disabled={!canSave}
icon={<FiSave size='1.25rem' className={canSave ? 'clr-text-primary' : ''}/>} icon={<FiSave size='1.25rem' className={canSave ? 'clr-text-primary' : ''}/>}
onClick={onSubmit} onClick={onSubmit}
/> />
<MiniButton <MiniButton
tooltip='Сбросить несохраненные изменения' title='Сбросить несохраненные изменения'
disabled={!canSave} disabled={!canSave}
onClick={onReset} onClick={onReset}
icon={<BiReset size='1.25rem' className={canSave ? 'clr-text-primary' : ''} />} icon={<BiReset size='1.25rem' className={canSave ? 'clr-text-primary' : ''} />}
/> />
<MiniButton <MiniButton
tooltip='Создать конституенту после данной' title='Создать конституенту после данной'
disabled={!isMutable} disabled={!isMutable}
onClick={onCreate} onClick={onCreate}
icon={<BiPlusCircle size={'1.25rem'} className={isMutable ? 'clr-text-success' : ''} />} icon={<BiPlusCircle size={'1.25rem'} className={isMutable ? 'clr-text-success' : ''} />}
/> />
<MiniButton <MiniButton
tooltip='Клонировать конституенту [Alt + V]' title='Клонировать конституенту [Alt + V]'
disabled={!isMutable} disabled={!isMutable}
onClick={onClone} onClick={onClone}
icon={<BiDuplicate size='1.25rem' className={isMutable ? 'clr-text-success' : ''} />} icon={<BiDuplicate size='1.25rem' className={isMutable ? 'clr-text-success' : ''} />}
/> />
<MiniButton <MiniButton
tooltip='Удалить редактируемую конституенту' title='Удалить редактируемую конституенту'
disabled={!isMutable} disabled={!isMutable}
onClick={onDelete} onClick={onDelete}
icon={<BiTrash size='1.25rem' className={isMutable ? 'clr-text-warning' : ''} />} icon={<BiTrash size='1.25rem' className={isMutable ? 'clr-text-warning' : ''} />}

View File

@ -13,6 +13,7 @@ import TextArea from '@/components/Common/TextArea';
import RefsInput from '@/components/RefsInput'; import RefsInput from '@/components/RefsInput';
import { useRSForm } from '@/context/RSFormContext'; import { useRSForm } from '@/context/RSFormContext';
import { IConstituenta, ICstRenameData, ICstUpdateData } from '@/models/rsform'; import { IConstituenta, ICstRenameData, ICstUpdateData } from '@/models/rsform';
import { classnames } from '@/utils/constants';
import { labelCstTypification } from '@/utils/labels'; import { labelCstTypification } from '@/utils/labels';
import EditorRSExpression from '../EditorRSExpression'; import EditorRSExpression from '../EditorRSExpression';
@ -111,18 +112,18 @@ function FormConstituenta({
className='flex select-none' className='flex select-none'
> >
<MiniButton <MiniButton
tooltip={`Редактировать словоформы термина: ${constituenta?.term_forms.length ?? 0}`} title={`Редактировать словоформы термина: ${constituenta?.term_forms.length ?? 0}`}
disabled={disabled} disabled={disabled}
noHover noHover
onClick={onEditTerm} onClick={onEditTerm}
icon={<LiaEdit size='1rem' className={!disabled ? 'clr-text-primary' : ''} />} icon={<LiaEdit size='1rem' className={!disabled ? 'clr-text-primary' : ''} />}
/> />
<div className='pt-1 pl-[1.375rem] text-sm font-semibold whitespace-nowrap w-fit'> <div className='pt-1 pl-[1.375rem] text-sm font-semibold whitespace-nowrap'>
<span>Имя </span> <span>Имя </span>
<span className='ml-1'>{constituenta?.alias ?? ''}</span> <span className='ml-1'>{constituenta?.alias ?? ''}</span>
</div> </div>
<MiniButton noHover <MiniButton noHover
tooltip='Переименовать конституенту' title='Переименовать конституенту'
disabled={disabled} disabled={disabled}
onClick={handleRename} onClick={handleRename}
icon={<LiaEdit size='1rem' className={!disabled ? 'clr-text-primary' : ''} />} icon={<LiaEdit size='1rem' className={!disabled ? 'clr-text-primary' : ''} />}
@ -131,7 +132,8 @@ function FormConstituenta({
<form id={id} <form id={id}
className={clsx( className={clsx(
'mt-1 min-w-[47.8rem] max-w-[47.8rem]', 'mt-1 min-w-[47.8rem] max-w-[47.8rem]',
'px-4 py-1 flex flex-col gap-3' 'px-4 py-1',
classnames.flex_col
)} )}
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >

View File

@ -138,12 +138,12 @@ function EditorRSExpression({
<div> <div>
<Overlay position='top-0 right-0 flex'> <Overlay position='top-0 right-0 flex'>
<MiniButton noHover <MiniButton noHover
tooltip='Включение специальной клавиатуры' title='Включение специальной клавиатуры'
onClick={() => setShowControls(prev => !prev)} onClick={() => setShowControls(prev => !prev)}
icon={<FaRegKeyboard size='1.25rem' className={showControls ? 'clr-text-primary': ''} />} icon={<FaRegKeyboard size='1.25rem' className={showControls ? 'clr-text-primary': ''} />}
/> />
<MiniButton noHover <MiniButton noHover
tooltip='Дерево разбора выражения' title='Дерево разбора выражения'
onClick={handleShowAST} onClick={handleShowAST}
icon={<RiNodeTree size='1.25rem' className='clr-text-primary' />} icon={<RiNodeTree size='1.25rem' className='clr-text-primary' />}
/> />

View File

@ -52,30 +52,30 @@ const MAIN_THIRD_ROW: TokenID[] = [
]; ];
const SECONDARY_FIRST_ROW = [ const SECONDARY_FIRST_ROW = [
{text: 'μ', tooltip: 'q'}, {text: 'μ', title: 'q'},
{text: 'ω', tooltip: 'w'}, {text: 'ω', title: 'w'},
{text: 'ε', tooltip: 'e'}, {text: 'ε', title: 'e'},
{text: 'ρ', tooltip: 'r'}, {text: 'ρ', title: 'r'},
{text: 'τ', tooltip: 't'}, {text: 'τ', title: 't'},
{text: 'π', tooltip: 'y'} {text: 'π', title: 'y'}
]; ];
const SECONDARY_SECOND_ROW = [ const SECONDARY_SECOND_ROW = [
{text: 'α', tooltip: 'a'}, {text: 'α', title: 'a'},
{text: 'σ', tooltip: 's'}, {text: 'σ', title: 's'},
{text: 'δ', tooltip: 'd'}, {text: 'δ', title: 'd'},
{text: 'φ', tooltip: 'f'}, {text: 'φ', title: 'f'},
{text: 'γ', tooltip: 'g'}, {text: 'γ', title: 'g'},
{text: 'λ', tooltip: 'h'} {text: 'λ', title: 'h'}
]; ];
const SECONDARY_THIRD_ROW = [ const SECONDARY_THIRD_ROW = [
{text: 'ζ', tooltip: 'z'}, {text: 'ζ', title: 'z'},
{text: 'ξ', tooltip: 'x'}, {text: 'ξ', title: 'x'},
{text: 'ψ', tooltip: 'c'}, {text: 'ψ', title: 'c'},
{text: 'θ', tooltip: 'v'}, {text: 'θ', title: 'v'},
{text: 'β', tooltip: 'b'}, {text: 'β', title: 'b'},
{text: 'η', tooltip: 'n'} {text: 'η', title: 'n'}
]; ];
interface RSEditorControlsProps { interface RSEditorControlsProps {
@ -92,9 +92,9 @@ function RSEditorControls({ onEdit, disabled }: RSEditorControlsProps) {
token={token} onInsert={onEdit} disabled={disabled} token={token} onInsert={onEdit} disabled={disabled}
/>)} />)}
{SECONDARY_FIRST_ROW.map( {SECONDARY_FIRST_ROW.map(
({text, tooltip}) => ({text, title}) =>
<RSLocalButton key={`${prefixes.rsedit_btn}${tooltip}`} <RSLocalButton key={`${prefixes.rsedit_btn}${title}`}
text={text} tooltip={tooltip} onInsert={onEdit} disabled={disabled} text={text} title={title} onInsert={onEdit} disabled={disabled}
/>)} />)}
{MAIN_SECOND_ROW.map( {MAIN_SECOND_ROW.map(
@ -103,9 +103,9 @@ function RSEditorControls({ onEdit, disabled }: RSEditorControlsProps) {
token={token} onInsert={onEdit} disabled={disabled} token={token} onInsert={onEdit} disabled={disabled}
/>)} />)}
{SECONDARY_SECOND_ROW.map( {SECONDARY_SECOND_ROW.map(
({text, tooltip}) => ({text, title}) =>
<RSLocalButton key={`${prefixes.rsedit_btn}${tooltip}`} <RSLocalButton key={`${prefixes.rsedit_btn}${title}`}
text={text} tooltip={tooltip} onInsert={onEdit} disabled={disabled} text={text} title={title} onInsert={onEdit} disabled={disabled}
/>)} />)}
{MAIN_THIRD_ROW.map( {MAIN_THIRD_ROW.map(
@ -114,9 +114,9 @@ function RSEditorControls({ onEdit, disabled }: RSEditorControlsProps) {
token={token} onInsert={onEdit} disabled={disabled} token={token} onInsert={onEdit} disabled={disabled}
/>)} />)}
{SECONDARY_THIRD_ROW.map( {SECONDARY_THIRD_ROW.map(
({text, tooltip}) => ({text, title}) =>
<RSLocalButton key={`${prefixes.rsedit_btn}${tooltip}`} <RSLocalButton key={`${prefixes.rsedit_btn}${title}`}
text={text} tooltip={tooltip} onInsert={onEdit} disabled={disabled} text={text} title={title} onInsert={onEdit} disabled={disabled}
/>)} />)}
</div>); </div>);
} }

View File

@ -2,16 +2,16 @@ import { TokenID } from '@/models/rslang';
interface RSLocalButtonProps { interface RSLocalButtonProps {
text: string text: string
tooltip: string title: string
disabled?: boolean disabled?: boolean
onInsert: (token: TokenID, key?: string) => void onInsert: (token: TokenID, key?: string) => void
} }
function RSLocalButton({ text, tooltip, disabled, onInsert }: RSLocalButtonProps) { function RSLocalButton({ text, title, disabled, onInsert }: RSLocalButtonProps) {
return ( return (
<button type='button' tabIndex={-1} <button type='button' tabIndex={-1}
disabled={disabled} disabled={disabled}
title={tooltip} title={title}
className='w-[2rem] h-6 cursor-pointer disabled:cursor-default rounded-none clr-hover clr-btn-clear' className='w-[2rem] h-6 cursor-pointer disabled:cursor-default rounded-none clr-hover clr-btn-clear'
onClick={() => onInsert(TokenID.ID_LOCAL, text)} onClick={() => onInsert(TokenID.ID_LOCAL, text)}
> >

View File

@ -3,6 +3,7 @@
import { Dispatch, SetStateAction } from 'react'; import { Dispatch, SetStateAction } from 'react';
import Divider from '@/components/Common/Divider'; import Divider from '@/components/Common/Divider';
import FlexColumn from '@/components/Common/FlexColumn';
import InfoLibraryItem from '@/components/Shared/InfoLibraryItem'; import InfoLibraryItem from '@/components/Shared/InfoLibraryItem';
import { useAuth } from '@/context/AuthContext'; import { useAuth } from '@/context/AuthContext';
import { useRSForm } from '@/context/RSFormContext'; import { useRSForm } from '@/context/RSFormContext';
@ -69,7 +70,7 @@ function EditorRSForm({
className='flex' className='flex'
onKeyDown={handleInput} onKeyDown={handleInput}
> >
<div className='flex flex-col gap-3 px-4 pb-2'> <FlexColumn className='px-4 pb-2'>
<FormRSForm disabled={!isMutable} <FormRSForm disabled={!isMutable}
id={globalIDs.library_item_editor} id={globalIDs.library_item_editor}
isModified={isModified} isModified={isModified}
@ -79,7 +80,7 @@ function EditorRSForm({
<Divider margins='my-2' /> <Divider margins='my-2' />
<InfoLibraryItem item={schema} /> <InfoLibraryItem item={schema} />
</div> </FlexColumn>
<RSFormStats stats={schema?.stats}/> <RSFormStats stats={schema?.stats}/>
</div> </div>

View File

@ -1,5 +1,6 @@
'use client'; 'use client';
import clsx from 'clsx';
import { Dispatch, SetStateAction, useEffect, useLayoutEffect, useState } from 'react'; import { Dispatch, SetStateAction, useEffect, useLayoutEffect, useState } from 'react';
import { FiSave } from 'react-icons/fi'; import { FiSave } from 'react-icons/fi';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
@ -12,7 +13,7 @@ import { useAuth } from '@/context/AuthContext';
import { useRSForm } from '@/context/RSFormContext'; import { useRSForm } from '@/context/RSFormContext';
import { LibraryItemType } from '@/models/library'; import { LibraryItemType } from '@/models/library';
import { IRSFormCreateData } from '@/models/rsform'; import { IRSFormCreateData } from '@/models/rsform';
import { limits, patterns } from '@/utils/constants'; import { classnames, limits, patterns } from '@/utils/constants';
interface FormRSFormProps { interface FormRSFormProps {
id?: string id?: string
@ -79,7 +80,11 @@ function FormRSForm({
return ( return (
<form id={id} <form id={id}
className='flex flex-col gap-3 mt-1 py-1 min-w-[22rem] w-[30rem]' className={clsx(
'mt-1 min-w-[22rem] w-[30rem]',
'py-1',
classnames.flex_col
)}
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
<TextInput required <TextInput required
@ -92,7 +97,7 @@ function FormRSForm({
label='Сокращение' label='Сокращение'
className='w-[14rem]' className='w-[14rem]'
pattern={patterns.alias} pattern={patterns.alias}
tooltip={`не более ${limits.alias_len} символов`} title={`не более ${limits.alias_len} символов`}
disabled={disabled} disabled={disabled}
value={alias} value={alias}
onChange={event => setAlias(event.target.value)} onChange={event => setAlias(event.target.value)}
@ -106,14 +111,14 @@ function FormRSForm({
<div className='flex justify-between whitespace-nowrap'> <div className='flex justify-between whitespace-nowrap'>
<Checkbox <Checkbox
label='Общедоступная схема' label='Общедоступная схема'
tooltip='Общедоступные схемы видны всем пользователям и могут быть изменены' title='Общедоступные схемы видны всем пользователям и могут быть изменены'
disabled={disabled} disabled={disabled}
value={common} value={common}
setValue={value => setCommon(value)} setValue={value => setCommon(value)}
/> />
<Checkbox <Checkbox
label='Неизменная схема' label='Неизменная схема'
tooltip='Только администраторы могут присваивать схемам неизменный статус' title='Только администраторы могут присваивать схемам неизменный статус'
disabled={disabled || !user?.is_staff} disabled={disabled || !user?.is_staff}
value={canonical} value={canonical}
setValue={value => setCanonical(value)} setValue={value => setCanonical(value)}

View File

@ -11,7 +11,7 @@ function RSFormStats({ stats }: RSFormStatsProps) {
return null; return null;
} }
return ( return (
<div className='flex flex-col gap-1 px-4 mt-8 min-w-[16rem] max-w-[16rem]'> <div className='flex flex-col gap-1 px-4 mt-8 w-[16rem]'>
<LabeledValue id='count_all' <LabeledValue id='count_all'
label='Всего конституент ' label='Всего конституент '
text={stats.count_all} text={stats.count_all}

View File

@ -36,40 +36,39 @@ function RSFormToolbar({
return ( return (
<Overlay position='top-1 right-1/2 translate-x-1/2' className='flex'> <Overlay position='top-1 right-1/2 translate-x-1/2' className='flex'>
<MiniButton <MiniButton
tooltip='Сохранить изменения [Ctrl + S]' title='Сохранить изменения [Ctrl + S]'
disabled={!canSave} disabled={!canSave}
icon={<FiSave size='1.25rem' className={canSave ? 'clr-text-primary' : ''}/>} icon={<FiSave size='1.25rem' className={canSave ? 'clr-text-primary' : ''}/>}
onClick={onSubmit} onClick={onSubmit}
/> />
<MiniButton <MiniButton
tooltip='Поделиться схемой' title='Поделиться схемой'
icon={<BiShareAlt size='1.25rem' className='clr-text-primary'/>} icon={<BiShareAlt size='1.25rem' className='clr-text-primary'/>}
onClick={onShare} onClick={onShare}
/> />
<MiniButton <MiniButton
tooltip='Скачать TRS файл' title='Скачать TRS файл'
icon={<BiDownload size='1.25rem' className='clr-text-primary'/>} icon={<BiDownload size='1.25rem' className='clr-text-primary'/>}
onClick={onDownload} onClick={onDownload}
/> />
<MiniButton <MiniButton
tooltip={'отслеживание: ' + (isSubscribed ? '[включено]' : '[выключено]')} title={'отслеживание: ' + (isSubscribed ? '[включено]' : '[выключено]')}
disabled={anonymous || processing} disabled={anonymous || processing}
icon={isSubscribed icon={isSubscribed
? <FiBell size='1.25rem' className='clr-text-primary' /> ? <FiBell size='1.25rem' className='clr-text-primary' />
: <FiBellOff size='1.25rem' className='clr-text-controls' /> : <FiBellOff size='1.25rem' className='clr-text-controls' />
} }
dimensions='h-full w-fit'
style={{outlineColor: 'transparent'}} style={{outlineColor: 'transparent'}}
onClick={onToggleSubscribe} onClick={onToggleSubscribe}
/> />
<MiniButton <MiniButton
tooltip={claimable ? 'Стать владельцем' : 'Невозможно стать владельцем' } title={claimable ? 'Стать владельцем' : 'Невозможно стать владельцем' }
icon={<LuCrown size='1.25rem' className={!claimable ? '' : 'clr-text-success'}/>} icon={<LuCrown size='1.25rem' className={!claimable ? '' : 'clr-text-success'}/>}
disabled={!claimable || anonymous || processing} disabled={!claimable || anonymous || processing}
onClick={onClaim} onClick={onClaim}
/> />
<MiniButton <MiniButton
tooltip='Удалить схему' title='Удалить схему'
disabled={!isMutable} disabled={!isMutable}
onClick={onDestroy} onClick={onDestroy}
icon={<BiTrash size='1.25rem' className={isMutable ? 'clr-text-warning' : ''} />} icon={<BiTrash size='1.25rem' className={isMutable ? 'clr-text-warning' : ''} />}

View File

@ -40,25 +40,25 @@ function RSListToolbar({
return ( return (
<Overlay position='top-1 right-1/2 translate-x-1/2' className='flex'> <Overlay position='top-1 right-1/2 translate-x-1/2' className='flex'>
<MiniButton <MiniButton
tooltip='Переместить вверх [Alt + вверх]' title='Переместить вверх [Alt + вверх]'
icon={<BiUpvote size='1.25rem' className={isMutable && !nothingSelected ? 'clr-text-primary': ''}/>} icon={<BiUpvote size='1.25rem' className={isMutable && !nothingSelected ? 'clr-text-primary': ''}/>}
disabled={!isMutable || nothingSelected} disabled={!isMutable || nothingSelected}
onClick={onMoveUp} onClick={onMoveUp}
/> />
<MiniButton <MiniButton
tooltip='Переместить вниз [Alt + вниз]' title='Переместить вниз [Alt + вниз]'
icon={<BiDownvote size='1.25rem' className={isMutable && !nothingSelected ? 'clr-text-primary': ''}/>} icon={<BiDownvote size='1.25rem' className={isMutable && !nothingSelected ? 'clr-text-primary': ''}/>}
disabled={!isMutable || nothingSelected} disabled={!isMutable || nothingSelected}
onClick={onMoveDown} onClick={onMoveDown}
/> />
<MiniButton <MiniButton
tooltip='Клонировать конституенту [Alt + V]' title='Клонировать конституенту [Alt + V]'
icon={<BiDuplicate size='1.25rem' className={isMutable && selectedCount === 1 ? 'clr-text-success': ''} />} icon={<BiDuplicate size='1.25rem' className={isMutable && selectedCount === 1 ? 'clr-text-success': ''} />}
disabled={!isMutable || selectedCount !== 1} disabled={!isMutable || selectedCount !== 1}
onClick={onClone} onClick={onClone}
/> />
<MiniButton <MiniButton
tooltip='Добавить новую конституенту... [Alt + `]' title='Добавить новую конституенту... [Alt + `]'
icon={<BiPlusCircle size='1.25rem' className={isMutable ? 'clr-text-success': ''} />} icon={<BiPlusCircle size='1.25rem' className={isMutable ? 'clr-text-success': ''} />}
disabled={!isMutable} disabled={!isMutable}
onClick={() => onCreate()} onClick={() => onCreate()}
@ -66,7 +66,7 @@ function RSListToolbar({
<div ref={insertMenu.ref} className='flex justify-center'> <div ref={insertMenu.ref} className='flex justify-center'>
<div> <div>
<MiniButton <MiniButton
tooltip='Добавить пустую конституенту' title='Добавить пустую конституенту'
icon={<BiDownArrowCircle size='1.25rem' className={isMutable ? 'clr-text-success': ''} />} icon={<BiDownArrowCircle size='1.25rem' className={isMutable ? 'clr-text-success': ''} />}
disabled={!isMutable} disabled={!isMutable}
onClick={insertMenu.toggle} onClick={insertMenu.toggle}
@ -79,14 +79,14 @@ function RSListToolbar({
key={`${prefixes.csttype_list}${typeStr}`} key={`${prefixes.csttype_list}${typeStr}`}
text={`${getCstTypePrefix(typeStr as CstType)}1 — ${labelCstType(typeStr as CstType)}`} text={`${getCstTypePrefix(typeStr as CstType)}1 — ${labelCstType(typeStr as CstType)}`}
onClick={() => onCreate(typeStr as CstType)} onClick={() => onCreate(typeStr as CstType)}
tooltip={getCstTypeShortcut(typeStr as CstType)} title={getCstTypeShortcut(typeStr as CstType)}
/> />
)} )}
</Dropdown> : null} </Dropdown> : null}
</div> </div>
</div> </div>
<MiniButton <MiniButton
tooltip='Удалить выбранные [Delete]' title='Удалить выбранные [Delete]'
icon={<BiTrash size='1.25rem' className={isMutable && !nothingSelected ? 'clr-text-warning' : ''} />} icon={<BiTrash size='1.25rem' className={isMutable && !nothingSelected ? 'clr-text-warning' : ''} />}
disabled={!isMutable || nothingSelected} disabled={!isMutable || nothingSelected}
onClick={onDelete} onClick={onDelete}

View File

@ -14,7 +14,7 @@ import useLocalStorage from '@/hooks/useLocalStorage';
import { GraphColoringScheme, GraphFilterParams } from '@/models/miscelanious'; import { GraphColoringScheme, GraphFilterParams } from '@/models/miscelanious';
import { CstType, ICstCreateData } from '@/models/rsform'; import { CstType, ICstCreateData } from '@/models/rsform';
import { colorbgGraphNode } from '@/utils/color'; import { colorbgGraphNode } from '@/utils/color';
import { TIMEOUT_GRAPH_REFRESH } from '@/utils/constants'; import { classnames, TIMEOUT_GRAPH_REFRESH } from '@/utils/constants';
import GraphSidebar from './GraphSidebar'; import GraphSidebar from './GraphSidebar';
import GraphToolbar from './GraphToolbar'; import GraphToolbar from './GraphToolbar';
@ -235,7 +235,7 @@ function EditorTermGraph({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Ed
layer='z-tooltip' layer='z-tooltip'
position='top-[1.6rem] left-[2.6rem]' position='top-[1.6rem] left-[2.6rem]'
className={clsx( className={clsx(
'w-[25rem] h-fit min-h-[11rem]', 'w-[25rem]',
'px-3', 'px-3',
'overflow-y-auto', 'overflow-y-auto',
'border shadow-md', 'border shadow-md',
@ -243,6 +243,7 @@ function EditorTermGraph({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Ed
)} )}
> >
<InfoConstituenta <InfoConstituenta
className='pt-1 pb-2'
data={hoverCst} data={hoverCst}
/> />
</Overlay> : null} </Overlay> : null}
@ -250,8 +251,8 @@ function EditorTermGraph({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Ed
<Overlay <Overlay
position='top-0 left-0' position='top-0 left-0'
className={clsx( className={clsx(
'max-w-[13.5rem] min-w-[13.5rem]', 'w-[13.5rem]',
'flex flex-col gap-3' classnames.flex_col
)} )}
> >
<GraphSidebar <GraphSidebar

View File

@ -34,12 +34,12 @@ function GraphToolbar({
return ( return (
<Overlay position='top-1 right-1/2 translate-x-1/2' className='flex'> <Overlay position='top-1 right-1/2 translate-x-1/2' className='flex'>
<MiniButton <MiniButton
tooltip='Настройки фильтрации узлов и связей' title='Настройки фильтрации узлов и связей'
icon={<BiFilterAlt size='1.25rem' className='clr-text-primary' />} icon={<BiFilterAlt size='1.25rem' className='clr-text-primary' />}
onClick={showParamsDialog} onClick={showParamsDialog}
/> />
<MiniButton <MiniButton
tooltip={!noText ? 'Скрыть текст' : 'Отобразить текст'} title={!noText ? 'Скрыть текст' : 'Отобразить текст'}
icon={ icon={
!noText !noText
? <BiFontFamily size='1.25rem' className='clr-text-success' /> ? <BiFontFamily size='1.25rem' className='clr-text-success' />
@ -49,28 +49,28 @@ function GraphToolbar({
/> />
<MiniButton <MiniButton
icon={<BiCollapse size='1.25rem' className='clr-text-primary' />} icon={<BiCollapse size='1.25rem' className='clr-text-primary' />}
tooltip='Восстановить камеру' title='Восстановить камеру'
onClick={onResetViewpoint} onClick={onResetViewpoint}
/> />
<MiniButton <MiniButton
icon={<BiPlanet size='1.25rem' className={!is3D ? '' : orbit ? 'clr-text-success' : 'clr-text-primary'} />} icon={<BiPlanet size='1.25rem' className={!is3D ? '' : orbit ? 'clr-text-success' : 'clr-text-primary'} />}
tooltip='Анимация вращения' title='Анимация вращения'
disabled={!is3D} disabled={!is3D}
onClick={toggleOrbit} onClick={toggleOrbit}
/> />
<MiniButton <MiniButton
tooltip='Новая конституента' title='Новая конституента'
icon={<BiPlusCircle size='1.25rem' className={isMutable ? 'clr-text-success' : ''} />} icon={<BiPlusCircle size='1.25rem' className={isMutable ? 'clr-text-success' : ''} />}
disabled={!isMutable} disabled={!isMutable}
onClick={onCreate} onClick={onCreate}
/> />
<MiniButton <MiniButton
tooltip='Удалить выбранные' title='Удалить выбранные'
icon={<BiTrash size='1.25rem' className={isMutable && !nothingSelected ? 'clr-text-warning' : ''} />} icon={<BiTrash size='1.25rem' className={isMutable && !nothingSelected ? 'clr-text-warning' : ''} />}
disabled={!isMutable || nothingSelected} disabled={!isMutable || nothingSelected}
onClick={onDelete} onClick={onDelete}
/> />
<HelpButton topic={HelpTopic.GRAPH_TERM} dimensions='max-w-[calc(100vw-20rem)]' offset={4} /> <HelpButton topic={HelpTopic.GRAPH_TERM} className='max-w-[calc(100vw-20rem)]' offset={4} />
</Overlay>); </Overlay>);
} }

View File

@ -43,7 +43,7 @@ function ViewHidden({
return null; return null;
} }
return ( return (
<div className='flex flex-col text-sm ml-2 border clr-app max-w-[12.5rem] min-w-[12.5rem]'> <div className='flex flex-col text-sm ml-2 border clr-app w-[12.5rem]'>
<p className='mt-2 text-center'><b>Скрытые конституенты</b></p> <p className='mt-2 text-center'><b>Скрытые конституенты</b></p>
<div className='flex flex-wrap justify-center gap-2 py-2 overflow-y-auto' style={{ maxHeight: dismissedHeight }}> <div className='flex flex-wrap justify-center gap-2 py-2 overflow-y-auto' style={{ maxHeight: dismissedHeight }}>
{items.map( {items.map(

View File

@ -415,11 +415,11 @@ function RSTabs() {
/> />
<ConceptTab <ConceptTab
label='Карточка' label='Карточка'
tooltip={`Название схемы: ${schema.title ?? ''}`} title={`Название схемы: ${schema.title ?? ''}`}
/> />
<ConceptTab <ConceptTab
label='Содержание' label='Содержание'
tooltip={[ title={[
`Всего конституент: ${schema.stats?.count_all ?? 0}`, `Всего конституент: ${schema.stats?.count_all ?? 0}`,
`Количество ошибок: ${schema.stats?.count_errors ?? 0}` `Количество ошибок: ${schema.stats?.count_errors ?? 0}`
].join('\n')} ].join('\n')}

View File

@ -98,7 +98,7 @@ function RSTabsMenu({
<div className='flex'> <div className='flex'>
<div ref={schemaMenu.ref}> <div ref={schemaMenu.ref}>
<Button noBorder dense tabIndex={-1} <Button noBorder dense tabIndex={-1}
tooltip='Меню' title='Меню'
icon={<BiMenu size='1.25rem' className='clr-text-controls' />} icon={<BiMenu size='1.25rem' className='clr-text-controls' />}
className='h-full pl-2' className='h-full pl-2'
style={{outlineColor: 'transparent'}} style={{outlineColor: 'transparent'}}
@ -108,7 +108,7 @@ function RSTabsMenu({
<Dropdown> <Dropdown>
<DropdownButton <DropdownButton
text={isOwned ? 'Вы — владелец' : 'Стать владельцем'} text={isOwned ? 'Вы — владелец' : 'Стать владельцем'}
tooltip={!user || !isClaimable ? 'Взять во владение можно общую изменяемую схему' : ''} title={!user || !isClaimable ? 'Взять во владение можно общую изменяемую схему' : ''}
icon={<LuCrown size='1rem' className={isOwned ? 'clr-text-success' : 'clr-text-controls'} />} icon={<LuCrown size='1rem' className={isOwned ? 'clr-text-success' : 'clr-text-controls'} />}
onClick={(!isOwned && user && isClaimable) ? handleClaimOwner : undefined} onClick={(!isOwned && user && isClaimable) ? handleClaimOwner : undefined}
/> />
@ -147,7 +147,7 @@ function RSTabsMenu({
<div ref={editMenu.ref}> <div ref={editMenu.ref}>
<Button dense noBorder tabIndex={-1} <Button dense noBorder tabIndex={-1}
tooltip={'Редактирование'} title={'Редактирование'}
className='h-full' className='h-full'
style={{outlineColor: 'transparent'}} style={{outlineColor: 'transparent'}}
icon={<FiEdit size='1.25rem' className={isMutable ? 'clr-text-success' : 'clr-text-warning'}/>} icon={<FiEdit size='1.25rem' className={isMutable ? 'clr-text-success' : 'clr-text-warning'}/>}
@ -157,13 +157,13 @@ function RSTabsMenu({
<Dropdown> <Dropdown>
<DropdownButton disabled={!isMutable} <DropdownButton disabled={!isMutable}
text='Сброс имён' text='Сброс имён'
tooltip='Присвоить порядковые имена и обновить выражения' title='Присвоить порядковые имена и обновить выражения'
icon={<BiAnalyse size='1rem' className={isMutable ? 'clr-text-primary': ''} />} icon={<BiAnalyse size='1rem' className={isMutable ? 'clr-text-primary': ''} />}
onClick={handleReindex} onClick={handleReindex}
/> />
<DropdownButton disabled={!isMutable} <DropdownButton disabled={!isMutable}
text='Банк выражений' text='Банк выражений'
tooltip='Создать конституенту из шаблона' title='Создать конституенту из шаблона'
icon={<BiDiamond size='1rem' className={isMutable ? 'clr-text-success': ''} />} icon={<BiDiamond size='1rem' className={isMutable ? 'clr-text-success': ''} />}
onClick={handleTemplates} onClick={handleTemplates}
/> />
@ -172,7 +172,7 @@ function RSTabsMenu({
<div ref={accessMenu.ref}> <div ref={accessMenu.ref}>
<Button dense noBorder tabIndex={-1} <Button dense noBorder tabIndex={-1}
tooltip={`режим ${labelAccessMode(mode)}`} title={`режим ${labelAccessMode(mode)}`}
className='h-full pr-2' className='h-full pr-2'
style={{outlineColor: 'transparent'}} style={{outlineColor: 'transparent'}}
icon={ icon={
@ -186,19 +186,19 @@ function RSTabsMenu({
<Dropdown> <Dropdown>
<DropdownButton <DropdownButton
text={labelAccessMode(UserAccessMode.READER)} text={labelAccessMode(UserAccessMode.READER)}
tooltip={describeAccessMode(UserAccessMode.READER)} title={describeAccessMode(UserAccessMode.READER)}
icon={<LuGlasses size='1rem' className='clr-text-primary' />} icon={<LuGlasses size='1rem' className='clr-text-primary' />}
onClick={() => handleChangeMode(UserAccessMode.READER)} onClick={() => handleChangeMode(UserAccessMode.READER)}
/> />
<DropdownButton disabled={!isOwned} <DropdownButton disabled={!isOwned}
text={labelAccessMode(UserAccessMode.OWNER)} text={labelAccessMode(UserAccessMode.OWNER)}
tooltip={describeAccessMode(UserAccessMode.OWNER)} title={describeAccessMode(UserAccessMode.OWNER)}
icon={<LuCrown size='1rem' className={isOwned ? 'clr-text-primary': ''} />} icon={<LuCrown size='1rem' className={isOwned ? 'clr-text-primary': ''} />}
onClick={() => handleChangeMode(UserAccessMode.OWNER)} onClick={() => handleChangeMode(UserAccessMode.OWNER)}
/> />
<DropdownButton disabled={!user?.is_staff} <DropdownButton disabled={!user?.is_staff}
text={labelAccessMode(UserAccessMode.ADMIN)} text={labelAccessMode(UserAccessMode.ADMIN)}
tooltip={describeAccessMode(UserAccessMode.ADMIN)} title={describeAccessMode(UserAccessMode.ADMIN)}
icon={<BiMeteor size='1rem' className={user?.is_staff ? 'clr-text-primary': ''} />} icon={<BiMeteor size='1rem' className={user?.is_staff ? 'clr-text-primary': ''} />}
onClick={() => handleChangeMode(UserAccessMode.ADMIN)} onClick={() => handleChangeMode(UserAccessMode.ADMIN)}
/> />

View File

@ -77,15 +77,15 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }:
return ( return (
<div className='flex items-stretch border-b clr-input'> <div className='flex items-stretch border-b clr-input'>
<ConceptSearch noBorder <ConceptSearch noBorder
dimensions='min-w-[6rem] pr-2 w-full' className='min-w-[6rem] pr-2 flex-grow'
value={filterText} value={filterText}
onChange={setFilterText} onChange={setFilterText}
/> />
<div ref={matchModeMenu.ref}> <div ref={matchModeMenu.ref}>
<SelectorButton transparent tabIndex={-1} <SelectorButton transparent tabIndex={-1}
tooltip='Настройка атрибутов для фильтрации' title='Настройка атрибутов для фильтрации'
dimensions='w-fit h-full' className='h-full'
icon={<BiFilterAlt size='1.25rem' />} icon={<BiFilterAlt size='1.25rem' />}
text={labelCstMathchMode(filterMatch)} text={labelCstMathchMode(filterMatch)}
onClick={matchModeMenu.toggle} onClick={matchModeMenu.toggle}
@ -108,8 +108,8 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }:
<div ref={sourceMenu.ref}> <div ref={sourceMenu.ref}>
<SelectorButton transparent tabIndex={-1} <SelectorButton transparent tabIndex={-1}
tooltip='Настройка фильтрации по графу термов' title='Настройка фильтрации по графу термов'
dimensions='w-fit h-full pr-2' className='h-full pr-2'
icon={<BiCog size='1.25rem' />} icon={<BiCog size='1.25rem' />}
text={labelCstSource(filterSource)} text={labelCstSource(filterSource)}
onClick={sourceMenu.toggle} onClick={sourceMenu.toggle}

View File

@ -1,5 +1,6 @@
'use client'; 'use client';
import clsx from 'clsx';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { BiInfoCircle } from 'react-icons/bi'; import { BiInfoCircle } from 'react-icons/bi';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
@ -7,6 +8,7 @@ import { toast } from 'react-toastify';
import Button from '@/components/Common/Button'; import Button from '@/components/Common/Button';
import Checkbox from '@/components/Common/Checkbox'; import Checkbox from '@/components/Common/Checkbox';
import ConceptTooltip from '@/components/Common/ConceptTooltip'; import ConceptTooltip from '@/components/Common/ConceptTooltip';
import FlexColumn from '@/components/Common/FlexColumn';
import Overlay from '@/components/Common/Overlay'; import Overlay from '@/components/Common/Overlay';
import SubmitButton from '@/components/Common/SubmitButton'; import SubmitButton from '@/components/Common/SubmitButton';
import TextInput from '@/components/Common/TextInput'; import TextInput from '@/components/Common/TextInput';
@ -16,7 +18,7 @@ import InfoError from '@/components/InfoError';
import { useAuth } from '@/context/AuthContext'; import { useAuth } from '@/context/AuthContext';
import { useConceptNavigation } from '@/context/NagivationContext'; import { useConceptNavigation } from '@/context/NagivationContext';
import { type IUserSignupData } from '@/models/library'; import { type IUserSignupData } from '@/models/library';
import { globalIDs, patterns } from '@/utils/constants'; import { classnames, globalIDs, patterns } from '@/utils/constants';
function RegisterPage() { function RegisterPage() {
const router = useConceptNavigation(); const router = useConceptNavigation();
@ -66,12 +68,12 @@ function RegisterPage() {
} }
return ( return (
<form <form
className='flex flex-col gap-3 px-6 py-3 h-fit' className={clsx('px-6 py-3', classnames.flex_col)}
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
<h1>Новый пользователь</h1> <h1>Новый пользователь</h1>
<div className='flex gap-12'> <div className='flex gap-12'>
<div className='flex flex-col gap-3'> <FlexColumn>
<div className='absolute'> <div className='absolute'>
<Overlay <Overlay
id={globalIDs.password_tooltip} id={globalIDs.password_tooltip}
@ -91,7 +93,7 @@ function RegisterPage() {
<TextInput id='username' required <TextInput id='username' required
label='Имя пользователя (логин)' label='Имя пользователя (логин)'
pattern={patterns.login} pattern={patterns.login}
tooltip='Минимум 3 знака. Латинские буквы и цифры. Не может начинаться с цифры' title='Минимум 3 знака. Латинские буквы и цифры. Не может начинаться с цифры'
value={username} value={username}
className='w-[15rem]' className='w-[15rem]'
onChange={event => setUsername(event.target.value)} onChange={event => setUsername(event.target.value)}
@ -108,12 +110,12 @@ function RegisterPage() {
value={password2} value={password2}
onChange={event => setPassword2(event.target.value)} onChange={event => setPassword2(event.target.value)}
/> />
</div> </FlexColumn>
<div className='flex flex-col gap-3 w-[15rem]'> <FlexColumn className='w-[15rem]'>
<TextInput id='email' required <TextInput id='email' required
label='Электронная почта (email)' label='Электронная почта (email)'
tooltip='электронная почта в корректном формате, например: i.petrov@mycompany.ru.com' title='электронная почта в корректном формате, например: i.petrov@mycompany.ru.com'
value={email} value={email}
onChange={event => setEmail(event.target.value)} onChange={event => setEmail(event.target.value)}
/> />
@ -127,7 +129,7 @@ function RegisterPage() {
value={lastName} value={lastName}
onChange={event => setLastName(event.target.value)} onChange={event => setLastName(event.target.value)}
/> />
</div> </FlexColumn>
</div> </div>
<div className='flex gap-1 text-sm'> <div className='flex gap-1 text-sm'>

View File

@ -5,6 +5,7 @@ import clsx from 'clsx';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import FlexColumn from '@/components/Common/FlexColumn';
import SubmitButton from '@/components/Common/SubmitButton'; import SubmitButton from '@/components/Common/SubmitButton';
import TextInput from '@/components/Common/TextInput'; import TextInput from '@/components/Common/TextInput';
import InfoError, { ErrorData } from '@/components/InfoError'; import InfoError, { ErrorData } from '@/components/InfoError';
@ -78,7 +79,7 @@ function EditorPassword() {
)} )}
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
<div className='flex flex-col gap-3'> <FlexColumn>
<TextInput id='old_password' type='password' allowEnter <TextInput id='old_password' type='password' allowEnter
label='Старый пароль' label='Старый пароль'
value={oldPassword} value={oldPassword}
@ -101,7 +102,7 @@ function EditorPassword() {
}} }}
/> />
{error ? <ProcessError error={error} /> : null} {error ? <ProcessError error={error} /> : null}
</div> </FlexColumn>
<SubmitButton <SubmitButton
text='Сменить пароль' text='Сменить пароль'
className='self-center' className='self-center'

View File

@ -1,5 +1,6 @@
'use client'; 'use client';
import clsx from 'clsx';
import { useLayoutEffect, useMemo, useState } from 'react'; import { useLayoutEffect, useMemo, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
@ -8,6 +9,7 @@ import TextInput from '@/components/Common/TextInput';
import { useBlockNavigation } from '@/context/NagivationContext'; import { useBlockNavigation } from '@/context/NagivationContext';
import { useUserProfile } from '@/context/UserProfileContext'; import { useUserProfile } from '@/context/UserProfileContext';
import { IUserUpdateData } from '@/models/library'; import { IUserUpdateData } from '@/models/library';
import { classnames } from '@/utils/constants';
function EditorProfile() { function EditorProfile() {
const { updateUser, user, processing } = useUserProfile(); const { updateUser, user, processing } = useUserProfile();
@ -53,11 +55,15 @@ function EditorProfile() {
return ( return (
<form <form
onSubmit={handleSubmit} onSubmit={handleSubmit}
className='px-6 py-2 flex flex-col gap-3 min-w-[18rem]' className={clsx(
'min-w-[18rem]',
'px-6 py-2',
classnames.flex_col
)}
> >
<TextInput id='username' disabled <TextInput id='username' disabled
label='Логин' label='Логин'
tooltip='Логин изменить нельзя' title='Логин изменить нельзя'
value={username} value={username}
/> />
<TextInput id='first_name' allowEnter <TextInput id='first_name' allowEnter

View File

@ -36,7 +36,7 @@ function UserTabs() {
<div> <div>
<Overlay position='top-0 right-0'> <Overlay position='top-0 right-0'>
<MiniButton <MiniButton
tooltip='Показать/Скрыть список отслеживаний' title='Показать/Скрыть список отслеживаний'
icon={showSubs icon={showSubs
? <FiBell size='1.25rem' className='clr-text-primary' /> ? <FiBell size='1.25rem' className='clr-text-primary' />
: <FiBellOff size='1.25rem' className='clr-text-primary' /> : <FiBellOff size='1.25rem' className='clr-text-primary' />

View File

@ -137,7 +137,7 @@ export function domTooltipConstituenta(cst: IConstituenta) {
const dom = document.createElement('div'); const dom = document.createElement('div');
dom.className = clsx( dom.className = clsx(
'z-tooltip', 'z-tooltip',
'max-h-[25rem] max-w-[25rem] min-w-[10rem] w-fit', 'max-h-[25rem] max-w-[25rem] min-w-[10rem]',
'p-2', 'p-2',
'border shadow-md', 'border shadow-md',
'overflow-y-auto', 'overflow-y-auto',
@ -181,7 +181,7 @@ export function domTooltipEntityReference(ref: IEntityReference, cst: IConstitue
const dom = document.createElement('div'); const dom = document.createElement('div');
dom.className = clsx( dom.className = clsx(
'z-tooltip', 'z-tooltip',
'max-h-[25rem] max-w-[25rem] min-w-[10rem] w-fit', 'max-h-[25rem] max-w-[25rem] min-w-[10rem]',
'p-2 flex flex-col', 'p-2 flex flex-col',
'border shadow-md', 'border shadow-md',
'overflow-y-auto', 'overflow-y-auto',
@ -229,7 +229,7 @@ export function domTooltipSyntacticReference(ref: ISyntacticReference, masterRef
const dom = document.createElement('div'); const dom = document.createElement('div');
dom.className = clsx( dom.className = clsx(
'z-tooltip', 'z-tooltip',
'max-h-[25rem] max-w-[25rem] min-w-[10rem] w-fit', 'max-h-[25rem] max-w-[25rem] min-w-[10rem]',
'p-2 flex flex-col', 'p-2 flex flex-col',
'border shadow-md', 'border shadow-md',
'overflow-y-auto', 'overflow-y-auto',

View File

@ -55,6 +55,14 @@ export const youtube = {
intro: '0Ty9mu9sOJo' intro: '0Ty9mu9sOJo'
}; };
/**
* Classname combinations.
* Note: using clsx in conjunction with tailwindCss is preferred to creating custom CSS
*/
export const classnames = {
flex_col: 'flex flex-col gap-3'
};
/** /**
* Constant URLs. * Constant URLs.
*/ */