Compare commits

...

4 Commits

Author SHA1 Message Date
Ivan
168ff94c42 F: Improve RSEditor layout responsiveness
Some checks failed
Frontend CI / build (22.x) (push) Has been cancelled
2024-09-06 14:19:09 +03:00
Ivan
8035a604bd B: Fix renaming validation 2024-09-05 21:24:57 +03:00
Ivan
05ef45fac8 B: Save ordering when selection changes 2024-09-05 20:28:01 +03:00
Ivan
68cc96a779 M: Fix text justify for manuals 2024-09-05 20:13:33 +03:00
17 changed files with 72 additions and 30 deletions

View File

@ -4,29 +4,32 @@ import { IConstituenta } from '@/models/rsform';
import { isMockCst } from '@/models/rsformAPI'; import { isMockCst } from '@/models/rsformAPI';
import { colorFgCstStatus, IColorTheme } from '@/styling/color'; import { colorFgCstStatus, IColorTheme } from '@/styling/color';
import { CProps } from '../props';
import TooltipConstituenta from './TooltipConstituenta'; import TooltipConstituenta from './TooltipConstituenta';
interface BadgeConstituentaProps { interface BadgeConstituentaProps extends CProps.Styling {
prefixID?: string; prefixID?: string;
value: IConstituenta; value: IConstituenta;
theme: IColorTheme; theme: IColorTheme;
} }
function BadgeConstituenta({ value, prefixID, theme }: BadgeConstituentaProps) { function BadgeConstituenta({ value, prefixID, className, style, theme }: BadgeConstituentaProps) {
return ( return (
<div <div
id={`${prefixID}${value.alias}`} id={`${prefixID}${value.alias}`}
className={clsx( className={clsx(
'min-w-[3.1rem] max-w-[3.1rem]', // prettier: split lines 'min-w-[3.1rem] max-w-[3.1rem]',
'px-1', 'px-1',
'border rounded-md', 'border rounded-md',
value.is_inherited && 'border-dashed', value.is_inherited && 'border-dashed',
'text-center font-medium whitespace-nowrap' 'text-center font-medium whitespace-nowrap',
className
)} )}
style={{ style={{
borderColor: colorFgCstStatus(value.status, theme), borderColor: colorFgCstStatus(value.status, theme),
color: colorFgCstStatus(value.status, theme), color: colorFgCstStatus(value.status, theme),
backgroundColor: isMockCst(value) ? theme.bgWarning : theme.bgInput backgroundColor: isMockCst(value) ? theme.bgWarning : theme.bgInput,
...style
}} }}
> >
{value.alias} {value.alias}

View File

@ -91,12 +91,14 @@ function Modal({
{header ? <h1 className='px-12 py-2 select-none'>{header}</h1> : null} {header ? <h1 className='px-12 py-2 select-none'>{header}</h1> : null}
<div <div
className={clsx('overflow-auto overscroll-contain', className)} className={clsx(
style={{ 'overscroll-contain max-h-[calc(100svh-8rem)] max-w-[100svw] xs:max-w-[calc(100svw-2rem)]',
overflow: overflowVisible ? 'visible' : 'auto', {
maxHeight: 'calc(100svh - 8rem)', 'overflow-auto': !overflowVisible,
maxWidth: 'calc(100svw - 2rem)' 'overflow-visible': overflowVisible
}} },
className
)}
> >
{children} {children}
</div> </div>

View File

@ -78,7 +78,7 @@ function FormCreateCst({ schema, state, partialUpdate, setValidated }: FormCreat
fitContent fitContent
spellCheck spellCheck
label='Термин' label='Термин'
placeholder='Обозначение, используемое в текстовых определениях' placeholder='Обозначение для текстовых определений'
className='max-h-[3.6rem]' className='max-h-[3.6rem]'
value={state.term_raw} value={state.term_raw}
onChange={event => partialUpdate({ term_raw: event.target.value })} onChange={event => partialUpdate({ term_raw: event.target.value })}

View File

@ -18,10 +18,11 @@ import { SelectorCstType } from '@/utils/selectors';
interface DlgRenameCstProps extends Pick<ModalProps, 'hideWindow'> { interface DlgRenameCstProps extends Pick<ModalProps, 'hideWindow'> {
initial: ICstRenameData; initial: ICstRenameData;
allowChangeType: boolean;
onRename: (data: ICstRenameData) => void; onRename: (data: ICstRenameData) => void;
} }
function DlgRenameCst({ hideWindow, initial, onRename }: DlgRenameCstProps) { function DlgRenameCst({ hideWindow, initial, allowChangeType, onRename }: DlgRenameCstProps) {
const { schema } = useRSForm(); const { schema } = useRSForm();
const [validated, setValidated] = useState(false); const [validated, setValidated] = useState(false);
const [cstData, updateData] = usePartialUpdate(initial); const [cstData, updateData] = usePartialUpdate(initial);
@ -52,6 +53,7 @@ function DlgRenameCst({ hideWindow, initial, onRename }: DlgRenameCstProps) {
id='dlg_cst_type' id='dlg_cst_type'
placeholder='Выберите тип' placeholder='Выберите тип'
className='min-w-[16rem] self-center' className='min-w-[16rem] self-center'
isDisabled={!allowChangeType}
options={SelectorCstType} options={SelectorCstType}
value={{ value={{
value: cstData.cst_type, value: cstData.cst_type,

View File

@ -255,7 +255,20 @@ export function isFunctional(type: CstType): boolean {
* Validate new alias against {@link CstType} and {@link IRSForm}. * Validate new alias against {@link CstType} and {@link IRSForm}.
*/ */
export function validateNewAlias(alias: string, type: CstType, schema: IRSForm): boolean { export function validateNewAlias(alias: string, type: CstType, schema: IRSForm): boolean {
return alias.length >= 2 && alias.startsWith(getCstTypePrefix(type)) && !schema.cstByAlias.has(alias); if (alias.length < 2) {
return false;
}
const prefix = getCstTypePrefix(type);
if (!alias.startsWith(prefix)) {
return false;
}
if (schema.cstByAlias.has(alias)) {
return false;
}
if (!/^\d+$/.exec(alias.substring(prefix.length))) {
return false;
}
return true;
} }
/** /**

View File

@ -3,7 +3,7 @@ import { HelpTopic } from '@/models/miscellaneous';
function HelpConceptRelations() { function HelpConceptRelations() {
return ( return (
<div> <div className='text-justify'>
<h1>Связи между конституентами</h1> <h1>Связи между конституентами</h1>
<p> <p>
Конституенты связаны между собой через использование одних конституент при определении других. Такую связь в Конституенты связаны между собой через использование одних конституент при определении других. Такую связь в

View File

@ -3,7 +3,7 @@ import { HelpTopic } from '@/models/miscellaneous';
function HelpConceptSynthesis() { function HelpConceptSynthesis() {
return ( return (
<div> <div className='text-justify'>
<h1>Синтез концептуальных схем</h1> <h1>Синтез концептуальных схем</h1>
<p> <p>
Работа с крупными предметными областями требуют рассмотрения различных точек зрения в рамках одной предметной Работа с крупными предметными областями требуют рассмотрения различных точек зрения в рамках одной предметной

View File

@ -3,7 +3,7 @@ import { HelpTopic } from '@/models/miscellaneous';
function HelpConceptSystem() { function HelpConceptSystem() {
return ( return (
<div> <div className='text-justify'>
<h1>Концептуальная схема Система определений</h1> <h1>Концептуальная схема Система определений</h1>
<p> <p>
Данный раздел вводит <b>систему определений</b> как предмет концептуализации предметных областей. Под системой Данный раздел вводит <b>систему определений</b> как предмет концептуализации предметных областей. Под системой

View File

@ -4,7 +4,7 @@ import { external_urls } from '@/utils/constants';
function HelpRules() { function HelpRules() {
return ( return (
<div> <div className='text-justify'>
<h1>Правила поведения участников Портала</h1> <h1>Правила поведения участников Портала</h1>
<p> <p>

View File

@ -56,7 +56,11 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
const onSelectionChange = useCallback( const onSelectionChange = useCallback(
({ nodes }: { nodes: Node[] }) => { ({ nodes }: { nodes: Node[] }) => {
controller.setSelected(nodes.map(node => Number(node.id))); const ids = nodes.map(node => Number(node.id));
controller.setSelected(prev => [
...prev.filter(nodeID => ids.includes(nodeID)),
...ids.filter(nodeID => !prev.includes(Number(nodeID)))
]);
}, },
[controller] [controller]
); );

View File

@ -149,7 +149,7 @@ function FormConstituenta({
id='cst_term' id='cst_term'
label='Термин' label='Термин'
maxHeight='8rem' maxHeight='8rem'
placeholder='Обозначение, используемое в текстовых определениях' placeholder='Обозначение для текстовых определений'
schema={schema} schema={schema}
onOpenEdit={onOpenEdit} onOpenEdit={onOpenEdit}
value={term} value={term}
@ -240,7 +240,7 @@ function FormConstituenta({
</AnimateFade> </AnimateFade>
{!disabled || processing ? ( {!disabled || processing ? (
<div className='self-center flex'> <div className='mx-auto flex'>
<SubmitButton <SubmitButton
key='cst_form_submit' key='cst_form_submit'
id='cst_form_submit' id='cst_form_submit'

View File

@ -165,7 +165,7 @@ function EditorRSExpression({
toggleControls={() => setShowControls(prev => !prev)} toggleControls={() => setShowControls(prev => !prev)}
/> />
<Overlay position='top-[-0.5rem] pl-[8rem] sm:pl-[4rem] right-1/2 translate-x-1/2 flex'> <Overlay position='top-[-0.5rem]' className='pl-[8.5rem] xs:pl-[2rem] flex justify-center w-full gap-1'>
<StatusBar <StatusBar
processing={parser.processing} processing={parser.processing}
isModified={isModified} isModified={isModified}

View File

@ -92,7 +92,7 @@ function RSEditorControls({ isOpen, disabled, onEdit }: RSEditorControlsProps) {
return ( return (
<motion.div <motion.div
className={clsx( className={clsx(
'max-w-[38.5rem] sm:max-w-[40rem] sm:min-w-[40rem] md:max-w-fit mx-1 sm:mx-0', 'max-w-[28rem] min-w-[28rem] xs:max-w-[38.5rem] xs:min-w-[38.5rem] sm:max-w-[40rem] sm:min-w-[40rem] md:max-w-fit mx-1 sm:mx-0',
'flex-wrap', 'flex-wrap',
'divide-solid', 'divide-solid',
'text-xs md:text-sm', 'text-xs md:text-sm',
@ -112,6 +112,7 @@ function RSEditorControls({ isOpen, disabled, onEdit }: RSEditorControlsProps) {
title={title} title={title}
onInsert={onEdit} onInsert={onEdit}
disabled={disabled} disabled={disabled}
className='hidden xs:inline'
/> />
))} ))}
@ -121,6 +122,7 @@ function RSEditorControls({ isOpen, disabled, onEdit }: RSEditorControlsProps) {
{SECONDARY_SECOND_ROW.map(({ text, title }) => ( {SECONDARY_SECOND_ROW.map(({ text, title }) => (
<RSLocalButton <RSLocalButton
key={`${prefixes.rsedit_btn}${title}`} key={`${prefixes.rsedit_btn}${title}`}
className='hidden xs:inline'
text={text} text={text}
title={title} title={title}
onInsert={onEdit} onInsert={onEdit}
@ -134,6 +136,7 @@ function RSEditorControls({ isOpen, disabled, onEdit }: RSEditorControlsProps) {
{SECONDARY_THIRD_ROW.map(({ text, title }) => ( {SECONDARY_THIRD_ROW.map(({ text, title }) => (
<RSLocalButton <RSLocalButton
key={`${prefixes.rsedit_btn}${title}`} key={`${prefixes.rsedit_btn}${title}`}
className='hidden xs:inline'
text={text} text={text}
title={title} title={title}
onInsert={onEdit} onInsert={onEdit}

View File

@ -4,13 +4,22 @@ import { CProps } from '@/components/props';
import { TokenID } from '@/models/rslang'; import { TokenID } from '@/models/rslang';
import { globals } from '@/utils/constants'; import { globals } from '@/utils/constants';
interface RSLocalButtonProps extends CProps.Titled { interface RSLocalButtonProps extends CProps.Titled, CProps.Styling {
text: string; text: string;
disabled?: boolean; disabled?: boolean;
onInsert: (token: TokenID, key?: string) => void; onInsert: (token: TokenID, key?: string) => void;
} }
function RSLocalButton({ text, title, titleHtml, hideTitle, disabled, onInsert }: RSLocalButtonProps) { function RSLocalButton({
text,
title,
titleHtml,
hideTitle,
disabled,
className,
onInsert,
...restProps
}: RSLocalButtonProps) {
return ( return (
<button <button
type='button' type='button'
@ -25,9 +34,11 @@ function RSLocalButton({ text, title, titleHtml, hideTitle, disabled, onInsert }
'cursor-pointer disabled:cursor-default', 'cursor-pointer disabled:cursor-default',
'rounded-none', 'rounded-none',
'clr-hover clr-btn-clear', 'clr-hover clr-btn-clear',
'font-math' 'font-math',
className
)} )}
onClick={() => onInsert(TokenID.ID_LOCAL, text)} onClick={() => onInsert(TokenID.ID_LOCAL, text)}
{...restProps}
> >
{text} {text}
</button> </button>

View File

@ -41,12 +41,10 @@ function StatusBar({ isModified, processing, activeCst, parseData, onAnalyze }:
tabIndex={0} tabIndex={0}
className={clsx( className={clsx(
'w-[10rem] h-[1.75rem]', 'w-[10rem] h-[1.75rem]',
'scale-75 sm:scale-100 mx-[-2.5rem] sm:m-0',
'px-2 flex items-center justify-center gap-2', 'px-2 flex items-center justify-center gap-2',
'border', 'border',
'select-none', 'select-none',
'cursor-pointer', 'cursor-pointer',
'translate-x-[-1.5rem]',
'focus-frame', 'focus-frame',
'duration-500 transition-colors' 'duration-500 transition-colors'
)} )}

View File

@ -689,10 +689,11 @@ export const RSEditState = ({
initial={createInitialData} initial={createInitialData}
/> />
) : null} ) : null}
{showRenameCst && renameInitialData ? ( {activeCst && showRenameCst && renameInitialData ? (
<DlgRenameCst <DlgRenameCst
hideWindow={() => setShowRenameCst(false)} hideWindow={() => setShowRenameCst(false)}
onRename={handleRenameCst} onRename={handleRenameCst}
allowChangeType={!activeCst.is_inherited}
initial={renameInitialData} initial={renameInitialData}
/> />
) : null} ) : null}

View File

@ -69,7 +69,12 @@ function TableSideConstituents({
minSize: 65, minSize: 65,
footer: undefined, footer: undefined,
cell: props => ( cell: props => (
<BadgeConstituenta theme={colors} value={props.row.original} prefixID={prefixes.cst_side_table} /> <BadgeConstituenta
className='mr-[-0.5rem]'
theme={colors}
value={props.row.original}
prefixID={prefixes.cst_side_table}
/>
) )
}), }),
columnHelper.accessor(cst => describeConstituenta(cst), { columnHelper.accessor(cst => describeConstituenta(cst), {