mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50:36 +03:00
F: Replace clickOutside with onBlur trigger for dropdowns
This commit is contained in:
parent
ac4fd37b65
commit
25663322e3
|
@ -2,18 +2,21 @@
|
||||||
|
|
||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
|
|
||||||
import { useClickedOutside } from '@/hooks/use-clicked-outside';
|
|
||||||
|
|
||||||
export function useDropdown() {
|
export function useDropdown() {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const ref = useRef(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useClickedOutside(isOpen, ref, () => setIsOpen(false));
|
function handleBlur(event: React.FocusEvent<HTMLDivElement>) {
|
||||||
|
if (!ref.current?.contains(event.relatedTarget as Node)) {
|
||||||
|
setIsOpen(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ref,
|
ref,
|
||||||
isOpen,
|
isOpen,
|
||||||
setIsOpen,
|
setIsOpen,
|
||||||
|
handleBlur,
|
||||||
toggle: () => setIsOpen(!isOpen),
|
toggle: () => setIsOpen(!isOpen),
|
||||||
hide: () => setIsOpen(false)
|
hide: () => setIsOpen(false)
|
||||||
};
|
};
|
||||||
|
|
|
@ -30,6 +30,7 @@ export function TopicsDropdown({ activeTopic, onChangeTopic }: TopicsDropdownPro
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={menu.ref}
|
ref={menu.ref}
|
||||||
|
onBlur={menu.handleBlur}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'absolute left-0 w-54', //
|
'absolute left-0 w-54', //
|
||||||
noNavigation ? 'top-0' : 'top-12',
|
noNavigation ? 'top-0' : 'top-12',
|
||||||
|
|
|
@ -101,7 +101,7 @@ export function EditorLibraryItem({ schema, isAttachedToOSS }: EditorLibraryItem
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='relative'>
|
<div className='relative' ref={ownerSelector.ref} onBlur={ownerSelector.handleBlur}>
|
||||||
{ownerSelector.isOpen ? (
|
{ownerSelector.isOpen ? (
|
||||||
<div className='absolute -top-2 right-0'>
|
<div className='absolute -top-2 right-0'>
|
||||||
<SelectUser className='w-100 text-sm' value={schema.owner} onChange={onSelectUser} />
|
<SelectUser className='w-100 text-sm' value={schema.owner} onChange={onSelectUser} />
|
||||||
|
|
|
@ -44,7 +44,7 @@ export function MenuRole({ isOwned, isEditor }: MenuRoleProps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={accessMenu.ref} className='relative'>
|
<div ref={accessMenu.ref} onBlur={accessMenu.handleBlur} className='relative'>
|
||||||
<Button
|
<Button
|
||||||
dense
|
dense
|
||||||
noBorder
|
noBorder
|
||||||
|
|
|
@ -28,7 +28,12 @@ export function MiniSelectorOSS({ items, onSelect, className, ...restProps }: Mi
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={ossMenu.ref} className={clsx('relative flex items-center', className)} {...restProps}>
|
<div
|
||||||
|
ref={ossMenu.ref}
|
||||||
|
onBlur={ossMenu.handleBlur}
|
||||||
|
className={clsx('relative flex items-center', className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Операционные схемы'
|
title='Операционные схемы'
|
||||||
icon={<IconOSS size='1.25rem' className='icon-primary' />}
|
icon={<IconOSS size='1.25rem' className='icon-primary' />}
|
||||||
|
|
|
@ -35,7 +35,12 @@ export function SelectLocationContext({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={menu.ref} className={clsx('relative text-right self-start', className)} {...restProps}>
|
<div
|
||||||
|
ref={menu.ref} //
|
||||||
|
onBlur={menu.handleBlur}
|
||||||
|
className={clsx('relative text-right self-start', className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title={title}
|
title={title}
|
||||||
hideTitle={menu.isOpen}
|
hideTitle={menu.isOpen}
|
||||||
|
|
|
@ -112,7 +112,7 @@ export function PickSchema({
|
||||||
query={filterText}
|
query={filterText}
|
||||||
onChangeQuery={newValue => setFilterText(newValue)}
|
onChangeQuery={newValue => setFilterText(newValue)}
|
||||||
/>
|
/>
|
||||||
<div className='relative' ref={locationMenu.ref}>
|
<div className='relative' ref={locationMenu.ref} onBlur={locationMenu.handleBlur}>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Фильтр по расположению'
|
title='Фильтр по расположению'
|
||||||
icon={<IconFolderTree size='1.25rem' className={!!filterLocation ? 'icon-green' : 'icon-primary'} />}
|
icon={<IconFolderTree size='1.25rem' className={!!filterLocation ? 'icon-green' : 'icon-primary'} />}
|
||||||
|
|
|
@ -38,7 +38,7 @@ export function SelectAccessPolicy({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={menu.ref} className={clsx('relative', className)} {...restProps}>
|
<div ref={menu.ref} onBlur={menu.handleBlur} className={clsx('relative', className)} {...restProps}>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title={`Доступ: ${labelAccessPolicy(value)}`}
|
title={`Доступ: ${labelAccessPolicy(value)}`}
|
||||||
hideTitle={menu.isOpen}
|
hideTitle={menu.isOpen}
|
||||||
|
|
|
@ -37,7 +37,7 @@ export function SelectItemType({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={menu.ref} className={clsx('relative', className)} {...restProps}>
|
<div ref={menu.ref} onBlur={menu.handleBlur} className={clsx('relative', className)} {...restProps}>
|
||||||
<SelectorButton
|
<SelectorButton
|
||||||
transparent
|
transparent
|
||||||
title={describeLibraryItemType(value)}
|
title={describeLibraryItemType(value)}
|
||||||
|
|
|
@ -33,7 +33,12 @@ export function SelectLocationHead({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={menu.ref} className={clsx('text-right relative', className)} {...restProps}>
|
<div
|
||||||
|
ref={menu.ref} //
|
||||||
|
onBlur={menu.handleBlur}
|
||||||
|
className={clsx('text-right relative', className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
<SelectorButton
|
<SelectorButton
|
||||||
transparent
|
transparent
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
|
|
|
@ -32,8 +32,8 @@ interface ToolbarSearchProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps) {
|
export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps) {
|
||||||
const headMenu = useDropdown();
|
|
||||||
const userMenu = useDropdown();
|
const userMenu = useDropdown();
|
||||||
|
const headMenu = useDropdown();
|
||||||
|
|
||||||
const query = useLibrarySearchStore(state => state.query);
|
const query = useLibrarySearchStore(state => state.query);
|
||||||
const setQuery = useLibrarySearchStore(state => state.setQuery);
|
const setQuery = useLibrarySearchStore(state => state.setQuery);
|
||||||
|
@ -88,7 +88,7 @@ export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps
|
||||||
onClick={toggleVisible}
|
onClick={toggleVisible}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div ref={userMenu.ref} className='relative flex'>
|
<div ref={userMenu.ref} onBlur={userMenu.handleBlur} className='relative flex'>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Поиск пользователя'
|
title='Поиск пользователя'
|
||||||
hideTitle={userMenu.isOpen}
|
hideTitle={userMenu.isOpen}
|
||||||
|
@ -137,7 +137,11 @@ export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps
|
||||||
onChangeQuery={setQuery}
|
onChangeQuery={setQuery}
|
||||||
/>
|
/>
|
||||||
{!folderMode ? (
|
{!folderMode ? (
|
||||||
<div ref={headMenu.ref} className='relative flex items-center h-full select-none'>
|
<div
|
||||||
|
ref={headMenu.ref}
|
||||||
|
onBlur={headMenu.handleBlur}
|
||||||
|
className='relative flex items-center h-full select-none'
|
||||||
|
>
|
||||||
<SelectorButton
|
<SelectorButton
|
||||||
transparent
|
transparent
|
||||||
className='rounded-lg py-1'
|
className='rounded-lg py-1'
|
||||||
|
|
|
@ -18,7 +18,6 @@ import {
|
||||||
IconNewRSForm,
|
IconNewRSForm,
|
||||||
IconRSForm
|
IconRSForm
|
||||||
} from '@/components/icons';
|
} from '@/components/icons';
|
||||||
import { useClickedOutside } from '@/hooks/use-clicked-outside';
|
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { errorMsg } from '@/utils/labels';
|
import { errorMsg } from '@/utils/labels';
|
||||||
import { prepareTooltip } from '@/utils/utils';
|
import { prepareTooltip } from '@/utils/utils';
|
||||||
|
@ -82,7 +81,12 @@ export function NodeContextMenu({ isOpen, operation, cursorX, cursorY, onHide }:
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
useClickedOutside(isOpen, ref, onHide);
|
|
||||||
|
function handleBlur(event: React.FocusEvent<HTMLDivElement>) {
|
||||||
|
if (!ref.current?.contains(event.relatedTarget as Node)) {
|
||||||
|
onHide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleOpenSchema() {
|
function handleOpenSchema() {
|
||||||
if (!operation) {
|
if (!operation) {
|
||||||
|
@ -167,7 +171,12 @@ export function NodeContextMenu({ isOpen, operation, cursorX, cursorY, onHide }:
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={ref} className='relative' style={{ top: `calc(${cursorY}px - 2.5rem)`, left: cursorX }}>
|
<div
|
||||||
|
ref={ref}
|
||||||
|
onBlur={handleBlur}
|
||||||
|
className='relative'
|
||||||
|
style={{ top: `calc(${cursorY}px - 2.5rem)`, left: cursorX }}
|
||||||
|
>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
stretchLeft={cursorX >= window.innerWidth - MENU_WIDTH}
|
stretchLeft={cursorX >= window.innerWidth - MENU_WIDTH}
|
||||||
|
|
|
@ -11,14 +11,14 @@ import { useOssEdit } from './oss-edit-context';
|
||||||
|
|
||||||
export function MenuEditOss() {
|
export function MenuEditOss() {
|
||||||
const { isAnonymous } = useAuthSuspense();
|
const { isAnonymous } = useAuthSuspense();
|
||||||
const editMenu = useDropdown();
|
const menu = useDropdown();
|
||||||
const { schema, isMutable } = useOssEdit();
|
const { schema, isMutable } = useOssEdit();
|
||||||
const isProcessing = useMutatingOss();
|
const isProcessing = useMutatingOss();
|
||||||
|
|
||||||
const showRelocateConstituents = useDialogsStore(state => state.showRelocateConstituents);
|
const showRelocateConstituents = useDialogsStore(state => state.showRelocateConstituents);
|
||||||
|
|
||||||
function handleRelocate() {
|
function handleRelocate() {
|
||||||
editMenu.hide();
|
menu.hide();
|
||||||
showRelocateConstituents({
|
showRelocateConstituents({
|
||||||
oss: schema,
|
oss: schema,
|
||||||
initialTarget: undefined,
|
initialTarget: undefined,
|
||||||
|
@ -31,19 +31,19 @@ export function MenuEditOss() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={editMenu.ref} className='relative'>
|
<div ref={menu.ref} onBlur={menu.handleBlur} className='relative'>
|
||||||
<Button
|
<Button
|
||||||
dense
|
dense
|
||||||
noBorder
|
noBorder
|
||||||
noOutline
|
noOutline
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
title='Редактирование'
|
title='Редактирование'
|
||||||
hideTitle={editMenu.isOpen}
|
hideTitle={menu.isOpen}
|
||||||
className='h-full px-2'
|
className='h-full px-2'
|
||||||
icon={<IconEdit2 size='1.25rem' className={isMutable ? 'icon-green' : 'icon-red'} />}
|
icon={<IconEdit2 size='1.25rem' className={isMutable ? 'icon-green' : 'icon-red'} />}
|
||||||
onClick={editMenu.toggle}
|
onClick={menu.toggle}
|
||||||
/>
|
/>
|
||||||
<Dropdown isOpen={editMenu.isOpen} margin='mt-3'>
|
<Dropdown isOpen={menu.isOpen} margin='mt-3'>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
text='Конституенты'
|
text='Конституенты'
|
||||||
titleHtml='Перенос конституент</br>между схемами'
|
titleHtml='Перенос конституент</br>между схемами'
|
||||||
|
|
|
@ -24,15 +24,15 @@ export function MenuMain() {
|
||||||
|
|
||||||
const showQR = useDialogsStore(state => state.showQR);
|
const showQR = useDialogsStore(state => state.showQR);
|
||||||
|
|
||||||
const schemaMenu = useDropdown();
|
const menu = useDropdown();
|
||||||
|
|
||||||
function handleDelete() {
|
function handleDelete() {
|
||||||
schemaMenu.hide();
|
menu.hide();
|
||||||
deleteSchema();
|
deleteSchema();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleShare() {
|
function handleShare() {
|
||||||
schemaMenu.hide();
|
menu.hide();
|
||||||
sharePage();
|
sharePage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,24 +41,24 @@ export function MenuMain() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleShowQR() {
|
function handleShowQR() {
|
||||||
schemaMenu.hide();
|
menu.hide();
|
||||||
showQR({ target: generatePageQR() });
|
showQR({ target: generatePageQR() });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={schemaMenu.ref} className='relative'>
|
<div ref={menu.ref} onBlur={menu.handleBlur} className='relative'>
|
||||||
<Button
|
<Button
|
||||||
dense
|
dense
|
||||||
noBorder
|
noBorder
|
||||||
noOutline
|
noOutline
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
title='Меню'
|
title='Меню'
|
||||||
hideTitle={schemaMenu.isOpen}
|
hideTitle={menu.isOpen}
|
||||||
icon={<IconMenu size='1.25rem' className='clr-text-controls' />}
|
icon={<IconMenu size='1.25rem' className='clr-text-controls' />}
|
||||||
className='h-full pl-2'
|
className='h-full pl-2'
|
||||||
onClick={schemaMenu.toggle}
|
onClick={menu.toggle}
|
||||||
/>
|
/>
|
||||||
<Dropdown isOpen={schemaMenu.isOpen} margin='mt-3'>
|
<Dropdown isOpen={menu.isOpen} margin='mt-3'>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
text='Поделиться'
|
text='Поделиться'
|
||||||
title='Скопировать ссылку в буфер обмена'
|
title='Скопировать ссылку в буфер обмена'
|
||||||
|
|
|
@ -30,7 +30,7 @@ interface ToolbarRSListProps {
|
||||||
|
|
||||||
export function ToolbarRSList({ className }: ToolbarRSListProps) {
|
export function ToolbarRSList({ className }: ToolbarRSListProps) {
|
||||||
const isProcessing = useMutatingRSForm();
|
const isProcessing = useMutatingRSForm();
|
||||||
const insertMenu = useDropdown();
|
const menu = useDropdown();
|
||||||
const {
|
const {
|
||||||
schema,
|
schema,
|
||||||
selected,
|
selected,
|
||||||
|
@ -74,15 +74,15 @@ export function ToolbarRSList({ className }: ToolbarRSListProps) {
|
||||||
onClick={moveDown}
|
onClick={moveDown}
|
||||||
disabled={isProcessing || selected.length === 0 || selected.length === schema.items.length}
|
disabled={isProcessing || selected.length === 0 || selected.length === schema.items.length}
|
||||||
/>
|
/>
|
||||||
<div ref={insertMenu.ref} className='relative'>
|
<div ref={menu.ref} onBlur={menu.handleBlur} className='relative'>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Добавить пустую конституенту'
|
title='Добавить пустую конституенту'
|
||||||
hideTitle={insertMenu.isOpen}
|
hideTitle={menu.isOpen}
|
||||||
icon={<IconOpenList size='1.25rem' className='icon-green' />}
|
icon={<IconOpenList size='1.25rem' className='icon-green' />}
|
||||||
onClick={insertMenu.toggle}
|
onClick={menu.toggle}
|
||||||
disabled={isProcessing}
|
disabled={isProcessing}
|
||||||
/>
|
/>
|
||||||
<Dropdown isOpen={insertMenu.isOpen} className='-translate-x-1/2'>
|
<Dropdown isOpen={menu.isOpen} className='-translate-x-1/2'>
|
||||||
{Object.values(CstType).map(typeStr => (
|
{Object.values(CstType).map(typeStr => (
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
key={`${prefixes.csttype_list}${typeStr}`}
|
key={`${prefixes.csttype_list}${typeStr}`}
|
||||||
|
|
|
@ -31,7 +31,7 @@ export function MenuEditSchema() {
|
||||||
const { isAnonymous } = useAuthSuspense();
|
const { isAnonymous } = useAuthSuspense();
|
||||||
const { isModified } = useModificationStore();
|
const { isModified } = useModificationStore();
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const editMenu = useDropdown();
|
const menu = useDropdown();
|
||||||
const { schema, activeCst, setSelected, isArchive, isContentEditable, promptTemplate, deselectAll } = useRSEdit();
|
const { schema, activeCst, setSelected, isArchive, isContentEditable, promptTemplate, deselectAll } = useRSEdit();
|
||||||
const isProcessing = useMutatingRSForm();
|
const isProcessing = useMutatingRSForm();
|
||||||
|
|
||||||
|
@ -43,17 +43,17 @@ export function MenuEditSchema() {
|
||||||
const showSubstituteCst = useDialogsStore(state => state.showSubstituteCst);
|
const showSubstituteCst = useDialogsStore(state => state.showSubstituteCst);
|
||||||
|
|
||||||
function handleReindex() {
|
function handleReindex() {
|
||||||
editMenu.hide();
|
menu.hide();
|
||||||
void resetAliases({ itemID: schema.id });
|
void resetAliases({ itemID: schema.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRestoreOrder() {
|
function handleRestoreOrder() {
|
||||||
editMenu.hide();
|
menu.hide();
|
||||||
void restoreOrder({ itemID: schema.id });
|
void restoreOrder({ itemID: schema.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSubstituteCst() {
|
function handleSubstituteCst() {
|
||||||
editMenu.hide();
|
menu.hide();
|
||||||
if (isModified && !promptUnsaved()) {
|
if (isModified && !promptUnsaved()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -64,12 +64,12 @@ export function MenuEditSchema() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTemplates() {
|
function handleTemplates() {
|
||||||
editMenu.hide();
|
menu.hide();
|
||||||
promptTemplate();
|
promptTemplate();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleProduceStructure(targetCst: IConstituenta | null) {
|
function handleProduceStructure(targetCst: IConstituenta | null) {
|
||||||
editMenu.hide();
|
menu.hide();
|
||||||
if (!targetCst) {
|
if (!targetCst) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ export function MenuEditSchema() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleInlineSynthesis() {
|
function handleInlineSynthesis() {
|
||||||
editMenu.hide();
|
menu.hide();
|
||||||
if (isModified && !promptUnsaved()) {
|
if (isModified && !promptUnsaved()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ export function MenuEditSchema() {
|
||||||
noOutline
|
noOutline
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
titleHtml='<b>Архив</b>: Редактирование запрещено<br />Перейти к актуальной версии'
|
titleHtml='<b>Архив</b>: Редактирование запрещено<br />Перейти к актуальной версии'
|
||||||
hideTitle={editMenu.isOpen}
|
hideTitle={menu.isOpen}
|
||||||
className='h-full px-2'
|
className='h-full px-2'
|
||||||
icon={<IconArchive size='1.25rem' className='icon-primary' />}
|
icon={<IconArchive size='1.25rem' className='icon-primary' />}
|
||||||
onClick={event => router.push({ path: urls.schema(schema.id), newTab: event.ctrlKey || event.metaKey })}
|
onClick={event => router.push({ path: urls.schema(schema.id), newTab: event.ctrlKey || event.metaKey })}
|
||||||
|
@ -118,19 +118,19 @@ export function MenuEditSchema() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={editMenu.ref} className='relative'>
|
<div ref={menu.ref} onBlur={menu.handleBlur} className='relative'>
|
||||||
<Button
|
<Button
|
||||||
dense
|
dense
|
||||||
noBorder
|
noBorder
|
||||||
noOutline
|
noOutline
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
title='Редактирование'
|
title='Редактирование'
|
||||||
hideTitle={editMenu.isOpen}
|
hideTitle={menu.isOpen}
|
||||||
className='h-full px-2'
|
className='h-full px-2'
|
||||||
icon={<IconEdit2 size='1.25rem' className={isContentEditable ? 'icon-green' : 'icon-red'} />}
|
icon={<IconEdit2 size='1.25rem' className={isContentEditable ? 'icon-green' : 'icon-red'} />}
|
||||||
onClick={editMenu.toggle}
|
onClick={menu.toggle}
|
||||||
/>
|
/>
|
||||||
<Dropdown isOpen={editMenu.isOpen} margin='mt-3'>
|
<Dropdown isOpen={menu.isOpen} margin='mt-3'>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
text='Шаблоны'
|
text='Шаблоны'
|
||||||
title='Создать конституенту из шаблона'
|
title='Создать конституенту из шаблона'
|
||||||
|
|
|
@ -47,7 +47,7 @@ export function MenuMain() {
|
||||||
const showClone = useDialogsStore(state => state.showCloneLibraryItem);
|
const showClone = useDialogsStore(state => state.showCloneLibraryItem);
|
||||||
const showUpload = useDialogsStore(state => state.showUploadRSForm);
|
const showUpload = useDialogsStore(state => state.showUploadRSForm);
|
||||||
|
|
||||||
const schemaMenu = useDropdown();
|
const menu = useDropdown();
|
||||||
|
|
||||||
function calculateCloneLocation() {
|
function calculateCloneLocation() {
|
||||||
const location = schema.location;
|
const location = schema.location;
|
||||||
|
@ -62,12 +62,12 @@ export function MenuMain() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDelete() {
|
function handleDelete() {
|
||||||
schemaMenu.hide();
|
menu.hide();
|
||||||
deleteSchema();
|
deleteSchema();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDownload() {
|
function handleDownload() {
|
||||||
schemaMenu.hide();
|
menu.hide();
|
||||||
if (isModified && !promptUnsaved()) {
|
if (isModified && !promptUnsaved()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -85,12 +85,12 @@ export function MenuMain() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleUpload() {
|
function handleUpload() {
|
||||||
schemaMenu.hide();
|
menu.hide();
|
||||||
showUpload({ itemID: schema.id });
|
showUpload({ itemID: schema.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleClone() {
|
function handleClone() {
|
||||||
schemaMenu.hide();
|
menu.hide();
|
||||||
if (isModified && !promptUnsaved()) {
|
if (isModified && !promptUnsaved()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -103,29 +103,29 @@ export function MenuMain() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleShare() {
|
function handleShare() {
|
||||||
schemaMenu.hide();
|
menu.hide();
|
||||||
sharePage();
|
sharePage();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleShowQR() {
|
function handleShowQR() {
|
||||||
schemaMenu.hide();
|
menu.hide();
|
||||||
showQR({ target: generatePageQR() });
|
showQR({ target: generatePageQR() });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={schemaMenu.ref} className='relative'>
|
<div ref={menu.ref} onBlur={menu.handleBlur} className='relative'>
|
||||||
<Button
|
<Button
|
||||||
dense
|
dense
|
||||||
noBorder
|
noBorder
|
||||||
noOutline
|
noOutline
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
title='Меню'
|
title='Меню'
|
||||||
hideTitle={schemaMenu.isOpen}
|
hideTitle={menu.isOpen}
|
||||||
icon={<IconMenu size='1.25rem' className='clr-text-controls' />}
|
icon={<IconMenu size='1.25rem' className='clr-text-controls' />}
|
||||||
className='h-full pl-2'
|
className='h-full pl-2'
|
||||||
onClick={schemaMenu.toggle}
|
onClick={menu.toggle}
|
||||||
/>
|
/>
|
||||||
<Dropdown isOpen={schemaMenu.isOpen} margin='mt-3'>
|
<Dropdown isOpen={menu.isOpen} margin='mt-3'>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
text='Поделиться'
|
text='Поделиться'
|
||||||
titleHtml={tooltipText.shareItem(schema.access_policy === AccessPolicy.PUBLIC)}
|
titleHtml={tooltipText.shareItem(schema.access_policy === AccessPolicy.PUBLIC)}
|
||||||
|
|
|
@ -28,7 +28,7 @@ export function SelectGraphFilter({ value, dense, className, onChange, ...restPr
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={menu.ref} className={clsx('relative', className)} {...restProps}>
|
<div ref={menu.ref} onBlur={menu.handleBlur} className={clsx('relative', className)} {...restProps}>
|
||||||
<SelectorButton
|
<SelectorButton
|
||||||
transparent
|
transparent
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
|
|
|
@ -28,7 +28,7 @@ export function SelectMatchMode({ value, dense, className, onChange, ...restProp
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={menu.ref} className={clsx('relative', className)} {...restProps}>
|
<div ref={menu.ref} onBlur={menu.handleBlur} className={clsx('relative', className)} {...restProps}>
|
||||||
<SelectorButton
|
<SelectorButton
|
||||||
transparent
|
transparent
|
||||||
titleHtml='Настройка фильтрации <br/>по проверяемым атрибутам'
|
titleHtml='Настройка фильтрации <br/>по проверяемым атрибутам'
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
|
|
||||||
import { assertIsNode } from '@/utils/utils';
|
|
||||||
|
|
||||||
export function useClickedOutside(
|
|
||||||
enabled: boolean,
|
|
||||||
ref: React.RefObject<HTMLDivElement | null>,
|
|
||||||
callback?: () => void
|
|
||||||
) {
|
|
||||||
useEffect(() => {
|
|
||||||
if (!enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleClickOutside(event: MouseEvent) {
|
|
||||||
assertIsNode(event.target);
|
|
||||||
if (ref?.current && !ref.current.contains(event.target)) {
|
|
||||||
callback?.();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.addEventListener('mouseup', handleClickOutside);
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener('mouseup', handleClickOutside);
|
|
||||||
};
|
|
||||||
}, [ref, callback, enabled]);
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user