diff --git a/plugins/ui/src/js/src/elements/ElementConstants.ts b/plugins/ui/src/js/src/elements/ElementConstants.ts index 3cacf5b6a..49a9bcab3 100644 --- a/plugins/ui/src/js/src/elements/ElementConstants.ts +++ b/plugins/ui/src/js/src/elements/ElementConstants.ts @@ -5,39 +5,39 @@ import type * as icons from '@deephaven/icons'; export const UI_COMPONENTS_NAMESPACE = 'deephaven.ui.components'; export const UI_ELEMENTS_NAMESPACE = 'deephaven.ui.elements'; -/** Table */ -export const UITABLE_ELEMENT_TYPE = `${UI_ELEMENTS_NAMESPACE}.UITable` as const; -export type UITableElementName = typeof UITABLE_ELEMENT_TYPE; - -/** Layout */ -export const PANEL_ELEMENT_NAME = `${UI_COMPONENTS_NAMESPACE}.Panel` as const; -export const ROW_ELEMENT_NAME = `${UI_COMPONENTS_NAMESPACE}.Row` as const; -export const COLUMN_ELEMENT_NAME = `${UI_COMPONENTS_NAMESPACE}.Column` as const; -export const STACK_ELEMENT_NAME = `${UI_COMPONENTS_NAMESPACE}.Stack` as const; -export const DASHBOARD_ELEMENT_NAME = - `${UI_COMPONENTS_NAMESPACE}.Dashboard` as const; - -export type PanelElementType = typeof PANEL_ELEMENT_NAME; -export type RowElementType = typeof ROW_ELEMENT_NAME; -export type ColumnElementType = typeof COLUMN_ELEMENT_NAME; -export type StackElementType = typeof STACK_ELEMENT_NAME; -export type DashboardElementType = typeof DASHBOARD_ELEMENT_NAME; - -/** Icons */ -export const ICON_ELEMENT_TYPE_PREFIX = 'deephaven.ui.icons.'; -export type IconElementName = - `${typeof ICON_ELEMENT_TYPE_PREFIX}${keyof typeof icons}`; - -/** HTML */ -export const HTML_ELEMENT_NAME_PREFIX = 'deephaven.ui.html.'; -export type HTMLElementType = - `${typeof HTML_ELEMENT_NAME_PREFIX}${keyof ReactHTML}`; - -/** Specific Components */ -export const FRAGMENT_ELEMENT_NAME = - `${UI_COMPONENTS_NAMESPACE}.Fragment` as const; -export const ITEM_ELEMENT_NAME = `${UI_COMPONENTS_NAMESPACE}.Item` as const; -export const LIST_VIEW_NAME = `${UI_COMPONENTS_NAMESPACE}.ListView` as const; -export const PICKER_ELEMENT_NAME = `${UI_COMPONENTS_NAMESPACE}.Picker` as const; -export const SECTION_ELEMENT_NAME = - `${UI_COMPONENTS_NAMESPACE}.Section` as const; +const uiComponentName = (name: T) => + `${UI_COMPONENTS_NAMESPACE}.${name}` as const; + +const uiElementName = (name: T) => + `${UI_ELEMENTS_NAMESPACE}.${name}` as const; + +export const ELEMENT_NAME = { + /** Elements */ + uiTable: uiElementName('UITable'), + + /** Layout Components */ + column: uiComponentName('Column'), + dashboard: uiComponentName('Dashboard'), + panel: uiComponentName('Panel'), + row: uiComponentName('Row'), + stack: uiComponentName('Stack'), + + /** Other Components */ + fragment: uiComponentName('Fragment'), + item: uiComponentName('Item'), + listView: uiComponentName('ListView'), + picker: uiComponentName('Picker'), + section: uiComponentName('Section'), +} as const; + +export type ElementName = typeof ELEMENT_NAME; + +export const ELEMENT_PREFIX = { + iconPrefix: 'deephaven.ui.icons.' as const, + htmlPrefix: 'deephaven.ui.html.' as const, +}; + +export type ElementPrefix = { + iconPrefix: `${typeof ELEMENT_PREFIX.iconPrefix}${keyof typeof icons}`; + htmlPrefix: `${typeof ELEMENT_PREFIX.htmlPrefix}${keyof ReactHTML}`; +}; diff --git a/plugins/ui/src/js/src/elements/HTMLElementUtils.ts b/plugins/ui/src/js/src/elements/HTMLElementUtils.ts index d7926c48c..4afd5ecd5 100644 --- a/plugins/ui/src/js/src/elements/HTMLElementUtils.ts +++ b/plugins/ui/src/js/src/elements/HTMLElementUtils.ts @@ -1,5 +1,5 @@ import { ReactHTML } from 'react'; -import { HTMLElementType, HTML_ELEMENT_NAME_PREFIX } from './ElementConstants'; +import { ELEMENT_PREFIX, ElementPrefix } from './ElementConstants'; import { ELEMENT_KEY, ElementNode, isElementNode } from './ElementUtils'; /** @@ -8,12 +8,12 @@ import { ELEMENT_KEY, ElementNode, isElementNode } from './ElementUtils'; * For example, `deephaven.ui.html.div` would be rendered as `
`. * The props are passed directly to the HTML element as attributes. */ -export type HTMLElementNode = ElementNode; +export type HTMLElementNode = ElementNode; export function isHTMLElementNode(obj: unknown): obj is HTMLElementNode { return ( isElementNode(obj) && - (obj as HTMLElementNode)[ELEMENT_KEY].startsWith(HTML_ELEMENT_NAME_PREFIX) + (obj as HTMLElementNode)[ELEMENT_KEY].startsWith(ELEMENT_PREFIX.htmlPrefix) ); } @@ -22,6 +22,6 @@ export function isHTMLElementNode(obj: unknown): obj is HTMLElementNode { * @param name Name of the element * @returns The HTML tag name for the element */ -export function getHTMLTag(name: HTMLElementType): keyof ReactHTML { - return name.substring(HTML_ELEMENT_NAME_PREFIX.length) as keyof ReactHTML; +export function getHTMLTag(name: ElementPrefix['htmlPrefix']): keyof ReactHTML { + return name.substring(ELEMENT_PREFIX.htmlPrefix.length) as keyof ReactHTML; } diff --git a/plugins/ui/src/js/src/elements/IconElementUtils.ts b/plugins/ui/src/js/src/elements/IconElementUtils.ts index 871d23a25..a3c76efd4 100644 --- a/plugins/ui/src/js/src/elements/IconElementUtils.ts +++ b/plugins/ui/src/js/src/elements/IconElementUtils.ts @@ -1,5 +1,5 @@ import * as icons from '@deephaven/icons'; -import { IconElementName, ICON_ELEMENT_TYPE_PREFIX } from './ElementConstants'; +import { ELEMENT_PREFIX, ElementPrefix } from './ElementConstants'; import { ELEMENT_KEY, ElementNode, isElementNode } from './ElementUtils'; /** @@ -8,17 +8,19 @@ import { ELEMENT_KEY, ElementNode, isElementNode } from './ElementUtils'; * For example, `deephaven.ui.icons.vsBell` will render the icon named `vsBell`. * The props are passed directly to the icon component. */ -export type IconElementNode = ElementNode; +export type IconElementNode = ElementNode; export function isIconElementNode(obj: unknown): obj is IconElementNode { return ( isElementNode(obj) && - (obj as IconElementNode)[ELEMENT_KEY].startsWith(ICON_ELEMENT_TYPE_PREFIX) + (obj as IconElementNode)[ELEMENT_KEY].startsWith(ELEMENT_PREFIX.iconPrefix) ); } -export function getIcon(name: IconElementName): icons.IconDefinition { +export function getIcon( + name: ElementPrefix['iconPrefix'] +): icons.IconDefinition { return icons[ - name.substring(ICON_ELEMENT_TYPE_PREFIX.length) as keyof typeof icons + name.substring(ELEMENT_PREFIX.iconPrefix.length) as keyof typeof icons ] as icons.IconDefinition; } diff --git a/plugins/ui/src/js/src/elements/SpectrumElementUtils.ts b/plugins/ui/src/js/src/elements/SpectrumElementUtils.ts index 6a07ce442..623694ee1 100644 --- a/plugins/ui/src/js/src/elements/SpectrumElementUtils.ts +++ b/plugins/ui/src/js/src/elements/SpectrumElementUtils.ts @@ -1,6 +1,7 @@ import { ButtonGroup, Checkbox } from '@adobe/react-spectrum'; import { ValueOf } from '@deephaven/utils'; import { + ActionGroup, Content, ContextualHelp, Grid, @@ -8,6 +9,7 @@ import { Icon, Item, IllustratedMessage, + ListActionGroup, NumberField, Switch, Tabs, diff --git a/plugins/ui/src/js/src/elements/UITableUtils.tsx b/plugins/ui/src/js/src/elements/UITableUtils.tsx index b86dc7bb4..e9a6d48fb 100644 --- a/plugins/ui/src/js/src/elements/UITableUtils.tsx +++ b/plugins/ui/src/js/src/elements/UITableUtils.tsx @@ -1,7 +1,7 @@ import type { dh } from '@deephaven/jsapi-types'; import { ColumnName, DehydratedSort, RowIndex } from '@deephaven/iris-grid'; import { ELEMENT_KEY, ElementNode, isElementNode } from './ElementUtils'; -import { UITableElementName, UITABLE_ELEMENT_TYPE } from './ElementConstants'; +import { ELEMENT_NAME, ElementName } from './ElementConstants'; export type CellData = { type: string; @@ -37,12 +37,12 @@ export interface UITableProps { } export type UITableNode = Required< - ElementNode + ElementNode >; export function isUITable(obj: unknown): obj is UITableNode { return ( isElementNode(obj) && - (obj as UITableNode)[ELEMENT_KEY] === UITABLE_ELEMENT_TYPE + (obj as UITableNode)[ELEMENT_KEY] === ELEMENT_NAME.uiTable ); } diff --git a/plugins/ui/src/js/src/layout/LayoutUtils.tsx b/plugins/ui/src/js/src/layout/LayoutUtils.tsx index 459565c05..6cca370dd 100644 --- a/plugins/ui/src/js/src/layout/LayoutUtils.tsx +++ b/plugins/ui/src/js/src/layout/LayoutUtils.tsx @@ -13,18 +13,7 @@ import Column from './Column'; import Row from './Row'; import Stack from './Stack'; import ReactPanel from './ReactPanel'; -import { - ColumnElementType, - COLUMN_ELEMENT_NAME, - DashboardElementType, - DASHBOARD_ELEMENT_NAME, - PanelElementType, - PANEL_ELEMENT_NAME, - RowElementType, - ROW_ELEMENT_NAME, - StackElementType, - STACK_ELEMENT_NAME, -} from '../elements/ElementConstants'; +import { ElementName, ELEMENT_NAME } from '../elements/ElementConstants'; export type GoldenLayoutParent = RowOrColumn | GLStack | Root; @@ -37,7 +26,10 @@ export type ReactPanelProps = React.PropsWithChildren<{ * Describes a panel element that can be rendered in the UI. * Will be placed in the current dashboard, or within a user created dashboard if specified. */ -export type PanelElementNode = ElementNode; +export type PanelElementNode = ElementNode< + ElementName['panel'], + ReactPanelProps +>; /** * Check if an object is a PanelElementNode @@ -47,7 +39,7 @@ export type PanelElementNode = ElementNode; export function isPanelElementNode(obj: unknown): obj is PanelElementNode { return ( isElementNode(obj) && - (obj as ElementNode)[ELEMENT_KEY] === PANEL_ELEMENT_NAME + (obj as ElementNode)[ELEMENT_KEY] === ELEMENT_NAME.panel ); } @@ -58,7 +50,7 @@ export type RowElementProps = React.PropsWithChildren<{ /** * Describes a row element that can be rendered in the UI. */ -export type RowElementNode = ElementNode; +export type RowElementNode = ElementNode; /** * Check if an object is a RowElementNode @@ -67,7 +59,7 @@ export type RowElementNode = ElementNode; */ export function isRowElementNode(obj: unknown): obj is RowElementNode { return ( - isElementNode(obj) && (obj as ElementNode)[ELEMENT_KEY] === ROW_ELEMENT_NAME + isElementNode(obj) && (obj as ElementNode)[ELEMENT_KEY] === ELEMENT_NAME.row ); } @@ -79,7 +71,7 @@ export type ColumnElementProps = React.PropsWithChildren<{ * Describes a column element that can be rendered in the UI. */ export type ColumnElementNode = ElementNode< - ColumnElementType, + ElementName['column'], ColumnElementProps >; @@ -91,7 +83,7 @@ export type ColumnElementNode = ElementNode< export function isColumnElementNode(obj: unknown): obj is ColumnElementNode { return ( isElementNode(obj) && - (obj as ElementNode)[ELEMENT_KEY] === COLUMN_ELEMENT_NAME + (obj as ElementNode)[ELEMENT_KEY] === ELEMENT_NAME.column ); } @@ -104,7 +96,10 @@ export type StackElementProps = React.PropsWithChildren<{ /** * Describes a stack element that can be rendered in the UI. */ -export type StackElementNode = ElementNode; +export type StackElementNode = ElementNode< + ElementName['stack'], + StackElementProps +>; /** * Check if an object is a StackElementNode @@ -114,7 +109,7 @@ export type StackElementNode = ElementNode; export function isStackElementNode(obj: unknown): obj is StackElementNode { return ( isElementNode(obj) && - (obj as ElementNode)[ELEMENT_KEY] === STACK_ELEMENT_NAME + (obj as ElementNode)[ELEMENT_KEY] === ELEMENT_NAME.stack ); } @@ -126,7 +121,7 @@ export type DashboardElementProps = React.PropsWithChildren< * Describes a dashboard element that can be rendered in the UI. */ export type DashboardElementNode = ElementNode< - DashboardElementType, + ElementName['dashboard'], DashboardElementProps >; @@ -140,7 +135,7 @@ export function isDashboardElementNode( ): obj is DashboardElementNode { return ( isElementNode(obj) && - (obj as ElementNode)[ELEMENT_KEY] === DASHBOARD_ELEMENT_NAME + (obj as ElementNode)[ELEMENT_KEY] === ELEMENT_NAME.dashboard ); } diff --git a/plugins/ui/src/js/src/widget/WidgetUtils.tsx b/plugins/ui/src/js/src/widget/WidgetUtils.tsx index 73172ae8c..ffe190997 100644 --- a/plugins/ui/src/js/src/widget/WidgetUtils.tsx +++ b/plugins/ui/src/js/src/widget/WidgetUtils.tsx @@ -4,6 +4,7 @@ import React, { ComponentType } from 'react'; // Importing `Item` and `Section` compnents directly since they should not be // wrapped due to how Spectrum collection components consume them. import { Item, Section } from '@deephaven/components'; +import { ValueOf } from '@deephaven/utils'; import { ReadonlyWidgetData } from './WidgetTypes'; import { ElementNode, @@ -18,19 +19,7 @@ import SpectrumElementView from '../elements/SpectrumElementView'; import { isIconElementNode } from '../elements/IconElementUtils'; import IconElementView from '../elements/IconElementView'; import UITable from '../elements/UITable'; -import { - COLUMN_ELEMENT_NAME, - DASHBOARD_ELEMENT_NAME, - FRAGMENT_ELEMENT_NAME, - ITEM_ELEMENT_NAME, - LIST_VIEW_NAME, - PANEL_ELEMENT_NAME, - PICKER_ELEMENT_NAME, - ROW_ELEMENT_NAME, - SECTION_ELEMENT_NAME, - STACK_ELEMENT_NAME, - UITABLE_ELEMENT_TYPE, -} from '../elements/ElementConstants'; +import { ELEMENT_NAME, ElementName } from '../elements/ElementConstants'; import ReactPanel from '../layout/ReactPanel'; import Row from '../layout/Row'; import Stack from '../layout/Stack'; @@ -43,18 +32,23 @@ import Picker from '../elements/Picker'; * Map element node names to their corresponding React components */ export const elementComponentMap = { - [PANEL_ELEMENT_NAME]: ReactPanel, - [ROW_ELEMENT_NAME]: Row, - [COLUMN_ELEMENT_NAME]: Column, - [FRAGMENT_ELEMENT_NAME]: React.Fragment, - [STACK_ELEMENT_NAME]: Stack, - [DASHBOARD_ELEMENT_NAME]: Dashboard, - [ITEM_ELEMENT_NAME]: Item, - [LIST_VIEW_NAME]: ListView, - [PICKER_ELEMENT_NAME]: Picker, - [SECTION_ELEMENT_NAME]: Section, - [UITABLE_ELEMENT_TYPE]: UITable, -} as const; + // Elements + [ELEMENT_NAME.uiTable]: UITable, + + // Layout + [ELEMENT_NAME.column]: Column, + [ELEMENT_NAME.dashboard]: Dashboard, + [ELEMENT_NAME.panel]: ReactPanel, + [ELEMENT_NAME.row]: Row, + [ELEMENT_NAME.stack]: Stack, + + // Other components + [ELEMENT_NAME.fragment]: React.Fragment, + [ELEMENT_NAME.item]: Item, + [ELEMENT_NAME.listView]: ListView, + [ELEMENT_NAME.picker]: Picker, + [ELEMENT_NAME.section]: Section, +} as const satisfies Record, unknown>; export function getComponentTypeForElement

>( element: ElementNode