F: Refactor z-index and stacking

This commit is contained in:
Ivan 2025-03-07 20:38:40 +03:00
parent 293f1cad6f
commit 81fa0c5796
39 changed files with 317 additions and 352 deletions

View File

@ -15,6 +15,7 @@ interface OverlayProps extends Styling {
/** /**
* Displays a transparent overlay over the main content. * Displays a transparent overlay over the main content.
* Note: Overlay should be inside a relative container.
*/ */
export function Overlay({ export function Overlay({
children, children,
@ -24,10 +25,8 @@ export function Overlay({
...restProps ...restProps
}: React.PropsWithChildren<OverlayProps>) { }: React.PropsWithChildren<OverlayProps>) {
return ( return (
<div className='relative'>
<div className={clsx('absolute', className, position, layer)} {...restProps}> <div className={clsx('absolute', className, position, layer)} {...restProps}>
{children} {children}
</div> </div>
</div>
); );
} }

View File

@ -39,6 +39,7 @@ export function Tooltip({
delayHide={100} delayHide={100}
opacity={1} opacity={1}
className={clsx( className={clsx(
'relative',
'max-h-[calc(100svh-6rem)]', 'max-h-[calc(100svh-6rem)]',
'overflow-y-auto overflow-x-hidden sm:overflow-hidden overscroll-contain', 'overflow-y-auto overflow-x-hidden sm:overflow-hidden overscroll-contain',
'border shadow-md', 'border shadow-md',

View File

@ -23,6 +23,7 @@ interface DropdownProps extends Styling {
/** /**
* Animated list of children with optional positioning and visibility control. * Animated list of children with optional positioning and visibility control.
* Note: Dropdown should be inside a relative container.
*/ */
export function Dropdown({ export function Dropdown({
isOpen, isOpen,

View File

@ -1,6 +1,5 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { Overlay } from '@/components/Container';
import { IconSearch } from '@/components/Icons'; import { IconSearch } from '@/components/Icons';
import { type Styling } from '@/components/props'; import { type Styling } from '@/components/props';
@ -35,15 +34,17 @@ export function SearchBar({
noIcon, noIcon,
onChangeQuery, onChangeQuery,
noBorder, noBorder,
className,
placeholder = 'Поиск', placeholder = 'Поиск',
...restProps ...restProps
}: SearchBarProps) { }: SearchBarProps) {
return ( return (
<div {...restProps}> <div className={clsx('relative', className)} {...restProps}>
{!noIcon ? ( {!noIcon ? (
<Overlay position='top-[-0.125rem] left-3 translate-y-1/2' className='pointer-events-none clr-text-controls'> <IconSearch
<IconSearch size='1.25rem' /> className='absolute top-[-0.125rem] left-3 translate-y-1/2 pointer-events-none clr-text-controls'
</Overlay> size='1.25rem'
/>
) : null} ) : null}
<TextInput <TextInput
id={id} id={id}

View File

@ -3,7 +3,6 @@ import clsx from 'clsx';
import { globalIDs, PARAMETER } from '@/utils/constants'; import { globalIDs, PARAMETER } from '@/utils/constants';
import { Overlay } from '../Container';
import { MiniButton } from '../Control'; import { MiniButton } from '../Control';
import { IconDropArrow, IconPageRight } from '../Icons'; import { IconDropArrow, IconPageRight } from '../Icons';
import { type Styling } from '../props'; import { type Styling } from '../props';
@ -86,6 +85,7 @@ export function SelectTree<ItemType>({
<div <div
key={`${prefix}${index}`} key={`${prefix}${index}`}
className={clsx( className={clsx(
'relative',
'pr-3 pl-6 border-b', 'pr-3 pl-6 border-b',
'cc-scroll-row', 'cc-scroll-row',
'bg-prim-200 clr-hover cc-animate-color', 'bg-prim-200 clr-hover cc-animate-color',
@ -108,14 +108,13 @@ export function SelectTree<ItemType>({
}} }}
> >
{foldable.has(item) ? ( {foldable.has(item) ? (
<Overlay position='left-[-1.3rem]' className={clsx(!folded.includes(item) && 'top-[0.1rem]')}>
<MiniButton <MiniButton
className={clsx('absolute left-[0.3rem]', !folded.includes(item) ? 'top-[0.4rem]' : 'top-1')}
noPadding noPadding
noHover noHover
icon={!folded.includes(item) ? <IconDropArrow size='1rem' /> : <IconPageRight size='1.25rem' />} icon={!folded.includes(item) ? <IconDropArrow size='1rem' /> : <IconPageRight size='1.25rem' />}
onClick={event => handleClickFold(event, item, folded.includes(item))} onClick={event => handleClickFold(event, item, folded.includes(item))}
/> />
</Overlay>
) : null} ) : null}
{getParent(item) === item ? getLabel(item) : `- ${getLabel(item).toLowerCase()}`} {getParent(item) === item ? getLabel(item) : `- ${getLabel(item).toLowerCase()}`}
</div> </div>

View File

@ -9,9 +9,9 @@ interface ModalBackdropProps {
export function ModalBackdrop({ onHide }: ModalBackdropProps) { export function ModalBackdrop({ onHide }: ModalBackdropProps) {
return ( 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 <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} onClick={onHide}
/> />
</> </>

View File

@ -10,7 +10,6 @@ import { useDialogsStore } from '@/stores/dialogs';
import { PARAMETER } from '@/utils/constants'; import { PARAMETER } from '@/utils/constants';
import { prepareTooltip } from '@/utils/utils'; import { prepareTooltip } from '@/utils/utils';
import { Overlay } from '../Container';
import { Button, MiniButton, SubmitButton } from '../Control'; import { Button, MiniButton, SubmitButton } from '../Control';
import { IconClose } from '../Icons'; import { IconClose } from '../Icons';
import { type Styling } from '../props'; import { type Styling } from '../props';
@ -89,12 +88,12 @@ export function ModalForm({
} }
return ( 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} /> <ModalBackdrop onHide={handleCancel} />
<form <form
className={clsx( className={clsx(
'cc-animate-modal', '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' 'border rounded-xl bg-prim-100'
)} )}
onSubmit={handleSubmit} onSubmit={handleSubmit}
@ -105,7 +104,6 @@ export function ModalForm({
</div> </div>
) : null} ) : null}
<Overlay className='z-modalOverlay'>
<MiniButton <MiniButton
noPadding noPadding
titleHtml={prepareTooltip('Закрыть диалоговое окно', 'ESC')} titleHtml={prepareTooltip('Закрыть диалоговое окно', 'ESC')}
@ -113,7 +111,6 @@ export function ModalForm({
className='float-right mt-2 mr-2' className='float-right mt-2 mr-2'
onClick={handleCancel} onClick={handleCancel}
/> />
</Overlay>
{header ? <h1 className='px-12 py-2 select-none'>{header}</h1> : null} {header ? <h1 className='px-12 py-2 select-none'>{header}</h1> : null}
@ -133,7 +130,7 @@ export function ModalForm({
{children} {children}
</div> </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 <SubmitButton
autoFocus autoFocus
text={submitText} text={submitText}

View File

@ -4,9 +4,9 @@ import { Loader } from '@/components/Loader';
export function ModalLoader() { export function ModalLoader() {
return ( 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'>
<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')} /> <div className={clsx('z-bottom fixed top-0 left-0', 'w-full h-full', 'bg-prim-0 opacity-25')} />
<div <div
className={clsx( className={clsx(
'cc-animate-modal p-20', 'cc-animate-modal p-20',

View File

@ -9,7 +9,6 @@ import { useDialogsStore } from '@/stores/dialogs';
import { PARAMETER } from '@/utils/constants'; import { PARAMETER } from '@/utils/constants';
import { prepareTooltip } from '@/utils/utils'; import { prepareTooltip } from '@/utils/utils';
import { Overlay } from '../Container';
import { Button, MiniButton } from '../Control'; import { Button, MiniButton } from '../Control';
import { IconClose } from '../Icons'; import { IconClose } from '../Icons';
@ -34,7 +33,7 @@ export function ModalView({
useEscapeKey(hideDialog); useEscapeKey(hideDialog);
return ( 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} /> <ModalBackdrop onHide={hideDialog} />
<div <div
className={clsx( className={clsx(
@ -49,7 +48,6 @@ export function ModalView({
</div> </div>
) : null} ) : null}
<Overlay className='z-modalOverlay'>
<MiniButton <MiniButton
noPadding noPadding
titleHtml={prepareTooltip('Закрыть диалоговое окно', 'ESC')} titleHtml={prepareTooltip('Закрыть диалоговое окно', 'ESC')}
@ -57,7 +55,6 @@ export function ModalView({
className='float-right mt-2 mr-2' className='float-right mt-2 mr-2'
onClick={hideDialog} onClick={hideDialog}
/> />
</Overlay>
{header ? <h1 className='px-12 py-2 select-none'>{header}</h1> : null} {header ? <h1 className='px-12 py-2 select-none'>{header}</h1> : null}
@ -77,7 +74,7 @@ export function ModalView({
{children} {children}
</div> </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} /> <Button text='Закрыть' className='min-w-[7rem]' onClick={hideDialog} />
</div> </div>
</div> </div>

View File

@ -39,13 +39,11 @@ export function BadgeHelp({ topic, padding = 'p-1', ...restProps }: BadgeHelpPro
return ( return (
<div tabIndex={-1} id={`help-${topic}`} className={padding}> <div tabIndex={-1} id={`help-${topic}`} className={padding}>
<IconHelp size='1.25rem' className='icon-primary' /> <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 />}> <Suspense fallback={<Loader />}>
<div className='relative' onClick={event => event.stopPropagation()}> <div className='absolute right-1 text-sm top-[0.4rem] clr-input' onClick={event => event.stopPropagation()}>
<div className='absolute right-0 text-sm top-[0.4rem] clr-input'>
<TextURL text='Справка...' href={`/manuals?topic=${topic}`} /> <TextURL text='Справка...' href={`/manuals?topic=${topic}`} />
</div> </div>
</div>
<TopicPage topic={topic} /> <TopicPage topic={topic} />
</Suspense> </Suspense>
</Tooltip> </Tooltip>

View File

@ -33,7 +33,7 @@ export function TopicsDropdown({ activeTopic, onChangeTopic }: TopicsDropdownPro
className={clsx( className={clsx(
'absolute left-0 w-[13.5rem]', // prettier: split-lines 'absolute left-0 w-[13.5rem]', // prettier: split-lines
'flex flex-col', 'flex flex-col',
'z-modal-tooltip', 'z-topmost',
'text-xs sm:text-sm', 'text-xs sm:text-sm',
'select-none', 'select-none',
{ {

View File

@ -5,7 +5,7 @@ import { urls, useConceptNavigation } from '@/app';
import { useLabelUser, useRoleStore, UserRole } from '@/features/users'; import { useLabelUser, useRoleStore, UserRole } from '@/features/users';
import { InfoUsers, SelectUser } from '@/features/users/components'; import { InfoUsers, SelectUser } from '@/features/users/components';
import { Overlay, Tooltip } from '@/components/Container'; import { Tooltip } from '@/components/Container';
import { MiniButton } from '@/components/Control'; import { MiniButton } from '@/components/Control';
import { useDropdown } from '@/components/Dropdown'; import { useDropdown } from '@/components/Dropdown';
import { import {
@ -83,7 +83,7 @@ export function EditorLibraryItem({ schema, isAttachedToOSS }: EditorLibraryItem
return ( return (
<div className='flex flex-col'> <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 <MiniButton
noHover noHover
noPadding noPadding
@ -101,12 +101,11 @@ export function EditorLibraryItem({ schema, isAttachedToOSS }: EditorLibraryItem
/> />
</div> </div>
<div className='relative'>
{ownerSelector.isOpen ? ( {ownerSelector.isOpen ? (
<Overlay position='top-[-0.5rem] left-[4rem] cc-icons'> <div className='absolute top-[-0.5rem] left-[4rem]'>
{ownerSelector.isOpen ? (
<SelectUser className='w-[25rem] sm:w-[26rem] text-sm' value={schema.owner} onChange={onSelectUser} /> <SelectUser className='w-[25rem] sm:w-[26rem] text-sm' value={schema.owner} onChange={onSelectUser} />
) : null} </div>
</Overlay>
) : null} ) : null}
<ValueIcon <ValueIcon
className='sm:mb-1' className='sm:mb-1'
@ -116,6 +115,7 @@ export function EditorLibraryItem({ schema, isAttachedToOSS }: EditorLibraryItem
onClick={ownerSelector.toggle} onClick={ownerSelector.toggle}
disabled={isModified || isProcessing || isAttachedToOSS || role < UserRole.OWNER} disabled={isModified || isProcessing || isAttachedToOSS || role < UserRole.OWNER}
/> />
</div>
<div className='sm:mb-1 flex justify-between items-center'> <div className='sm:mb-1 flex justify-between items-center'>
<ValueIcon <ValueIcon
@ -126,7 +126,7 @@ export function EditorLibraryItem({ schema, isAttachedToOSS }: EditorLibraryItem
onClick={handleEditEditors} onClick={handleEditEditors}
disabled={isModified || isProcessing || role < UserRole.OWNER} disabled={isModified || isProcessing || role < UserRole.OWNER}
/> />
<Tooltip anchorSelect='#editor_stats' layer='z-modal-tooltip'> <Tooltip anchorSelect='#editor_stats'>
<Suspense fallback={<Loader scale={2} />}> <Suspense fallback={<Loader scale={2} />}>
<InfoUsers items={schema.editors} prefix={prefixes.user_editors} header='Редакторы' /> <InfoUsers items={schema.editors} prefix={prefixes.user_editors} header='Редакторы' />
</Suspense> </Suspense>

View File

@ -120,7 +120,7 @@ export function PickSchema({
className='mt-1' className='mt-1'
onClick={() => locationMenu.toggle()} 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 <SelectLocation
value={filterLocation} value={filterLocation}
prefix={prefixes.folders_list} prefix={prefixes.folders_list}

View File

@ -48,7 +48,7 @@ export function SelectLocationContext({
/> />
<Dropdown <Dropdown
isOpen={menu.isOpen} 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]' margin='mt-[-0.25rem]'
> >
<SelectLocation <SelectLocation

View File

@ -45,7 +45,7 @@ export function SelectLocationHead({
onClick={menu.toggle} onClick={menu.toggle}
/> />
<Dropdown isOpen={menu.isOpen} className='z-modal-tooltip' margin='mt-2'> <Dropdown isOpen={menu.isOpen} margin='mt-2'>
{Object.values(LocationHead) {Object.values(LocationHead)
.filter(head => !excluded.includes(head)) .filter(head => !excluded.includes(head))
.map((head, index) => { .map((head, index) => {

View File

@ -42,7 +42,7 @@ export function ToolbarItemAccess({
} }
return ( 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' /> <Label text='Доступ' className='self-center select-none' />
<div className='ml-auto cc-icons'> <div className='ml-auto cc-icons'>
<SelectAccessPolicy <SelectAccessPolicy

View File

@ -155,7 +155,7 @@ export function DlgCloneLibraryItem() {
/> />
</div> </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 ? ( {selected.length > 0 ? (
<Controller <Controller

View File

@ -8,7 +8,6 @@ import clsx from 'clsx';
import { urls, useConceptNavigation } from '@/app'; import { urls, useConceptNavigation } from '@/app';
import { useAuthSuspense } from '@/features/auth'; import { useAuthSuspense } from '@/features/auth';
import { Overlay } from '@/components/Container';
import { Button, MiniButton, SubmitButton } from '@/components/Control'; import { Button, MiniButton, SubmitButton } from '@/components/Control';
import { IconDownload } from '@/components/Icons'; import { IconDownload } from '@/components/Icons';
import { InfoError } from '@/components/InfoError'; import { InfoError } from '@/components/InfoError';
@ -108,9 +107,9 @@ export function FormCreateItem() {
onSubmit={event => void handleSubmit(onSubmit)(event)} onSubmit={event => void handleSubmit(onSubmit)(event)}
onChange={resetErrors} onChange={resetErrors}
> >
<h1 className='select-none'> <h1 className='select-none relative'>
{itemType == LibraryItemType.RSFORM ? ( {itemType == LibraryItemType.RSFORM ? (
<Overlay position='top-0 right-[0.5rem]'> <>
<Controller <Controller
control={control} control={control}
name='file' name='file'
@ -127,10 +126,11 @@ export function FormCreateItem() {
/> />
<MiniButton <MiniButton
title='Загрузить из Экстеор' title='Загрузить из Экстеор'
className='absolute top-0 right-0'
icon={<IconDownload size='1.25rem' className='icon-primary' />} icon={<IconDownload size='1.25rem' className='icon-primary' />}
onClick={() => inputRef.current?.click()} onClick={() => inputRef.current?.click()}
/> />
</Overlay> </>
) : null} ) : null}
Создание схемы Создание схемы
</h1> </h1>

View File

@ -3,10 +3,8 @@
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import fileDownload from 'js-file-download'; import fileDownload from 'js-file-download';
import { Overlay } from '@/components/Container';
import { MiniButton } from '@/components/Control'; import { MiniButton } from '@/components/Control';
import { IconCSV } from '@/components/Icons'; import { IconCSV } from '@/components/Icons';
import { useAppLayoutStore } from '@/stores/appLayout';
import { useDialogsStore } from '@/stores/dialogs'; import { useDialogsStore } from '@/stores/dialogs';
import { infoMsg } from '@/utils/labels'; import { infoMsg } from '@/utils/labels';
import { convertToCSV } from '@/utils/utils'; import { convertToCSV } from '@/utils/utils';
@ -24,8 +22,6 @@ export function LibraryPage() {
const { items: libraryItems } = useLibrarySuspense(); const { items: libraryItems } = useLibrarySuspense();
const { renameLocation } = useRenameLocation(); const { renameLocation } = useRenameLocation();
const noNavigation = useAppLayoutStore(state => state.noNavigation);
const folderMode = useLibrarySearchStore(state => state.folderMode); const folderMode = useLibrarySearchStore(state => state.folderMode);
const location = useLibrarySearchStore(state => state.location); const location = useLibrarySearchStore(state => state.location);
const setLocation = useLibrarySearchStore(state => state.setLocation); const setLocation = useLibrarySearchStore(state => state.setLocation);
@ -57,20 +53,15 @@ export function LibraryPage() {
return ( return (
<> <>
<Overlay <ToolbarSearch total={libraryItems.length} filtered={filtered.length} />
position={noNavigation ? 'top-[0.25rem] right-[3rem]' : 'top-[0.25rem] right-0'} <div className='relative cc-fade-in flex'>
layer='z-tooltip'
className='cc-animate-position'
>
<MiniButton <MiniButton
className='absolute z-tooltip top-[0.25rem] right-0 cc-animate-position'
title='Выгрузить в формате CSV' title='Выгрузить в формате CSV'
icon={<IconCSV size='1.25rem' className='icon-green' />} icon={<IconCSV size='1.25rem' className='icon-green' />}
onClick={handleDownloadCSV} onClick={handleDownloadCSV}
/> />
</Overlay>
<ToolbarSearch total={libraryItems.length} filtered={filtered.length} />
<div className='cc-fade-in flex'>
<ViewSideLocation <ViewSideLocation
isVisible={folderMode} isVisible={folderMode}
onRenameLocation={() => showChangeLocation({ initial: location, onChangeLocation: handleRenameLocation })} onRenameLocation={() => showChangeLocation({ initial: location, onChangeLocation: handleRenameLocation })}

View File

@ -166,7 +166,7 @@ export function ToolbarSearch({ total, filtered }: ToolbarSearchProps) {
text={head ?? '//'} text={head ?? '//'}
/> />
<Dropdown isOpen={headMenu.isOpen} stretchLeft className='z-modal-tooltip'> <Dropdown isOpen={headMenu.isOpen} stretchLeft>
<DropdownButton title='Переключение в режим Проводник' onClick={handleToggleFolder}> <DropdownButton title='Переключение в режим Проводник' onClick={handleToggleFolder}>
<div className='inline-flex items-center gap-3'> <div className='inline-flex items-center gap-3'>
<IconFolderTree size='1rem' className='clr-text-controls' /> <IconFolderTree size='1rem' className='clr-text-controls' />

View File

@ -69,7 +69,7 @@ export function FormOSS() {
disabled={!isMutable} disabled={!isMutable}
error={errors.title} error={errors.title}
/> />
<div className='flex justify-between gap-3 mb-3'> <div className='relative flex justify-between gap-3 mb-3'>
<TextInput <TextInput
id='schema_alias' id='schema_alias'
{...register('alias')} {...register('alias')}

View File

@ -190,7 +190,7 @@ export function OssFlow() {
} }
return ( return (
<div tabIndex={-1} onKeyDown={handleKeyDown}> <div tabIndex={-1} className='relative' onKeyDown={handleKeyDown}>
<Overlay <Overlay
position='top-[1.9rem] pt-1 right-1/2 translate-x-1/2' 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' className='rounded-b-2xl cc-blur hover:bg-prim-100 hover:bg-opacity-50'

View File

@ -23,7 +23,12 @@ export function NodeCore({ node }: NodeCoreProps) {
const longLabel = node.data.label.length > LONG_LABEL_CHARS; const longLabel = node.data.label.length > LONG_LABEL_CHARS;
return ( 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]'> <Overlay position='top-0 right-0' className='flex flex-col gap-1 p-[2px]'>
<Indicator <Indicator
noPadding noPadding
@ -40,23 +45,13 @@ export function NodeCore({ node }: NodeCoreProps) {
</Overlay> </Overlay>
{node.data.operation.operation_type === OperationType.INPUT ? ( {node.data.operation.operation_type === OperationType.INPUT ? (
<Overlay position='top-[1px] right-1/2 translate-x-1/2' className='flex'> <div className='absolute top-[1px] right-1/2 translate-x-1/2 border-t w-[30px]' />
<div className='border-t w-[30px]'></div>
</Overlay>
) : null} ) : null}
{!node.data.operation.is_owned ? ( {!node.data.operation.is_owned ? (
<Overlay position='left-[2px] top-[6px]'> <div className='absolute left-[2px] top-[6px] border-r rounded-none clr-input h-[22px]' />
<div className='border-r rounded-none clr-input h-[22px]'></div>
</Overlay>
) : null} ) : 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)}
>
<div <div
className='text-center line-clamp-2' className='text-center line-clamp-2'
style={{ style={{
@ -69,6 +64,5 @@ export function NodeCore({ node }: NodeCoreProps) {
{node.data.label} {node.data.label}
</div> </div>
</div> </div>
</>
); );
} }

View File

@ -5,7 +5,6 @@ import clsx from 'clsx';
import { useConceptNavigation } from '@/app/Navigation/NavigationContext'; import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
import { Overlay } from '@/components/Container';
import { TabLabel, TabList, TabPanel, Tabs } from '@/components/Tabs'; import { TabLabel, TabList, TabPanel, Tabs } from '@/components/Tabs';
import { useAppLayoutStore } from '@/stores/appLayout'; import { useAppLayoutStore } from '@/stores/appLayout';
@ -61,16 +60,22 @@ export function OssTabs({ activeTab }: OssTabsProps) {
onSelect={onSelectTab} onSelect={onSelectTab}
defaultFocus defaultFocus
selectedTabClassName='clr-selected' 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'
>
<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'
)}
> >
<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 /> <MenuOssTabs />
<TabLabel label='Карточка' title={schema.title ?? ''} /> <TabLabel label='Карточка' title={schema.title ?? ''} />
<TabLabel label='Граф' /> <TabLabel label='Граф' />
</TabList> </TabList>
</Overlay>
<div className='overflow-x-hidden'> <div className='overflow-x-hidden'>
<TabPanel> <TabPanel>

View File

@ -26,13 +26,10 @@ export function DlgShowAST() {
const [isDragging, setIsDragging] = useState(false); const [isDragging, setIsDragging] = useState(false);
return ( return (
<ModalView <ModalView className='relative w-[calc(100dvw-3rem)] h-[calc(100dvh-6rem)]' helpTopic={HelpTopic.UI_FORMULA_TREE}>
className='flex flex-col justify-stretch w-[calc(100dvw-3rem)] h-[calc(100dvh-6rem)]'
helpTopic={HelpTopic.UI_FORMULA_TREE}
>
<Overlay <Overlay
position='top-2 right-1/2 translate-x-1/2' position='top-0 -mt-1 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' className='px-2 rounded-2xl cc-blur bg-prim-100 max-w-[60ch] text-lg text-center'
> >
{!hoverNode || isDragging ? expression : null} {!hoverNode || isDragging ? expression : null}
{!isDragging && hoverNode ? ( {!isDragging && hoverNode ? (
@ -43,6 +40,7 @@ export function DlgShowAST() {
</div> </div>
) : null} ) : null}
</Overlay> </Overlay>
<ReactFlowProvider> <ReactFlowProvider>
<ASTFlow <ASTFlow
data={syntaxTree} data={syntaxTree}

View File

@ -5,17 +5,14 @@ import clsx from 'clsx';
import { useWindowSize } from '@/hooks/useWindowSize'; import { useWindowSize } from '@/hooks/useWindowSize';
import { useMainHeight } from '@/stores/appLayout'; import { useMainHeight } from '@/stores/appLayout';
import { useDialogsStore } from '@/stores/dialogs';
import { useModificationStore } from '@/stores/modification'; import { useModificationStore } from '@/stores/modification';
import { usePreferencesStore } from '@/stores/preferences'; import { usePreferencesStore } from '@/stores/preferences';
import { globalIDs } from '@/utils/constants'; import { globalIDs } from '@/utils/constants';
import { promptUnsaved } from '@/utils/utils';
import { useMutatingRSForm } from '../../../backend/useMutatingRSForm'; import { useMutatingRSForm } from '../../../backend/useMutatingRSForm';
import { useRSEdit } from '../RSEditContext'; import { useRSEdit } from '../RSEditContext';
import { ViewConstituents } from '../ViewConstituents'; import { ViewConstituents } from '../ViewConstituents';
import { EditorControls } from './EditorControls';
import { FormConstituenta } from './FormConstituenta'; import { FormConstituenta } from './FormConstituenta';
import { ToolbarConstituenta } from './ToolbarConstituenta'; import { ToolbarConstituenta } from './ToolbarConstituenta';
@ -28,7 +25,6 @@ export function EditorConstituenta() {
const mainHeight = useMainHeight(); const mainHeight = useMainHeight();
const showList = usePreferencesStore(state => state.showCstSideList); const showList = usePreferencesStore(state => state.showCstSideList);
const showEditTerm = useDialogsStore(state => state.showEditWordForms);
const { isModified } = useModificationStore(); const { isModified } = useModificationStore();
const [toggleReset, setToggleReset] = useState(false); 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() { function initiateSubmit() {
const element = document.getElementById(globalIDs.constituenta_editor) as HTMLFormElement; const element = document.getElementById(globalIDs.constituenta_editor) as HTMLFormElement;
if (element) { if (element) {
@ -85,16 +71,10 @@ export function EditorConstituenta() {
} }
return ( return (
<>
<ToolbarConstituenta
activeCst={activeCst}
disabled={disabled}
onSubmit={initiateSubmit}
onReset={() => setToggleReset(prev => !prev)}
/>
<div <div
tabIndex={-1} tabIndex={-1}
className={clsx( className={clsx(
'relative',
'cc-fade-in', 'cc-fade-in',
'min-h-[20rem] max-w-[calc(min(100vw,95rem))] mx-auto', 'min-h-[20rem] max-w-[calc(min(100vw,95rem))] mx-auto',
'flex pt-[1.9rem]', 'flex pt-[1.9rem]',
@ -104,17 +84,16 @@ export function EditorConstituenta() {
style={{ maxHeight: mainHeight }} style={{ maxHeight: mainHeight }}
onKeyDown={handleInput} onKeyDown={handleInput}
> >
<ToolbarConstituenta
activeCst={activeCst}
disabled={disabled}
onSubmit={initiateSubmit}
onReset={() => setToggleReset(prev => !prev)}
/>
<div className='mx-0 md:mx-auto pt-[2rem] md:w-[48.8rem] shrink-0 xs:pt-0'> <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 ? ( {activeCst ? (
<FormConstituenta <FormConstituenta
id={globalIDs.constituenta_editor} // id={globalIDs.constituenta_editor}
disabled={disabled} disabled={disabled}
toggleReset={toggleReset} toggleReset={toggleReset}
activeCst={activeCst} activeCst={activeCst}
@ -125,6 +104,5 @@ export function EditorConstituenta() {
</div> </div>
<ViewConstituents isMounted={showList} isBottom={isNarrow} /> <ViewConstituents isMounted={showList} isBottom={isNarrow} />
</div> </div>
</>
); );
} }

View File

@ -29,7 +29,7 @@ export function EditorControls({ constituenta, disabled, onEditTerm }: EditorCon
} }
return ( 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 ? ( {!disabled || isProcessing ? (
<MiniButton <MiniButton
title={isModified ? tooltipText.unsaved : `Редактировать словоформы термина`} title={isModified ? tooltipText.unsaved : `Редактировать словоформы термина`}

View File

@ -14,6 +14,7 @@ import { Indicator } from '@/components/View';
import { useDialogsStore } from '@/stores/dialogs'; import { useDialogsStore } from '@/stores/dialogs';
import { useModificationStore } from '@/stores/modification'; import { useModificationStore } from '@/stores/modification';
import { errorMsg } from '@/utils/labels'; import { errorMsg } from '@/utils/labels';
import { promptUnsaved } from '@/utils/utils';
import { import {
CstType, CstType,
@ -30,6 +31,8 @@ import { type IConstituenta, type IRSForm } from '../../../models/rsform';
import { isBaseSet, isBasicConcept, isFunctional } from '../../../models/rsformAPI'; import { isBaseSet, isBasicConcept, isFunctional } from '../../../models/rsformAPI';
import { EditorRSExpression } from '../EditorRSExpression'; import { EditorRSExpression } from '../EditorRSExpression';
import { EditorControls } from './EditorControls';
interface FormConstituentaProps { interface FormConstituentaProps {
id?: string; id?: string;
disabled: boolean; disabled: boolean;
@ -41,8 +44,6 @@ interface FormConstituentaProps {
} }
export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst, onOpenEdit }: 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 { isModified, setIsModified } = useModificationStore();
const isProcessing = useMutatingRSForm(); const isProcessing = useMutatingRSForm();
@ -54,6 +55,10 @@ export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst,
formState: { isDirty } formState: { isDirty }
} = useForm<ICstUpdateDTO>({ resolver: zodResolver(schemaCstUpdate) }); } = 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 [localParse, setLocalParse] = useState<IExpressionParseDTO | null>(null);
const typification = useMemo( const typification = useMemo(
@ -115,8 +120,20 @@ export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst,
showTypification({ items: typeInfo ? [typeInfo] : [] }); showTypification({ items: typeInfo ? [typeInfo] : [] });
} }
function handleEditTermForms() {
if (!activeCst) {
return;
}
if (isModified && !promptUnsaved()) {
return;
}
showEditTerm({ itemID: schema.id, target: activeCst });
}
return ( 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 <Controller
control={control} control={control}
name='item_data.term_raw' name='item_data.term_raw'
@ -228,13 +245,13 @@ export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst,
) : null} ) : null}
{!disabled || isProcessing ? ( {!disabled || isProcessing ? (
<div className='mx-auto flex'> <div className='relative mx-auto flex'>
<SubmitButton <SubmitButton
text='Сохранить изменения' text='Сохранить изменения'
disabled={disabled || !isModified} disabled={disabled || !isModified}
icon={<IconSave size='1.25rem' />} 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 ? ( {activeCst.has_inherited_children && !activeCst.is_inherited ? (
<Indicator <Indicator
icon={<IconPredecessor size='1.25rem' className='text-sec-600' />} icon={<IconPredecessor size='1.25rem' className='text-sec-600' />}

View File

@ -4,10 +4,6 @@ import { useEffect, useRef, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { type ReactCodeMirrorRef } from '@uiw/react-codemirror'; 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 { useDialogsStore } from '@/stores/dialogs';
import { usePreferencesStore } from '@/stores/preferences'; import { usePreferencesStore } from '@/stores/preferences';
import { errorMsg } from '@/utils/labels'; import { errorMsg } from '@/utils/labels';
@ -158,14 +154,9 @@ export function EditorRSExpression({
} }
return ( return (
<div className='cc-fade-in'> <div className='relative cc-fade-in'>
<ToolbarRSExpression disabled={disabled} showAST={handleShowAST} showTypeGraph={onShowTypeGraph} /> <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 <StatusBar
processing={isPending} processing={isPending}
isModified={isModified} isModified={isModified}
@ -173,8 +164,6 @@ export function EditorRSExpression({
parseData={parseData} parseData={parseData}
onAnalyze={() => handleCheckExpression()} onAnalyze={() => handleCheckExpression()}
/> />
<BadgeHelp topic={HelpTopic.UI_CST_STATUS} offset={4} />
</Overlay>
<RSInput <RSInput
ref={rsInput} ref={rsInput}

View File

@ -2,6 +2,10 @@
import clsx from 'clsx'; 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 { Loader } from '@/components/Loader';
import { APP_COLORS } from '@/styling/colors'; import { APP_COLORS } from '@/styling/colors';
import { globalIDs } from '@/utils/constants'; import { globalIDs } from '@/utils/constants';
@ -35,6 +39,11 @@ export function StatusBar({ isModified, processing, activeCst, parseData, onAnal
})(); })();
return ( return (
<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'
>
<div <div
tabIndex={0} tabIndex={0}
className={clsx( className={clsx(
@ -43,7 +52,7 @@ export function StatusBar({ isModified, processing, activeCst, parseData, onAnal
'border', 'border',
'select-none', 'select-none',
'cursor-pointer', 'cursor-pointer',
'focus-frame', 'focus-frame outline-none',
'transition-colors duration-500' 'transition-colors duration-500'
)} )}
style={{ backgroundColor: processing ? APP_COLORS.bgDefault : colorStatusBar(status) }} style={{ backgroundColor: processing ? APP_COLORS.bgDefault : colorStatusBar(status) }}
@ -53,7 +62,6 @@ export function StatusBar({ isModified, processing, activeCst, parseData, onAnal
> >
{processing ? ( {processing ? (
<div className='cc-fade-in'> <div className='cc-fade-in'>
{' '}
<Loader scale={3} /> <Loader scale={3} />
</div> </div>
) : null} ) : null}
@ -64,5 +72,7 @@ export function StatusBar({ isModified, processing, activeCst, parseData, onAnal
</div> </div>
) : null} ) : null}
</div> </div>
<BadgeHelp topic={HelpTopic.UI_CST_STATUS} offset={4} />
</Overlay>
); );
} }

View File

@ -35,16 +35,17 @@ export function EditorRSFormCard() {
} }
return ( return (
<>
<ToolbarRSFormCard onSubmit={initiateSubmit} schema={schema} isMutable={isMutable} deleteSchema={deleteSchema} />
<div <div
onKeyDown={handleInput} onKeyDown={handleInput}
className={clsx( className={clsx(
'relative',
'cc-fade-in', 'cc-fade-in',
'md:w-fit md:max-w-fit max-w-[32rem]', 'md:w-fit md:max-w-fit max-w-[32rem]',
'flex flex-row flex-wrap px-6 pt-[1.9rem]' 'flex flex-row flex-wrap px-6 pt-[1.9rem]'
)} )}
> >
<ToolbarRSFormCard onSubmit={initiateSubmit} schema={schema} isMutable={isMutable} deleteSchema={deleteSchema} />
<FlexColumn className='shrink'> <FlexColumn className='shrink'>
<FormRSForm /> <FormRSForm />
<EditorLibraryItem schema={schema} isAttachedToOSS={isAttachedToOSS} /> <EditorLibraryItem schema={schema} isAttachedToOSS={isAttachedToOSS} />
@ -52,6 +53,5 @@ export function EditorRSFormCard() {
<RSFormStats stats={schema.stats} isArchive={isArchive} /> <RSFormStats stats={schema.stats} isArchive={isArchive} />
</div> </div>
</>
); );
} }

View File

@ -91,7 +91,7 @@ export function FormRSForm() {
disabled={!isContentEditable} disabled={!isContentEditable}
error={errors.alias} error={errors.alias}
/> />
<div className='flex flex-col'> <div className='relative flex flex-col'>
<ToolbarVersioning blockReload={schema.oss.length > 0} /> <ToolbarVersioning blockReload={schema.oss.length > 0} />
<ToolbarItemAccess <ToolbarItemAccess
visible={visible} visible={visible}

View File

@ -4,7 +4,6 @@ import { useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import fileDownload from 'js-file-download'; import fileDownload from 'js-file-download';
import { Overlay } from '@/components/Container';
import { MiniButton } from '@/components/Control'; import { MiniButton } from '@/components/Control';
import { type RowSelectionState } from '@/components/DataTable'; import { type RowSelectionState } from '@/components/DataTable';
import { IconCSV } from '@/components/Icons'; import { IconCSV } from '@/components/Icons';
@ -127,9 +126,16 @@ export function EditorRSList() {
const tableHeight = useFitHeight('4.05rem + 5px'); const tableHeight = useFitHeight('4.05rem + 5px');
return ( return (
<> <div tabIndex={-1} onKeyDown={handleKeyDown} className='relative cc-fade-in pt-[1.9rem]'>
{isContentEditable ? <ToolbarRSList /> : null} {isContentEditable ? <ToolbarRSList /> : null}
<div tabIndex={-1} onKeyDown={handleKeyDown} className='cc-fade-in pt-[1.9rem]'>
<MiniButton
className='absolute z-tooltip top-[2.15rem] right-[1rem]'
title='Выгрузить в формате CSV'
icon={<IconCSV size='1.25rem' className='icon-green' />}
onClick={handleDownloadCSV}
/>
{isContentEditable ? ( {isContentEditable ? (
<div className='flex items-center border-b'> <div className='flex items-center border-b'>
<div className='px-2'> <div className='px-2'>
@ -145,14 +151,6 @@ export function EditorRSList() {
</div> </div>
) : null} ) : null}
<Overlay position='top-[0.25rem] right-[1rem]' layer='z-tooltip'>
<MiniButton
title='Выгрузить в формате CSV'
icon={<IconCSV size='1.25rem' className='icon-green' />}
onClick={handleDownloadCSV}
/>
</Overlay>
<TableRSList <TableRSList
items={filtered} items={filtered}
maxHeight={tableHeight} maxHeight={tableHeight}
@ -163,6 +161,5 @@ export function EditorRSList() {
onCreateNew={createCstDefault} onCreateNew={createCstDefault}
/> />
</div> </div>
</>
); );
} }

View File

@ -20,7 +20,7 @@ export function GraphSelectors() {
const setColoring = useTermGraphStore(state => state.setColoring); const setColoring = useTermGraphStore(state => state.setColoring);
return ( 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]'> <Overlay position='right-[2.5rem] top-[0.25rem]'>
{coloring === 'status' ? <BadgeHelp topic={HelpTopic.UI_CST_STATUS} className='min-w-[25rem]' /> : null} {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} {coloring === 'type' ? <BadgeHelp topic={HelpTopic.UI_CST_CLASS} className='min-w-[25rem]' /> : null}

View File

@ -40,7 +40,6 @@ export function SchemasGuide() {
<IconHelp size='1.25rem' className='icon-primary' /> <IconHelp size='1.25rem' className='icon-primary' />
<Tooltip <Tooltip
anchorSelect={`#${globalIDs.graph_schemas}`} anchorSelect={`#${globalIDs.graph_schemas}`}
layer='z-modal-tooltip'
place='right' place='right'
className='max-w-[25rem] break-words text-base' className='max-w-[25rem] break-words text-base'
> >

View File

@ -2,7 +2,6 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { Overlay } from '@/components/Container';
import { MiniButton } from '@/components/Control'; import { MiniButton } from '@/components/Control';
import { IconDropArrow, IconDropArrowUp } from '@/components/Icons'; import { IconDropArrow, IconDropArrowUp } from '@/components/Icons';
import { useWindowSize } from '@/hooks/useWindowSize'; import { useWindowSize } from '@/hooks/useWindowSize';
@ -47,16 +46,16 @@ export function ViewHidden({ items }: ViewHiddenProps) {
return null; return null;
} }
return ( return (
<div className='flex flex-col'> <div className='flex flex-col relative'>
<Overlay position='right-[calc(0.7rem-2px)] top-2 pointer-events-auto'>
<MiniButton <MiniButton
className='absolute right-[calc(0.7rem-2px)] top-2 pointer-events-auto'
noPadding noPadding
noHover noHover
title={!isFolded ? 'Свернуть' : 'Развернуть'} title={!isFolded ? 'Свернуть' : 'Развернуть'}
icon={!isFolded ? <IconDropArrowUp size='1.25rem' /> : <IconDropArrow size='1.25rem' />} icon={!isFolded ? <IconDropArrowUp size='1.25rem' /> : <IconDropArrow size='1.25rem' />}
onClick={toggleFolded} onClick={toggleFolded}
/> />
</Overlay>
<div className={clsx('pt-2 clr-input border-x pb-2', { 'border-b rounded-b-md': isFolded })}> <div className={clsx('pt-2 clr-input border-x pb-2', { 'border-b rounded-b-md': isFolded })}>
<div <div
className='w-fit select-none' className='w-fit select-none'

View File

@ -5,7 +5,6 @@ import clsx from 'clsx';
import { useConceptNavigation } from '@/app/Navigation/NavigationContext'; import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
import { Overlay } from '@/components/Container';
import { TabLabel, TabList, TabPanel, Tabs } from '@/components/Tabs'; import { TabLabel, TabList, TabPanel, Tabs } from '@/components/Tabs';
import { useAppLayoutStore } from '@/stores/appLayout'; import { useAppLayoutStore } from '@/stores/appLayout';
import { useModificationStore } from '@/stores/modification'; import { useModificationStore } from '@/stores/modification';
@ -72,17 +71,21 @@ export function RSTabs({ activeID, activeTab }: RSTabsProps) {
} }
return ( return (
<>
<Tabs <Tabs
selectedIndex={activeTab} selectedIndex={activeTab}
onSelect={onSelectTab} onSelect={onSelectTab}
defaultFocus defaultFocus
selectedTabClassName='clr-selected' 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 <TabList
className={clsx('mx-auto w-fit', 'flex items-stretch', 'border-b-2 border-x-2 divide-x-2', 'bg-prim-200')} 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'
)}
> >
<MenuRSTabs /> <MenuRSTabs />
@ -97,7 +100,6 @@ export function RSTabs({ activeID, activeTab }: RSTabsProps) {
<TabLabel label='Редактор' /> <TabLabel label='Редактор' />
<TabLabel label='Граф термов' /> <TabLabel label='Граф термов' />
</TabList> </TabList>
</Overlay>
<div className='overflow-x-hidden'> <div className='overflow-x-hidden'>
<TabPanel> <TabPanel>
@ -117,6 +119,5 @@ export function RSTabs({ activeID, activeTab }: RSTabsProps) {
</TabPanel> </TabPanel>
</div> </div>
</Tabs> </Tabs>
</>
); );
} }

View File

@ -9,7 +9,7 @@ import { urls, useConceptNavigation } from '@/app';
import { HelpTopic } from '@/features/help'; import { HelpTopic } from '@/features/help';
import { isAxiosError } from '@/backend/apiTransport'; 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 { Button, SubmitButton, TextURL } from '@/components/Control';
import { IconHelp } from '@/components/Icons'; import { IconHelp } from '@/components/Icons';
import { type ErrorData } from '@/components/InfoError'; import { type ErrorData } from '@/components/InfoError';
@ -60,15 +60,7 @@ export function FormSignup() {
onSubmit={event => void handleSubmit(onSubmit)(event)} onSubmit={event => void handleSubmit(onSubmit)(event)}
onChange={resetErrors} onChange={resetErrors}
> >
<h1> <h1>Новый пользователь</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>
<div className='flex gap-12'> <div className='flex gap-12'>
<FlexColumn> <FlexColumn>
<TextInput <TextInput
@ -102,7 +94,11 @@ export function FormSignup() {
/> />
</FlexColumn> </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 <TextInput
id='email' id='email'
{...register('email')} {...register('email')}

View File

@ -42,8 +42,6 @@
--z-index-tooltip: 30; --z-index-tooltip: 30;
--z-index-navigation: 50; --z-index-navigation: 50;
--z-index-modal: 60; --z-index-modal: 60;
--z-index-modal-controls: 70;
--z-index-modal-tooltip: 90;
--breakpoint-*: initial; --breakpoint-*: initial;
--breakpoint-xs: 475px; --breakpoint-xs: 475px;