mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50:36 +03:00
F: Refactor z-index and stacking
This commit is contained in:
parent
293f1cad6f
commit
81fa0c5796
|
@ -15,6 +15,7 @@ interface OverlayProps extends Styling {
|
|||
|
||||
/**
|
||||
* Displays a transparent overlay over the main content.
|
||||
* Note: Overlay should be inside a relative container.
|
||||
*/
|
||||
export function Overlay({
|
||||
children,
|
||||
|
@ -24,10 +25,8 @@ export function Overlay({
|
|||
...restProps
|
||||
}: React.PropsWithChildren<OverlayProps>) {
|
||||
return (
|
||||
<div className='relative'>
|
||||
<div className={clsx('absolute', className, position, layer)} {...restProps}>
|
||||
{children}
|
||||
</div>
|
||||
<div className={clsx('absolute', className, position, layer)} {...restProps}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ export function Tooltip({
|
|||
delayHide={100}
|
||||
opacity={1}
|
||||
className={clsx(
|
||||
'relative',
|
||||
'max-h-[calc(100svh-6rem)]',
|
||||
'overflow-y-auto overflow-x-hidden sm:overflow-hidden overscroll-contain',
|
||||
'border shadow-md',
|
||||
|
|
|
@ -23,6 +23,7 @@ interface DropdownProps extends Styling {
|
|||
|
||||
/**
|
||||
* Animated list of children with optional positioning and visibility control.
|
||||
* Note: Dropdown should be inside a relative container.
|
||||
*/
|
||||
export function Dropdown({
|
||||
isOpen,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { Overlay } from '@/components/Container';
|
||||
import { IconSearch } from '@/components/Icons';
|
||||
import { type Styling } from '@/components/props';
|
||||
|
||||
|
@ -35,15 +34,17 @@ export function SearchBar({
|
|||
noIcon,
|
||||
onChangeQuery,
|
||||
noBorder,
|
||||
className,
|
||||
placeholder = 'Поиск',
|
||||
...restProps
|
||||
}: SearchBarProps) {
|
||||
return (
|
||||
<div {...restProps}>
|
||||
<div className={clsx('relative', className)} {...restProps}>
|
||||
{!noIcon ? (
|
||||
<Overlay position='top-[-0.125rem] left-3 translate-y-1/2' className='pointer-events-none clr-text-controls'>
|
||||
<IconSearch size='1.25rem' />
|
||||
</Overlay>
|
||||
<IconSearch
|
||||
className='absolute top-[-0.125rem] left-3 translate-y-1/2 pointer-events-none clr-text-controls'
|
||||
size='1.25rem'
|
||||
/>
|
||||
) : null}
|
||||
<TextInput
|
||||
id={id}
|
||||
|
|
|
@ -3,7 +3,6 @@ import clsx from 'clsx';
|
|||
|
||||
import { globalIDs, PARAMETER } from '@/utils/constants';
|
||||
|
||||
import { Overlay } from '../Container';
|
||||
import { MiniButton } from '../Control';
|
||||
import { IconDropArrow, IconPageRight } from '../Icons';
|
||||
import { type Styling } from '../props';
|
||||
|
@ -86,6 +85,7 @@ export function SelectTree<ItemType>({
|
|||
<div
|
||||
key={`${prefix}${index}`}
|
||||
className={clsx(
|
||||
'relative',
|
||||
'pr-3 pl-6 border-b',
|
||||
'cc-scroll-row',
|
||||
'bg-prim-200 clr-hover cc-animate-color',
|
||||
|
@ -108,14 +108,13 @@ export function SelectTree<ItemType>({
|
|||
}}
|
||||
>
|
||||
{foldable.has(item) ? (
|
||||
<Overlay position='left-[-1.3rem]' className={clsx(!folded.includes(item) && 'top-[0.1rem]')}>
|
||||
<MiniButton
|
||||
noPadding
|
||||
noHover
|
||||
icon={!folded.includes(item) ? <IconDropArrow size='1rem' /> : <IconPageRight size='1.25rem' />}
|
||||
onClick={event => handleClickFold(event, item, folded.includes(item))}
|
||||
/>
|
||||
</Overlay>
|
||||
<MiniButton
|
||||
className={clsx('absolute left-[0.3rem]', !folded.includes(item) ? 'top-[0.4rem]' : 'top-1')}
|
||||
noPadding
|
||||
noHover
|
||||
icon={!folded.includes(item) ? <IconDropArrow size='1rem' /> : <IconPageRight size='1.25rem' />}
|
||||
onClick={event => handleClickFold(event, item, folded.includes(item))}
|
||||
/>
|
||||
) : null}
|
||||
{getParent(item) === item ? getLabel(item) : `- ${getLabel(item).toLowerCase()}`}
|
||||
</div>
|
||||
|
|
|
@ -9,9 +9,9 @@ interface ModalBackdropProps {
|
|||
export function ModalBackdrop({ onHide }: ModalBackdropProps) {
|
||||
return (
|
||||
<>
|
||||
<div className={clsx('z-navigation', 'fixed top-0 left-0', 'w-full h-full', 'backdrop-blur-[3px] opacity-50')} />
|
||||
<div className={clsx('z-bottom', 'fixed top-0 left-0', 'w-full h-full', 'backdrop-blur-[3px] opacity-50')} />
|
||||
<div
|
||||
className={clsx('z-navigation', 'fixed top-0 left-0', 'w-full h-full', 'bg-prim-0 opacity-25')}
|
||||
className={clsx('z-bottom', 'fixed top-0 left-0', 'w-full h-full', 'bg-prim-0 opacity-25')}
|
||||
onClick={onHide}
|
||||
/>
|
||||
</>
|
||||
|
|
|
@ -10,7 +10,6 @@ import { useDialogsStore } from '@/stores/dialogs';
|
|||
import { PARAMETER } from '@/utils/constants';
|
||||
import { prepareTooltip } from '@/utils/utils';
|
||||
|
||||
import { Overlay } from '../Container';
|
||||
import { Button, MiniButton, SubmitButton } from '../Control';
|
||||
import { IconClose } from '../Icons';
|
||||
import { type Styling } from '../props';
|
||||
|
@ -89,12 +88,12 @@ export function ModalForm({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='fixed top-0 left-0 w-full h-full z-modal cursor-default'>
|
||||
<div className='fixed top-0 left-0 w-full h-full z-modal isolate cursor-default'>
|
||||
<ModalBackdrop onHide={handleCancel} />
|
||||
<form
|
||||
className={clsx(
|
||||
'cc-animate-modal',
|
||||
'z-modal absolute bottom-1/2 left-1/2 -translate-x-1/2 translate-y-1/2',
|
||||
'absolute bottom-1/2 left-1/2 -translate-x-1/2 translate-y-1/2',
|
||||
'border rounded-xl bg-prim-100'
|
||||
)}
|
||||
onSubmit={handleSubmit}
|
||||
|
@ -105,15 +104,13 @@ export function ModalForm({
|
|||
</div>
|
||||
) : null}
|
||||
|
||||
<Overlay className='z-modalOverlay'>
|
||||
<MiniButton
|
||||
noPadding
|
||||
titleHtml={prepareTooltip('Закрыть диалоговое окно', 'ESC')}
|
||||
icon={<IconClose size='1.25rem' />}
|
||||
className='float-right mt-2 mr-2'
|
||||
onClick={handleCancel}
|
||||
/>
|
||||
</Overlay>
|
||||
<MiniButton
|
||||
noPadding
|
||||
titleHtml={prepareTooltip('Закрыть диалоговое окно', 'ESC')}
|
||||
icon={<IconClose size='1.25rem' />}
|
||||
className='float-right mt-2 mr-2'
|
||||
onClick={handleCancel}
|
||||
/>
|
||||
|
||||
{header ? <h1 className='px-12 py-2 select-none'>{header}</h1> : null}
|
||||
|
||||
|
@ -133,7 +130,7 @@ export function ModalForm({
|
|||
{children}
|
||||
</div>
|
||||
|
||||
<div className='z-modal-controls my-2 flex gap-12 justify-center text-sm'>
|
||||
<div className='z-pop my-2 flex gap-12 justify-center text-sm'>
|
||||
<SubmitButton
|
||||
autoFocus
|
||||
text={submitText}
|
||||
|
|
|
@ -4,9 +4,9 @@ import { Loader } from '@/components/Loader';
|
|||
|
||||
export function ModalLoader() {
|
||||
return (
|
||||
<div className='fixed top-0 left-0 w-full h-full z-modal cursor-default'>
|
||||
<div className={clsx('z-navigation', 'fixed top-0 left-0', 'w-full h-full', 'backdrop-blur-[3px] opacity-50')} />
|
||||
<div className={clsx('z-navigation', 'fixed top-0 left-0', 'w-full h-full', 'bg-prim-0 opacity-25')} />
|
||||
<div className='fixed top-0 left-0 w-full h-full z-modal isolate cursor-default'>
|
||||
<div className={clsx('z-bottom fixed top-0 left-0', 'w-full h-full', 'backdrop-blur-[3px] opacity-50')} />
|
||||
<div className={clsx('z-bottom fixed top-0 left-0', 'w-full h-full', 'bg-prim-0 opacity-25')} />
|
||||
<div
|
||||
className={clsx(
|
||||
'cc-animate-modal p-20',
|
||||
|
|
|
@ -9,7 +9,6 @@ import { useDialogsStore } from '@/stores/dialogs';
|
|||
import { PARAMETER } from '@/utils/constants';
|
||||
import { prepareTooltip } from '@/utils/utils';
|
||||
|
||||
import { Overlay } from '../Container';
|
||||
import { Button, MiniButton } from '../Control';
|
||||
import { IconClose } from '../Icons';
|
||||
|
||||
|
@ -34,7 +33,7 @@ export function ModalView({
|
|||
useEscapeKey(hideDialog);
|
||||
|
||||
return (
|
||||
<div className='fixed top-0 left-0 w-full h-full z-modal cursor-default'>
|
||||
<div className='fixed top-0 left-0 w-full h-full z-modal isolate cursor-default'>
|
||||
<ModalBackdrop onHide={hideDialog} />
|
||||
<div
|
||||
className={clsx(
|
||||
|
@ -49,15 +48,13 @@ export function ModalView({
|
|||
</div>
|
||||
) : null}
|
||||
|
||||
<Overlay className='z-modalOverlay'>
|
||||
<MiniButton
|
||||
noPadding
|
||||
titleHtml={prepareTooltip('Закрыть диалоговое окно', 'ESC')}
|
||||
icon={<IconClose size='1.25rem' />}
|
||||
className='float-right mt-2 mr-2'
|
||||
onClick={hideDialog}
|
||||
/>
|
||||
</Overlay>
|
||||
<MiniButton
|
||||
noPadding
|
||||
titleHtml={prepareTooltip('Закрыть диалоговое окно', 'ESC')}
|
||||
icon={<IconClose size='1.25rem' />}
|
||||
className='float-right mt-2 mr-2'
|
||||
onClick={hideDialog}
|
||||
/>
|
||||
|
||||
{header ? <h1 className='px-12 py-2 select-none'>{header}</h1> : null}
|
||||
|
||||
|
@ -77,7 +74,7 @@ export function ModalView({
|
|||
{children}
|
||||
</div>
|
||||
|
||||
<div className='z-modal-controls my-2 flex gap-12 justify-center text-sm'>
|
||||
<div className='z-pop my-2 flex gap-12 justify-center text-sm'>
|
||||
<Button text='Закрыть' className='min-w-[7rem]' onClick={hideDialog} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -39,12 +39,10 @@ export function BadgeHelp({ topic, padding = 'p-1', ...restProps }: BadgeHelpPro
|
|||
return (
|
||||
<div tabIndex={-1} id={`help-${topic}`} className={padding}>
|
||||
<IconHelp size='1.25rem' className='icon-primary' />
|
||||
<Tooltip clickable anchorSelect={`#help-${topic}`} layer='z-modal-tooltip' {...restProps}>
|
||||
<Tooltip clickable anchorSelect={`#help-${topic}`} layer='z-topmost' {...restProps}>
|
||||
<Suspense fallback={<Loader />}>
|
||||
<div className='relative' onClick={event => event.stopPropagation()}>
|
||||
<div className='absolute right-0 text-sm top-[0.4rem] clr-input'>
|
||||
<TextURL text='Справка...' href={`/manuals?topic=${topic}`} />
|
||||
</div>
|
||||
<div className='absolute right-1 text-sm top-[0.4rem] clr-input' onClick={event => event.stopPropagation()}>
|
||||
<TextURL text='Справка...' href={`/manuals?topic=${topic}`} />
|
||||
</div>
|
||||
<TopicPage topic={topic} />
|
||||
</Suspense>
|
||||
|
|
|
@ -33,7 +33,7 @@ export function TopicsDropdown({ activeTopic, onChangeTopic }: TopicsDropdownPro
|
|||
className={clsx(
|
||||
'absolute left-0 w-[13.5rem]', // prettier: split-lines
|
||||
'flex flex-col',
|
||||
'z-modal-tooltip',
|
||||
'z-topmost',
|
||||
'text-xs sm:text-sm',
|
||||
'select-none',
|
||||
{
|
||||
|
|
|
@ -5,7 +5,7 @@ import { urls, useConceptNavigation } from '@/app';
|
|||
import { useLabelUser, useRoleStore, UserRole } from '@/features/users';
|
||||
import { InfoUsers, SelectUser } from '@/features/users/components';
|
||||
|
||||
import { Overlay, Tooltip } from '@/components/Container';
|
||||
import { Tooltip } from '@/components/Container';
|
||||
import { MiniButton } from '@/components/Control';
|
||||
import { useDropdown } from '@/components/Dropdown';
|
||||
import {
|
||||
|
@ -83,7 +83,7 @@ export function EditorLibraryItem({ schema, isAttachedToOSS }: EditorLibraryItem
|
|||
|
||||
return (
|
||||
<div className='flex flex-col'>
|
||||
<div className='flex justify-stretch sm:mb-1 max-w-[30rem] gap-3'>
|
||||
<div className='relative flex justify-stretch sm:mb-1 max-w-[30rem] gap-3'>
|
||||
<MiniButton
|
||||
noHover
|
||||
noPadding
|
||||
|
@ -101,21 +101,21 @@ export function EditorLibraryItem({ schema, isAttachedToOSS }: EditorLibraryItem
|
|||
/>
|
||||
</div>
|
||||
|
||||
{ownerSelector.isOpen ? (
|
||||
<Overlay position='top-[-0.5rem] left-[4rem] cc-icons'>
|
||||
{ownerSelector.isOpen ? (
|
||||
<div className='relative'>
|
||||
{ownerSelector.isOpen ? (
|
||||
<div className='absolute top-[-0.5rem] left-[4rem]'>
|
||||
<SelectUser className='w-[25rem] sm:w-[26rem] text-sm' value={schema.owner} onChange={onSelectUser} />
|
||||
) : null}
|
||||
</Overlay>
|
||||
) : null}
|
||||
<ValueIcon
|
||||
className='sm:mb-1'
|
||||
icon={<IconOwner size='1.25rem' className='icon-primary' />}
|
||||
value={getUserLabel(schema.owner)}
|
||||
title={isAttachedToOSS ? 'Владелец наследуется от ОСС' : 'Владелец'}
|
||||
onClick={ownerSelector.toggle}
|
||||
disabled={isModified || isProcessing || isAttachedToOSS || role < UserRole.OWNER}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<ValueIcon
|
||||
className='sm:mb-1'
|
||||
icon={<IconOwner size='1.25rem' className='icon-primary' />}
|
||||
value={getUserLabel(schema.owner)}
|
||||
title={isAttachedToOSS ? 'Владелец наследуется от ОСС' : 'Владелец'}
|
||||
onClick={ownerSelector.toggle}
|
||||
disabled={isModified || isProcessing || isAttachedToOSS || role < UserRole.OWNER}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='sm:mb-1 flex justify-between items-center'>
|
||||
<ValueIcon
|
||||
|
@ -126,7 +126,7 @@ export function EditorLibraryItem({ schema, isAttachedToOSS }: EditorLibraryItem
|
|||
onClick={handleEditEditors}
|
||||
disabled={isModified || isProcessing || role < UserRole.OWNER}
|
||||
/>
|
||||
<Tooltip anchorSelect='#editor_stats' layer='z-modal-tooltip'>
|
||||
<Tooltip anchorSelect='#editor_stats'>
|
||||
<Suspense fallback={<Loader scale={2} />}>
|
||||
<InfoUsers items={schema.editors} prefix={prefixes.user_editors} header='Редакторы' />
|
||||
</Suspense>
|
||||
|
|
|
@ -120,7 +120,7 @@ export function PickSchema({
|
|||
className='mt-1'
|
||||
onClick={() => locationMenu.toggle()}
|
||||
/>
|
||||
<Dropdown isOpen={locationMenu.isOpen} stretchLeft className='w-[20rem] h-[12.5rem] z-modal-tooltip'>
|
||||
<Dropdown isOpen={locationMenu.isOpen} stretchLeft className='w-[20rem] h-[12.5rem]'>
|
||||
<SelectLocation
|
||||
value={filterLocation}
|
||||
prefix={prefixes.folders_list}
|
||||
|
|
|
@ -48,7 +48,7 @@ export function SelectLocationContext({
|
|||
/>
|
||||
<Dropdown
|
||||
isOpen={menu.isOpen}
|
||||
className={clsx('w-[20rem] h-[12.5rem] z-modal-tooltip', dropdownHeight)}
|
||||
className={clsx('w-[20rem] h-[12.5rem] z-tooltip', dropdownHeight)}
|
||||
margin='mt-[-0.25rem]'
|
||||
>
|
||||
<SelectLocation
|
||||
|
|
|
@ -45,7 +45,7 @@ export function SelectLocationHead({
|
|||
onClick={menu.toggle}
|
||||
/>
|
||||
|
||||
<Dropdown isOpen={menu.isOpen} className='z-modal-tooltip' margin='mt-2'>
|
||||
<Dropdown isOpen={menu.isOpen} margin='mt-2'>
|
||||
{Object.values(LocationHead)
|
||||
.filter(head => !excluded.includes(head))
|
||||
.map((head, index) => {
|
||||
|
|
|
@ -42,7 +42,7 @@ export function ToolbarItemAccess({
|
|||
}
|
||||
|
||||
return (
|
||||
<Overlay position='top-[4.5rem] right-0 w-[12rem] pr-2' className='flex' layer='z-bottom'>
|
||||
<Overlay position='top-[4.5rem] right-0' className='w-[12rem] flex pr-2' layer='z-bottom'>
|
||||
<Label text='Доступ' className='self-center select-none' />
|
||||
<div className='ml-auto cc-icons'>
|
||||
<SelectAccessPolicy
|
||||
|
|
|
@ -155,7 +155,7 @@ export function DlgCloneLibraryItem() {
|
|||
/>
|
||||
</div>
|
||||
|
||||
<TextArea id='dlg_comment' {...register('comment')} label='Описание' error={errors.comment} />
|
||||
<TextArea id='dlg_comment' {...register('comment')} label='Описание' rows={4} error={errors.comment} />
|
||||
|
||||
{selected.length > 0 ? (
|
||||
<Controller
|
||||
|
|
|
@ -8,7 +8,6 @@ import clsx from 'clsx';
|
|||
import { urls, useConceptNavigation } from '@/app';
|
||||
import { useAuthSuspense } from '@/features/auth';
|
||||
|
||||
import { Overlay } from '@/components/Container';
|
||||
import { Button, MiniButton, SubmitButton } from '@/components/Control';
|
||||
import { IconDownload } from '@/components/Icons';
|
||||
import { InfoError } from '@/components/InfoError';
|
||||
|
@ -108,9 +107,9 @@ export function FormCreateItem() {
|
|||
onSubmit={event => void handleSubmit(onSubmit)(event)}
|
||||
onChange={resetErrors}
|
||||
>
|
||||
<h1 className='select-none'>
|
||||
<h1 className='select-none relative'>
|
||||
{itemType == LibraryItemType.RSFORM ? (
|
||||
<Overlay position='top-0 right-[0.5rem]'>
|
||||
<>
|
||||
<Controller
|
||||
control={control}
|
||||
name='file'
|
||||
|
@ -127,10 +126,11 @@ export function FormCreateItem() {
|
|||
/>
|
||||
<MiniButton
|
||||
title='Загрузить из Экстеор'
|
||||
className='absolute top-0 right-0'
|
||||
icon={<IconDownload size='1.25rem' className='icon-primary' />}
|
||||
onClick={() => inputRef.current?.click()}
|
||||
/>
|
||||
</Overlay>
|
||||
</>
|
||||
) : null}
|
||||
Создание схемы
|
||||
</h1>
|
||||
|
|
|
@ -3,10 +3,8 @@
|
|||
import { toast } from 'react-toastify';
|
||||
import fileDownload from 'js-file-download';
|
||||
|
||||
import { Overlay } from '@/components/Container';
|
||||
import { MiniButton } from '@/components/Control';
|
||||
import { IconCSV } from '@/components/Icons';
|
||||
import { useAppLayoutStore } from '@/stores/appLayout';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
import { infoMsg } from '@/utils/labels';
|
||||
import { convertToCSV } from '@/utils/utils';
|
||||
|
@ -24,8 +22,6 @@ export function LibraryPage() {
|
|||
const { items: libraryItems } = useLibrarySuspense();
|
||||
const { renameLocation } = useRenameLocation();
|
||||
|
||||
const noNavigation = useAppLayoutStore(state => state.noNavigation);
|
||||
|
||||
const folderMode = useLibrarySearchStore(state => state.folderMode);
|
||||
const location = useLibrarySearchStore(state => state.location);
|
||||
const setLocation = useLibrarySearchStore(state => state.setLocation);
|
||||
|
@ -57,20 +53,15 @@ export function LibraryPage() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Overlay
|
||||
position={noNavigation ? 'top-[0.25rem] right-[3rem]' : 'top-[0.25rem] right-0'}
|
||||
layer='z-tooltip'
|
||||
className='cc-animate-position'
|
||||
>
|
||||
<ToolbarSearch total={libraryItems.length} filtered={filtered.length} />
|
||||
<div className='relative cc-fade-in flex'>
|
||||
<MiniButton
|
||||
className='absolute z-tooltip top-[0.25rem] right-0 cc-animate-position'
|
||||
title='Выгрузить в формате CSV'
|
||||
icon={<IconCSV size='1.25rem' className='icon-green' />}
|
||||
onClick={handleDownloadCSV}
|
||||
/>
|
||||
</Overlay>
|
||||
<ToolbarSearch total={libraryItems.length} filtered={filtered.length} />
|
||||
|
||||
<div className='cc-fade-in flex'>
|
||||
<ViewSideLocation
|
||||
isVisible={folderMode}
|
||||
onRenameLocation={() => showChangeLocation({ initial: location, onChangeLocation: handleRenameLocation })}
|
||||
|
|
|
@ -166,7 +166,7 @@ export function ToolbarSearch({ total, filtered }: ToolbarSearchProps) {
|
|||
text={head ?? '//'}
|
||||
/>
|
||||
|
||||
<Dropdown isOpen={headMenu.isOpen} stretchLeft className='z-modal-tooltip'>
|
||||
<Dropdown isOpen={headMenu.isOpen} stretchLeft>
|
||||
<DropdownButton title='Переключение в режим Проводник' onClick={handleToggleFolder}>
|
||||
<div className='inline-flex items-center gap-3'>
|
||||
<IconFolderTree size='1rem' className='clr-text-controls' />
|
||||
|
|
|
@ -69,7 +69,7 @@ export function FormOSS() {
|
|||
disabled={!isMutable}
|
||||
error={errors.title}
|
||||
/>
|
||||
<div className='flex justify-between gap-3 mb-3'>
|
||||
<div className='relative flex justify-between gap-3 mb-3'>
|
||||
<TextInput
|
||||
id='schema_alias'
|
||||
{...register('alias')}
|
||||
|
|
|
@ -190,7 +190,7 @@ export function OssFlow() {
|
|||
}
|
||||
|
||||
return (
|
||||
<div tabIndex={-1} onKeyDown={handleKeyDown}>
|
||||
<div tabIndex={-1} className='relative' onKeyDown={handleKeyDown}>
|
||||
<Overlay
|
||||
position='top-[1.9rem] pt-1 right-1/2 translate-x-1/2'
|
||||
className='rounded-b-2xl cc-blur hover:bg-prim-100 hover:bg-opacity-50'
|
||||
|
|
|
@ -23,7 +23,12 @@ export function NodeCore({ node }: NodeCoreProps) {
|
|||
const longLabel = node.data.label.length > LONG_LABEL_CHARS;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className='relative h-[34px] w-[144px] flex items-center justify-center'
|
||||
data-tooltip-id={globalIDs.operation_tooltip}
|
||||
data-tooltip-hidden={node.dragging}
|
||||
onMouseEnter={() => setHover(node.data.operation)}
|
||||
>
|
||||
<Overlay position='top-0 right-0' className='flex flex-col gap-1 p-[2px]'>
|
||||
<Indicator
|
||||
noPadding
|
||||
|
@ -40,35 +45,24 @@ export function NodeCore({ node }: NodeCoreProps) {
|
|||
</Overlay>
|
||||
|
||||
{node.data.operation.operation_type === OperationType.INPUT ? (
|
||||
<Overlay position='top-[1px] right-1/2 translate-x-1/2' className='flex'>
|
||||
<div className='border-t w-[30px]'></div>
|
||||
</Overlay>
|
||||
<div className='absolute top-[1px] right-1/2 translate-x-1/2 border-t w-[30px]' />
|
||||
) : null}
|
||||
|
||||
{!node.data.operation.is_owned ? (
|
||||
<Overlay position='left-[2px] top-[6px]'>
|
||||
<div className='border-r rounded-none clr-input h-[22px]'></div>
|
||||
</Overlay>
|
||||
<div className='absolute left-[2px] top-[6px] border-r rounded-none clr-input h-[22px]' />
|
||||
) : null}
|
||||
|
||||
<div
|
||||
className='h-[34px] w-[144px] flex items-center justify-center'
|
||||
data-tooltip-id={globalIDs.operation_tooltip}
|
||||
data-tooltip-hidden={node.dragging}
|
||||
onMouseEnter={() => setHover(node.data.operation)}
|
||||
className='text-center line-clamp-2'
|
||||
style={{
|
||||
fontSize: longLabel ? '12px' : '14px',
|
||||
lineHeight: longLabel ? '16px' : '20px',
|
||||
paddingLeft: '4px',
|
||||
paddingRight: longLabel ? '10px' : '4px'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className='text-center line-clamp-2'
|
||||
style={{
|
||||
fontSize: longLabel ? '12px' : '14px',
|
||||
lineHeight: longLabel ? '16px' : '20px',
|
||||
paddingLeft: '4px',
|
||||
paddingRight: longLabel ? '10px' : '4px'
|
||||
}}
|
||||
>
|
||||
{node.data.label}
|
||||
</div>
|
||||
{node.data.label}
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import clsx from 'clsx';
|
|||
|
||||
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
||||
|
||||
import { Overlay } from '@/components/Container';
|
||||
import { TabLabel, TabList, TabPanel, Tabs } from '@/components/Tabs';
|
||||
import { useAppLayoutStore } from '@/stores/appLayout';
|
||||
|
||||
|
@ -61,16 +60,22 @@ export function OssTabs({ activeTab }: OssTabsProps) {
|
|||
onSelect={onSelectTab}
|
||||
defaultFocus
|
||||
selectedTabClassName='clr-selected'
|
||||
className='flex flex-col mx-auto min-w-fit items-center'
|
||||
className='relative flex flex-col mx-auto min-w-fit items-center'
|
||||
>
|
||||
<Overlay position='top-0 right-1/2 translate-x-1/2' layer='z-sticky'>
|
||||
<TabList className={clsx('w-fit', 'flex items-stretch', 'border-b-2 border-x-2 divide-x-2', 'bg-prim-200')}>
|
||||
<MenuOssTabs />
|
||||
<TabList
|
||||
className={clsx(
|
||||
'absolute z-sticky',
|
||||
'top-0 right-1/2 w-fit translate-x-1/2',
|
||||
'flex items-stretch',
|
||||
'border-b-2 border-x-2 divide-x-2',
|
||||
'bg-prim-200'
|
||||
)}
|
||||
>
|
||||
<MenuOssTabs />
|
||||
|
||||
<TabLabel label='Карточка' title={schema.title ?? ''} />
|
||||
<TabLabel label='Граф' />
|
||||
</TabList>
|
||||
</Overlay>
|
||||
<TabLabel label='Карточка' title={schema.title ?? ''} />
|
||||
<TabLabel label='Граф' />
|
||||
</TabList>
|
||||
|
||||
<div className='overflow-x-hidden'>
|
||||
<TabPanel>
|
||||
|
|
|
@ -26,13 +26,10 @@ export function DlgShowAST() {
|
|||
const [isDragging, setIsDragging] = useState(false);
|
||||
|
||||
return (
|
||||
<ModalView
|
||||
className='flex flex-col justify-stretch w-[calc(100dvw-3rem)] h-[calc(100dvh-6rem)]'
|
||||
helpTopic={HelpTopic.UI_FORMULA_TREE}
|
||||
>
|
||||
<ModalView className='relative w-[calc(100dvw-3rem)] h-[calc(100dvh-6rem)]' helpTopic={HelpTopic.UI_FORMULA_TREE}>
|
||||
<Overlay
|
||||
position='top-2 right-1/2 translate-x-1/2'
|
||||
className='px-2 py-1 rounded-2xl cc-blur bg-prim-100 max-w-[60ch] text-lg text-center'
|
||||
position='top-0 -mt-1 right-1/2 translate-x-1/2'
|
||||
className='px-2 rounded-2xl cc-blur bg-prim-100 max-w-[60ch] text-lg text-center'
|
||||
>
|
||||
{!hoverNode || isDragging ? expression : null}
|
||||
{!isDragging && hoverNode ? (
|
||||
|
@ -43,6 +40,7 @@ export function DlgShowAST() {
|
|||
</div>
|
||||
) : null}
|
||||
</Overlay>
|
||||
|
||||
<ReactFlowProvider>
|
||||
<ASTFlow
|
||||
data={syntaxTree}
|
||||
|
|
|
@ -5,17 +5,14 @@ import clsx from 'clsx';
|
|||
|
||||
import { useWindowSize } from '@/hooks/useWindowSize';
|
||||
import { useMainHeight } from '@/stores/appLayout';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
import { useModificationStore } from '@/stores/modification';
|
||||
import { usePreferencesStore } from '@/stores/preferences';
|
||||
import { globalIDs } from '@/utils/constants';
|
||||
import { promptUnsaved } from '@/utils/utils';
|
||||
|
||||
import { useMutatingRSForm } from '../../../backend/useMutatingRSForm';
|
||||
import { useRSEdit } from '../RSEditContext';
|
||||
import { ViewConstituents } from '../ViewConstituents';
|
||||
|
||||
import { EditorControls } from './EditorControls';
|
||||
import { FormConstituenta } from './FormConstituenta';
|
||||
import { ToolbarConstituenta } from './ToolbarConstituenta';
|
||||
|
||||
|
@ -28,7 +25,6 @@ export function EditorConstituenta() {
|
|||
const mainHeight = useMainHeight();
|
||||
|
||||
const showList = usePreferencesStore(state => state.showCstSideList);
|
||||
const showEditTerm = useDialogsStore(state => state.showEditWordForms);
|
||||
const { isModified } = useModificationStore();
|
||||
|
||||
const [toggleReset, setToggleReset] = useState(false);
|
||||
|
@ -57,16 +53,6 @@ export function EditorConstituenta() {
|
|||
}
|
||||
}
|
||||
|
||||
function handleEditTermForms() {
|
||||
if (!activeCst) {
|
||||
return;
|
||||
}
|
||||
if (isModified && !promptUnsaved()) {
|
||||
return;
|
||||
}
|
||||
showEditTerm({ itemID: schema.id, target: activeCst });
|
||||
}
|
||||
|
||||
function initiateSubmit() {
|
||||
const element = document.getElementById(globalIDs.constituenta_editor) as HTMLFormElement;
|
||||
if (element) {
|
||||
|
@ -85,46 +71,38 @@ export function EditorConstituenta() {
|
|||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
tabIndex={-1}
|
||||
className={clsx(
|
||||
'relative',
|
||||
'cc-fade-in',
|
||||
'min-h-[20rem] max-w-[calc(min(100vw,95rem))] mx-auto',
|
||||
'flex pt-[1.9rem]',
|
||||
'overflow-y-auto overflow-x-clip',
|
||||
{ 'flex-col md:items-center': isNarrow }
|
||||
)}
|
||||
style={{ maxHeight: mainHeight }}
|
||||
onKeyDown={handleInput}
|
||||
>
|
||||
<ToolbarConstituenta
|
||||
activeCst={activeCst}
|
||||
disabled={disabled}
|
||||
onSubmit={initiateSubmit}
|
||||
onReset={() => setToggleReset(prev => !prev)}
|
||||
/>
|
||||
<div
|
||||
tabIndex={-1}
|
||||
className={clsx(
|
||||
'cc-fade-in',
|
||||
'min-h-[20rem] max-w-[calc(min(100vw,95rem))] mx-auto',
|
||||
'flex pt-[1.9rem]',
|
||||
'overflow-y-auto overflow-x-clip',
|
||||
{ 'flex-col md:items-center': isNarrow }
|
||||
)}
|
||||
style={{ maxHeight: mainHeight }}
|
||||
onKeyDown={handleInput}
|
||||
>
|
||||
<div className='mx-0 md:mx-auto pt-[2rem] md:w-[48.8rem] shrink-0 xs:pt-0'>
|
||||
{activeCst ? (
|
||||
<EditorControls
|
||||
disabled={disabled} //
|
||||
constituenta={activeCst}
|
||||
onEditTerm={handleEditTermForms}
|
||||
/>
|
||||
) : null}
|
||||
{activeCst ? (
|
||||
<FormConstituenta
|
||||
id={globalIDs.constituenta_editor} //
|
||||
disabled={disabled}
|
||||
toggleReset={toggleReset}
|
||||
activeCst={activeCst}
|
||||
schema={schema}
|
||||
onOpenEdit={navigateCst}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
<ViewConstituents isMounted={showList} isBottom={isNarrow} />
|
||||
<div className='mx-0 md:mx-auto pt-[2rem] md:w-[48.8rem] shrink-0 xs:pt-0'>
|
||||
{activeCst ? (
|
||||
<FormConstituenta
|
||||
id={globalIDs.constituenta_editor}
|
||||
disabled={disabled}
|
||||
toggleReset={toggleReset}
|
||||
activeCst={activeCst}
|
||||
schema={schema}
|
||||
onOpenEdit={navigateCst}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</>
|
||||
<ViewConstituents isMounted={showList} isBottom={isNarrow} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ export function EditorControls({ constituenta, disabled, onEditTerm }: EditorCon
|
|||
}
|
||||
|
||||
return (
|
||||
<Overlay position='top-1 left-[4.7rem]' className='flex select-none'>
|
||||
<Overlay position='top-0 left-[4.7rem]' className='flex select-none'>
|
||||
{!disabled || isProcessing ? (
|
||||
<MiniButton
|
||||
title={isModified ? tooltipText.unsaved : `Редактировать словоформы термина`}
|
||||
|
|
|
@ -14,6 +14,7 @@ import { Indicator } from '@/components/View';
|
|||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
import { useModificationStore } from '@/stores/modification';
|
||||
import { errorMsg } from '@/utils/labels';
|
||||
import { promptUnsaved } from '@/utils/utils';
|
||||
|
||||
import {
|
||||
CstType,
|
||||
|
@ -30,6 +31,8 @@ import { type IConstituenta, type IRSForm } from '../../../models/rsform';
|
|||
import { isBaseSet, isBasicConcept, isFunctional } from '../../../models/rsformAPI';
|
||||
import { EditorRSExpression } from '../EditorRSExpression';
|
||||
|
||||
import { EditorControls } from './EditorControls';
|
||||
|
||||
interface FormConstituentaProps {
|
||||
id?: string;
|
||||
disabled: boolean;
|
||||
|
@ -41,8 +44,6 @@ interface FormConstituentaProps {
|
|||
}
|
||||
|
||||
export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst, onOpenEdit }: FormConstituentaProps) {
|
||||
const { cstUpdate } = useCstUpdate();
|
||||
const showTypification = useDialogsStore(state => state.showShowTypeGraph);
|
||||
const { isModified, setIsModified } = useModificationStore();
|
||||
const isProcessing = useMutatingRSForm();
|
||||
|
||||
|
@ -54,6 +55,10 @@ export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst,
|
|||
formState: { isDirty }
|
||||
} = useForm<ICstUpdateDTO>({ resolver: zodResolver(schemaCstUpdate) });
|
||||
|
||||
const { cstUpdate } = useCstUpdate();
|
||||
const showTypification = useDialogsStore(state => state.showShowTypeGraph);
|
||||
const showEditTerm = useDialogsStore(state => state.showEditWordForms);
|
||||
|
||||
const [localParse, setLocalParse] = useState<IExpressionParseDTO | null>(null);
|
||||
|
||||
const typification = useMemo(
|
||||
|
@ -115,8 +120,20 @@ export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst,
|
|||
showTypification({ items: typeInfo ? [typeInfo] : [] });
|
||||
}
|
||||
|
||||
function handleEditTermForms() {
|
||||
if (!activeCst) {
|
||||
return;
|
||||
}
|
||||
if (isModified && !promptUnsaved()) {
|
||||
return;
|
||||
}
|
||||
showEditTerm({ itemID: schema.id, target: activeCst });
|
||||
}
|
||||
|
||||
return (
|
||||
<form id={id} className='cc-column mt-1 px-6 py-1' onSubmit={event => void handleSubmit(onSubmit)(event)}>
|
||||
<form id={id} className='relative cc-column mt-1 px-6 py-1' onSubmit={event => void handleSubmit(onSubmit)(event)}>
|
||||
<EditorControls disabled={disabled} constituenta={activeCst} onEditTerm={handleEditTermForms} />
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name='item_data.term_raw'
|
||||
|
@ -228,13 +245,13 @@ export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst,
|
|||
) : null}
|
||||
|
||||
{!disabled || isProcessing ? (
|
||||
<div className='mx-auto flex'>
|
||||
<div className='relative mx-auto flex'>
|
||||
<SubmitButton
|
||||
text='Сохранить изменения'
|
||||
disabled={disabled || !isModified}
|
||||
icon={<IconSave size='1.25rem' />}
|
||||
/>
|
||||
<Overlay position='top-[0.1rem] left-[0.4rem]' className='cc-icons'>
|
||||
<Overlay position='top-[0.1rem] left-full' className='cc-icons'>
|
||||
{activeCst.has_inherited_children && !activeCst.is_inherited ? (
|
||||
<Indicator
|
||||
icon={<IconPredecessor size='1.25rem' className='text-sec-600' />}
|
||||
|
|
|
@ -4,10 +4,6 @@ import { useEffect, useRef, useState } from 'react';
|
|||
import { toast } from 'react-toastify';
|
||||
import { type ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
||||
|
||||
import { HelpTopic } from '@/features/help';
|
||||
import { BadgeHelp } from '@/features/help/components';
|
||||
|
||||
import { Overlay } from '@/components/Container';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
import { usePreferencesStore } from '@/stores/preferences';
|
||||
import { errorMsg } from '@/utils/labels';
|
||||
|
@ -158,23 +154,16 @@ export function EditorRSExpression({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='cc-fade-in'>
|
||||
<div className='relative cc-fade-in'>
|
||||
<ToolbarRSExpression disabled={disabled} showAST={handleShowAST} showTypeGraph={onShowTypeGraph} />
|
||||
|
||||
<Overlay
|
||||
position='top-[-0.5rem] right-1/2 translate-x-1/2'
|
||||
layer='z-pop'
|
||||
className='w-fit pl-[8.5rem] xs:pl-[2rem] flex gap-1'
|
||||
>
|
||||
<StatusBar
|
||||
processing={isPending}
|
||||
isModified={isModified}
|
||||
activeCst={activeCst}
|
||||
parseData={parseData}
|
||||
onAnalyze={() => handleCheckExpression()}
|
||||
/>
|
||||
<BadgeHelp topic={HelpTopic.UI_CST_STATUS} offset={4} />
|
||||
</Overlay>
|
||||
<StatusBar
|
||||
processing={isPending}
|
||||
isModified={isModified}
|
||||
activeCst={activeCst}
|
||||
parseData={parseData}
|
||||
onAnalyze={() => handleCheckExpression()}
|
||||
/>
|
||||
|
||||
<RSInput
|
||||
ref={rsInput}
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { HelpTopic } from '@/features/help';
|
||||
import { BadgeHelp } from '@/features/help/components';
|
||||
|
||||
import { Overlay } from '@/components/Container';
|
||||
import { Loader } from '@/components/Loader';
|
||||
import { APP_COLORS } from '@/styling/colors';
|
||||
import { globalIDs } from '@/utils/constants';
|
||||
|
@ -35,34 +39,40 @@ export function StatusBar({ isModified, processing, activeCst, parseData, onAnal
|
|||
})();
|
||||
|
||||
return (
|
||||
<div
|
||||
tabIndex={0}
|
||||
className={clsx(
|
||||
'w-[10rem] h-[1.75rem]',
|
||||
'px-2 flex items-center justify-center',
|
||||
'border',
|
||||
'select-none',
|
||||
'cursor-pointer',
|
||||
'focus-frame',
|
||||
'transition-colors duration-500'
|
||||
)}
|
||||
style={{ backgroundColor: processing ? APP_COLORS.bgDefault : colorStatusBar(status) }}
|
||||
data-tooltip-id={globalIDs.tooltip}
|
||||
data-tooltip-html={prepareTooltip('Проверить определение', 'Ctrl + Q')}
|
||||
onClick={onAnalyze}
|
||||
<Overlay
|
||||
position='top-[-0.5rem] right-1/2 translate-x-1/2'
|
||||
layer='z-pop'
|
||||
className='w-fit pl-[8.5rem] xs:pl-[2rem] flex gap-1'
|
||||
>
|
||||
{processing ? (
|
||||
<div className='cc-fade-in'>
|
||||
{' '}
|
||||
<Loader scale={3} />
|
||||
</div>
|
||||
) : null}
|
||||
{!processing ? (
|
||||
<div className='cc-fade-in flex items-center gap-2'>
|
||||
<IconExpressionStatus size='1rem' value={status} />
|
||||
<span className='pb-[0.125rem] font-controls pr-2'>{labelExpressionStatus(status)}</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div
|
||||
tabIndex={0}
|
||||
className={clsx(
|
||||
'w-[10rem] h-[1.75rem]',
|
||||
'px-2 flex items-center justify-center',
|
||||
'border',
|
||||
'select-none',
|
||||
'cursor-pointer',
|
||||
'focus-frame outline-none',
|
||||
'transition-colors duration-500'
|
||||
)}
|
||||
style={{ backgroundColor: processing ? APP_COLORS.bgDefault : colorStatusBar(status) }}
|
||||
data-tooltip-id={globalIDs.tooltip}
|
||||
data-tooltip-html={prepareTooltip('Проверить определение', 'Ctrl + Q')}
|
||||
onClick={onAnalyze}
|
||||
>
|
||||
{processing ? (
|
||||
<div className='cc-fade-in'>
|
||||
<Loader scale={3} />
|
||||
</div>
|
||||
) : null}
|
||||
{!processing ? (
|
||||
<div className='cc-fade-in flex items-center gap-2'>
|
||||
<IconExpressionStatus size='1rem' value={status} />
|
||||
<span className='pb-[0.125rem] font-controls pr-2'>{labelExpressionStatus(status)}</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<BadgeHelp topic={HelpTopic.UI_CST_STATUS} offset={4} />
|
||||
</Overlay>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -35,23 +35,23 @@ export function EditorRSFormCard() {
|
|||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
onKeyDown={handleInput}
|
||||
className={clsx(
|
||||
'relative',
|
||||
'cc-fade-in',
|
||||
'md:w-fit md:max-w-fit max-w-[32rem]',
|
||||
'flex flex-row flex-wrap px-6 pt-[1.9rem]'
|
||||
)}
|
||||
>
|
||||
<ToolbarRSFormCard onSubmit={initiateSubmit} schema={schema} isMutable={isMutable} deleteSchema={deleteSchema} />
|
||||
<div
|
||||
onKeyDown={handleInput}
|
||||
className={clsx(
|
||||
'cc-fade-in',
|
||||
'md:w-fit md:max-w-fit max-w-[32rem]',
|
||||
'flex flex-row flex-wrap px-6 pt-[1.9rem]'
|
||||
)}
|
||||
>
|
||||
<FlexColumn className='shrink'>
|
||||
<FormRSForm />
|
||||
<EditorLibraryItem schema={schema} isAttachedToOSS={isAttachedToOSS} />
|
||||
</FlexColumn>
|
||||
|
||||
<RSFormStats stats={schema.stats} isArchive={isArchive} />
|
||||
</div>
|
||||
</>
|
||||
<FlexColumn className='shrink'>
|
||||
<FormRSForm />
|
||||
<EditorLibraryItem schema={schema} isAttachedToOSS={isAttachedToOSS} />
|
||||
</FlexColumn>
|
||||
|
||||
<RSFormStats stats={schema.stats} isArchive={isArchive} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ export function FormRSForm() {
|
|||
disabled={!isContentEditable}
|
||||
error={errors.alias}
|
||||
/>
|
||||
<div className='flex flex-col'>
|
||||
<div className='relative flex flex-col'>
|
||||
<ToolbarVersioning blockReload={schema.oss.length > 0} />
|
||||
<ToolbarItemAccess
|
||||
visible={visible}
|
||||
|
|
|
@ -4,7 +4,6 @@ import { useState } from 'react';
|
|||
import { toast } from 'react-toastify';
|
||||
import fileDownload from 'js-file-download';
|
||||
|
||||
import { Overlay } from '@/components/Container';
|
||||
import { MiniButton } from '@/components/Control';
|
||||
import { type RowSelectionState } from '@/components/DataTable';
|
||||
import { IconCSV } from '@/components/Icons';
|
||||
|
@ -127,42 +126,40 @@ export function EditorRSList() {
|
|||
const tableHeight = useFitHeight('4.05rem + 5px');
|
||||
|
||||
return (
|
||||
<>
|
||||
<div tabIndex={-1} onKeyDown={handleKeyDown} className='relative cc-fade-in pt-[1.9rem]'>
|
||||
{isContentEditable ? <ToolbarRSList /> : null}
|
||||
<div tabIndex={-1} onKeyDown={handleKeyDown} className='cc-fade-in pt-[1.9rem]'>
|
||||
{isContentEditable ? (
|
||||
<div className='flex items-center border-b'>
|
||||
<div className='px-2'>
|
||||
Выбор {selected.length} из {schema.stats?.count_all}
|
||||
</div>
|
||||
<SearchBar
|
||||
id='constituents_search'
|
||||
noBorder
|
||||
className='w-[8rem]'
|
||||
query={filterText}
|
||||
onChangeQuery={setFilterText}
|
||||
/>
|
||||
|
||||
<MiniButton
|
||||
className='absolute z-tooltip top-[2.15rem] right-[1rem]'
|
||||
title='Выгрузить в формате CSV'
|
||||
icon={<IconCSV size='1.25rem' className='icon-green' />}
|
||||
onClick={handleDownloadCSV}
|
||||
/>
|
||||
|
||||
{isContentEditable ? (
|
||||
<div className='flex items-center border-b'>
|
||||
<div className='px-2'>
|
||||
Выбор {selected.length} из {schema.stats?.count_all}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<Overlay position='top-[0.25rem] right-[1rem]' layer='z-tooltip'>
|
||||
<MiniButton
|
||||
title='Выгрузить в формате CSV'
|
||||
icon={<IconCSV size='1.25rem' className='icon-green' />}
|
||||
onClick={handleDownloadCSV}
|
||||
<SearchBar
|
||||
id='constituents_search'
|
||||
noBorder
|
||||
className='w-[8rem]'
|
||||
query={filterText}
|
||||
onChangeQuery={setFilterText}
|
||||
/>
|
||||
</Overlay>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<TableRSList
|
||||
items={filtered}
|
||||
maxHeight={tableHeight}
|
||||
enableSelection={isContentEditable}
|
||||
selected={rowSelection}
|
||||
setSelected={handleRowSelection}
|
||||
onEdit={navigateCst}
|
||||
onCreateNew={createCstDefault}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
<TableRSList
|
||||
items={filtered}
|
||||
maxHeight={tableHeight}
|
||||
enableSelection={isContentEditable}
|
||||
selected={rowSelection}
|
||||
setSelected={handleRowSelection}
|
||||
onEdit={navigateCst}
|
||||
onCreateNew={createCstDefault}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ export function GraphSelectors() {
|
|||
const setColoring = useTermGraphStore(state => state.setColoring);
|
||||
|
||||
return (
|
||||
<div className='border rounded-b-none select-none clr-input rounded-t-md pointer-events-auto'>
|
||||
<div className='relative border rounded-b-none select-none clr-input rounded-t-md pointer-events-auto'>
|
||||
<Overlay position='right-[2.5rem] top-[0.25rem]'>
|
||||
{coloring === 'status' ? <BadgeHelp topic={HelpTopic.UI_CST_STATUS} className='min-w-[25rem]' /> : null}
|
||||
{coloring === 'type' ? <BadgeHelp topic={HelpTopic.UI_CST_CLASS} className='min-w-[25rem]' /> : null}
|
||||
|
|
|
@ -40,7 +40,6 @@ export function SchemasGuide() {
|
|||
<IconHelp size='1.25rem' className='icon-primary' />
|
||||
<Tooltip
|
||||
anchorSelect={`#${globalIDs.graph_schemas}`}
|
||||
layer='z-modal-tooltip'
|
||||
place='right'
|
||||
className='max-w-[25rem] break-words text-base'
|
||||
>
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { Overlay } from '@/components/Container';
|
||||
import { MiniButton } from '@/components/Control';
|
||||
import { IconDropArrow, IconDropArrowUp } from '@/components/Icons';
|
||||
import { useWindowSize } from '@/hooks/useWindowSize';
|
||||
|
@ -47,16 +46,16 @@ export function ViewHidden({ items }: ViewHiddenProps) {
|
|||
return null;
|
||||
}
|
||||
return (
|
||||
<div className='flex flex-col'>
|
||||
<Overlay position='right-[calc(0.7rem-2px)] top-2 pointer-events-auto'>
|
||||
<MiniButton
|
||||
noPadding
|
||||
noHover
|
||||
title={!isFolded ? 'Свернуть' : 'Развернуть'}
|
||||
icon={!isFolded ? <IconDropArrowUp size='1.25rem' /> : <IconDropArrow size='1.25rem' />}
|
||||
onClick={toggleFolded}
|
||||
/>
|
||||
</Overlay>
|
||||
<div className='flex flex-col relative'>
|
||||
<MiniButton
|
||||
className='absolute right-[calc(0.7rem-2px)] top-2 pointer-events-auto'
|
||||
noPadding
|
||||
noHover
|
||||
title={!isFolded ? 'Свернуть' : 'Развернуть'}
|
||||
icon={!isFolded ? <IconDropArrowUp size='1.25rem' /> : <IconDropArrow size='1.25rem' />}
|
||||
onClick={toggleFolded}
|
||||
/>
|
||||
|
||||
<div className={clsx('pt-2 clr-input border-x pb-2', { 'border-b rounded-b-md': isFolded })}>
|
||||
<div
|
||||
className='w-fit select-none'
|
||||
|
|
|
@ -5,7 +5,6 @@ import clsx from 'clsx';
|
|||
|
||||
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
||||
|
||||
import { Overlay } from '@/components/Container';
|
||||
import { TabLabel, TabList, TabPanel, Tabs } from '@/components/Tabs';
|
||||
import { useAppLayoutStore } from '@/stores/appLayout';
|
||||
import { useModificationStore } from '@/stores/modification';
|
||||
|
@ -72,51 +71,53 @@ export function RSTabs({ activeID, activeTab }: RSTabsProps) {
|
|||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tabs
|
||||
selectedIndex={activeTab}
|
||||
onSelect={onSelectTab}
|
||||
defaultFocus
|
||||
selectedTabClassName='clr-selected'
|
||||
className='flex flex-col mx-auto min-w-fit items-center'
|
||||
<Tabs
|
||||
selectedIndex={activeTab}
|
||||
onSelect={onSelectTab}
|
||||
defaultFocus
|
||||
selectedTabClassName='clr-selected'
|
||||
className='relative flex flex-col mx-auto min-w-fit items-center'
|
||||
>
|
||||
<TabList
|
||||
className={clsx(
|
||||
'absolute z-sticky',
|
||||
'mx-auto w-fit top-0 right-1/2 translate-x-1/2',
|
||||
'flex items-stretch',
|
||||
'border-b-2 border-x-2 divide-x-2',
|
||||
'bg-prim-200'
|
||||
)}
|
||||
>
|
||||
<Overlay position='top-0 right-1/2 translate-x-1/2' layer='z-sticky'>
|
||||
<TabList
|
||||
className={clsx('mx-auto w-fit', 'flex items-stretch', 'border-b-2 border-x-2 divide-x-2', 'bg-prim-200')}
|
||||
>
|
||||
<MenuRSTabs />
|
||||
<MenuRSTabs />
|
||||
|
||||
<TabLabel
|
||||
label='Карточка'
|
||||
titleHtml={`${schema.title ?? ''}<br />Версия: ${labelVersion(schema.version, schema.versions)}`}
|
||||
/>
|
||||
<TabLabel
|
||||
label='Содержание'
|
||||
titleHtml={`Конституент: ${schema.stats?.count_all ?? 0}<br />Ошибок: ${schema.stats?.count_errors ?? 0}`}
|
||||
/>
|
||||
<TabLabel label='Редактор' />
|
||||
<TabLabel label='Граф термов' />
|
||||
</TabList>
|
||||
</Overlay>
|
||||
<TabLabel
|
||||
label='Карточка'
|
||||
titleHtml={`${schema.title ?? ''}<br />Версия: ${labelVersion(schema.version, schema.versions)}`}
|
||||
/>
|
||||
<TabLabel
|
||||
label='Содержание'
|
||||
titleHtml={`Конституент: ${schema.stats?.count_all ?? 0}<br />Ошибок: ${schema.stats?.count_errors ?? 0}`}
|
||||
/>
|
||||
<TabLabel label='Редактор' />
|
||||
<TabLabel label='Граф термов' />
|
||||
</TabList>
|
||||
|
||||
<div className='overflow-x-hidden'>
|
||||
<TabPanel>
|
||||
<EditorRSFormCard />
|
||||
</TabPanel>
|
||||
<div className='overflow-x-hidden'>
|
||||
<TabPanel>
|
||||
<EditorRSFormCard />
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel>
|
||||
<EditorRSList />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<EditorRSList />
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel>
|
||||
<EditorConstituenta />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<EditorConstituenta />
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel>
|
||||
<EditorTermGraph />
|
||||
</TabPanel>
|
||||
</div>
|
||||
</Tabs>
|
||||
</>
|
||||
<TabPanel>
|
||||
<EditorTermGraph />
|
||||
</TabPanel>
|
||||
</div>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import { urls, useConceptNavigation } from '@/app';
|
|||
import { HelpTopic } from '@/features/help';
|
||||
|
||||
import { isAxiosError } from '@/backend/apiTransport';
|
||||
import { FlexColumn, Overlay, Tooltip } from '@/components/Container';
|
||||
import { FlexColumn, Tooltip } from '@/components/Container';
|
||||
import { Button, SubmitButton, TextURL } from '@/components/Control';
|
||||
import { IconHelp } from '@/components/Icons';
|
||||
import { type ErrorData } from '@/components/InfoError';
|
||||
|
@ -60,15 +60,7 @@ export function FormSignup() {
|
|||
onSubmit={event => void handleSubmit(onSubmit)(event)}
|
||||
onChange={resetErrors}
|
||||
>
|
||||
<h1>
|
||||
<span>Новый пользователь</span>
|
||||
<Overlay id={globalIDs.email_tooltip} position='top-[0.5rem] right-[1.75rem]'>
|
||||
<IconHelp size='1.25rem' className='icon-primary' />
|
||||
</Overlay>
|
||||
<Tooltip anchorSelect={`#${globalIDs.email_tooltip}`} offset={6}>
|
||||
электронная почта используется для восстановления пароля
|
||||
</Tooltip>
|
||||
</h1>
|
||||
<h1>Новый пользователь</h1>
|
||||
<div className='flex gap-12'>
|
||||
<FlexColumn>
|
||||
<TextInput
|
||||
|
@ -102,7 +94,11 @@ export function FormSignup() {
|
|||
/>
|
||||
</FlexColumn>
|
||||
|
||||
<FlexColumn className='w-[15rem]'>
|
||||
<FlexColumn className='w-[15rem] relative'>
|
||||
<IconHelp id={globalIDs.email_tooltip} className='absolute top-0 right-0 icon-primary' size='1.25rem' />
|
||||
<Tooltip anchorSelect={`#${globalIDs.email_tooltip}`} offset={6}>
|
||||
электронная почта используется для восстановления пароля
|
||||
</Tooltip>
|
||||
<TextInput
|
||||
id='email'
|
||||
{...register('email')}
|
||||
|
|
|
@ -42,8 +42,6 @@
|
|||
--z-index-tooltip: 30;
|
||||
--z-index-navigation: 50;
|
||||
--z-index-modal: 60;
|
||||
--z-index-modal-controls: 70;
|
||||
--z-index-modal-tooltip: 90;
|
||||
|
||||
--breakpoint-*: initial;
|
||||
--breakpoint-xs: 475px;
|
||||
|
|
Loading…
Reference in New Issue
Block a user