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

View File

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

View File

@ -78,7 +78,7 @@ function FormCreateCst({ schema, state, partialUpdate, setValidated }: FormCreat
fitContent
spellCheck
label='Термин'
placeholder='Обозначение, используемое в текстовых определениях'
placeholder='Обозначение для текстовых определений'
className='max-h-[3.6rem]'
value={state.term_raw}
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'> {
initial: ICstRenameData;
allowChangeType: boolean;
onRename: (data: ICstRenameData) => void;
}
function DlgRenameCst({ hideWindow, initial, onRename }: DlgRenameCstProps) {
function DlgRenameCst({ hideWindow, initial, allowChangeType, onRename }: DlgRenameCstProps) {
const { schema } = useRSForm();
const [validated, setValidated] = useState(false);
const [cstData, updateData] = usePartialUpdate(initial);
@ -52,6 +53,7 @@ function DlgRenameCst({ hideWindow, initial, onRename }: DlgRenameCstProps) {
id='dlg_cst_type'
placeholder='Выберите тип'
className='min-w-[16rem] self-center'
isDisabled={!allowChangeType}
options={SelectorCstType}
value={{
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}.
*/
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() {
return (
<div>
<div className='text-justify'>
<h1>Связи между конституентами</h1>
<p>
Конституенты связаны между собой через использование одних конституент при определении других. Такую связь в

View File

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

View File

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

View File

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

View File

@ -56,7 +56,11 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
const onSelectionChange = useCallback(
({ 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]
);

View File

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

View File

@ -165,7 +165,7 @@ function EditorRSExpression({
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
processing={parser.processing}
isModified={isModified}

View File

@ -92,7 +92,7 @@ function RSEditorControls({ isOpen, disabled, onEdit }: RSEditorControlsProps) {
return (
<motion.div
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',
'divide-solid',
'text-xs md:text-sm',
@ -112,6 +112,7 @@ function RSEditorControls({ isOpen, disabled, onEdit }: RSEditorControlsProps) {
title={title}
onInsert={onEdit}
disabled={disabled}
className='hidden xs:inline'
/>
))}
@ -121,6 +122,7 @@ function RSEditorControls({ isOpen, disabled, onEdit }: RSEditorControlsProps) {
{SECONDARY_SECOND_ROW.map(({ text, title }) => (
<RSLocalButton
key={`${prefixes.rsedit_btn}${title}`}
className='hidden xs:inline'
text={text}
title={title}
onInsert={onEdit}
@ -134,6 +136,7 @@ function RSEditorControls({ isOpen, disabled, onEdit }: RSEditorControlsProps) {
{SECONDARY_THIRD_ROW.map(({ text, title }) => (
<RSLocalButton
key={`${prefixes.rsedit_btn}${title}`}
className='hidden xs:inline'
text={text}
title={title}
onInsert={onEdit}

View File

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

View File

@ -41,12 +41,10 @@ function StatusBar({ isModified, processing, activeCst, parseData, onAnalyze }:
tabIndex={0}
className={clsx(
'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',
'border',
'select-none',
'cursor-pointer',
'translate-x-[-1.5rem]',
'focus-frame',
'duration-500 transition-colors'
)}

View File

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

View File

@ -69,7 +69,12 @@ function TableSideConstituents({
minSize: 65,
footer: undefined,
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), {