diff --git a/package-lock.json b/package-lock.json index a6853d4a16..d5b6928a2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30636,9 +30636,11 @@ "@deephaven/react-hooks": "file:../react-hooks", "@deephaven/redux": "file:../redux", "@deephaven/utils": "file:../utils", + "classnames": "^2.3.1", "fast-deep-equal": "^3.1.3", "lodash.ismatch": "^4.1.1", "lodash.throttle": "^4.1.1", + "memoize-one": "^5.1.1", "nanoid": "^5.0.7", "prop-types": "^15.7.2" }, @@ -33550,9 +33552,11 @@ "@deephaven/test-utils": "file:../test-utils", "@deephaven/utils": "file:../utils", "@types/lodash.ismatch": "^4.4.0", + "classnames": "^2.3.1", "fast-deep-equal": "^3.1.3", "lodash.ismatch": "^4.1.1", "lodash.throttle": "^4.1.1", + "memoize-one": "^5.1.1", "nanoid": "^5.0.7", "prop-types": "^15.7.2" }, diff --git a/packages/dashboard-core-plugins/src/events/TabEventMap.ts b/packages/dashboard-core-plugins/src/events/TabEventMap.ts index b3fcc94bfc..b3cc01aa1a 100644 --- a/packages/dashboard-core-plugins/src/events/TabEventMap.ts +++ b/packages/dashboard-core-plugins/src/events/TabEventMap.ts @@ -1,5 +1,5 @@ import { type ValueOf } from '@deephaven/utils'; -import type TabEvent from './TabEvent'; +import { type TabEvent } from '@deephaven/dashboard'; export type TabEventType = ValueOf; diff --git a/packages/dashboard-core-plugins/src/events/index.ts b/packages/dashboard-core-plugins/src/events/index.ts index 27ea0c53a7..3e928ba5cd 100644 --- a/packages/dashboard-core-plugins/src/events/index.ts +++ b/packages/dashboard-core-plugins/src/events/index.ts @@ -5,4 +5,6 @@ export { default as IrisGridEvent } from './IrisGridEvent'; export { default as MarkdownEvent } from './MarkdownEvent'; export { default as NotebookEvent } from './NotebookEvent'; export { default as PandasEvent } from './PandasEvent'; -export { default as TabEvent } from './TabEvent'; + +// Deprecated - use TabEvent from @deephaven/dashboard +export { type TabEvent } from '@deephaven/dashboard'; diff --git a/packages/dashboard-core-plugins/src/panels/CommandHistoryPanel.tsx b/packages/dashboard-core-plugins/src/panels/CommandHistoryPanel.tsx index c989196937..9c8d6a6f4c 100644 --- a/packages/dashboard-core-plugins/src/panels/CommandHistoryPanel.tsx +++ b/packages/dashboard-core-plugins/src/panels/CommandHistoryPanel.tsx @@ -15,7 +15,7 @@ import { assertNotNull, Pending } from '@deephaven/utils'; import type { dh } from '@deephaven/jsapi-types'; import { ConsoleEvent, NotebookEvent } from '../events'; import './CommandHistoryPanel.scss'; -import Panel from './Panel'; +import Panel from './CorePanel'; import { getDashboardSessionWrapper } from '../redux'; const log = Log.module('CommandHistoryPanel'); diff --git a/packages/dashboard-core-plugins/src/panels/ConsolePanel.tsx b/packages/dashboard-core-plugins/src/panels/ConsolePanel.tsx index 39d6599793..2d793734da 100644 --- a/packages/dashboard-core-plugins/src/panels/ConsolePanel.tsx +++ b/packages/dashboard-core-plugins/src/panels/ConsolePanel.tsx @@ -38,7 +38,7 @@ import { } from '@deephaven/plugin'; import type { JSZipObject } from 'jszip'; import { ConsoleEvent } from '../events'; -import Panel from './Panel'; +import Panel from './CorePanel'; import { getDashboardSessionWrapper } from '../redux'; import './ConsolePanel.scss'; diff --git a/packages/dashboard-core-plugins/src/panels/CorePanel.tsx b/packages/dashboard-core-plugins/src/panels/CorePanel.tsx new file mode 100644 index 0000000000..e54a6edb01 --- /dev/null +++ b/packages/dashboard-core-plugins/src/panels/CorePanel.tsx @@ -0,0 +1,80 @@ +import React, { PureComponent, type ReactElement } from 'react'; +import { createXComponent } from '@deephaven/components'; +import { type BasePanelProps, Panel } from '@deephaven/dashboard'; +import type { dh } from '@deephaven/jsapi-types'; +import { ConsoleEvent, InputFilterEvent } from '../events'; + +export type CorePanelProps = BasePanelProps & { + onClearAllFilters?: (...args: unknown[]) => void; + onSessionClose?: (session: dh.IdeSession) => void; + onSessionOpen?: ( + session: dh.IdeSession, + { language, sessionId }: { language: string; sessionId: string } + ) => void; +}; + +/** + * Generic panel component that emits mount/unmount/focus events. + * Also wires up some triggers for common events: + * Focus, Resize, Show, Session open/close, client disconnect/reconnect. + */ +class CorePanel extends PureComponent { + constructor(props: CorePanelProps) { + super(props); + + this.handleClearAllFilters = this.handleClearAllFilters.bind(this); + this.handleSessionClosed = this.handleSessionClosed.bind(this); + this.handleSessionOpened = this.handleSessionOpened.bind(this); + } + + componentDidMount(): void { + const { glEventHub } = this.props; + + glEventHub.on(ConsoleEvent.SESSION_CLOSED, this.handleSessionClosed); + glEventHub.on(ConsoleEvent.SESSION_OPENED, this.handleSessionOpened); + glEventHub.on( + InputFilterEvent.CLEAR_ALL_FILTERS, + this.handleClearAllFilters + ); + } + + componentWillUnmount(): void { + const { glEventHub } = this.props; + + glEventHub.off(ConsoleEvent.SESSION_CLOSED, this.handleSessionClosed); + glEventHub.off(ConsoleEvent.SESSION_OPENED, this.handleSessionOpened); + glEventHub.off( + InputFilterEvent.CLEAR_ALL_FILTERS, + this.handleClearAllFilters + ); + } + + handleClearAllFilters(...args: unknown[]): void { + const { onClearAllFilters } = this.props; + onClearAllFilters?.(...args); + } + + handleSessionClosed(session: dh.IdeSession): void { + const { onSessionClose } = this.props; + onSessionClose?.(session); + } + + handleSessionOpened( + session: dh.IdeSession, + params: { language: string; sessionId: string } + ): void { + const { onSessionOpen } = this.props; + onSessionOpen?.(session, params); + } + + render(): ReactElement { + const { children, ...otherProps } = this.props; + + // eslint-disable-next-line react/jsx-props-no-spreading + return {children}; + } +} + +const XCorePanel = createXComponent(CorePanel); + +export default XCorePanel; diff --git a/packages/dashboard-core-plugins/src/panels/FileExplorerPanel.tsx b/packages/dashboard-core-plugins/src/panels/FileExplorerPanel.tsx index bfeb2821ad..d4f8dfa8d8 100644 --- a/packages/dashboard-core-plugins/src/panels/FileExplorerPanel.tsx +++ b/packages/dashboard-core-plugins/src/panels/FileExplorerPanel.tsx @@ -12,7 +12,7 @@ import FileExplorer, { import React, { type ReactNode } from 'react'; import { connect, type ConnectedProps } from 'react-redux'; import type { dh } from '@deephaven/jsapi-types'; -import Panel from './Panel'; +import Panel from './CorePanel'; import { NotebookEvent } from '../events'; import './FileExplorerPanel.scss'; import { getDashboardSessionWrapper } from '../redux'; diff --git a/packages/dashboard-core-plugins/src/panels/FilterSetManagerPanel.tsx b/packages/dashboard-core-plugins/src/panels/FilterSetManagerPanel.tsx index d1b8df6782..8f805e7113 100644 --- a/packages/dashboard-core-plugins/src/panels/FilterSetManagerPanel.tsx +++ b/packages/dashboard-core-plugins/src/panels/FilterSetManagerPanel.tsx @@ -22,7 +22,7 @@ import { getTableMapForDashboard, setDashboardFilterSets as setDashboardFilterSetsAction, } from '../redux'; -import Panel from './Panel'; +import Panel from './CorePanel'; import FilterSetManager, { type ChangeHandlerArgs, type FilterSet, diff --git a/packages/dashboard-core-plugins/src/panels/InputFilterPanel.tsx b/packages/dashboard-core-plugins/src/panels/InputFilterPanel.tsx index 9553fcd4fe..33cb3ceb6d 100644 --- a/packages/dashboard-core-plugins/src/panels/InputFilterPanel.tsx +++ b/packages/dashboard-core-plugins/src/panels/InputFilterPanel.tsx @@ -3,7 +3,7 @@ import { connect } from 'react-redux'; import debounce from 'lodash.debounce'; import type { Container, EventEmitter } from '@deephaven/golden-layout'; import { type RootState } from '@deephaven/redux'; -import Panel from './Panel'; +import Panel from './CorePanel'; import InputFilter, { type InputFilterColumn, } from '../controls/input-filter/InputFilter'; diff --git a/packages/dashboard-core-plugins/src/panels/LogPanel.tsx b/packages/dashboard-core-plugins/src/panels/LogPanel.tsx index 03c34ff7f1..3c9e015da8 100644 --- a/packages/dashboard-core-plugins/src/panels/LogPanel.tsx +++ b/packages/dashboard-core-plugins/src/panels/LogPanel.tsx @@ -8,7 +8,7 @@ import { type DashboardPanelProps } from '@deephaven/dashboard'; import Log from '@deephaven/log'; import { type RootState } from '@deephaven/redux'; import './LogPanel.scss'; -import Panel from './Panel'; +import Panel from './CorePanel'; import { getDashboardSessionWrapper } from '../redux'; const log = Log.module('LogPanel'); diff --git a/packages/dashboard-core-plugins/src/panels/MarkdownPanel.tsx b/packages/dashboard-core-plugins/src/panels/MarkdownPanel.tsx index 9c9823457f..5cd6892a45 100644 --- a/packages/dashboard-core-plugins/src/panels/MarkdownPanel.tsx +++ b/packages/dashboard-core-plugins/src/panels/MarkdownPanel.tsx @@ -21,7 +21,7 @@ import type * as monaco from 'monaco-editor'; import { assertNotNull } from '@deephaven/utils'; import { type RootState } from '@deephaven/redux'; import { LoadingOverlay } from '@deephaven/components'; -import Panel from './Panel'; +import Panel from './CorePanel'; import MarkdownContainer from '../controls/markdown/MarkdownContainer'; import MarkdownStartPage from '../controls/markdown/MarkdownStartPage'; import './MarkdownPanel.scss'; diff --git a/packages/dashboard-core-plugins/src/panels/NotebookPanel.tsx b/packages/dashboard-core-plugins/src/panels/NotebookPanel.tsx index f5101dbd9a..fb59baa21b 100644 --- a/packages/dashboard-core-plugins/src/panels/NotebookPanel.tsx +++ b/packages/dashboard-core-plugins/src/panels/NotebookPanel.tsx @@ -58,7 +58,7 @@ import type { Tab, CloseOptions } from '@deephaven/golden-layout'; import type { dh } from '@deephaven/jsapi-types'; import { ConsoleEvent, NotebookEvent } from '../events'; import { getDashboardSessionWrapper } from '../redux'; -import Panel from './Panel'; +import Panel from './CorePanel'; import './NotebookPanel.scss'; const MarkdownNotebook = lazy(() => import('./MarkdownNotebook')); diff --git a/packages/dashboard-core-plugins/src/panels/WidgetPanel.tsx b/packages/dashboard-core-plugins/src/panels/WidgetPanel.tsx index b8832b0387..13a4aa7c15 100644 --- a/packages/dashboard-core-plugins/src/panels/WidgetPanel.tsx +++ b/packages/dashboard-core-plugins/src/panels/WidgetPanel.tsx @@ -8,7 +8,7 @@ import { } from '@deephaven/components'; import type { dh } from '@deephaven/jsapi-types'; import { copyToClipboard, EMPTY_ARRAY } from '@deephaven/utils'; -import Panel, { type CorePanelProps } from './Panel'; +import Panel, { type CorePanelProps } from './CorePanel'; import WidgetPanelTooltip from './WidgetPanelTooltip'; import './WidgetPanel.scss'; import { type WidgetPanelDescriptor } from './WidgetPanelTypes'; diff --git a/packages/dashboard-core-plugins/src/panels/index.ts b/packages/dashboard-core-plugins/src/panels/index.ts index cb3ad02b9b..60e7c52ddb 100644 --- a/packages/dashboard-core-plugins/src/panels/index.ts +++ b/packages/dashboard-core-plugins/src/panels/index.ts @@ -17,7 +17,9 @@ export { default as MarkdownPanel } from './MarkdownPanel'; export { default as NotebookPanel } from './NotebookPanel'; export { default as PandasPanel } from './PandasPanel'; export * from './PandasPanel'; -export { default as Panel } from './Panel'; +export { default as CorePanel } from './CorePanel'; +// Deprecated - use CorePanel instead +export { default as Panel } from './CorePanel'; export * from './WidgetPanelTypes'; export { default as WidgetPanel, type WidgetPanelProps } from './WidgetPanel'; export { default as WidgetPanelTooltip } from './WidgetPanelTooltip'; diff --git a/packages/dashboard/package.json b/packages/dashboard/package.json index c8de3f3559..bfd22b57f7 100644 --- a/packages/dashboard/package.json +++ b/packages/dashboard/package.json @@ -28,9 +28,11 @@ "@deephaven/react-hooks": "file:../react-hooks", "@deephaven/redux": "file:../redux", "@deephaven/utils": "file:../utils", + "classnames": "^2.3.1", "fast-deep-equal": "^3.1.3", "lodash.ismatch": "^4.1.1", "lodash.throttle": "^4.1.1", + "memoize-one": "^5.1.1", "nanoid": "^5.0.7", "prop-types": "^15.7.2" }, diff --git a/packages/dashboard-core-plugins/src/panels/Panel.scss b/packages/dashboard/src/Panel.scss similarity index 100% rename from packages/dashboard-core-plugins/src/panels/Panel.scss rename to packages/dashboard/src/Panel.scss diff --git a/packages/dashboard-core-plugins/src/panels/Panel.test.tsx b/packages/dashboard/src/Panel.test.tsx similarity index 100% rename from packages/dashboard-core-plugins/src/panels/Panel.test.tsx rename to packages/dashboard/src/Panel.test.tsx diff --git a/packages/dashboard-core-plugins/src/panels/Panel.tsx b/packages/dashboard/src/Panel.tsx similarity index 84% rename from packages/dashboard-core-plugins/src/panels/Panel.tsx rename to packages/dashboard/src/Panel.tsx index 86ec513284..c5d961399c 100644 --- a/packages/dashboard-core-plugins/src/panels/Panel.tsx +++ b/packages/dashboard/src/Panel.tsx @@ -16,11 +16,6 @@ import { type ResolvableContextAction, Tooltip, } from '@deephaven/components'; -import { - LayoutUtils, - type PanelComponent, - PanelEvent, -} from '@deephaven/dashboard'; import type { Container, EventEmitter, @@ -29,15 +24,17 @@ import type { } from '@deephaven/golden-layout'; import { assertNotNull, EMPTY_ARRAY } from '@deephaven/utils'; import Log from '@deephaven/log'; -import type { dh } from '@deephaven/jsapi-types'; -import { ConsoleEvent, InputFilterEvent, TabEvent } from '../events'; +import LayoutUtils from './layout/LayoutUtils'; +import { type PanelComponent } from './DashboardPlugin'; +import PanelEvent from './PanelEvent'; import PanelContextMenu from './PanelContextMenu'; import RenameDialog from './RenameDialog'; +import TabEvent from './TabEvent'; import './Panel.scss'; const log = Log.module('Panel'); -export type CorePanelProps = { +export type BasePanelProps = { /** * Reference to the component panel. * Will wait until it is set before emitting mount/unmount events. @@ -52,14 +49,8 @@ export type CorePanelProps = { onBlur?: FocusEventHandler; onTab?: (tab: Tab) => void; onTabClicked?: (e: MouseEvent) => void; - onClearAllFilters?: (...args: unknown[]) => void; onHide?: (...args: unknown[]) => void; onResize?: (...args: unknown[]) => void; - onSessionClose?: (session: dh.IdeSession) => void; - onSessionOpen?: ( - session: dh.IdeSession, - { language, sessionId }: { language: string; sessionId: string } - ) => void; onBeforeShow?: (...args: unknown[]) => void; onShow?: (...args: unknown[]) => void; onTabBlur?: (...args: unknown[]) => void; @@ -83,18 +74,15 @@ interface PanelState { * Also wires up some triggers for common events: * Focus, Resize, Show, Session open/close, client disconnect/reconnect. */ -class Panel extends PureComponent { - constructor(props: CorePanelProps) { +class Panel extends PureComponent { + constructor(props: BasePanelProps) { super(props); - this.handleClearAllFilters = this.handleClearAllFilters.bind(this); this.handleCopyPanel = this.handleCopyPanel.bind(this); this.handleFocus = this.handleFocus.bind(this); this.handleBlur = this.handleBlur.bind(this); this.handleHide = this.handleHide.bind(this); this.handleResize = this.handleResize.bind(this); - this.handleSessionClosed = this.handleSessionClosed.bind(this); - this.handleSessionOpened = this.handleSessionOpened.bind(this); this.handleBeforeShow = this.handleBeforeShow.bind(this); this.handleShow = this.handleShow.bind(this); this.handleTabBlur = this.handleTabBlur.bind(this); @@ -124,14 +112,8 @@ class Panel extends PureComponent { glContainer.on('hide', this.handleHide); glContainer.on('tab', this.handleTab); glContainer.on('tabClicked', this.handleTabClicked); - glEventHub.on(ConsoleEvent.SESSION_CLOSED, this.handleSessionClosed); - glEventHub.on(ConsoleEvent.SESSION_OPENED, this.handleSessionOpened); glEventHub.on(TabEvent.focus, this.handleTabFocus); glEventHub.on(TabEvent.blur, this.handleTabBlur); - glEventHub.on( - InputFilterEvent.CLEAR_ALL_FILTERS, - this.handleClearAllFilters - ); glEventHub.emit(PanelEvent.MOUNT, componentPanel ?? this); @@ -150,14 +132,8 @@ class Panel extends PureComponent { glContainer.off('hide', this.handleHide); glContainer.off('tab', this.handleTab); glContainer.off('tabClicked', this.handleTabClicked); - glEventHub.off(ConsoleEvent.SESSION_CLOSED, this.handleSessionClosed); - glEventHub.off(ConsoleEvent.SESSION_OPENED, this.handleSessionOpened); glEventHub.off(TabEvent.focus, this.handleTabFocus); glEventHub.off(TabEvent.blur, this.handleTabBlur); - glEventHub.off( - InputFilterEvent.CLEAR_ALL_FILTERS, - this.handleClearAllFilters - ); glEventHub.emit(PanelEvent.UNMOUNT, componentPanel ?? this); } @@ -183,11 +159,6 @@ class Panel extends PureComponent { onTabClicked?.(e); } - handleClearAllFilters(...args: unknown[]): void { - const { onClearAllFilters } = this.props; - onClearAllFilters?.(...args); - } - handleFocus(event: FocusEvent): void { const { componentPanel, glEventHub } = this.props; glEventHub.emit(PanelEvent.FOCUS, componentPanel ?? this); @@ -211,19 +182,6 @@ class Panel extends PureComponent { onResize?.(...args); } - handleSessionClosed(session: dh.IdeSession): void { - const { onSessionClose } = this.props; - onSessionClose?.(session); - } - - handleSessionOpened( - session: dh.IdeSession, - params: { language: string; sessionId: string } - ): void { - const { onSessionOpen } = this.props; - onSessionOpen?.(session, params); - } - handleBeforeShow(...args: unknown[]): void { const { onBeforeShow } = this.props; onBeforeShow?.(...args); diff --git a/packages/dashboard-core-plugins/src/panels/PanelContextMenu.test.tsx b/packages/dashboard/src/PanelContextMenu.test.tsx similarity index 100% rename from packages/dashboard-core-plugins/src/panels/PanelContextMenu.test.tsx rename to packages/dashboard/src/PanelContextMenu.test.tsx diff --git a/packages/dashboard-core-plugins/src/panels/PanelContextMenu.tsx b/packages/dashboard/src/PanelContextMenu.tsx similarity index 97% rename from packages/dashboard-core-plugins/src/panels/PanelContextMenu.tsx rename to packages/dashboard/src/PanelContextMenu.tsx index 97d2f4cf86..9e286ffdf1 100644 --- a/packages/dashboard-core-plugins/src/panels/PanelContextMenu.tsx +++ b/packages/dashboard/src/PanelContextMenu.tsx @@ -11,11 +11,9 @@ import { setWorkspace as setWorkspaceAction, } from '@deephaven/redux'; import { connect } from 'react-redux'; -import { - type ClosedPanel, - LayoutUtils, - PanelEvent, -} from '@deephaven/dashboard'; +import { type ClosedPanel } from './PanelManager'; +import { LayoutUtils } from './layout'; +import { PanelEvent } from './PanelEvent'; interface PanelContextMenuProps { additionalActions: ResolvableContextAction[]; diff --git a/packages/dashboard-core-plugins/src/panels/RenameDialog.tsx b/packages/dashboard/src/RenameDialog.tsx similarity index 100% rename from packages/dashboard-core-plugins/src/panels/RenameDialog.tsx rename to packages/dashboard/src/RenameDialog.tsx diff --git a/packages/dashboard-core-plugins/src/events/TabEvent.ts b/packages/dashboard/src/TabEvent.ts similarity index 100% rename from packages/dashboard-core-plugins/src/events/TabEvent.ts rename to packages/dashboard/src/TabEvent.ts diff --git a/packages/dashboard/src/index.ts b/packages/dashboard/src/index.ts index 41c3fd3fd0..8e4cf7054a 100644 --- a/packages/dashboard/src/index.ts +++ b/packages/dashboard/src/index.ts @@ -12,7 +12,10 @@ export { default as DashboardUtils } from './DashboardUtils'; export * from './LazyDashboard'; export * from './layout'; export * from './redux'; +export { type BasePanelProps } from './Panel'; +export { default as Panel } from './Panel'; export * from './PanelManager'; export * from './PanelEvent'; export { default as PanelErrorBoundary } from './PanelErrorBoundary'; export { default as PanelManager } from './PanelManager'; +export { default as TabEvent } from './TabEvent';