From faa723fafc0da4ac16a62ef2d1bec37e12cd5f36 Mon Sep 17 00:00:00 2001 From: mikebender Date: Thu, 24 Jul 2025 17:00:56 -0400 Subject: [PATCH 1/6] WIP it's embedding one dashboard - But I can't embed another dashboard within a dashboard .. --- .../src/deephaven/ui/components/dashboard.py | 7 +- plugins/ui/src/js/src/layout/Column.tsx | 1 + plugins/ui/src/js/src/layout/Dashboard.tsx | 126 +++++++++++++++++- plugins/ui/src/js/src/layout/LayoutUtils.tsx | 1 + plugins/ui/src/js/src/layout/Row.tsx | 2 + .../ui/src/js/src/widget/WidgetHandler.tsx | 12 +- 6 files changed, 135 insertions(+), 14 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/components/dashboard.py b/plugins/ui/src/deephaven/ui/components/dashboard.py index 44ae34491..7b518cb14 100644 --- a/plugins/ui/src/deephaven/ui/components/dashboard.py +++ b/plugins/ui/src/deephaven/ui/components/dashboard.py @@ -1,10 +1,10 @@ from __future__ import annotations from typing import Any -from ..elements import DashboardElement, FunctionElement +from ..elements import BaseElement, FunctionElement -def dashboard(element: FunctionElement) -> DashboardElement: +def dashboard(element: FunctionElement) -> BaseElement: """ A dashboard is the container for an entire layout. @@ -15,4 +15,5 @@ def dashboard(element: FunctionElement) -> DashboardElement: Returns: The rendered dashboard. """ - return DashboardElement(element) + # return DashboardElement(element) + return BaseElement("deephaven.ui.components.Dashboard", element) # type: ignore[return-value] diff --git a/plugins/ui/src/js/src/layout/Column.tsx b/plugins/ui/src/js/src/layout/Column.tsx index c60cbae36..62e24881e 100644 --- a/plugins/ui/src/js/src/layout/Column.tsx +++ b/plugins/ui/src/js/src/layout/Column.tsx @@ -13,6 +13,7 @@ function LayoutColumn({ children, width, }: ColumnElementProps): JSX.Element | null { + console.log('xxx doing a LayoutColumn'); const layoutManager = useLayoutManager(); const parent = useParentItem(); diff --git a/plugins/ui/src/js/src/layout/Dashboard.tsx b/plugins/ui/src/js/src/layout/Dashboard.tsx index 9ce1fe0c1..d522dba23 100644 --- a/plugins/ui/src/js/src/layout/Dashboard.tsx +++ b/plugins/ui/src/js/src/layout/Dashboard.tsx @@ -1,20 +1,136 @@ -import React from 'react'; +import React, { useCallback, useMemo, useRef, useState } from 'react'; +import { nanoid } from 'nanoid'; +import { + Dashboard as DHCDasbhoard, + LayoutManagerContext, +} from '@deephaven/dashboard'; +import GoldenLayout from '@deephaven/golden-layout'; +import { useDashboardPlugins } from '@deephaven/plugin'; +import Log from '@deephaven/log'; import { normalizeDashboardChildren, type DashboardElementProps, } from './LayoutUtils'; import { ParentItemContext, useParentItem } from './ParentItemContext'; +import ReactPanel from './ReactPanel'; +import { ReactPanelContext } from './ReactPanelContext'; +import useWidgetStatus from './useWidgetStatus'; +import { ReactPanelManagerContext } from './ReactPanelManager'; + +const log = Log.module('@deephaven/js-plugin-ui/DocumentHandler'); function Dashboard({ children }: DashboardElementProps): JSX.Element | null { - const parent = useParentItem(); + const [childLayout, setChildLayout] = useState(); + // const parent = useParentItem(); + const plugins = useDashboardPlugins(); const normalizedChildren = normalizeDashboardChildren(children); + console.log('xxx doing a dashboard with layout', childLayout); + + const panelIdIndex = useRef(0); + // panelIds that are currently opened within this document. This list is tracked by the `onOpen`/`onClose` call on the `ReactPanelManager` from a child component. + // Note that the initial widget data provided will be the `panelIds` for this document to use; this array is what is actually opened currently. + const panelIds = useRef([]); + + // Flag to signal the panel counts have changed in the last render + // We may need to check if we need to close this widget if all panels are closed + const [isPanelsDirty, setPanelsDirty] = useState(false); + + const handleOpen = useCallback( + (panelId: string) => { + if (panelIds.current.includes(panelId)) { + throw new Error('Duplicate panel opens received'); + } + + panelIds.current.push(panelId); + log.debug('Panel opened, open count', panelIds.current.length); + + setPanelsDirty(true); + }, + [panelIds] + ); + + const handleClose = useCallback( + (panelId: string) => { + const panelIndex = panelIds.current.indexOf(panelId); + if (panelIndex === -1) { + throw new Error('Panel close received for unknown panel'); + } + + panelIds.current.splice(panelIndex, 1); + log.debug('Panel closed, open count', panelIds.current.length); + + setPanelsDirty(true); + }, + [panelIds] + ); + + const getPanelId = useCallback(() => { + // On rehydration, yield known IDs first + // If there are no more known IDs, generate a new one. + // This can happen if the document hasn't been opened before, or if it's rehydrated and a new panel is added. + // Note that if the order of panels changes, the worst case scenario is that panels appear in the wrong location in the layout. + const panelId = nanoid(); + panelIdIndex.current += 1; + return panelId; + }, []); + + const widgetStatus = useWidgetStatus(); + const panelManager = useMemo( + () => ({ + metadata: widgetStatus.descriptor, + onOpen: handleOpen, + onClose: handleClose, + onDataChange: () => log.debug('xxx Panel data changed'), + getPanelId, + getInitialData: () => [], + }), + [ + widgetStatus, + getPanelId, + handleClose, + handleOpen, + // handleDataChange, + // getInitialData, + ] + ); + const [isLayoutInitialized, setLayoutInitialized] = useState(false); return ( - - {normalizedChildren} - + // <> + + + + + setLayoutInitialized(true)} + > + {plugins} + {isLayoutInitialized && <>{normalizedChildren}} + + + + + {/* Resetting the panel ID so the children don't get confused */} + {/* + + {childLayout != null && ( + + + {normalizedChildren} + + + )} + + */} + + // ); + // + // {normalizedChildren} + // + // } export default Dashboard; diff --git a/plugins/ui/src/js/src/layout/LayoutUtils.tsx b/plugins/ui/src/js/src/layout/LayoutUtils.tsx index b2d78ec49..a678e8901 100644 --- a/plugins/ui/src/js/src/layout/LayoutUtils.tsx +++ b/plugins/ui/src/js/src/layout/LayoutUtils.tsx @@ -153,6 +153,7 @@ export function isDashboardElementNode( export function normalizeDashboardChildren( children: React.ReactNode ): React.ReactNode { + console.log('xxx normalizeDashboardChildren', children); const needsWrapper = Children.count(children) > 1; const hasRows = Children.toArray(children).some( child => isValidElement(child) && child.type === Row diff --git a/plugins/ui/src/js/src/layout/Row.tsx b/plugins/ui/src/js/src/layout/Row.tsx index cad61b97e..7c7c1dcb2 100644 --- a/plugins/ui/src/js/src/layout/Row.tsx +++ b/plugins/ui/src/js/src/layout/Row.tsx @@ -7,6 +7,7 @@ import { ParentItemContext, useParentItem } from './ParentItemContext'; import { usePanelId } from './ReactPanelContext'; function LayoutRow({ children, height }: RowElementProps): JSX.Element | null { + console.log('xxx doing a LayoutRow'); const layoutManager = useLayoutManager(); const parent = useParentItem(); const row = useMemo(() => { @@ -45,6 +46,7 @@ function Row({ children, height }: RowElementProps): JSX.Element { return {children}; } + console.log('xxx doing a flexRow with panelId', panelId); return ( {children} diff --git a/plugins/ui/src/js/src/widget/WidgetHandler.tsx b/plugins/ui/src/js/src/widget/WidgetHandler.tsx index 91646d7db..57dd97185 100644 --- a/plugins/ui/src/js/src/widget/WidgetHandler.tsx +++ b/plugins/ui/src/js/src/widget/WidgetHandler.tsx @@ -98,12 +98,12 @@ function WidgetHandler({ if (widget !== prevWidget) { setPrevWidget(widget); - if (widget != null && widget.type === DASHBOARD_ELEMENT) { - log.info( - 'Dashboard widget has changed, removing previous elements from layout' - ); - layoutManager.root.contentItems.forEach(item => item.remove()); - } + // if (widget != null && widget.type === DASHBOARD_ELEMENT) { + // log.info( + // 'Dashboard widget has changed, removing previous elements from layout' + // ); + // layoutManager.root.contentItems.forEach(item => item.remove()); + // } } if (widgetError != null && isLoading) { From 96a91340bfdd6661b136e2068518c4057e8504f9 Mon Sep 17 00:00:00 2001 From: mikebender Date: Fri, 25 Jul 2025 15:11:53 -0400 Subject: [PATCH 2/6] WIP Dirtyyyy dirtyyy dashboards within dashboards --- plugins/ui/src/js/src/DashboardPlugin.tsx | 35 ++++++++++--------- plugins/ui/src/js/src/layout/Dashboard.tsx | 35 +++++++++++-------- .../ui/src/js/src/widget/DocumentHandler.tsx | 1 + .../ui/src/js/src/widget/DocumentUtils.tsx | 2 +- 4 files changed, 41 insertions(+), 32 deletions(-) diff --git a/plugins/ui/src/js/src/DashboardPlugin.tsx b/plugins/ui/src/js/src/DashboardPlugin.tsx index 9f27f04cf..7ecc7e3cf 100644 --- a/plugins/ui/src/js/src/DashboardPlugin.tsx +++ b/plugins/ui/src/js/src/DashboardPlugin.tsx @@ -70,7 +70,9 @@ export function DashboardPlugin( id, PLUGIN_NAME ) as unknown as [DashboardPluginData, (data: DashboardPluginData) => void]; - const [initialPluginData] = useState(pluginData); + const [initialPluginData] = useState({ + openWidgets: {}, + } as DashboardPluginData); // Keep track of the widgets we've got opened. const [widgetMap, setWidgetMap] = useState< @@ -147,20 +149,21 @@ export function DashboardPlugin( log.debug('loadInitialPluginData', initialPluginData); setWidgetMap(prevWidgetMap => { - const newWidgetMap = new Map(prevWidgetMap); - const { openWidgets } = initialPluginData; - if (openWidgets != null) { - Object.entries(openWidgets).forEach( - ([widgetId, { descriptor, data }]) => { - newWidgetMap.set(widgetId, { - id: widgetId, - widget: descriptor, - data, - }); - } - ); - } - return newWidgetMap; + return new Map(); + // const newWidgetMap = new Map(prevWidgetMap); + // const { openWidgets } = initialPluginData; + // if (openWidgets != null) { + // Object.entries(openWidgets).forEach( + // ([widgetId, { descriptor, data }]) => { + // newWidgetMap.set(widgetId, { + // id: widgetId, + // widget: descriptor, + // data, + // }); + // } + // ); + // } + // return newWidgetMap; }); }, [initialPluginData, id] @@ -278,7 +281,7 @@ export function DashboardPlugin( diff --git a/plugins/ui/src/js/src/layout/Dashboard.tsx b/plugins/ui/src/js/src/layout/Dashboard.tsx index d522dba23..a0aab8318 100644 --- a/plugins/ui/src/js/src/layout/Dashboard.tsx +++ b/plugins/ui/src/js/src/layout/Dashboard.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useMemo, useRef, useState } from 'react'; import { nanoid } from 'nanoid'; import { - Dashboard as DHCDasbhoard, + Dashboard as DHCDashboard, LayoutManagerContext, } from '@deephaven/dashboard'; import GoldenLayout from '@deephaven/golden-layout'; @@ -13,9 +13,10 @@ import { } from './LayoutUtils'; import { ParentItemContext, useParentItem } from './ParentItemContext'; import ReactPanel from './ReactPanel'; -import { ReactPanelContext } from './ReactPanelContext'; +import { ReactPanelContext, usePanelId } from './ReactPanelContext'; import useWidgetStatus from './useWidgetStatus'; import { ReactPanelManagerContext } from './ReactPanelManager'; +import PortalPanelManager from './PortalPanelManager'; const log = Log.module('@deephaven/js-plugin-ui/DocumentHandler'); @@ -98,20 +99,24 @@ function Dashboard({ children }: DashboardElementProps): JSX.Element | null { return ( // <> - - - + <> + {/* */} + setLayoutInitialized(true)} + > + {plugins} + - setLayoutInitialized(true)} - > - {plugins} - {isLayoutInitialized && <>{normalizedChildren}} - + + + {isLayoutInitialized && normalizedChildren} + + - - + + + {/* */} {/* Resetting the panel ID so the children don't get confused */} {/* @@ -124,7 +129,7 @@ function Dashboard({ children }: DashboardElementProps): JSX.Element | null { )} */} - + // ); // diff --git a/plugins/ui/src/js/src/widget/DocumentHandler.tsx b/plugins/ui/src/js/src/widget/DocumentHandler.tsx index 8791a86b3..f381d36ea 100644 --- a/plugins/ui/src/js/src/widget/DocumentHandler.tsx +++ b/plugins/ui/src/js/src/widget/DocumentHandler.tsx @@ -130,6 +130,7 @@ function DocumentHandler({ panelIds.current.length ); if (panelIds.current.length === 0) { + // Let's just ignore this for now ... log.debug('Widget', widget.id, 'closed all panels, triggering onClose'); onClose?.(); } else { diff --git a/plugins/ui/src/js/src/widget/DocumentUtils.tsx b/plugins/ui/src/js/src/widget/DocumentUtils.tsx index c145b5874..0274ea4b8 100644 --- a/plugins/ui/src/js/src/widget/DocumentUtils.tsx +++ b/plugins/ui/src/js/src/widget/DocumentUtils.tsx @@ -45,7 +45,7 @@ export function getRootChildren( throw new MixedPanelsError('Cannot mix Panel and Dashboard elements'); } - if (nonLayoutCount === childrenArray.length) { + if (nonLayoutCount === childrenArray.length || dashboardCount > 0) { // Just wrap it in a panel return ( From 6236c04c960b9476762e7a63fb896eb6b89abf18 Mon Sep 17 00:00:00 2001 From: mikebender Date: Mon, 28 Jul 2025 15:15:51 -0400 Subject: [PATCH 3/6] WIP adding some styling to dashboards in dashboards --- plugins/ui/src/js/src/styles.scss | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plugins/ui/src/js/src/styles.scss b/plugins/ui/src/js/src/styles.scss index 223537d1c..caec6e43b 100644 --- a/plugins/ui/src/js/src/styles.scss +++ b/plugins/ui/src/js/src/styles.scss @@ -102,3 +102,12 @@ .ui-markdown { padding: 0; } + +.dashboard-container { + .dashboard-container { + border: 5px solid red; + .dashboard-container { + border: 5px solid blue; + } + } +} From 150ca29b5c76c454fde27930b427457ee16e52c9 Mon Sep 17 00:00:00 2001 From: mikebender Date: Mon, 28 Jul 2025 15:55:35 -0400 Subject: [PATCH 4/6] Adjust colours/sizing of nested dashboards - Have a nested dashboard styling, and double nested styling - Just adjust the header height and font size to be a little smaller at each level... - Adjust the background behind the header tabs as well to be darker the more nested you go, so you kind of get a sense of the boundary of the dashboard - Looks okay? I dunno. - Used the example dashboard from the docs then amalgamated into a double dashboard: ``` from deephaven import ui double_dash2 = ui.dashboard( ui.row( ui.panel(example_dashboard, title="Example Dashboard"), ui.panel(ui.dashboard( ui.column( ui.row(ui.panel(my_table, title="Stocks"), ui.panel(dog_prices, title="Dog Prices")), ui.panel(line_plot, title="Line plot") ) ), title="My Dash") ) ) ``` --- plugins/ui/src/js/src/styles.scss | 38 +++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/plugins/ui/src/js/src/styles.scss b/plugins/ui/src/js/src/styles.scss index caec6e43b..72ff9ce6f 100644 --- a/plugins/ui/src/js/src/styles.scss +++ b/plugins/ui/src/js/src/styles.scss @@ -103,11 +103,45 @@ padding: 0; } +$lm-header-nested-height: 18px; +$lm-header-double-nested-height: 14px; +$lm-header-nested-font-size: 11px; +$lm-header-double-nested-font-size: 10px; +$lm-header-nested-color: var(--dh-color-gray-600); +$lm-header-double-nested-color: var(--dh-color-gray-500); +$lm-header-nested-bg: var(--dh-color-gray-50); +$lm-header-double-nested-bg: var(--dh-color-true-black); + .dashboard-container { .dashboard-container { - border: 5px solid red; + // border: 5px solid red; + .lm_tab { + height: $lm-header-nested-height; + font-size: $lm-header-nested-font-size; + // color: $lm-header-nested-color; + } + .lm_tabs { + background: $lm-header-nested-bg; + } + .lm_header { + height: $lm-header-nested-height; + } + .lm_close_tab { + display: none; // hide close icon for nested tabs + } .dashboard-container { - border: 5px solid blue; + // border: 5px solid blue; + .lm_tab { + height: $lm-header-double-nested-height; + font-size: $lm-header-double-nested-font-size; + // color: $lm-header-double-nested-color; + } + .lm_tabs { + background: $lm-header-double-nested-bg; + } + .lm_header { + height: $lm-header-double-nested-height; + } } } } From 1ffe451cdeb903562ad56329a7e5ef60afc731f6 Mon Sep 17 00:00:00 2001 From: mikebender Date: Fri, 1 Aug 2025 10:00:44 -0400 Subject: [PATCH 5/6] WIP Build up a dashboard list - Example of Avi's desired app --- .../src/deephaven/ui/components/dashboard.py | 13 +++- plugins/ui/src/js/src/layout/Dashboard.tsx | 60 +++++++++++++++++-- plugins/ui/src/js/src/layout/LayoutUtils.tsx | 9 ++- 3 files changed, 72 insertions(+), 10 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/components/dashboard.py b/plugins/ui/src/deephaven/ui/components/dashboard.py index 7b518cb14..15e28749f 100644 --- a/plugins/ui/src/deephaven/ui/components/dashboard.py +++ b/plugins/ui/src/deephaven/ui/components/dashboard.py @@ -1,10 +1,14 @@ from __future__ import annotations -from typing import Any +from typing import Any, Union from ..elements import BaseElement, FunctionElement -def dashboard(element: FunctionElement) -> BaseElement: +def dashboard( + element: FunctionElement, + show_close_icon: Union[bool, None] = None, + show_headers: Union[bool, None] = None, +) -> BaseElement: """ A dashboard is the container for an entire layout. @@ -12,8 +16,11 @@ def dashboard(element: FunctionElement) -> BaseElement: element: Element to render as the dashboard. The element should render a layout that contains 1 root column or row. + show_close_icon: Whether to show the close icon in the top right corner of the dashboard. Defaults to False. + show_headers: Whether to show headers for the dashboard. Defaults to True. + Returns: The rendered dashboard. """ # return DashboardElement(element) - return BaseElement("deephaven.ui.components.Dashboard", element) # type: ignore[return-value] + return BaseElement("deephaven.ui.components.Dashboard", element, show_close_icon=show_close_icon, show_headers=show_headers) # type: ignore[return-value] diff --git a/plugins/ui/src/js/src/layout/Dashboard.tsx b/plugins/ui/src/js/src/layout/Dashboard.tsx index a0aab8318..c9280fb8e 100644 --- a/plugins/ui/src/js/src/layout/Dashboard.tsx +++ b/plugins/ui/src/js/src/layout/Dashboard.tsx @@ -1,10 +1,18 @@ -import React, { useCallback, useMemo, useRef, useState } from 'react'; +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; import { nanoid } from 'nanoid'; import { Dashboard as DHCDashboard, LayoutManagerContext, } from '@deephaven/dashboard'; -import GoldenLayout from '@deephaven/golden-layout'; +import GoldenLayout, { + Settings as LayoutSettings, +} from '@deephaven/golden-layout'; import { useDashboardPlugins } from '@deephaven/plugin'; import Log from '@deephaven/log'; import { @@ -20,13 +28,22 @@ import PortalPanelManager from './PortalPanelManager'; const log = Log.module('@deephaven/js-plugin-ui/DocumentHandler'); -function Dashboard({ children }: DashboardElementProps): JSX.Element | null { +const DEFAULT_SETTINGS: Partial = Object.freeze({ + showCloseIcon: false, + constrainDragToContainer: true, +}); + +function Dashboard({ + children, + showCloseIcon = false, + showHeaders = true, +}: DashboardElementProps): JSX.Element | null { const [childLayout, setChildLayout] = useState(); // const parent = useParentItem(); const plugins = useDashboardPlugins(); const normalizedChildren = normalizeDashboardChildren(children); - console.log('xxx doing a dashboard with layout', childLayout); + console.log('xxx doing a dashboard with layout', childLayout, showCloseIcon); const panelIdIndex = useRef(0); // panelIds that are currently opened within this document. This list is tracked by the `onOpen`/`onClose` call on the `ReactPanelManager` from a child component. @@ -66,6 +83,32 @@ function Dashboard({ children }: DashboardElementProps): JSX.Element | null { [panelIds] ); + /** + * When there are changes made to panels in a render cycle, check if they've all been closed and fire an `onClose` event if they are. + * Otherwise, fire an `onDataChange` event with the updated panelIds that are open. + */ + // useEffect( + // function syncOpenPanels() { + // if (!isPanelsDirty) { + // return; + // } + + // setPanelsDirty(false); + + // // Check if all the panels in this widget are closed + // // We do it outside of the `handleClose` function in case a new panel opens up in the same render cycle + // log.debug2('dashboard', 'open panel count', panelIds.current.length); + // if (panelIds.current.length === 0) { + // // Let's just ignore this for now ... + // log.debug('Dashboard', 'closed all panels, triggering onClose'); + // // onClose?.(); + // } else { + // // onDataChange({ ...widgetData, panelIds: panelIds.current }); + // } + // }, + // [isPanelsDirty] + // ); + const getPanelId = useCallback(() => { // On rehydration, yield known IDs first // If there are no more known IDs, generate a new one. @@ -96,6 +139,14 @@ function Dashboard({ children }: DashboardElementProps): JSX.Element | null { ] ); const [isLayoutInitialized, setLayoutInitialized] = useState(false); + const layoutSettings: Partial = useMemo( + () => ({ + ...DEFAULT_SETTINGS, + showCloseIcon, + hasHeaders: showHeaders, + }), + [showCloseIcon, showHeaders] + ); return ( // <> @@ -104,6 +155,7 @@ function Dashboard({ children }: DashboardElementProps): JSX.Element | null { setLayoutInitialized(true)} + layoutSettings={layoutSettings} > {plugins} diff --git a/plugins/ui/src/js/src/layout/LayoutUtils.tsx b/plugins/ui/src/js/src/layout/LayoutUtils.tsx index a678e8901..ff7934df3 100644 --- a/plugins/ui/src/js/src/layout/LayoutUtils.tsx +++ b/plugins/ui/src/js/src/layout/LayoutUtils.tsx @@ -113,9 +113,12 @@ export function isStackElementNode(obj: unknown): obj is StackElementNode { ); } -export type DashboardElementProps = React.PropsWithChildren< - Record ->; +export type DashboardElementProps = React.PropsWithChildren<{ + /** Whether to show the close icon in the top right corner of the dashboard */ + showCloseIcon?: boolean; + /** Whether to show headers for the dashboard */ + showHeaders?: boolean; +}>; /** * Describes a dashboard element that can be rendered in the UI. From d1f5e6c6a53490d1c3f10db27b6066ebd8755471 Mon Sep 17 00:00:00 2001 From: dsmmcken Date: Thu, 30 Oct 2025 18:03:15 -0400 Subject: [PATCH 6/6] style changes to nested dashboards, expects pr for web-client-ui related to lm_dragging on lm_root --- plugins/ui/src/js/src/styles.scss | 62 ++++++++----------------------- 1 file changed, 16 insertions(+), 46 deletions(-) diff --git a/plugins/ui/src/js/src/styles.scss b/plugins/ui/src/js/src/styles.scss index 72ff9ce6f..9bf789692 100644 --- a/plugins/ui/src/js/src/styles.scss +++ b/plugins/ui/src/js/src/styles.scss @@ -59,15 +59,19 @@ position: absolute; } } + + .dashboard-container { + border: 1px solid var(--dh-color-bg); + } } - &:has(.dh-inner-react-panel > .iris-grid:only-child), + &:has(> .dh-inner-react-panel > .iris-grid:only-child), &:has( - .dh-inner-react-panel + > .dh-inner-react-panel > .ui-table-container:only-child > .iris-grid:only-child ), - &:has(.dh-inner-react-panel > .chart-wrapper:only-child) { + &:has(> .dh-inner-react-panel > .chart-wrapper:only-child) { // remove the default panel padding when grid or chart is the only child padding: 0 !important; // important required to override inline spectrum style .iris-grid { @@ -75,6 +79,15 @@ border-radius: 0; } } + + // remove padding and border around single child dashboards in react panels + &:has(> .dh-inner-react-panel > .dashboard-container:only-child) { + padding: 0 !important; // important required to override inline spectrum style + + > .dh-inner-react-panel > .dashboard-container { + border: none; + } + } } .ui-text-wrap-balance { @@ -102,46 +115,3 @@ .ui-markdown { padding: 0; } - -$lm-header-nested-height: 18px; -$lm-header-double-nested-height: 14px; -$lm-header-nested-font-size: 11px; -$lm-header-double-nested-font-size: 10px; -$lm-header-nested-color: var(--dh-color-gray-600); -$lm-header-double-nested-color: var(--dh-color-gray-500); -$lm-header-nested-bg: var(--dh-color-gray-50); -$lm-header-double-nested-bg: var(--dh-color-true-black); - -.dashboard-container { - .dashboard-container { - // border: 5px solid red; - .lm_tab { - height: $lm-header-nested-height; - font-size: $lm-header-nested-font-size; - // color: $lm-header-nested-color; - } - .lm_tabs { - background: $lm-header-nested-bg; - } - .lm_header { - height: $lm-header-nested-height; - } - .lm_close_tab { - display: none; // hide close icon for nested tabs - } - .dashboard-container { - // border: 5px solid blue; - .lm_tab { - height: $lm-header-double-nested-height; - font-size: $lm-header-double-nested-font-size; - // color: $lm-header-double-nested-color; - } - .lm_tabs { - background: $lm-header-double-nested-bg; - } - .lm_header { - height: $lm-header-double-nested-height; - } - } - } -}