UNPKG

@finos/legend-lego

Version:
126 lines 7.88 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; /** * Copyright (c) 2020-present, Goldman Sachs * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { ChevronDownIcon, clsx, ContextMenu, DragPreviewLayer, ControlledDropdownMenu, MenuContent, MenuContentItem, PanelEntryDropZonePlaceholder, TimesIcon, useDragPreviewLayer, MenuContentDivider, PushPinIcon, } from '@finos/legend-art'; import { observer } from 'mobx-react-lite'; import { useRef, useCallback } from 'react'; import { useDrop, useDrag } from 'react-dnd'; export const TAB_MANAGER__TAB_TEST_ID = 'tab-manager__tab'; const horizontalToVerticalScroll = (event) => { // if scrolling is more horizontal than vertical, there's nothing much to do, the OS should handle it just fine // else, intercept if (Math.abs(event.deltaY) <= Math.abs(event.deltaX)) { return; } event.stopPropagation(); let deltaX; // NOTE: only convert horizontal to vertical scroll when the scroll causes more horizontal than vertical displacement // let the direction of `deltaY` be the direction of the scroll, i.e. // - if we scroll upward, that translate to a left scroll // - if we scroll downward, that translates to a right scroll if (event.deltaY === 0) { deltaX = event.deltaY; } else if (event.deltaY < 0) { deltaX = -Math.abs(event.deltaY); } else { deltaX = Math.abs(event.deltaY); } event.currentTarget.scrollBy(deltaX, 0); }; const TabContextMenu = observer((props) => { const { tabState, managerTabState } = props; const close = () => managerTabState.closeTab(tabState); const closeOthers = () => managerTabState.closeAllOtherTabs(tabState); const closeAll = () => managerTabState.closeAllTabs(); const togglePin = () => { if (tabState.isPinned) { managerTabState.unpinTab(tabState); } else { managerTabState.pinTab(tabState); } }; return (_jsxs(MenuContent, { children: [_jsx(MenuContentItem, { onClick: close, children: "Close" }), _jsx(MenuContentItem, { disabled: managerTabState.tabs.length < 2, onClick: closeOthers, children: "Close Others" }), _jsx(MenuContentItem, { onClick: closeAll, children: "Close All" }), _jsx(MenuContentDivider, {}), _jsx(MenuContentItem, { onClick: togglePin, children: tabState.isPinned ? 'Unpin' : 'Pin' })] })); }); const Tab = observer((props) => { const ref = useRef(null); const { tabManagerState, tabState, tabRenderer } = props; // Drag and Drop const handleHover = useCallback((item, monitor) => { const draggingTab = item.tab; const hoveredTab = tabState; const dragIndex = tabManagerState.tabs.findIndex((e) => e === draggingTab); const hoverIndex = tabManagerState.tabs.findIndex((e) => e === hoveredTab); const hoverBoundingReact = ref.current?.getBoundingClientRect(); const distanceThreshold = ((hoverBoundingReact?.left ?? 0) - (hoverBoundingReact?.right ?? 0)) / 2; const dragDistance = (monitor.getClientOffset()?.x ?? 0) - (hoverBoundingReact?.right ?? 0); if (dragIndex < hoverIndex && dragDistance < distanceThreshold) { return; } if (dragIndex > hoverIndex && dragDistance > distanceThreshold) { return; } tabManagerState.swapTabs(draggingTab, hoveredTab); }, [tabManagerState, tabState]); const closeTabOnMiddleClick = (currTab) => (event) => { if (event.nativeEvent.button === 1) { tabManagerState.closeTab(currTab); } }; const [{ isBeingDraggedEditorPanel }, dropConnector] = useDrop(() => ({ accept: [tabManagerState.dndType], hover: (item, monitor) => handleHover(item, monitor), collect: (monitor) => ({ isBeingDraggedEditorPanel: monitor.getItem() ?.tab, }), }), [handleHover]); const isBeingDragged = tabState === isBeingDraggedEditorPanel; const [, dragConnector, dragPreviewConnector] = useDrag(() => ({ type: tabManagerState.dndType, item: () => ({ tab: tabState, }), }), [tabState, tabManagerState]); dragConnector(dropConnector(ref)); useDragPreviewLayer(dragPreviewConnector); return (_jsx("div", { ref: ref, "data-testid": TAB_MANAGER__TAB_TEST_ID, className: clsx('tab-manager__tab', { 'tab-manager__tab--active': tabState === tabManagerState.currentTab, 'tab-manager__tab--dragged': isBeingDragged, }), onMouseUp: closeTabOnMiddleClick(tabState), children: _jsx(PanelEntryDropZonePlaceholder, { isDragOver: false, className: "tab-manager__tab__dnd__placeholder", children: _jsxs(ContextMenu, { content: _jsx(TabContextMenu, { tabState: tabState, managerTabState: tabManagerState }), className: "tab-manager__tab__content", children: [_jsx("button", { className: "tab-manager__tab__label", tabIndex: -1, onClick: () => tabManagerState.openTab(tabState), title: tabState.description, children: tabRenderer?.(tabState) ?? tabState.label }), tabState.isPinned && (_jsx("button", { className: "tab-manager__tab__pin-btn", onClick: () => tabManagerState.unpinTab(tabState), tabIndex: -1, title: "Unpin", children: _jsx(PushPinIcon, {}) })), !tabState.isPinned && (_jsx("button", { className: "tab-manager__tab__close-btn", onClick: () => tabManagerState.closeTab(tabState), tabIndex: -1, title: "Close", children: _jsx(TimesIcon, {}) }))] }) }) })); }); const TabMenu = observer((props) => { const { managerTabState } = props; return (_jsx(ControlledDropdownMenu, { className: "tab-manager__menu__toggler", title: "Show All Tabs", content: _jsx(MenuContent, { className: "tab-manager__menu", children: managerTabState.tabs.map((tabState) => (_jsxs(MenuContentItem, { className: clsx('tab-manager__menu__item', { 'tab-manager__menu__item--active': tabState === managerTabState.currentTab, }), onClick: () => managerTabState.openTab(tabState), children: [_jsx("div", { className: "tab-manager__menu__item__label", children: tabState.label }), _jsx("div", { className: "tab-manager__menu__item__close-btn", onClick: (event) => { // NOTE: prevent default action of dropdown menu event.stopPropagation(); managerTabState.closeTab(tabState); }, tabIndex: -1, title: "Close", children: _jsx(TimesIcon, {}) })] }, tabState.uuid))) }), menuProps: { anchorOrigin: { vertical: 'bottom', horizontal: 'right' }, transformOrigin: { vertical: 'top', horizontal: 'right' }, }, children: _jsx(ChevronDownIcon, {}) })); }); export const TabManager = observer((props) => { const { tabManagerState, tabRenderer } = props; return (_jsxs("div", { className: "tab-manager", children: [_jsxs("div", { className: "tab-manager__content", onWheel: horizontalToVerticalScroll, children: [tabManagerState.tabs.map((tab) => (_jsx(Tab, { tabState: tab, tabManagerState: tabManagerState, tabRenderer: tabRenderer }, tab.uuid))), _jsx(DragPreviewLayer, { labelGetter: (item) => item.tab.label, types: [tabManagerState.dndType] })] }), _jsx(TabMenu, { managerTabState: tabManagerState })] })); }); //# sourceMappingURL=TabManager.js.map