diff --git a/vuu-ui/package-lock.json b/vuu-ui/package-lock.json index a5edb95de..15c18686b 100644 --- a/vuu-ui/package-lock.json +++ b/vuu-ui/package-lock.json @@ -2986,10 +2986,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/app-vuu-basket-trader": { - "resolved": "sample-apps/app-vuu-basket-trader", - "link": true - }, "node_modules/app-vuu-example": { "resolved": "sample-apps/app-vuu-example", "link": true @@ -11929,6 +11925,7 @@ "version": "0.0.26", "license": "Apache-2.0", "dependencies": { + "@finos/vuu-filters": "0.0.26", "@finos/vuu-popups": "0.0.26", "@finos/vuu-table": "0.0.26", "@finos/vuu-table-extras": "0.0.26", @@ -12089,6 +12086,7 @@ }, "sample-apps/app-vuu-basket-trader": { "version": "0.0.26", + "extraneous": true, "license": "Apache-2.0", "dependencies": { "@finos/vuu-data": "0.0.26", @@ -12123,9 +12121,7 @@ "@finos/vuu-utils": "0.0.26", "@fontsource/open-sans": "^4.5.13", "@salt-ds/core": "1.8.0", - "@salt-ds/icons": "1.5.1", "@salt-ds/lab": "1.0.0-alpha.15", - "@salt-ds/theme": "1.7.1", "classnames": "^2.3.1", "react": "^17.0.2", "react-dom": "^17.0.2" @@ -13219,6 +13215,7 @@ "@finos/vuu-layout": { "version": "file:packages/vuu-layout", "requires": { + "@finos/vuu-filters": "0.0.26", "@finos/vuu-popups": "0.0.26", "@finos/vuu-table": "0.0.26", "@finos/vuu-table-extras": "0.0.26", @@ -14605,24 +14602,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true }, - "app-vuu-basket-trader": { - "version": "file:sample-apps/app-vuu-basket-trader", - "requires": { - "@finos/vuu-data": "0.0.26", - "@finos/vuu-data-react": "0.0.26", - "@finos/vuu-datagrid-types": "0.0.26", - "@finos/vuu-layout": "0.0.26", - "@finos/vuu-popups": "0.0.26", - "@finos/vuu-shell": "0.0.26", - "@finos/vuu-utils": "0.0.26", - "@fontsource/open-sans": "^4.5.13", - "@salt-ds/core": "1.8.0", - "@salt-ds/lab": "1.0.0-alpha.15", - "classnames": "^2.3.1", - "react": "^17.0.2", - "react-dom": "^17.0.2" - } - }, "app-vuu-example": { "version": "file:sample-apps/app-vuu-example", "requires": { @@ -14635,9 +14614,7 @@ "@finos/vuu-utils": "0.0.26", "@fontsource/open-sans": "^4.5.13", "@salt-ds/core": "1.8.0", - "@salt-ds/icons": "1.5.1", "@salt-ds/lab": "1.0.0-alpha.15", - "@salt-ds/theme": "1.7.1", "classnames": "^2.3.1", "react": "^17.0.2", "react-dom": "^17.0.2" diff --git a/vuu-ui/packages/vuu-data/src/json-data-source.ts b/vuu-ui/packages/vuu-data/src/json-data-source.ts index 23472a741..8bc2e8be8 100644 --- a/vuu-ui/packages/vuu-data/src/json-data-source.ts +++ b/vuu-ui/packages/vuu-data/src/json-data-source.ts @@ -209,6 +209,19 @@ export class JsonDataSource console.log("noop"); return this; } + set data(data: JsonData) { + console.log(`set JsonDataSource data`); + [this.columnDescriptors, this.#data] = jsonToDataSourceRows(data); + this.visibleRows = this.#data + .filter((row) => row[DEPTH] === 0) + .map((row, index) => + ([index, index] as Partial).concat(row.slice(2)) + ) as DataSourceRow[]; + + requestAnimationFrame(() => { + this.sendRowsToClient(); + }); + } select(selected: Selection) { const updatedRows: DataSourceRow[] = []; @@ -324,10 +337,6 @@ export class JsonDataSource this.#aggregations = aggregations; } - set data(data: JsonData) { - console.log(`set JsonDataSource data`); - } - get sort() { return this.#sort; } diff --git a/vuu-ui/packages/vuu-filters/src/filter-clause/ExpandoCombobox.tsx b/vuu-ui/packages/vuu-filters/src/filter-clause/ExpandoCombobox.tsx index 1b1aa3933..03f201514 100644 --- a/vuu-ui/packages/vuu-filters/src/filter-clause/ExpandoCombobox.tsx +++ b/vuu-ui/packages/vuu-filters/src/filter-clause/ExpandoCombobox.tsx @@ -103,8 +103,8 @@ export const ExpandoCombobox = forwardRef(function ExpandoCombobox< displayedItemCount: 10, itemHeight: 22, maxWidth: 300, - minWidth: 100, - width: "auto", + minWidth: 80, + width: "content-width", }, ]; }, [InputPropsProp, handleInputChange, ListPropsProp]); @@ -128,7 +128,7 @@ export const ExpandoCombobox = forwardRef(function ExpandoCombobox< ); const popupProps = { - minWidth: 102, + minWidth: "fit-content", }; return props.source?.length === 0 ? null : ( diff --git a/vuu-ui/packages/vuu-popups/src/popup/Popup.tsx b/vuu-ui/packages/vuu-popups/src/popup/Popup.tsx index 893b7273e..88192586d 100644 --- a/vuu-ui/packages/vuu-popups/src/popup/Popup.tsx +++ b/vuu-ui/packages/vuu-popups/src/popup/Popup.tsx @@ -14,7 +14,7 @@ export type PopupPlacement = export interface PopupComponentProps extends HTMLAttributes { anchorElement: RefObject; - minWidth?: number; + minWidth?: number | string; offsetLeft?: number; offsetTop?: number; placement: PopupPlacement; diff --git a/vuu-ui/packages/vuu-shell/src/layout-management/useLayoutManager.tsx b/vuu-ui/packages/vuu-shell/src/layout-management/useLayoutManager.tsx index 9785c7c6a..11a9f71f6 100644 --- a/vuu-ui/packages/vuu-shell/src/layout-management/useLayoutManager.tsx +++ b/vuu-ui/packages/vuu-shell/src/layout-management/useLayoutManager.tsx @@ -72,6 +72,7 @@ export const LayoutManagementProvider = ( const saveApplicationLayout = useCallback( (layout: LayoutJSON) => { + console.log(`save application layout ${JSON.stringify(layout, null, 2)}`); const persistenceManager = getPersistenceManager(); setApplicationLayout(layout, false); persistenceManager.saveApplicationLayout(layout); diff --git a/vuu-ui/packages/vuu-shell/src/left-nav/LeftNav.css b/vuu-ui/packages/vuu-shell/src/left-nav/LeftNav.css index 7cef09867..e05b72301 100644 --- a/vuu-ui/packages/vuu-shell/src/left-nav/LeftNav.css +++ b/vuu-ui/packages/vuu-shell/src/left-nav/LeftNav.css @@ -14,11 +14,16 @@ --svg-layouts: url('data:image/svg+xml;utf8,'); --vuu-light-text-primary: #15171b; + --menu-level-2-width: 0px; + box-shadow: 3px 4px 4px 0px rgba(0, 0, 0, 0.15); + display: flex; margin-bottom: 4px; + overflow: hidden; position: relative; - transition: width .3s ease-in-out; + transition: width .2s ease-out; z-index: 0; + width: calc(var(--menu-width) + var(--menu-level-2-width)); } @@ -32,10 +37,12 @@ .vuuLeftNav-menu-icons-content { --menu-width: var(--nav-menu-collapsed-width); + --menu-level-2-width: var(--nav-menu-content-width); } .vuuLeftNav-menu-full-content { --menu-width: var(--nav-menu-expanded-width); + --menu-level-2-width: var(--nav-menu-content-width); } .vuuLeftNav-menu-icons-content .vuuLeftNav-menu-secondary, @@ -50,18 +57,22 @@ flex-direction: column; height: 100%; padding: 32px 16px; - transition: width ease .3s; - width: var(--menu-width, 100%); + transition: flex-basis ease-out .2s; + flex-grow:0; + flex-shrink:0; + flex-basis: var(--menu-width); } .vuuLeftNav-menu-secondary { flex: 1 1 auto; height: 100%; display: none; - position: absolute; + /* position: absolute; */ top:0; right: 0; - width: var(--nav-menu-content-width, 240px); + flex-grow:0; + flex-shrink:0; + flex-basis: var(--nav-menu-content-width, 240px); z-index: -1; } @@ -98,9 +109,6 @@ padding: 0; } -/* .vuuLeftNav [data-icon]:after { - transition: left ease .3s; -} */ .vuuLeftNav [data-icon='demo'] { --vuu-icon-svg: var(--svg-demo); diff --git a/vuu-ui/packages/vuu-shell/src/left-nav/LeftNav.tsx b/vuu-ui/packages/vuu-shell/src/left-nav/LeftNav.tsx index 722287349..f13540818 100644 --- a/vuu-ui/packages/vuu-shell/src/left-nav/LeftNav.tsx +++ b/vuu-ui/packages/vuu-shell/src/left-nav/LeftNav.tsx @@ -13,30 +13,39 @@ import "./LeftNav.css"; const classBase = "vuuLeftNav"; +export type NavDisplayStatus = + | "menu-full" + | "menu-icons" + | "menu-full-content" + | "menu-icons-content"; + +export type NavDisplayStatusHandler = ( + navDisplayStatus: NavDisplayStatus +) => void; interface LeftNavProps extends HTMLAttributes { "data-path"?: string; + defaultActiveTabIndex?: number; + defaultDisplayStatus?: NavDisplayStatus; features: FeatureProps[]; tableFeatures: FeatureProps[]; + onChangeDisplayStatus?: NavDisplayStatusHandler; onResize?: (size: number) => void; sizeCollapsed?: number; sizeContent?: number; sizeExpanded?: number; } -type NavStatus = - | "menu-full" - | "menu-icons" - | "menu-full-content" - | "menu-icons-content"; - type NavState = { activeTabIndex: number; - navStatus: NavStatus; + navStatus: NavDisplayStatus; }; export const LeftNav = ({ "data-path": path, + defaultDisplayStatus = "menu-full", + defaultActiveTabIndex = 0, features, + onChangeDisplayStatus, onResize, sizeCollapsed = 80, sizeContent = 300, @@ -47,13 +56,14 @@ export const LeftNav = ({ }: LeftNavProps) => { const dispatch = useLayoutProviderDispatch(); const [navState, setNavState] = useState({ - activeTabIndex: 0, - navStatus: "menu-full", + activeTabIndex: defaultActiveTabIndex, + navStatus: defaultDisplayStatus, }); const [themeClass] = useThemeAttributes(); + console.log(`navState ${navState}`); const toggleNavWidth = useCallback( - (navStatus: NavStatus) => { + (navStatus: NavDisplayStatus) => { switch (navStatus) { case "menu-icons": return sizeExpanded; @@ -68,7 +78,7 @@ export const LeftNav = ({ [sizeCollapsed, sizeContent, sizeExpanded] ); - const toggleNavStatus = (navStatus: NavStatus) => { + const toggleNavStatus = (navStatus: NavDisplayStatus) => { switch (navStatus) { case "menu-icons": return "menu-full"; @@ -82,7 +92,10 @@ export const LeftNav = ({ }; const getWidthAndStatus = useCallback( - (navState: NavStatus, tabIndex: number): [number, NavStatus] => { + ( + navState: NavDisplayStatus, + tabIndex: number + ): [number, NavDisplayStatus] => { if (tabIndex === 0) { const newNavState = navState === "menu-full-content" @@ -127,16 +140,18 @@ export const LeftNav = ({ const toggleSize = useCallback(() => { const { activeTabIndex, navStatus: currentNavStatus } = navState; + const newNavStatus = toggleNavStatus(currentNavStatus); setNavState({ activeTabIndex, - navStatus: toggleNavStatus(currentNavStatus), + navStatus: newNavStatus, }); dispatch({ type: Action.LAYOUT_RESIZE, path, size: toggleNavWidth(currentNavStatus), } as LayoutResizeAction); - }, [dispatch, navState, path, toggleNavWidth]); + onChangeDisplayStatus?.(newNavStatus); + }, [dispatch, navState, onChangeDisplayStatus, path, toggleNavWidth]); const style = { ...styleProp, diff --git a/vuu-ui/packages/vuu-table/src/table-next/context-menu/index.ts b/vuu-ui/packages/vuu-table/src/table-next/context-menu/index.ts index 8c6db9865..9c7d25131 100644 --- a/vuu-ui/packages/vuu-table/src/table-next/context-menu/index.ts +++ b/vuu-ui/packages/vuu-table/src/table-next/context-menu/index.ts @@ -1,2 +1,2 @@ export * from "./buildContextMenuDescriptors"; -export * from "./useTableContextMenu"; +export * from "./useHandleTableContextMenu"; diff --git a/vuu-ui/packages/vuu-table/src/table-next/context-menu/useTableContextMenu.ts b/vuu-ui/packages/vuu-table/src/table-next/context-menu/useHandleTableContextMenu.ts similarity index 99% rename from vuu-ui/packages/vuu-table/src/table-next/context-menu/useTableContextMenu.ts rename to vuu-ui/packages/vuu-table/src/table-next/context-menu/useHandleTableContextMenu.ts index fc3813a85..70b84b051 100644 --- a/vuu-ui/packages/vuu-table/src/table-next/context-menu/useTableContextMenu.ts +++ b/vuu-ui/packages/vuu-table/src/table-next/context-menu/useHandleTableContextMenu.ts @@ -49,7 +49,7 @@ const removeFilterColumn = ( const { Average, Count, Distinct, High, Low, Sum } = AggregationType; -export const useTableContextMenu = ({ +export const useHandleTableContextMenu = ({ dataSource, onPersistentColumnOperation, }: ContextMenuHookProps) => { diff --git a/vuu-ui/packages/vuu-table/src/table-next/header-cell/HeaderCell.css b/vuu-ui/packages/vuu-table/src/table-next/header-cell/HeaderCell.css index 8522a0901..e9a9a58cb 100644 --- a/vuu-ui/packages/vuu-table/src/table-next/header-cell/HeaderCell.css +++ b/vuu-ui/packages/vuu-table/src/table-next/header-cell/HeaderCell.css @@ -34,6 +34,7 @@ line-height: calc(var(--header-height) - 1px); overflow: hidden; text-overflow: ellipsis; + white-space: nowrap; } .vuuTableNextHeaderCell-right .vuuTableNextHeaderCell-label { diff --git a/vuu-ui/packages/vuu-table/src/table-next/useTableContextMenu.ts b/vuu-ui/packages/vuu-table/src/table-next/useTableContextMenu.ts index 339539b61..945e98938 100644 --- a/vuu-ui/packages/vuu-table/src/table-next/useTableContextMenu.ts +++ b/vuu-ui/packages/vuu-table/src/table-next/useTableContextMenu.ts @@ -45,7 +45,7 @@ export const useTableContextMenu = ({ }); } }, - [columns, data, dataSource, showContextMenu] + [columns, data, dataSource, getSelectedRows, showContextMenu] ); return onContextMenu; diff --git a/vuu-ui/packages/vuu-table/src/table-next/useTableModel.ts b/vuu-ui/packages/vuu-table/src/table-next/useTableModel.ts index 4b963d86b..2c0211a87 100644 --- a/vuu-ui/packages/vuu-table/src/table-next/useTableModel.ts +++ b/vuu-ui/packages/vuu-table/src/table-next/useTableModel.ts @@ -21,7 +21,7 @@ import { isTypeDescriptor, logger, metadataKeys, - moveItem, + replaceColumn, sortPinnedColumns, stripFilterFromColumns, subscribedOnly, @@ -107,7 +107,6 @@ export interface ColumnActionMove { type: "moveColumn"; column: KeyedColumnDescriptor; moveBy?: 1 | -1; - moveTo?: number; } export interface ColumnActionPin { @@ -331,7 +330,8 @@ const columnDescriptorToKeyedColumDescriptor = function moveColumn( state: InternalTableModel, - { column, moveBy, moveTo }: ColumnActionMove + // TODO do we ever use this ? + { column, moveBy }: ColumnActionMove ) { const { columns } = state; if (typeof moveBy === "number") { @@ -343,12 +343,6 @@ function moveColumn( ...state, columns: newColumns, }; - } else if (typeof moveTo === "number") { - const index = columns.indexOf(column); - return { - ...state, - columns: moveItem(columns, index, moveTo), - }; } return state; } @@ -513,10 +507,3 @@ function updateTableConfig( return result; } - -function replaceColumn( - state: KeyedColumnDescriptor[], - column: KeyedColumnDescriptor -) { - return state.map((col) => (col.name === column.name ? column : col)); -} diff --git a/vuu-ui/packages/vuu-table/src/table-next/useTableNext.ts b/vuu-ui/packages/vuu-table/src/table-next/useTableNext.ts index 9e8760d92..dafbe4099 100644 --- a/vuu-ui/packages/vuu-table/src/table-next/useTableNext.ts +++ b/vuu-ui/packages/vuu-table/src/table-next/useTableNext.ts @@ -24,6 +24,7 @@ import { isJsonGroup, isValidNumber, metadataKeys, + moveColumnTo, updateColumn, visibleColumnAtIndex, } from "@finos/vuu-utils"; @@ -38,17 +39,19 @@ import { } from "react"; import { buildContextMenuDescriptors, + ColumnActionHide, + ColumnActionPin, MeasuredProps, RowClickHandler, TableProps, useSelection, - useTableContextMenu, } from "../table"; import { TableColumnResizeHandler } from "./column-resizing"; import { updateTableConfig } from "./table-config"; import { useDataSource } from "./useDataSource"; import { useInitialValue } from "./useInitialValue"; -import { useTableContextMenu as useTableContextMenuNext } from "./useTableContextMenu"; +import { useTableContextMenu } from "./useTableContextMenu"; +import { useHandleTableContextMenu } from "./context-menu"; import { useCellEditing } from "./useCellEditing"; import { isShowColumnSettings, @@ -144,6 +147,18 @@ export const useTable = ({ }); }, [tableConfig, dataSource.config, dispatchColumnAction]); + const applyTableConfigChange = useCallback( + (config: TableConfig) => { + dispatchColumnAction({ + type: "init", + tableConfig: config, + dataSourceConfig: dataSource.config, + }); + onConfigChange?.(config); + }, + [dataSource.config, dispatchColumnAction, onConfigChange] + ); + const [stateColumns, setStateColumns] = useState(); const [columns, setColumnSize] = useMemo(() => { const setSize = (columnName: string, width: number) => { @@ -202,6 +217,11 @@ export const useTable = ({ const handleConfigChanged = useCallback( (tableConfig: TableConfig) => { + console.log( + `useTableNext handleConfigChanged`, + JSON.stringify(tableConfig, null, 2) + ); + dispatchColumnAction({ type: "init", tableConfig, @@ -222,23 +242,6 @@ export const useTable = ({ [dataSource] ); - const handleCreateCalculatedColumn = useCallback( - (column: ColumnDescriptor) => { - dataSource.columns = dataSource.columns.concat(column.name); - const newTableConfig = addColumn(tableConfig, column); - dispatchColumnAction({ - type: "init", - tableConfig: newTableConfig, - dataSourceConfig: dataSource.config, - }); - console.log(`dispatch onConfigChange`, { - newTableConfig, - }); - onConfigChange?.(newTableConfig); - }, - [dataSource, dispatchColumnAction, onConfigChange, tableConfig] - ); - useEffect(() => { dataSource.on("config", (config, confirmed) => { dispatchColumnAction({ @@ -249,6 +252,42 @@ export const useTable = ({ }); }, [dataSource, dispatchColumnAction]); + const handleCreateCalculatedColumn = useCallback( + (column: ColumnDescriptor) => { + dataSource.columns = dataSource.columns.concat(column.name); + applyTableConfigChange(addColumn(tableConfig, column)); + }, + [dataSource, tableConfig, applyTableConfigChange] + ); + + const hideColumns = useCallback( + (action: ColumnActionHide) => { + const { columns } = action; + const hiddenColumns = columns.map((c) => c.name); + const newTableConfig = { + ...tableConfig, + columns: tableConfig.columns.map((col) => + hiddenColumns.includes(col.name) ? { ...col, hidden: true } : col + ), + }; + applyTableConfigChange(newTableConfig); + }, + [tableConfig, applyTableConfigChange] + ); + + const pinColumn = useCallback( + (action: ColumnActionPin) => { + applyTableConfigChange({ + ...tableConfig, + columns: updateColumn(tableConfig.columns, { + ...action.column, + pin: action.pin, + }), + }); + }, + [tableConfig, applyTableConfigChange] + ); + const { showColumnSettingsPanel, showTableSettingsPanel } = useTableAndColumnSettings({ availableColumns: @@ -271,14 +310,26 @@ export const useTable = ({ } else if (isShowTableSettings(action)) { showTableSettingsPanel(); } else { - // expectConfigChangeRef.current = true; - dispatchColumnAction(action); + switch (action.type) { + case "hideColumns": + return hideColumns(action); + case "pinColumn": + return pinColumn(action); + default: + dispatchColumnAction(action); + } } }, - [dispatchColumnAction, showColumnSettingsPanel, showTableSettingsPanel] + [ + dispatchColumnAction, + hideColumns, + pinColumn, + showColumnSettingsPanel, + showTableSettingsPanel, + ] ); - const handleContextMenuAction = useTableContextMenu({ + const handleContextMenuAction = useHandleTableContextMenu({ dataSource, onPersistentColumnOperation, }); @@ -429,7 +480,7 @@ export const useTable = ({ [navigationKeyDown, editingKeyDown] ); - const onContextMenu = useTableContextMenuNext({ + const onContextMenu = useTableContextMenu({ columns, data, dataSource, @@ -500,16 +551,21 @@ export const useTable = ({ const handleDrop = useCallback( (moveFrom: number, moveTo: number) => { - // onMoveColumn?.(fromIndex, toIndex); - const column = columns[moveFrom]; + const column = tableConfig.columns[moveFrom]; + + const newTableConfig = { + ...tableConfig, + columns: moveColumnTo(tableConfig.columns, column, moveTo), + }; dispatchColumnAction({ - type: "moveColumn", - column, - moveTo, + type: "init", + tableConfig: newTableConfig, + dataSourceConfig: dataSource.config, }); + onConfigChange?.(newTableConfig); }, - [columns, dispatchColumnAction] + [dataSource.config, dispatchColumnAction, onConfigChange, tableConfig] ); const handleDataEdited = useCallback( diff --git a/vuu-ui/packages/vuu-table/src/table/context-menu/useTableContextMenu.ts b/vuu-ui/packages/vuu-table/src/table/context-menu/useTableContextMenu.ts index db4ec0320..fc3813a85 100644 --- a/vuu-ui/packages/vuu-table/src/table/context-menu/useTableContextMenu.ts +++ b/vuu-ui/packages/vuu-table/src/table/context-menu/useTableContextMenu.ts @@ -21,6 +21,11 @@ export interface ContextMenuOptions { } export interface ContextMenuHookProps { dataSource?: DataSource; + /** + * A persistent Column Operation is any manipulation of a table column that should be + * persisted across user sessions. e.g. if user pins a column, column should still be + * pinned next time user opens app. + */ onPersistentColumnOperation: (action: PersistentColumnAction) => void; } diff --git a/vuu-ui/packages/vuu-ui-controls/src/tabstrip/useAnimatedSelectionThumb.ts b/vuu-ui/packages/vuu-ui-controls/src/tabstrip/useAnimatedSelectionThumb.ts index beefb4049..03161598e 100644 --- a/vuu-ui/packages/vuu-ui-controls/src/tabstrip/useAnimatedSelectionThumb.ts +++ b/vuu-ui/packages/vuu-ui-controls/src/tabstrip/useAnimatedSelectionThumb.ts @@ -40,7 +40,6 @@ export const useAnimatedSelectionThumb = ( isValidNumber(newPosition) && isValidNumber(oldSize) ) { - console.log({ orientation, positionProp, oldPosition, newPosition }); offset = oldPosition - newPosition; size = oldSize; const speed = orientation === "horizontal" ? 1100 : 700; diff --git a/vuu-ui/packages/vuu-utils/src/column-utils.ts b/vuu-ui/packages/vuu-utils/src/column-utils.ts index 3bb469015..17bc6f4b2 100644 --- a/vuu-ui/packages/vuu-utils/src/column-utils.ts +++ b/vuu-ui/packages/vuu-utils/src/column-utils.ts @@ -28,6 +28,7 @@ import type { SchemaColumn } from "@finos/vuu-data"; import type { CSSProperties } from "react"; import type { CellRendererDescriptor } from "./component-registry"; import { isFilterClause, isMultiClauseFilter } from "./filter-utils"; +import { moveItem } from "./array-utils"; /** * ColumnMap provides a lookup of the index position of a data item within a row @@ -614,10 +615,10 @@ export const findColumn = ( } }; -export function updateColumn( - columns: KeyedColumnDescriptor[], - column: KeyedColumnDescriptor -): KeyedColumnDescriptor[]; +export function updateColumn( + columns: T[], + column: T +): T[]; export function updateColumn( columns: KeyedColumnDescriptor[], column: string, @@ -936,3 +937,19 @@ export const setCalculatedColumnExpression = ( name: `${name}:${type}:=${expression}`, }; }; + +export const moveColumnTo = ( + columns: ColumnDescriptor[], + column: ColumnDescriptor, + newIndex: number +) => { + const index = columns.indexOf(column); + return moveItem(columns, index, newIndex); +}; + +export function replaceColumn( + state: KeyedColumnDescriptor[], + column: KeyedColumnDescriptor +) { + return state.map((col) => (col.name === column.name ? column : col)); +} diff --git a/vuu-ui/packages/vuu-utils/src/json-utils.ts b/vuu-ui/packages/vuu-utils/src/json-utils.ts index 71e505675..0ddc5f1ac 100644 --- a/vuu-ui/packages/vuu-utils/src/json-utils.ts +++ b/vuu-ui/packages/vuu-utils/src/json-utils.ts @@ -41,6 +41,12 @@ const getCellValue = ( ): CellValue => { if (isJsonData(attributeValue)) { return { attribute: `${attribute}+`, attributeValue: "", type: "json" }; + } else if (attributeValue === undefined) { + return { + attribute, + attributeValue: "undefined", + type: "string", + }; } else if (isVuuRowDataItem(attributeValue)) { return { attribute, diff --git a/vuu-ui/sample-apps/feature-filter-table/src/useFilterTable.tsx b/vuu-ui/sample-apps/feature-filter-table/src/useFilterTable.tsx index e1b011728..073ee41b8 100644 --- a/vuu-ui/sample-apps/feature-filter-table/src/useFilterTable.tsx +++ b/vuu-ui/sample-apps/feature-filter-table/src/useFilterTable.tsx @@ -100,7 +100,6 @@ export const useFilterTable = ({ tableSchema }: FilterTableFeatureProps) => { const handleAvailableColumnsChange = useCallback( (columns: SchemaColumn[]) => { - console.log("save new available columns"); save?.(columns, "available-columns"); }, [save] @@ -108,7 +107,6 @@ export const useFilterTable = ({ tableSchema }: FilterTableFeatureProps) => { const handleTableConfigChange = useCallback( (config: TableConfig) => { - console.log(`table config changed`); save?.(config, "table-config"); }, [save] diff --git a/vuu-ui/showcase/src/examples/Shell/LeftNav.examples.tsx b/vuu-ui/showcase/src/examples/Shell/LeftNav.examples.tsx index aa00a8c25..0790df5b9 100644 --- a/vuu-ui/showcase/src/examples/Shell/LeftNav.examples.tsx +++ b/vuu-ui/showcase/src/examples/Shell/LeftNav.examples.tsx @@ -6,3 +6,38 @@ export const VerticalTabstrip = () => { return ; }; VerticalTabstrip.displaySequence = displaySequence++; + +export const VerticalTabstripCollapsed = () => { + return ( + + ); +}; +VerticalTabstripCollapsed.displaySequence = displaySequence++; + +export const VerticalTabstripCollapsedContent = () => { + return ( + + ); +}; +VerticalTabstripCollapsedContent.displaySequence = displaySequence++; + +export const VerticalTabstripContent = () => { + return ( + + ); +}; +VerticalTabstripContent.displaySequence = displaySequence++; diff --git a/vuu-ui/showcase/src/examples/VuuFeatures/FilterTableFeature.examples.tsx b/vuu-ui/showcase/src/examples/VuuFeatures/FilterTableFeature.examples.tsx index 551ddf9c3..3d5d6a432 100644 --- a/vuu-ui/showcase/src/examples/VuuFeatures/FilterTableFeature.examples.tsx +++ b/vuu-ui/showcase/src/examples/VuuFeatures/FilterTableFeature.examples.tsx @@ -6,9 +6,11 @@ import { View, } from "@finos/vuu-layout"; import { Feature, FeatureProps, useLayoutManager } from "@finos/vuu-shell"; -import { useCallback, useEffect } from "react"; +import { useCallback, useState } from "react"; import { FilterTableFeature } from "../../features/FilterTable.feature"; import { VuuBlotterHeader } from "./VuuBlotterHeader"; +import { JsonTable } from "@finos/vuu-datatable"; +import { JsonData } from "packages/vuu-utils/src"; registerComponent("FilterTableFeature", FilterTableFeature, "view"); @@ -24,31 +26,47 @@ export const DefaultFilterTableFeature = () => { //----------------------------------------------------------------------------------- const { applicationLayout, saveApplicationLayout } = useLayoutManager(); + // Save layout into state so we can display in JsonTable + const [savedLayoutJson, setSavedLayoutJson] = useState(applicationLayout); + const handleLayoutChange = useCallback( (layout) => { saveApplicationLayout(layout); + setSavedLayoutJson(layout); }, [saveApplicationLayout] ); // ---------------------------------------------------------------------------------- return ( - - + - - - + + + + +
+ +
+ ); }; DefaultFilterTableFeature.displaySequence = displaySequence++;