From 5ebffb964c411c7981bf1abeb1c697ffe4d9e7bf Mon Sep 17 00:00:00 2001 From: heswell Date: Sun, 18 Aug 2024 13:05:22 +0100 Subject: [PATCH] refactor feature and datasource provider useFullHeightLeftPanel addutuional filter changes --- .../VuuDataSourceProvider.tsx | 17 ++ .../vuu-data-react/src/hooks/useVuuTables.ts | 16 +- .../src/basket/basket-schemas.ts | 9 + .../vuu-data-test/src/basket/index.ts | 1 + .../LocalDatasourceProvider.tsx | 67 ++++++++ .../packages/vuu-data-test/src/simul/index.ts | 6 +- .../vuu-data-test/src/simul/simul-module.ts | 14 +- .../vuu-data-test/src/simul/simul-schemas.ts | 9 + .../vuu-filters/src/filter-bar/FilterBar.css | 4 +- .../vuu-filters/src/filter-bar/FilterBar.tsx | 16 +- .../DataSourceProvider.tsx | 47 ------ .../src/datasource-provider/index.ts | 1 - .../src/feature-list/FeatureList.tsx | 87 +++++----- .../src/feature-provider/FeatureProvider.tsx | 67 +++----- .../vuu-shell/src/feature/Feature.tsx | 12 +- .../src/feature/FeatureErrorBoundary.tsx | 4 +- vuu-ui/packages/vuu-shell/src/index.ts | 1 - .../vuu-shell/src/left-nav/LeftNav.tsx | 66 ++++---- .../useFullHeightLeftPanel.tsx | 4 +- .../inlay-left-panel/useInlayLeftPanel.tsx | 8 +- .../left-main-tabs/useLeftMainTabs.tsx | 57 +++++++ .../shell-layout-templates/useShellLayout.ts | 18 +- vuu-ui/packages/vuu-shell/src/shell.css | 4 + vuu-ui/packages/vuu-utils/src/ShellContext.ts | 6 +- .../context-definitions/DataSourceContext.tsx | 35 ++++ .../DataSourceProvider.tsx | 23 +++ .../packages/vuu-utils/src/feature-utils.ts | 64 +++---- vuu-ui/packages/vuu-utils/src/index.ts | 2 + .../vuu-utils/src/shell-layout-types.ts | 2 + .../sample-apps/app-vuu-example/src/App.tsx | 36 ++-- .../src/examples/Apps/NewTheme.examples.tsx | 126 -------------- ...me.examples.css => SampleApp.examples.css} | 0 .../src/examples/Apps/SampleApp.examples.tsx | 158 ++++++++++++++++++ vuu-ui/showcase/src/examples/Apps/index.ts | 2 +- .../FilterEditor/FilterEditor.examples.tsx | 12 +- .../examples/Shell/FeatureList.examples.tsx | 6 +- .../Shell/FeatureProvider.examples.tsx | 40 ++--- .../src/examples/Shell/LeftNav.examples.tsx | 22 +-- .../examples/Shell/ShellLayout.examples.tsx | 81 ++++++++- .../BasketTradingFeature.examples.tsx | 6 +- .../FilterTableFeature.examples.tsx | 6 +- .../InstrumentTilesFeature.examples.tsx | 4 +- 42 files changed, 709 insertions(+), 457 deletions(-) create mode 100644 vuu-ui/packages/vuu-data-react/src/datasource-provider/VuuDataSourceProvider.tsx create mode 100644 vuu-ui/packages/vuu-data-test/src/local-datasource-provider/LocalDatasourceProvider.tsx delete mode 100644 vuu-ui/packages/vuu-shell/src/datasource-provider/DataSourceProvider.tsx delete mode 100644 vuu-ui/packages/vuu-shell/src/datasource-provider/index.ts create mode 100644 vuu-ui/packages/vuu-shell/src/shell-layout-templates/left-main-tabs/useLeftMainTabs.tsx create mode 100644 vuu-ui/packages/vuu-utils/src/context-definitions/DataSourceContext.tsx create mode 100644 vuu-ui/packages/vuu-utils/src/context-definitions/DataSourceProvider.tsx delete mode 100644 vuu-ui/showcase/src/examples/Apps/NewTheme.examples.tsx rename vuu-ui/showcase/src/examples/Apps/{NewTheme.examples.css => SampleApp.examples.css} (100%) create mode 100644 vuu-ui/showcase/src/examples/Apps/SampleApp.examples.tsx diff --git a/vuu-ui/packages/vuu-data-react/src/datasource-provider/VuuDataSourceProvider.tsx b/vuu-ui/packages/vuu-data-react/src/datasource-provider/VuuDataSourceProvider.tsx new file mode 100644 index 000000000..056c90abc --- /dev/null +++ b/vuu-ui/packages/vuu-data-react/src/datasource-provider/VuuDataSourceProvider.tsx @@ -0,0 +1,17 @@ +import { VuuDataSource, getServerAPI } from "@finos/vuu-data-remote"; +import { DataSourceProvider } from "@finos/vuu-utils"; +import { ReactNode } from "react"; + +export const VuuDataSourceProvider = ({ + children, +}: { + children: ReactNode; +}) => ( + + {children} + +); diff --git a/vuu-ui/packages/vuu-data-react/src/hooks/useVuuTables.ts b/vuu-ui/packages/vuu-data-react/src/hooks/useVuuTables.ts index 328652086..6be2f5f2c 100644 --- a/vuu-ui/packages/vuu-data-react/src/hooks/useVuuTables.ts +++ b/vuu-ui/packages/vuu-data-react/src/hooks/useVuuTables.ts @@ -1,11 +1,12 @@ -import { getServerAPI } from "@finos/vuu-data-remote"; import type { TableSchema } from "@finos/vuu-data-types"; -import type { VuuTable } from "@finos/vuu-protocol-types"; +import { useDataSource } from "@finos/vuu-utils"; import { useCallback, useEffect, useState } from "react"; export const useVuuTables = () => { const [tables, setTables] = useState | undefined>(); + const { getServerAPI } = useDataSource(); + const buildTables = useCallback((schemas: TableSchema[]) => { const vuuTables = new Map(); schemas.forEach((schema) => { @@ -21,22 +22,19 @@ export const useVuuTables = () => { const { tables } = await server.getTableList(); const tableSchemas = buildTables( await Promise.all( - tables.map((vuuTable) => server.getTableSchema(vuuTable)) - ) + tables.map((vuuTable) => server.getTableSchema(vuuTable)), + ), ); setTables(tableSchemas); } catch (err) { console.warn( - `useVuuTables: unable to connect to Vuu server ${String(err)}` + `useVuuTables: error fetching table metedata ${String(err)}`, ); } } fetchTableMetadata(); - }, [buildTables]); + }, [buildTables, getServerAPI]); return tables; }; - -export const getVuuTableSchema = (table: VuuTable) => - getServerAPI().then((server) => server.getTableSchema(table)); diff --git a/vuu-ui/packages/vuu-data-test/src/basket/basket-schemas.ts b/vuu-ui/packages/vuu-data-test/src/basket/basket-schemas.ts index 122586490..a8700db34 100644 --- a/vuu-ui/packages/vuu-data-test/src/basket/basket-schemas.ts +++ b/vuu-ui/packages/vuu-data-test/src/basket/basket-schemas.ts @@ -1,4 +1,5 @@ import { TableSchema } from "@finos/vuu-data-types"; +import { VuuTable } from "@finos/vuu-protocol-types"; export type BasketsTableName = | "algoType" @@ -128,3 +129,11 @@ export const schemas: Readonly< table: { module: "BASKET", table: "priceStrategyType" }, }, }; + +export type BasketVuuTable = { + module: "BASKET"; + table: BasketsTableName; +}; + +export const isBasketTable = (table: VuuTable): table is BasketVuuTable => + table.module === "BASKET"; diff --git a/vuu-ui/packages/vuu-data-test/src/basket/index.ts b/vuu-ui/packages/vuu-data-test/src/basket/index.ts index c220740ad..4a828a03a 100644 --- a/vuu-ui/packages/vuu-data-test/src/basket/index.ts +++ b/vuu-ui/packages/vuu-data-test/src/basket/index.ts @@ -1,5 +1,6 @@ export { type BasketsTableName, schemas as basketSchemas, + isBasketTable, } from "./basket-schemas"; export * from "./basket-module"; diff --git a/vuu-ui/packages/vuu-data-test/src/local-datasource-provider/LocalDatasourceProvider.tsx b/vuu-ui/packages/vuu-data-test/src/local-datasource-provider/LocalDatasourceProvider.tsx new file mode 100644 index 000000000..14411d421 --- /dev/null +++ b/vuu-ui/packages/vuu-data-test/src/local-datasource-provider/LocalDatasourceProvider.tsx @@ -0,0 +1,67 @@ +import type { + DataSourceConstructorProps, + TableSchema, +} from "@finos/vuu-data-types"; +import type { VuuTable, VuuTableList } from "@finos/vuu-protocol-types"; +import { basketModule, basketSchemas, isBasketTable } from "../basket"; +import { isSimulTable, simulModule, simulSchemas } from "../simul"; +import { ReactNode } from "react"; +import { DataSourceProvider } from "@finos/vuu-utils"; + +interface ServerAPI { + getTableList: () => Promise; + getTableSchema: (table: VuuTable) => Promise; +} + +const serverAPI: ServerAPI = { + getTableList: async () => { + return { + tables: Object.values(simulSchemas) + .concat(Object.values(basketSchemas)) + .map((schema) => schema.table), + }; + }, + getTableSchema: async (vuuTable: VuuTable) => { + if (isSimulTable(vuuTable)) { + return simulSchemas[vuuTable.table]; + } else if (isBasketTable(vuuTable)) { + return basketSchemas[vuuTable.table]; + } else { + throw Error( + `unsupported module/table ${vuuTable.module}/${vuuTable.table}`, + ); + } + }, +}; + +const getServerAPI = async () => serverAPI; + +class VuuDataSource { + constructor({ table }: DataSourceConstructorProps) { + if (isSimulTable(table)) { + return simulModule.createDataSource(table.table); + } else if (isBasketTable(table)) { + return basketModule.createDataSource(table.table); + } else { + throw Error(`unsupported module/table ${table.module}/${table.table}`); + } + } +} + +export const LocalDataSourceProvider = ({ + children, + modules, +}: { + children: ReactNode; + modules: string[]; +}) => { + return ( + + {children} + + ); +}; diff --git a/vuu-ui/packages/vuu-data-test/src/simul/index.ts b/vuu-ui/packages/vuu-data-test/src/simul/index.ts index f8b44b378..028eca2d6 100644 --- a/vuu-ui/packages/vuu-data-test/src/simul/index.ts +++ b/vuu-ui/packages/vuu-data-test/src/simul/index.ts @@ -1,2 +1,6 @@ -export { type SimulTableName, schemas as simulSchemas } from "./simul-schemas"; +export { + type SimulTableName, + isSimulTable, + schemas as simulSchemas, +} from "./simul-schemas"; export * from "./simul-module"; diff --git a/vuu-ui/packages/vuu-data-test/src/simul/simul-module.ts b/vuu-ui/packages/vuu-data-test/src/simul/simul-module.ts index 037fddfce..1729e151e 100644 --- a/vuu-ui/packages/vuu-data-test/src/simul/simul-module.ts +++ b/vuu-ui/packages/vuu-data-test/src/simul/simul-module.ts @@ -4,15 +4,15 @@ import { VuuLink, VuuMenu, } from "@finos/vuu-protocol-types"; +import { MenuRpcResponse } from "@finos/vuu-data-types"; import { Table, buildDataColumnMap, joinTables } from "../Table"; +import { RpcService, RpcServiceRequest } from "../VuuModule"; +import { SimulModule } from "./SimulModule"; import { instrumentsTable } from "./reference-data/instruments"; import { instrumentsExtendedTable } from "./reference-data/instruments-extended"; import { ordersTable } from "./reference-data/orders"; import { pricesTable } from "./reference-data/prices"; import { schemas, type SimulTableName } from "./simul-schemas"; -import { RpcService, RpcServiceRequest } from "../VuuModule"; -import { MenuRpcResponse } from "packages/vuu-data-types"; -import { SimulModule } from "./SimulModule"; const undefinedTables = { childOrders: undefined, @@ -28,7 +28,7 @@ const tables: Record = { childOrders: new Table( schemas.childOrders, [], - buildDataColumnMap(schemas, "childOrders") + buildDataColumnMap(schemas, "childOrders"), ), instruments: instrumentsTable, instrumentsExtended: instrumentsExtendedTable, @@ -36,13 +36,13 @@ const tables: Record = { { module: "SIMUL", table: "instrumentPrices" }, instrumentsTable, pricesTable, - "ric" + "ric", ), orders: ordersTable, parentOrders: new Table( schemas.parentOrders, [], - buildDataColumnMap(schemas, "parentOrders") + buildDataColumnMap(schemas, "parentOrders"), ), prices: pricesTable, }; @@ -94,7 +94,7 @@ const menus: Record = { }; async function cancelOrder( - rpcRequest: RpcServiceRequest + rpcRequest: RpcServiceRequest, ): Promise, "requestId">> { const { rowKey } = rpcRequest as ClientToServerMenuRowRPC; const table = tables.orders; diff --git a/vuu-ui/packages/vuu-data-test/src/simul/simul-schemas.ts b/vuu-ui/packages/vuu-data-test/src/simul/simul-schemas.ts index 4691a1583..aae1e48a2 100644 --- a/vuu-ui/packages/vuu-data-test/src/simul/simul-schemas.ts +++ b/vuu-ui/packages/vuu-data-test/src/simul/simul-schemas.ts @@ -1,4 +1,5 @@ import { TableSchema } from "@finos/vuu-data-types"; +import { VuuTable } from "@finos/vuu-protocol-types"; export type SimulTableName = | "instruments" @@ -143,3 +144,11 @@ export const schemas: Readonly>> = table: { module: "SIMUL", table: "prices" }, }, }; + +export type SimulVuuTable = { + module: "SIMUL"; + table: SimulTableName; +}; + +export const isSimulTable = (table: VuuTable): table is SimulVuuTable => + table.module === "SIMUL"; diff --git a/vuu-ui/packages/vuu-filters/src/filter-bar/FilterBar.css b/vuu-ui/packages/vuu-filters/src/filter-bar/FilterBar.css index d54706974..ca36295a5 100644 --- a/vuu-ui/packages/vuu-filters/src/filter-bar/FilterBar.css +++ b/vuu-ui/packages/vuu-filters/src/filter-bar/FilterBar.css @@ -7,6 +7,7 @@ calc(var(--salt-size-base) + var(--salt-spacing-100)) ); --vuuFilterEditor-height: var(--filterbar-height); + --icon-container-height: auto; --flexbar-gap: var(--salt-spacing-100); --icon-marginTop: 0; @@ -27,6 +28,7 @@ } .vuuFilterBar-quick-filter { + --icon-container-height: 100%; --icon-marginTop: 20px; --filterbar-height: var( --vuuFilterBar-height, @@ -42,7 +44,7 @@ } .vuuFilterBar-iconContainer { - height: 100%; + height: var(--icon-container-height); .saltToggleButtonGroup { margin-top: var(--icon-marginTop); diff --git a/vuu-ui/packages/vuu-filters/src/filter-bar/FilterBar.tsx b/vuu-ui/packages/vuu-filters/src/filter-bar/FilterBar.tsx index 8fb29df7a..9ebacf270 100644 --- a/vuu-ui/packages/vuu-filters/src/filter-bar/FilterBar.tsx +++ b/vuu-ui/packages/vuu-filters/src/filter-bar/FilterBar.tsx @@ -93,9 +93,7 @@ export const FilterBar = ({ const startAdornment = useMemo(() => { if (!allowQuickFilters) { - return ; - } else if (!allowCustomFilters) { - return ; + return ; } else { return ( ); } - }, [allowCustomFilters, allowQuickFilters, filterMode, onChangeFilterMode]); + }, [allowQuickFilters, filterMode, onChangeFilterMode]); return (
-
{startAdornment}
+ {variant === "quick-filters" ? null : ( +
{startAdornment}
+ )} {filterMode === "custom-filter" ? ( Promise< - Pick - >; -} - -const DataSourceContext = createContext({ - isLocalData: false, - getServerAPI, - VuuDataSource, -}); - -/** - * If a client is adding a DataSOurceProvider, it will ususlly be - * to install local test data, so we default isLocalData to true - */ -export const DataSourceProvider = ({ - children, - getServerAPI, - isLocalData = true, - VuuDataSource, - vuuModuleName, -}: Omit & { - children: ReactNode; - isLocalData?: boolean; -}) => { - return ( - - {children} - - ); -}; - -export const useDataSource = () => useContext(DataSourceContext); diff --git a/vuu-ui/packages/vuu-shell/src/datasource-provider/index.ts b/vuu-ui/packages/vuu-shell/src/datasource-provider/index.ts deleted file mode 100644 index 9666f3f8b..000000000 --- a/vuu-ui/packages/vuu-shell/src/datasource-provider/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./DataSourceProvider"; diff --git a/vuu-ui/packages/vuu-shell/src/feature-list/FeatureList.tsx b/vuu-ui/packages/vuu-shell/src/feature-list/FeatureList.tsx index 2427ea654..8e1d98784 100644 --- a/vuu-ui/packages/vuu-shell/src/feature-list/FeatureList.tsx +++ b/vuu-ui/packages/vuu-shell/src/feature-list/FeatureList.tsx @@ -1,9 +1,10 @@ import { Palette, PaletteItem } from "@finos/vuu-layout"; import { Icon, ListProps } from "@finos/vuu-ui-controls"; import { - FeatureProps, - StaticFeatures, + DynamicFeatureProps, + StaticFeatureDescriptor, featureFromJson, + isStaticFeatures, } from "@finos/vuu-utils"; import { useComponentCssInjection } from "@salt-ds/styles"; import { useWindow } from "@salt-ds/window"; @@ -17,13 +18,14 @@ const classBase = "vuuFeatureList"; export type GroupedFeatureProps

= Record< string, - FeatureProps

[] + DynamicFeatureProps

[] >; -export interface FeatureListProps

- extends HTMLAttributes { - features: FeatureProps

[] | GroupedFeatureProps

| StaticFeatures; - isStatic?: boolean; +export interface FeatureListProps extends HTMLAttributes { + features: + | DynamicFeatureProps[] + | GroupedFeatureProps + | StaticFeatureDescriptor[]; } const listPropsFullHeight: Partial = { @@ -39,7 +41,6 @@ const listPropsAutoHeight: Partial = { export const FeatureList = ({ features, title = "VUU TABLES", - isStatic = false, ...htmlAttributes }: FeatureListProps) => { const targetWindow = useWindow(); @@ -50,27 +51,21 @@ export const FeatureList = ({ }); const content = useMemo(() => { - if (isStatic) { - return Object.entries(features).map(([heading, feature], index) => { + if (isStaticFeatures(features)) { + return features.map(({ label, type }, idx) => { return ( -

-
{heading}
- - - {feature.title} - -
+ + + {label} + ); }); } @@ -106,27 +101,29 @@ export const FeatureList = ({
{heading}
- {featureList.map((featureProps: FeatureProps, i: Key) => ( - } - key={i} - label={featureProps.title} - resizeable - resize="defer" - header - > - - - {featureProps.title} - - - ))} + {featureList.map( + (featureProps: DynamicFeatureProps, i: Key) => ( + } + key={i} + label={featureProps.title} + resizeable + resize="defer" + header + > + + + {featureProps.title} + + + ), + )} )); } - }, [features, isStatic]); + }, [features]); return (
diff --git a/vuu-ui/packages/vuu-shell/src/feature-provider/FeatureProvider.tsx b/vuu-ui/packages/vuu-shell/src/feature-provider/FeatureProvider.tsx index 2fc8ccdd9..94323eec3 100644 --- a/vuu-ui/packages/vuu-shell/src/feature-provider/FeatureProvider.tsx +++ b/vuu-ui/packages/vuu-shell/src/feature-provider/FeatureProvider.tsx @@ -1,8 +1,8 @@ import { - DynamicFeatures, - FeatureProps, + DynamicFeatureDescriptor, + DynamicFeatureProps, FilterTableFeatureProps, - StaticFeatures, + StaticFeatureDescriptor, getCustomAndTableFeatures, } from "@finos/vuu-utils"; import { @@ -15,49 +15,50 @@ import { import { useVuuTables } from "@finos/vuu-data-react"; export interface FeatureContextProps { - dynamicFeatures: FeatureProps[]; - tableFeatures: FeatureProps[]; - staticFeatures?: StaticFeatures; + dynamicFeatures?: DynamicFeatureProps[]; + tableFeatures?: DynamicFeatureProps[]; + staticFeatures?: StaticFeatureDescriptor[]; } -const NO_FEATURES: FeatureContextProps["dynamicFeatures"] = []; -const NO_TABLES: FeatureContextProps["tableFeatures"] = []; -const NO_STATICFEATURES: FeatureContextProps["staticFeatures"] = {}; +const NO_FEATURES: DynamicFeatureDescriptor[] = []; +const NO_STATICFEATURES: StaticFeatureDescriptor[] = []; -const NO_FEATURES_VUU: { - dynamicFeatures: FeatureProps[]; - tableFeatures: FeatureProps[]; -} = { dynamicFeatures: [], tableFeatures: [] }; +const NO_FEATURES_VUU = { + dynamicFeatures: NO_FEATURES, + tableFeatures: NO_FEATURES, +}; const FeatureContext = createContext({ dynamicFeatures: NO_FEATURES, - tableFeatures: NO_TABLES, + tableFeatures: NO_FEATURES, staticFeatures: NO_STATICFEATURES, }); export interface FeatureProviderProps extends Partial { children: ReactNode; - features: DynamicFeatures; - staticFeatures?: StaticFeatures; + dynamicFeatures?: DynamicFeatureDescriptor[]; + staticFeatures?: StaticFeatureDescriptor[]; } export const FeatureProvider = ({ children, - features, + dynamicFeatures: dynamicFeaturesProp = [], staticFeatures, }: FeatureProviderProps): ReactElement => { const vuuTables = useVuuTables(); const { dynamicFeatures, tableFeatures } = useMemo<{ - dynamicFeatures: FeatureProps[]; - tableFeatures: FeatureProps[]; + dynamicFeatures: DynamicFeatureProps[]; + tableFeatures: DynamicFeatureProps[]; }>( () => vuuTables - ? getCustomAndTableFeatures(features, vuuTables) + ? getCustomAndTableFeatures(dynamicFeaturesProp, vuuTables) : NO_FEATURES_VUU, - [features, vuuTables] + [dynamicFeaturesProp, vuuTables], ); + console.log({ tableFeatures }); + return ( -) => FeatureContextProps; -export const useFeatures: FeaturesHook = (localFeatures?) => { - const contextFeatures = useContext(FeatureContext); - if ( - localFeatures === undefined || - (localFeatures.dynamicFeatures === undefined && - localFeatures.tableFeatures === undefined && - localFeatures.staticFeatures === undefined) - ) { - return contextFeatures; - } else { - return { - dynamicFeatures: - localFeatures.dynamicFeatures ?? contextFeatures.dynamicFeatures, - tableFeatures: - localFeatures.tableFeatures ?? contextFeatures.tableFeatures, - staticFeatures: - localFeatures.staticFeatures ?? contextFeatures.staticFeatures, - }; - } -}; +export const useFeatures = () => useContext(FeatureContext); diff --git a/vuu-ui/packages/vuu-shell/src/feature/Feature.tsx b/vuu-ui/packages/vuu-shell/src/feature/Feature.tsx index b6227c697..7c3809063 100644 --- a/vuu-ui/packages/vuu-shell/src/feature/Feature.tsx +++ b/vuu-ui/packages/vuu-shell/src/feature/Feature.tsx @@ -1,4 +1,8 @@ -import { FeatureProps, importCSS, registerComponent } from "@finos/vuu-utils"; +import { + DynamicFeatureProps, + importCSS, + registerComponent, +} from "@finos/vuu-utils"; import React, { Suspense, useEffect } from "react"; import { FeatureErrorBoundary } from "./FeatureErrorBoundary"; import { Loader } from "./Loader"; @@ -12,13 +16,13 @@ const useCachedFeature = (url: string) => { () => () => { componentsMap.delete(url); }, - [url] + [url], ); if (!componentsMap.has(url)) { componentsMap.set( url, - React.lazy(() => import(/* @vite-ignore */ url)) + React.lazy(() => import(/* @vite-ignore */ url)), ); } @@ -36,7 +40,7 @@ function RawFeature({ css, ComponentProps: params, ...props -}: FeatureProps) { +}: DynamicFeatureProps) { if (css) { // import(/* @vite-ignore */ css, { assert: { type: "css" } }).then( // (cssModule) => { diff --git a/vuu-ui/packages/vuu-shell/src/feature/FeatureErrorBoundary.tsx b/vuu-ui/packages/vuu-shell/src/feature/FeatureErrorBoundary.tsx index f75e56c6d..6bb8d6d61 100644 --- a/vuu-ui/packages/vuu-shell/src/feature/FeatureErrorBoundary.tsx +++ b/vuu-ui/packages/vuu-shell/src/feature/FeatureErrorBoundary.tsx @@ -1,7 +1,7 @@ -import { FeatureProps } from "@finos/vuu-utils"; +import { DynamicFeatureProps } from "@finos/vuu-utils"; import React, { ErrorInfo, ReactNode } from "react"; -export interface FeatureErrorBoundaryProps extends FeatureProps { +export interface FeatureErrorBoundaryProps extends DynamicFeatureProps { children: ReactNode; } diff --git a/vuu-ui/packages/vuu-shell/src/index.ts b/vuu-ui/packages/vuu-shell/src/index.ts index 71b5adb29..1f66f362c 100644 --- a/vuu-ui/packages/vuu-shell/src/index.ts +++ b/vuu-ui/packages/vuu-shell/src/index.ts @@ -1,7 +1,6 @@ export * from "./application-provider"; export * from "./app-header"; export * from "./connection-status"; -export * from "./datasource-provider"; export * from "./feature"; export * from "./feature-provider"; export * from "./left-nav"; 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 701323478..0f7d3646c 100644 --- a/vuu-ui/packages/vuu-shell/src/left-nav/LeftNav.tsx +++ b/vuu-ui/packages/vuu-shell/src/left-nav/LeftNav.tsx @@ -6,7 +6,7 @@ import { } from "@finos/vuu-layout"; import { Tab, Tabstrip } from "@finos/vuu-ui-controls"; import { - FeatureProps, + DynamicFeatureProps, FilterTableFeatureProps, hasFilterTableFeatureProps, } from "@finos/vuu-utils"; @@ -36,7 +36,7 @@ export type NavDisplayStatus = const getDisplayStatus = ( activeTabIndex: number, - expanded: boolean + expanded: boolean, ): NavDisplayStatus => { if (activeTabIndex === 0) { return expanded ? "menu-full" : "menu-icons"; @@ -46,19 +46,17 @@ const getDisplayStatus = ( }; export type NavDisplayStatusHandler = ( - navDisplayStatus: NavDisplayStatus + navDisplayStatus: NavDisplayStatus, ) => void; export interface LeftNavProps extends HTMLAttributes { "data-path"?: string; defaultActiveTabIndex?: number; defaultExpanded?: boolean; - features?: FeatureProps[]; onActiveChange?: (activeTabIndex: number) => void; onTogglePrimaryMenu?: (expanded: boolean) => void; sizeCollapsed?: number; sizeContent?: number; sizeExpanded?: number; - tableFeatures?: FeatureProps[]; } type NavState = { @@ -67,8 +65,8 @@ type NavState = { }; const byModule = ( - f1: FeatureProps, - f2: FeatureProps + f1: DynamicFeatureProps, + f2: DynamicFeatureProps, ) => { const t1 = f1.ComponentProps?.tableSchema.table; const t2 = f2.ComponentProps?.tableSchema.table; @@ -97,14 +95,12 @@ export const LeftNav = (props: LeftNavProps) => { "data-path": path, defaultExpanded = true, defaultActiveTabIndex = 0, - features: featuresProp, onActiveChange, onTogglePrimaryMenu, sizeCollapsed = 80, sizeContent = 300, sizeExpanded = 240, style: styleProp, - tableFeatures: tableFeaturesProp, ...htmlAttributes } = props; const targetWindow = useWindow(); @@ -114,10 +110,7 @@ export const LeftNav = (props: LeftNavProps) => { window: targetWindow, }); - const { dynamicFeatures: features, tableFeatures } = useFeatures({ - dynamicFeatures: featuresProp, - tableFeatures: tableFeaturesProp, - }); + const { dynamicFeatures = [], tableFeatures = [] } = useFeatures(); const [navState, setNavState] = useState({ activeTabIndex: defaultActiveTabIndex, @@ -127,27 +120,26 @@ export const LeftNav = (props: LeftNavProps) => { const tableFeaturesByGroup = useMemo( () => tableFeatures - .sort(byModule) - .reduce>( - (acc, filterTableFeature) => { - if (hasFilterTableFeatureProps(filterTableFeature)) { - const { table } = filterTableFeature.ComponentProps.tableSchema; - const key = `${table.module} Tables`; - if (!acc[key]) { - acc[key] = []; - } - return { - ...acc, - [key]: acc[key].concat(filterTableFeature), - }; - } else { - return acc; - // throw Error("LeftNaV invalid tableFeature"); + ?.sort(byModule) + .reduce< + GroupedFeatureProps + >((acc, filterTableFeature) => { + if (hasFilterTableFeatureProps(filterTableFeature)) { + const { table } = filterTableFeature.ComponentProps.tableSchema; + const key = `${table.module} Tables`; + if (!acc[key]) { + acc[key] = []; } - }, - {} - ), - [tableFeatures] + return { + ...acc, + [key]: acc[key].concat(filterTableFeature), + }; + } else { + return acc; + // throw Error("LeftNaV invalid tableFeature"); + } + }, {}), + [tableFeatures], ); const getFullWidth = useCallback( @@ -160,7 +152,7 @@ export const LeftNav = (props: LeftNavProps) => { : sizeCollapsed + sizeContent; } }, - [sizeCollapsed, sizeContent, sizeExpanded] + [sizeCollapsed, sizeContent, sizeExpanded], ); const handleTabSelection = useCallback( @@ -178,12 +170,12 @@ export const LeftNav = (props: LeftNavProps) => { } onActiveChange?.(activeTabIndex); }, - [dispatch, getFullWidth, navState, onActiveChange] + [dispatch, getFullWidth, navState, onActiveChange], ); const displayStatus = getDisplayStatus( navState.activeTabIndex, - navState.expanded + navState.expanded, ); const toggleExpanded = useCallback(() => { @@ -252,7 +244,7 @@ export const LeftNav = (props: LeftNavProps) => { className={`${classBase}-menu-secondary`} showTabs={false} > - + diff --git a/vuu-ui/packages/vuu-shell/src/shell-layout-templates/full-height-left-panel/useFullHeightLeftPanel.tsx b/vuu-ui/packages/vuu-shell/src/shell-layout-templates/full-height-left-panel/useFullHeightLeftPanel.tsx index 5c64eefc4..c75b8cf21 100644 --- a/vuu-ui/packages/vuu-shell/src/shell-layout-templates/full-height-left-panel/useFullHeightLeftPanel.tsx +++ b/vuu-ui/packages/vuu-shell/src/shell-layout-templates/full-height-left-panel/useFullHeightLeftPanel.tsx @@ -7,7 +7,7 @@ import { useMemo } from "react"; export const useFullHeightLeftPanel: ShellLayoutTemplateHook = ({ appHeader, - LeftSidePanelProps, + SidePanelProps: LeftSidePanelProps, htmlAttributes, }) => useMemo( @@ -34,5 +34,5 @@ export const useFullHeightLeftPanel: ShellLayoutTemplateHook = ({ ), - [LeftSidePanelProps, appHeader, htmlAttributes] + [LeftSidePanelProps, appHeader, htmlAttributes], ); diff --git a/vuu-ui/packages/vuu-shell/src/shell-layout-templates/inlay-left-panel/useInlayLeftPanel.tsx b/vuu-ui/packages/vuu-shell/src/shell-layout-templates/inlay-left-panel/useInlayLeftPanel.tsx index 9b221ef25..b5f01eb69 100644 --- a/vuu-ui/packages/vuu-shell/src/shell-layout-templates/inlay-left-panel/useInlayLeftPanel.tsx +++ b/vuu-ui/packages/vuu-shell/src/shell-layout-templates/inlay-left-panel/useInlayLeftPanel.tsx @@ -19,7 +19,7 @@ import { import { ShellLayoutTemplateHook } from "../useShellLayout"; export const useInlayLeftPanel: ShellLayoutTemplateHook = ({ - LeftSidePanelProps, + SidePanelProps: LeftSidePanelProps, appHeader, htmlAttributes, }) => { @@ -32,7 +32,7 @@ export const useInlayLeftPanel: ShellLayoutTemplateHook = ({ setOpen(!open); } }, - [open] + [open], ); return useMemo(() => { @@ -58,7 +58,7 @@ export const useInlayLeftPanel: ShellLayoutTemplateHook = ({ > {leftSidePanel} - + , ); return drawers; @@ -80,7 +80,7 @@ export const useInlayLeftPanel: ShellLayoutTemplateHook = ({ id={VuuShellLocation.WorkspaceContainer} key="main-content" style={{ width: "100%", height: "100%" }} - /> + />, )} diff --git a/vuu-ui/packages/vuu-shell/src/shell-layout-templates/left-main-tabs/useLeftMainTabs.tsx b/vuu-ui/packages/vuu-shell/src/shell-layout-templates/left-main-tabs/useLeftMainTabs.tsx new file mode 100644 index 000000000..6d45d4679 --- /dev/null +++ b/vuu-ui/packages/vuu-shell/src/shell-layout-templates/left-main-tabs/useLeftMainTabs.tsx @@ -0,0 +1,57 @@ +import { + LayoutContainer, + Flexbox, + Stack, + Placeholder, +} from "@finos/vuu-layout"; + +import { VuuShellLocation } from "@finos/vuu-utils"; +import { useMemo } from "react"; +import { ShellLayoutTemplateHook } from "../useShellLayout"; + +export const useLeftMainTabs: ShellLayoutTemplateHook = ({ + appHeader, + htmlAttributes, + ToolbarProps, +}) => { + if (ToolbarProps === undefined) { + throw Error("LeftMainTabs layout requires ToolbarProps"); + } + + return useMemo(() => { + const flexBasis = ToolbarProps?.width ?? 48; + return ( + + {appHeader} + +
+ {ToolbarProps.children} +
+ + + + + +
+
+ ); + }, [ToolbarProps, appHeader, htmlAttributes]); +}; diff --git a/vuu-ui/packages/vuu-shell/src/shell-layout-templates/useShellLayout.ts b/vuu-ui/packages/vuu-shell/src/shell-layout-templates/useShellLayout.ts index cd8a68792..fd447a4c6 100644 --- a/vuu-ui/packages/vuu-shell/src/shell-layout-templates/useShellLayout.ts +++ b/vuu-ui/packages/vuu-shell/src/shell-layout-templates/useShellLayout.ts @@ -1,12 +1,14 @@ import { HTMLAttributes, ReactElement, ReactNode } from "react"; import { useFullHeightLeftPanel } from "./full-height-left-panel/useFullHeightLeftPanel"; import { useInlayLeftPanel } from "./inlay-left-panel/useInlayLeftPanel"; +import { useLeftMainTabs } from "./left-main-tabs/useLeftMainTabs"; import { useSimpleContentPane } from "./simple-content-pane/useSimpleContentPane"; -import { SidePanelProps } from "./side-panel"; +import { SidePanelProps as ShellSidePanelProps } from "./side-panel"; const LayoutHook = { "full-height": useFullHeightLeftPanel, inlay: useInlayLeftPanel, + "left-main-tabs": useLeftMainTabs, "simple-content-pane": useSimpleContentPane, }; @@ -17,8 +19,14 @@ export type ShellLayoutTemplateProps = Omit< "layoutTemplateId" >; +export type ShellToolbarProps = { + children: ReactNode; + position?: "left"; + width?: number; +}; + export type ShellLayoutTemplateHook = ( - props: ShellLayoutTemplateProps + props: ShellLayoutTemplateProps, ) => ReactElement; /** @@ -44,7 +52,11 @@ export interface ShellLayoutProps { /** * If template renders SidePanel, these props will be provided */ - LeftSidePanelProps?: SidePanelProps; + SidePanelProps?: ShellSidePanelProps; + /** + * If template renders Toolbar, these props will be provided + */ + ToolbarProps?: ShellToolbarProps; } /** diff --git a/vuu-ui/packages/vuu-shell/src/shell.css b/vuu-ui/packages/vuu-shell/src/shell.css index df3ae7bef..7cff0d1fc 100644 --- a/vuu-ui/packages/vuu-shell/src/shell.css +++ b/vuu-ui/packages/vuu-shell/src/shell.css @@ -2,6 +2,10 @@ background-color: var(--vuu-color-gray-25); height: var(--vuuShell-height, 100vh); width: var(--vuuShell-width, 100vw); + + .left-main-tabs { + --vuuOverflowContainer-width: 48px; + } } .vuu-workspace-tabs { diff --git a/vuu-ui/packages/vuu-utils/src/ShellContext.ts b/vuu-ui/packages/vuu-utils/src/ShellContext.ts index 6fb393f6d..2ef61d347 100644 --- a/vuu-ui/packages/vuu-utils/src/ShellContext.ts +++ b/vuu-ui/packages/vuu-utils/src/ShellContext.ts @@ -1,8 +1,8 @@ -import { RpcResponseHandler } from "@finos/vuu-data-types"; -import { +import type { RpcResponseHandler } from "@finos/vuu-data-types"; +import type { DefaultColumnConfiguration, DefaultTableConfiguration, -} from "packages/vuu-table-types"; +} from "@finos/vuu-table-types"; import { createContext, useContext } from "react"; import { LookupTableProvider } from "./feature-utils"; diff --git a/vuu-ui/packages/vuu-utils/src/context-definitions/DataSourceContext.tsx b/vuu-ui/packages/vuu-utils/src/context-definitions/DataSourceContext.tsx new file mode 100644 index 000000000..3584a1904 --- /dev/null +++ b/vuu-ui/packages/vuu-utils/src/context-definitions/DataSourceContext.tsx @@ -0,0 +1,35 @@ +import type { ServerAPI } from "@finos/vuu-data-remote"; +import type { + DataSource, + DataSourceConstructorProps, +} from "@finos/vuu-data-types"; +import { createContext } from "react"; + +export type DataSourceConstructor = { + new (props: DataSourceConstructorProps): DataSource; +}; + +export interface DataSourceContextProps { + isLocalData: boolean; + VuuDataSource: DataSourceConstructor; + vuuModuleNames?: string[]; + getServerAPI: () => Promise< + Pick + >; +} + +const getServerAPI = () => { + throw Error("no DataSourceProvider has been installed"); +}; + +class NullDataSource { + constructor() { + throw Error("no DataSourceProvider has been installed"); + } +} + +export const DataSourceContext = createContext({ + isLocalData: false, + getServerAPI, + VuuDataSource: NullDataSource as any, +}); diff --git a/vuu-ui/packages/vuu-utils/src/context-definitions/DataSourceProvider.tsx b/vuu-ui/packages/vuu-utils/src/context-definitions/DataSourceProvider.tsx new file mode 100644 index 000000000..9ca0b5058 --- /dev/null +++ b/vuu-ui/packages/vuu-utils/src/context-definitions/DataSourceProvider.tsx @@ -0,0 +1,23 @@ +import { DataSourceContext, DataSourceContextProps } from "@finos/vuu-utils"; +import { ReactNode, useContext } from "react"; + +export const DataSourceProvider = ({ + children, + getServerAPI, + isLocalData = true, + VuuDataSource, + vuuModuleNames, +}: Omit & { + children: ReactNode; + isLocalData?: boolean; +}) => { + return ( + + {children} + + ); +}; + +export const useDataSource = () => useContext(DataSourceContext); diff --git a/vuu-ui/packages/vuu-utils/src/feature-utils.ts b/vuu-ui/packages/vuu-utils/src/feature-utils.ts index 3b4511d2d..2a09872ac 100644 --- a/vuu-ui/packages/vuu-utils/src/feature-utils.ts +++ b/vuu-ui/packages/vuu-utils/src/feature-utils.ts @@ -20,7 +20,7 @@ export interface ViewConfig { header?: boolean; } -export interface FeatureProps

{ +export interface DynamicFeatureProps

{ /** props that will be passed to the lazily loaded component. */ @@ -60,6 +60,16 @@ export interface StaticFeatureDescriptor { type: string; } +const isStaticFeature = ( + feature: unknown, +): feature is StaticFeatureDescriptor => + feature !== null && typeof feature === "object" && "type" in feature; + +export const isStaticFeatures = ( + features: unknown, +): features is StaticFeatureDescriptor[] => + Array.isArray(features) && features.every(isStaticFeature); + export interface FilterTableFeatureProps { tableSchema: TableSchema; } @@ -68,21 +78,14 @@ export type DynamicFeatures = { [key: string]: DynamicFeatureDescriptor; }; -export type StaticFeatures = { - [key: string]: StaticFeatureDescriptor; -}; - -export function featureFromJson({ - type, - label, -}: StaticFeatureDescriptor): ReactElement { +export function featureFromJson({ type }: { type: string }): ReactElement { const componentType = type.match(/^[a-z]/) ? type : getLayoutComponent(type); if (componentType === undefined) { throw Error( - `layoutUtils unable to create component from JSON, unknown type ${type}` + `layoutUtils unable to create feature component from JSON, unknown type ${type}`, ); } - return React.createElement(componentType, { id: label, key: label }); + return React.createElement(componentType); } export interface VuuConfig { @@ -103,12 +106,12 @@ export const isTableSchema = (schema?: "*" | VuuTable): schema is VuuTable => typeof schema.table === "string"; export interface FeaturePropsWithFilterTableFeature - extends Omit { + extends Omit { ComponentProps: FilterTableFeatureProps; } export const hasFilterTableFeatureProps = ( - props: FeatureProps + props: DynamicFeatureProps, ): props is FeaturePropsWithFilterTableFeature => typeof props.ComponentProps === "object" && props.ComponentProps !== null && @@ -135,15 +138,15 @@ export type GetFeaturePaths = (params: { env: Environment; fileName: string; withCss?: boolean; -}) => FeatureProps; +}) => DynamicFeatureProps; export const getFilterTableFeatures = ( schemas: TableSchema[], - getFeaturePath: GetFeaturePaths + getFeaturePath: GetFeaturePaths, ) => schemas .sort(byModule) - .map>((schema) => ({ + .map>((schema) => ({ ...getFeaturePath({ env, fileName: "FilterTable" }), ComponentProps: { tableSchema: schema, @@ -161,11 +164,11 @@ export type Component = { export const assertComponentRegistered = ( componentName: string, - component: unknown + component: unknown, ) => { if (typeof component !== "function") { console.warn( - `${componentName} module not loaded, will be unabale to deserialize from layout JSON` + `${componentName} module not loaded, will be unabale to deserialize from layout JSON`, ); } }; @@ -177,19 +180,22 @@ export const assertComponentsRegistered = (componentList: Component[]) => { }; export const getCustomAndTableFeatures = ( - features: DynamicFeatures, - vuuTables: Map + dynamicFeatures: DynamicFeatureDescriptor[], + vuuTables: Map, ): { - dynamicFeatures: FeatureProps[]; - tableFeatures: FeatureProps[]; + dynamicFeatures: DynamicFeatureProps[]; + tableFeatures: DynamicFeatureProps[]; } => { + console.log(`getCustomAndTableFeatures`, { + vuuTables, + }); const [customFeatureConfig, tableFeaturesConfig] = partition( - Object.values(features), - isCustomFeature + dynamicFeatures, + isCustomFeature, ); - const customFeatures: FeatureProps[] = []; - const tableFeatures: FeatureProps[] = []; + const customFeatures: DynamicFeatureProps[] = []; + const tableFeatures: DynamicFeatureProps[] = []; for (const { featureProps = {}, @@ -205,7 +211,7 @@ export const getCustomAndTableFeatures = ( tableSchema, }, title: `${tableSchema.table.module} ${wordify( - tableSchema.table.table + tableSchema.table.table, )}`, ViewProps: { ...viewProps, @@ -248,11 +254,11 @@ export const getCustomAndTableFeatures = ( ComponentProps: schemas.reduce>( (map, schema) => { map[`${schema.table}Schema`] = vuuTables.get( - schema.table + schema.table, ) as TableSchema; return map; }, - {} + {}, ), ViewProps: viewProps, }); diff --git a/vuu-ui/packages/vuu-utils/src/index.ts b/vuu-ui/packages/vuu-utils/src/index.ts index 2a870607f..bd94c8ab6 100644 --- a/vuu-ui/packages/vuu-utils/src/index.ts +++ b/vuu-ui/packages/vuu-utils/src/index.ts @@ -54,4 +54,6 @@ export * from "./useLayoutEffectSkipFirst"; /** Context declarations hosted in utils to minimize intra package dependencies */ export * from "./ShellContext"; +export * from "./context-definitions/DataSourceContext"; +export * from "./context-definitions/DataSourceProvider"; export * from "./context-definitions/WorkspaceContext"; diff --git a/vuu-ui/packages/vuu-utils/src/shell-layout-types.ts b/vuu-ui/packages/vuu-utils/src/shell-layout-types.ts index 6bb4fdc2c..0892c7a76 100644 --- a/vuu-ui/packages/vuu-utils/src/shell-layout-types.ts +++ b/vuu-ui/packages/vuu-utils/src/shell-layout-types.ts @@ -5,7 +5,9 @@ */ export const VuuShellLocation = { ContextPanel: "context-panel", + MultiWorkspaceContainer: "vuu-multi-workspace-container", SidePanel: "vuu-side-panel", + SideToolbar: "vuu-side-toolbar", Workspace: "vuu-workspace", WorkspaceContainer: "vuu-workspace-container", } as const; diff --git a/vuu-ui/sample-apps/app-vuu-example/src/App.tsx b/vuu-ui/sample-apps/app-vuu-example/src/App.tsx index 000911aab..55e28720b 100644 --- a/vuu-ui/sample-apps/app-vuu-example/src/App.tsx +++ b/vuu-ui/sample-apps/app-vuu-example/src/App.tsx @@ -1,5 +1,7 @@ +import { VuuDataSourceProvider } from "@finos/vuu-data-react/src/datasource-provider/VuuDataSourceProvider"; import { FlexboxLayout, StackLayout } from "@finos/vuu-layout"; import { + FeatureProvider, LeftNav, LocalPersistenceManager, PersistenceProvider, @@ -12,12 +14,11 @@ import { TableSettingsPanel, } from "@finos/vuu-table-extras"; import { DragDropProvider } from "@finos/vuu-ui-controls"; -import type { StaticFeatures, VuuUser } from "@finos/vuu-utils"; +import type { VuuUser } from "@finos/vuu-utils"; import { assertComponentsRegistered, registerComponent, } from "@finos/vuu-utils"; -import { FeatureProvider } from "@finos/vuu-shell"; import { useMemo } from "react"; import { getDefaultColumnConfig } from "./columnMetaData"; import { useRpcResponseHandler } from "./useRpcResponseHandler"; @@ -43,6 +44,8 @@ const { features, } = await vuuConfig; +const dynamicFeatures = Object.values(features); + export const App = ({ user }: { user: VuuUser }) => { // this is causing full app re-render when tables are loaded const { handleRpcResponse } = useRpcResponseHandler(); @@ -51,24 +54,19 @@ export const App = ({ user }: { user: VuuUser }) => { () => ({ "basket-instruments": { dropTargets: "basket-constituents" }, }), - [] + [], ); const ShellLayoutProps = useMemo( () => ({ - LeftSidePanelProps: { + SidePanelProps: { children: , sizeOpen: 240, }, layoutTemplateId: "full-height", }), - [] + [], ); - console.log(`render App`); - - const staticFeatures: StaticFeatures = { - feature1: { label: "label", type: "type" }, - }; return ( @@ -76,14 +74,16 @@ export const App = ({ user }: { user: VuuUser }) => { - - - + + + + + diff --git a/vuu-ui/showcase/src/examples/Apps/NewTheme.examples.tsx b/vuu-ui/showcase/src/examples/Apps/NewTheme.examples.tsx deleted file mode 100644 index 7ba05a586..000000000 --- a/vuu-ui/showcase/src/examples/Apps/NewTheme.examples.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import { getAllSchemas } from "@finos/vuu-data-test"; -import { LeftNav, Shell, SidePanelProps } from "@finos/vuu-shell"; -import { - ColumnSettingsPanel, - TableSettingsPanel, -} from "@finos/vuu-table-extras"; -import { DragDropProvider } from "@finos/vuu-ui-controls"; -import { - FeatureProps, - GetFeaturePaths, - env, - getFilterTableFeatures, - registerComponent, -} from "@finos/vuu-utils"; -import { CSSProperties, useMemo } from "react"; - -import "./NewTheme.examples.css"; - -registerComponent("ColumnSettings", ColumnSettingsPanel, "view"); -registerComponent("TableSettings", TableSettingsPanel, "view"); - -const user = { username: "why-the-lucky-stiff", token: "test-token" }; -const schemas = getAllSchemas(); - -let displaySequence = 1; - -const getFeaturePath: GetFeaturePaths = ({ - env, - fileName, - withCss = env === "production", -}) => { - if (env === "production") { - const url = `/features/${fileName}.feature.js`; - return { - url, - css: withCss ? `/features/${fileName}.feature.css` : undefined, - }; - } else { - return { - url: `/src/features/${fileName}.feature`, - }; - } -}; - -const featurePaths: Record = { - FilterTableFeature: getFeaturePath({ env, fileName: "FilterTable" }), - InstrumentTiles: getFeaturePath({ env, fileName: "InstrumentTiles" }), - BasketTrading: getFeaturePath({ env, fileName: "BasketTrading" }), -}; - -const features: FeatureProps[] = [ - { - title: "Instrument Price Tiles", - ...featurePaths.InstrumentTiles, - ComponentProps: { - tableSchema: schemas.instrumentPrices, - }, - }, - { - title: "Basket Trading", - ...featurePaths.BasketTrading, - ViewProps: { - header: false, - }, - ComponentProps: { - basketSchema: schemas.basket, - basketTradingSchema: schemas.basketTrading, - basketTradingConstituentJoinSchema: schemas.basketTradingConstituentJoin, - basketConstituentSchema: schemas.basketConstituent, - }, - }, -]; - -const filterTableFeatures = getFilterTableFeatures( - Object.values(schemas), - getFeaturePath -); - -const ShellWithNewTheme = () => { - const dragSource = useMemo( - () => ({ - "basket-instruments": { - dropTargets: "basket-constituents", - payloadType: "key", - }, - }), - [] - ); - - const leftSidePanelProps = useMemo( - () => ({ - children: ( - - ), - sizeOpen: 240, - }), - [] - ); - - return ( - - - - ); -}; - -export const ShellWithNewThemeAndLayoutManagement = () => { - document.cookie = `vuu-username=${user.username}`; - - return ; -}; - -ShellWithNewThemeAndLayoutManagement.displaySequence = displaySequence++; diff --git a/vuu-ui/showcase/src/examples/Apps/NewTheme.examples.css b/vuu-ui/showcase/src/examples/Apps/SampleApp.examples.css similarity index 100% rename from vuu-ui/showcase/src/examples/Apps/NewTheme.examples.css rename to vuu-ui/showcase/src/examples/Apps/SampleApp.examples.css diff --git a/vuu-ui/showcase/src/examples/Apps/SampleApp.examples.tsx b/vuu-ui/showcase/src/examples/Apps/SampleApp.examples.tsx new file mode 100644 index 000000000..439baa10f --- /dev/null +++ b/vuu-ui/showcase/src/examples/Apps/SampleApp.examples.tsx @@ -0,0 +1,158 @@ +import { LocalDataSourceProvider } from "@finos/vuu-data-test/src/local-datasource-provider/LocalDatasourceProvider"; +import { + FeatureProvider, + LeftNav, + Shell, + SidePanelProps, +} from "@finos/vuu-shell"; +import { + ColumnSettingsPanel, + TableSettingsPanel, +} from "@finos/vuu-table-extras"; +import { DragDropProvider } from "@finos/vuu-ui-controls"; +import { + DynamicFeatureDescriptor, + DynamicFeatureProps, + GetFeaturePaths, + env, + registerComponent, +} from "@finos/vuu-utils"; +import { CSSProperties, useMemo } from "react"; + +import "./SampleApp.examples.css"; + +registerComponent("ColumnSettings", ColumnSettingsPanel, "view"); +registerComponent("TableSettings", TableSettingsPanel, "view"); + +const user = { username: "why-the-lucky-stiff", token: "test-token" }; + +let displaySequence = 1; + +const getFeaturePath: GetFeaturePaths = ({ + env, + fileName, + withCss = env === "production", +}) => { + if (env === "production") { + const url = `/features/${fileName}.feature.js`; + return { + url, + css: withCss ? `/features/${fileName}.feature.css` : undefined, + }; + } else { + return { + url: `/src/features/${fileName}.feature`, + }; + } +}; + +const featurePaths: Record = { + FilterTableFeature: getFeaturePath({ env, fileName: "FilterTable" }), + InstrumentTiles: getFeaturePath({ env, fileName: "InstrumentTiles" }), + BasketTrading: getFeaturePath({ env, fileName: "BasketTrading" }), +}; + +const dynamicFeatures: DynamicFeatureDescriptor[] = [ + { + title: "Vuu Filter Table", + name: "filter-table", + ...featurePaths.FilterTableFeature, + featureProps: { + schema: "*", + }, + leftNavLocation: "vuu-tables", + }, + { + title: "Instrument Price Tiles", + name: "instrument-tiles", + ...featurePaths.InstrumentTiles, + featureProps: { + schemas: [ + { + module: "SIMUL", + table: "instrumentPrices", + }, + ], + }, + leftNavLocation: "vuu-features", + }, + { + title: "Basket Trading", + name: "basket-trading", + ...featurePaths.BasketTrading, + viewProps: { + header: false, + }, + featureProps: { + schemas: [ + { + module: "BASKET", + table: "basket", + }, + { + module: "BASKET", + table: "basketTrading", + }, + { + module: "BASKET", + table: "basketTradingConstituentJoin", + }, + { + module: "BASKET", + table: "basketConstituent", + }, + ], + }, + leftNavLocation: "vuu-features", + }, +]; + +const ShellWithNewTheme = () => { + const dragSource = useMemo( + () => ({ + "basket-instruments": { + dropTargets: "basket-constituents", + payloadType: "key", + }, + }), + [], + ); + + const SidePanelProps = useMemo( + () => ({ + children: , + sizeOpen: 240, + }), + [], + ); + + return ( + + + + + + + + ); +}; + +export const SampleAppDefaultFeatures = () => { + document.cookie = `vuu-username=${user.username}`; + return ; +}; + +SampleAppDefaultFeatures.displaySequence = displaySequence++; diff --git a/vuu-ui/showcase/src/examples/Apps/index.ts b/vuu-ui/showcase/src/examples/Apps/index.ts index 88507172f..3c7680f6b 100644 --- a/vuu-ui/showcase/src/examples/Apps/index.ts +++ b/vuu-ui/showcase/src/examples/Apps/index.ts @@ -1,2 +1,2 @@ -export * from "./NewTheme.examples"; +export * as SampleApp from "./SampleApp.examples"; export * from "./SimpleApp.examples"; diff --git a/vuu-ui/showcase/src/examples/Filters/FilterEditor/FilterEditor.examples.tsx b/vuu-ui/showcase/src/examples/Filters/FilterEditor/FilterEditor.examples.tsx index 91fd37f96..217ef9edc 100644 --- a/vuu-ui/showcase/src/examples/Filters/FilterEditor/FilterEditor.examples.tsx +++ b/vuu-ui/showcase/src/examples/Filters/FilterEditor/FilterEditor.examples.tsx @@ -1,14 +1,14 @@ +import { getSchema, vuuModule } from "@finos/vuu-data-test"; +import type { SchemaColumn, TableSchema } from "@finos/vuu-data-types"; +import type { Filter } from "@finos/vuu-filter-types"; import { FilterEditCancelHandler, FilterEditor, - FilterEditorProps, - FilterEditSaveHandler, + type FilterEditorProps, + type FilterEditSaveHandler, } from "@finos/vuu-filters"; +import type { ColumnDescriptor } from "@finos/vuu-table-types"; import { useCallback, useMemo } from "react"; -import { getSchema, vuuModule } from "@finos/vuu-data-test"; -import { Filter } from "@finos/vuu-filter-types"; -import { SchemaColumn, TableSchema } from "packages/vuu-data-types"; -import { ColumnDescriptor } from "packages/vuu-table-types"; let displaySequence = 1; diff --git a/vuu-ui/showcase/src/examples/Shell/FeatureList.examples.tsx b/vuu-ui/showcase/src/examples/Shell/FeatureList.examples.tsx index 64efe0e19..f470e61f8 100644 --- a/vuu-ui/showcase/src/examples/Shell/FeatureList.examples.tsx +++ b/vuu-ui/showcase/src/examples/Shell/FeatureList.examples.tsx @@ -1,10 +1,10 @@ import { FeatureList, GroupedFeatureProps } from "@finos/vuu-shell"; -import { FeatureProps } from "@finos/vuu-utils"; +import { DynamicFeatureProps } from "@finos/vuu-utils"; let displaySequence = 1; export const DefaultFeatureList = () => { - const features: FeatureProps[] = [ + const features: DynamicFeatureProps[] = [ { title: "Component 1", url: "test" }, { title: "Component 2", url: "test" }, { title: "Component 3", url: "test" }, @@ -16,7 +16,7 @@ export const DefaultFeatureList = () => { DefaultFeatureList.displaySequence = displaySequence++; export const FeatureListWithTitle = () => { - const features: FeatureProps[] = [ + const features: DynamicFeatureProps[] = [ { title: "Component 1", url: "test" }, { title: "Component 2", url: "test" }, { title: "Component 3", url: "test" }, diff --git a/vuu-ui/showcase/src/examples/Shell/FeatureProvider.examples.tsx b/vuu-ui/showcase/src/examples/Shell/FeatureProvider.examples.tsx index 5cca63046..659c83f9a 100644 --- a/vuu-ui/showcase/src/examples/Shell/FeatureProvider.examples.tsx +++ b/vuu-ui/showcase/src/examples/Shell/FeatureProvider.examples.tsx @@ -1,39 +1,29 @@ import { FeatureList, useFeatures } from "@finos/vuu-shell"; -import { StaticFeatures } from "@finos/vuu-utils"; +import { StaticFeatureDescriptor } from "@finos/vuu-utils"; import { FeatureProvider } from "@finos/vuu-shell"; let displaySequence = 1; -const staticFeatures: StaticFeatures = { - feature1: { label: "label1", type: "Placeholder" }, - feature2: { label: "label2", type: "Component" }, - feature3: { label: "label3", type: "Placeholder" }, - feature4: { label: "label4", type: "View" }, - feature5: { label: "label5", type: "Placeholder" }, -}; +const staticFeatures: StaticFeatureDescriptor[] = [ + { label: "label1", type: "Placeholder" }, + { label: "label2", type: "Component" }, + { label: "label3", type: "Placeholder" }, + { label: "label4", type: "View" }, + { label: "label5", type: "Placeholder" }, +]; const StaticFeaturesTemplate = () => { - const features = useFeatures(); - if (features.staticFeatures) - return ( - <> - - - - - ); - else - return ( - <> - - - - ); + const { staticFeatures = [] } = useFeatures(); + return ( + <> + + + ); }; export const DefaultStaticFeatures = () => { return ( - + ); diff --git a/vuu-ui/showcase/src/examples/Shell/LeftNav.examples.tsx b/vuu-ui/showcase/src/examples/Shell/LeftNav.examples.tsx index 09e65d769..064862671 100644 --- a/vuu-ui/showcase/src/examples/Shell/LeftNav.examples.tsx +++ b/vuu-ui/showcase/src/examples/Shell/LeftNav.examples.tsx @@ -3,35 +3,21 @@ import { LeftNav } from "@finos/vuu-shell"; let displaySequence = 0; export const VerticalTabstrip = () => { - return ; + return ; }; VerticalTabstrip.displaySequence = displaySequence++; export const VerticalTabstripCollapsed = () => { - return ; + return ; }; VerticalTabstripCollapsed.displaySequence = displaySequence++; export const VerticalTabstripCollapsedContent = () => { - return ( - - ); + return ; }; VerticalTabstripCollapsedContent.displaySequence = displaySequence++; export const VerticalTabstripContent = () => { - return ( - - ); + return ; }; VerticalTabstripContent.displaySequence = displaySequence++; diff --git a/vuu-ui/showcase/src/examples/Shell/ShellLayout.examples.tsx b/vuu-ui/showcase/src/examples/Shell/ShellLayout.examples.tsx index 8fc3d3e8e..355a88fb7 100644 --- a/vuu-ui/showcase/src/examples/Shell/ShellLayout.examples.tsx +++ b/vuu-ui/showcase/src/examples/Shell/ShellLayout.examples.tsx @@ -1,12 +1,19 @@ -import { Placeholder } from "@finos/vuu-layout"; +import { Placeholder, useLayoutProviderDispatch } from "@finos/vuu-layout"; import { PersistenceProvider, Shell, StaticPersistenceManager, WorkspaceProps, } from "@finos/vuu-shell"; -import { registerComponent } from "@finos/vuu-utils"; -import { CSSProperties, HTMLAttributes, useMemo } from "react"; +import { Tab, Tabstrip } from "@finos/vuu-ui-controls"; +import { VuuShellLocation, registerComponent } from "@finos/vuu-utils"; +import { + CSSProperties, + HTMLAttributes, + useCallback, + useMemo, + useState, +} from "react"; registerComponent("Placeholder", Placeholder, "component"); @@ -96,7 +103,7 @@ export const FullHeightLeftPanel = () => { return ( My left Side

, sizeOpen: 200, }, @@ -119,7 +126,7 @@ export const FullHeightLeftPanelLeftPanelClosed = () => { return ( My left Side, open: false, sizeOpen: 200, @@ -147,7 +154,7 @@ export const InlayLeftPanel = () => { My left Side, sizeOpen: 200, }, @@ -167,6 +174,68 @@ export const InlayLeftPanel = () => { }; InlayLeftPanel.displaySequence = displaySequence++; +const ToolbarTabs = () => { + const dispatchLayoutAction = useLayoutProviderDispatch(); + const [active, setActive] = useState(0); + const handleTabSelection = useCallback( + (active: number) => { + console.log(`switch tab ${active}`); + dispatchLayoutAction({ + type: "set-props", + path: `#${VuuShellLocation.MultiWorkspaceContainer}`, + props: { active }, + }); + setActive(active); + }, + [dispatchLayoutAction], + ); + + return ( + + + + + + ); +}; + +export const LeftMainTabs = () => { + const persistNothing = useMemo(() => new StaticPersistenceManager({}), []); + return ( + + , + width: 50, + }, + htmlAttributes: { + style: { + padding: "4px 4px 4px 0", + }, + }, + layoutTemplateId: "left-main-tabs", + }} + loginUrl={window.location.toString()} + user={user} + style={ + { + "--vuuShell-height": "100%", + "--vuuShell-width": "100%", + } as CSSProperties + } + /> + + ); +}; +LeftMainTabs.displaySequence = displaySequence++; + export const SimpleShellCustomPlaceholder = () => { const persistNothing = useMemo(() => new StaticPersistenceManager({}), []); diff --git a/vuu-ui/showcase/src/examples/VuuFeatures/BasketTradingFeature.examples.tsx b/vuu-ui/showcase/src/examples/VuuFeatures/BasketTradingFeature.examples.tsx index 39fcf0f2a..1aa287103 100644 --- a/vuu-ui/showcase/src/examples/VuuFeatures/BasketTradingFeature.examples.tsx +++ b/vuu-ui/showcase/src/examples/VuuFeatures/BasketTradingFeature.examples.tsx @@ -2,7 +2,7 @@ import { getAllSchemas } from "@finos/vuu-data-test"; import { LayoutProvider, View } from "@finos/vuu-layout"; import { Feature, ShellContextProvider, useWorkspace } from "@finos/vuu-shell"; import { - FeatureProps, + DynamicFeatureProps, LookupTableProvider, registerComponent, } from "@finos/vuu-utils"; @@ -33,7 +33,7 @@ export const DefaultBasketTradingFeature = () => { console.log("layout change"); saveApplicationLayout(layout); }, - [saveApplicationLayout] + [saveApplicationLayout], ); // ---------------------------------------------------------------------------------- @@ -89,7 +89,7 @@ DefaultBasketTradingFeature.displaySequence = displaySequence++; type Environment = "development" | "production"; const env = process.env.NODE_ENV as Environment; -const featurePropsForEnv: Record = { +const featurePropsForEnv: Record = { development: { url: "/src/features/BasketTrading.feature", }, diff --git a/vuu-ui/showcase/src/examples/VuuFeatures/FilterTableFeature.examples.tsx b/vuu-ui/showcase/src/examples/VuuFeatures/FilterTableFeature.examples.tsx index dcc5aad43..ff6e131c4 100644 --- a/vuu-ui/showcase/src/examples/VuuFeatures/FilterTableFeature.examples.tsx +++ b/vuu-ui/showcase/src/examples/VuuFeatures/FilterTableFeature.examples.tsx @@ -6,7 +6,7 @@ import { FilterTableFeature } from "../../features/FilterTable.feature"; import { VuuBlotterHeader } from "./VuuBlotterHeader"; import { JsonTable } from "@finos/vuu-datatable"; import { - type FeatureProps, + type DynamicFeatureProps, type JsonData, registerComponent, } from "@finos/vuu-utils"; @@ -33,7 +33,7 @@ export const DefaultFilterTableFeature = () => { saveApplicationLayout(layout); setSavedLayoutJson(layout); }, - [saveApplicationLayout] + [saveApplicationLayout], ); // ---------------------------------------------------------------------------------- @@ -110,7 +110,7 @@ FilterTableFeatureFlexBox.displaySequence = displaySequence++; type Environment = "development" | "production"; const env = process.env.NODE_ENV as Environment; -const featurePropsForEnv: Record = { +const featurePropsForEnv: Record = { development: { url: "/src/features/FilterTable.feature", }, diff --git a/vuu-ui/showcase/src/examples/VuuFeatures/InstrumentTilesFeature.examples.tsx b/vuu-ui/showcase/src/examples/VuuFeatures/InstrumentTilesFeature.examples.tsx index 213485215..63e4027e6 100644 --- a/vuu-ui/showcase/src/examples/VuuFeatures/InstrumentTilesFeature.examples.tsx +++ b/vuu-ui/showcase/src/examples/VuuFeatures/InstrumentTilesFeature.examples.tsx @@ -1,7 +1,7 @@ import { getSchema } from "@finos/vuu-data-test"; import { LayoutProvider, View } from "@finos/vuu-layout"; import { Feature, useWorkspace } from "@finos/vuu-shell"; -import { FeatureProps, registerComponent } from "@finos/vuu-utils"; +import { DynamicFeatureProps, registerComponent } from "@finos/vuu-utils"; import { InstrumentTilesFeature } from "../../features/InstrumentTiles.feature"; import { VuuBlotterHeader } from "./VuuBlotterHeader"; @@ -41,7 +41,7 @@ DefaultInstrumentTilesFeature.displaySequence = displaySequence++; type Environment = "development" | "production"; const env = process.env.NODE_ENV as Environment; -const featurePropsForEnv: Record = { +const featurePropsForEnv: Record = { development: { url: "/src/features/InstrumentTiles.feature", },