Skip to content

Commit

Permalink
create example for Vuu Blotter Feature (#684)
Browse files Browse the repository at this point in the history
* create example for Vuu Blotter

* add column type check function

* exclude showcase index from semgrep

* add semgrep ignore for showcase file

* fix semgrepignore
  • Loading branch information
heswell authored May 7, 2023
1 parent 1d70064 commit 24e802b
Show file tree
Hide file tree
Showing 37 changed files with 756 additions and 291 deletions.
1 change: 1 addition & 0 deletions .semgrepignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ vuu/src/main/resources/www/ws-example.html
vuu/src/main/scala/org/finos/vuu/provider/simulation/SimulatedBigInstrumentsProvider.scala
vuu-ui/packages/vuu-datagrid-extras/src/column-expression-input/column-language-parser/walkExpressionTree.ts
vuu-ui/packages/vuu-popups/src/menu/useContextMenu.tsx
vuu-ui/showcase/src/index.tsx
vuu-ui/showcase/src/examples/Layout/Menu.examples.tsx
vuu-ui/tools/websocket-test.html
vuu/src/test/scala/org/finos/vuu/viewport/CreateViewPortScenarioTest.scala
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { GridConfig } from "@finos/vuu-datagrid-types";
import { Table, TableProps } from "@finos/vuu-table";
import { ReactElement, useCallback, useRef, useState } from "react";
import { itemsChanged, toDataSourceColumns } from "@finos/vuu-utils";
import { Dialog } from "@finos/vuu-popups";
import { DatagridSettingsPanel } from "@finos/vuu-table-extras";

export const ConfigurableTable = ({
config,
dataSource,
...restProps
}: TableProps) => {
const [dialogContent, setDialogContent] = useState<ReactElement | null>(null);
const configRef = useRef<Omit<GridConfig, "headings">>(config);
const [tableConfig, setTableConfig] =
useState<Omit<GridConfig, "headings">>(config);

const handleSettingsConfigChange = useCallback(
(config: Omit<GridConfig, "headings">, closePanel = false) => {
console.log(`Table.examples config changed`, {
config,
});
setTableConfig((currentConfig) => {
if (itemsChanged(currentConfig.columns, config.columns, "name")) {
// side effect: update columns on dataSource
dataSource.columns = config.columns.map(toDataSourceColumns);
}
return (configRef.current = config);
});
closePanel && setDialogContent(null);
},
[dataSource]
);

const showConfigEditor = useCallback(() => {
setDialogContent(
<DatagridSettingsPanel
availableColumns={config.columns}
gridConfig={configRef.current}
onConfigChange={handleSettingsConfigChange}
/>
);
}, [config.columns, handleSettingsConfigChange]);

const hideSettings = useCallback(() => {
setDialogContent(null);
}, []);

return (
<>
<Table
{...restProps}
allowConfigEditing
config={tableConfig}
dataSource={dataSource}
onShowConfigEditor={showConfigEditor}
/>
<Dialog
className="vuuDialog-gridConfig"
isOpen={dialogContent !== null}
onClose={hideSettings}
title="Grid and Column Settings"
>
{dialogContent}
</Dialog>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./ConfigurableTable";
1 change: 1 addition & 0 deletions vuu-ui/packages/vuu-datatable/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./configurable-table";
export * from "./json-table";
6 changes: 6 additions & 0 deletions vuu-ui/packages/vuu-filter-types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,9 @@ export interface OrFilter extends MultiClauseFilter {
}
*/
export declare type Filter = FilterClause | MultiClauseFilter;

export declare type FilterState = {
filter: Filter | undefined;
filterQuery: string;
filterName?: string;
};
8 changes: 7 additions & 1 deletion vuu-ui/packages/vuu-filters/src/filter-input/FilterInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ const classBase = "vuuFilterInput";
export interface FilterInputProps
extends SuggestionConsumer,
HTMLAttributes<HTMLDivElement> {
iconName?: string;
existingFilter?: Filter;
namedFilters?: Map<string, string>;
onSubmitFilter?: filterSubmissionHandler;
}

export const FilterInput = ({
existingFilter,
iconName = "filter",
namedFilters,
onSubmitFilter,
suggestionProvider,
Expand All @@ -34,7 +36,11 @@ export const FilterInput = ({

return (
<div {...props} className={classBase}>
<Button className={`${classBase}-FilterButton`} data-icon="filter" />
<Button
className={`${classBase}-FilterButton`}
data-icon={iconName}
tabIndex={-1}
/>
<div className={`${classBase}-Editor`} ref={editorRef} />
<Button
className={`${classBase}-ClearButton`}
Expand Down
44 changes: 30 additions & 14 deletions vuu-ui/packages/vuu-layout/src/flexbox/flexbox-utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { ReactElement } from 'react';
import { getIntrinsicSize, hasUnboundedFlexStyle } from '../layout-reducer/flexUtils';
import { getProp } from '../utils';
import type { BreakPoint, ContentMeta } from './flexboxTypes';
import { ReactElement } from "react";
import {
getIntrinsicSize,
hasUnboundedFlexStyle,
} from "../layout-reducer/flexUtils";
import { getProp } from "../utils";
import type { BreakPoint, ContentMeta } from "./flexboxTypes";

const NO_INTRINSIC_SIZE: {
height?: number;
Expand All @@ -11,9 +14,13 @@ const NO_INTRINSIC_SIZE: {
export const SPLITTER = 1;
export const PLACEHOLDER = 2;

const isIntrinsicallySized = (item: ContentMeta) => typeof item.intrinsicSize === 'number';
const isIntrinsicallySized = (item: ContentMeta) =>
typeof item.intrinsicSize === "number";

const getBreakPointValues = (breakPoints: BreakPoint[], component: ReactElement) => {
const getBreakPointValues = (
breakPoints: BreakPoint[],
component: ReactElement
) => {
const values: { [key: string]: number | undefined } = {};
breakPoints.forEach((breakPoint) => {
values[breakPoint] = getProp(component, breakPoint);
Expand All @@ -23,20 +30,21 @@ const getBreakPointValues = (breakPoints: BreakPoint[], component: ReactElement)

export const gatherChildMeta = (
children: ReactElement[],
dimension: 'width' | 'height',
dimension: "width" | "height",
breakPoints?: BreakPoint[]
) => {
return children.map((child, index) => {
const resizeable = getProp(child, 'resizeable');
const { [dimension]: intrinsicSize } = getIntrinsicSize(child) ?? NO_INTRINSIC_SIZE;
const resizeable = getProp(child, "resizeable");
const { [dimension]: intrinsicSize } =
getIntrinsicSize(child) ?? NO_INTRINSIC_SIZE;
const flexOpen = hasUnboundedFlexStyle(child);
if (breakPoints) {
return {
index,
flexOpen,
intrinsicSize,
resizeable,
...getBreakPointValues(breakPoints, child)
...getBreakPointValues(breakPoints, child),
};
} else {
return { index, flexOpen, intrinsicSize, resizeable };
Expand All @@ -47,7 +55,9 @@ export const gatherChildMeta = (
// Splitters are inserted AFTER the associated index, so
// never a splitter in last position.
// Placeholder goes before (first) OR after(last) index
export const findSplitterAndPlaceholderPositions = (childMeta: ContentMeta[]) => {
export const findSplitterAndPlaceholderPositions = (
childMeta: ContentMeta[]
) => {
const count = childMeta.length;
const allIntrinsic = childMeta.every(isIntrinsicallySized);
const splitterPositions = Array(count).fill(0);
Expand Down Expand Up @@ -81,15 +91,21 @@ export const findSplitterAndPlaceholderPositions = (childMeta: ContentMeta[]) =>
}
};

export const identifyResizeParties = (contentMeta: ContentMeta[], idx: number) => {
export const identifyResizeParties = (
contentMeta: ContentMeta[],
idx: number
) => {
const idx1 = getLeadingResizeablePos(contentMeta, idx);
const idx2 = getTrailingResizeablePos(contentMeta, idx);
const participants = idx1 !== -1 && idx2 !== -1 ? [idx1, idx2] : undefined;
const bystanders = identifyResizeBystanders(contentMeta, participants);
return [participants, bystanders];
};

function identifyResizeBystanders(contentMeta: ContentMeta[], participants?: number[]) {
function identifyResizeBystanders(
contentMeta: ContentMeta[],
participants?: number[]
) {
if (participants) {
const bystanders = [];
for (let i = 0; i < contentMeta.length; i++) {
Expand All @@ -115,7 +131,7 @@ function getTrailingResizeablePos(contentMeta: ContentMeta[], idx: number) {
let pos = idx,
resizeable = false;
const count = contentMeta.length;
while (pos < count && !resizeable) {
while (pos < count - 1 && !resizeable) {
pos = pos + 1;
resizeable = isResizeable(contentMeta, pos);
}
Expand Down
23 changes: 15 additions & 8 deletions vuu-ui/packages/vuu-layout/src/layout-header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,21 @@ export const Header = ({
};

const toolbarItems: ReactElement[] = [];
const contributedItems: ReactElement[] = [];
const postTitleContributedItems: ReactElement[] = [];
const actionButtons: ReactElement[] = [];

contributions?.forEach((contribution, i) => {
switch (contribution.location) {
case "pre-title":
toolbarItems.push(React.cloneElement(contribution.content, { key: i }));
break;
default:
postTitleContributedItems.push(
React.cloneElement(contribution.content, { key: i })
);
}
});

title &&
toolbarItems.push(
<ToolbarField className="vuuHeader-title" key="title">
Expand All @@ -120,10 +132,6 @@ export const Header = ({
</ToolbarField>
);

contributions?.forEach((contribution, i) => {
contributedItems.push(React.cloneElement(contribution.content, { key: i }));
});

closeable &&
actionButtons.push(
<ToolbarButton
Expand All @@ -135,10 +143,10 @@ export const Header = ({
</ToolbarButton>
);

contributedItems.length > 0 &&
postTitleContributedItems.length > 0 &&
toolbarItems.push(
<Tooltray data-align-end key="contributions">
{contributedItems}
{postTitleContributedItems}
</Tooltray>
);

Expand All @@ -152,7 +160,6 @@ export const Header = ({
return (
<Toolbar
className={className}
id="stevo"
orientation={orientationProp}
style={style}
onMouseDown={handleMouseDown}
Expand Down
5 changes: 3 additions & 2 deletions vuu-ui/packages/vuu-layout/src/layout-reducer/layoutTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { ReactElement } from "react";
import { DragDropRect, DragInstructions } from "../drag-drop";
import { DropTarget } from "../drag-drop/DropTarget";
import { ContributionLocation } from "../layout-view";

export interface WithProps {
props?: { [key: string]: any };
Expand Down Expand Up @@ -131,12 +132,12 @@ export type SaveAction = {

export type AddToolbarContributionViewAction = {
content: ReactElement;
location: string;
location: ContributionLocation;
type: "add-toolbar-contribution";
};

export type RemoveToolbarContributionViewAction = {
location: string;
location: ContributionLocation;
type: "remove-toolbar-contribution";
};

Expand Down
37 changes: 30 additions & 7 deletions vuu-ui/packages/vuu-layout/src/layout-view/View.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,39 @@ import React, {
ForwardedRef,
forwardRef,
ReactElement,
useCallback,
useMemo,
useRef,
useState,
} from "react";
import { Header } from "../layout-header/Header";
import { Header as VuuHeader } from "../layout-header/Header";
import { registerComponent } from "../registry/ComponentRegistry";
import { useView } from "./useView";
import { useViewResize } from "./useViewResize";
import { ViewContext } from "./ViewContext";
import { ViewContext, ViewContextProps } from "./ViewContext";
import { ViewProps } from "./viewTypes";

import "./View.css";

const classBase = "vuuView";

type Props = { [key: string]: unknown };

const getProps = (state?: Props, props?: Props) => {
if (state && props) {
return {
...state,
...props,
};
} else return state || props;
};

const View = forwardRef(function View(
props: ViewProps,
forwardedRef: ForwardedRef<HTMLDivElement>
) {
const {
Header = VuuHeader,
children,
className,
collapsed,
Expand All @@ -44,7 +60,7 @@ const View = forwardRef(function View(
const id = useId(idProp);
const rootRef = useRef<HTMLDivElement>(null);
const mainRef = useRef<HTMLDivElement>(null);

const [componentProps, _setComponentProps] = useState<Props>();
const {
contributions,
dispatchViewAction,
Expand All @@ -67,16 +83,21 @@ const View = forwardRef(function View(

useViewResize({ mainRef, resize, rootRef });

const classBase = "vuuView";
const setComponentProps = useCallback((props?: Props) => {
_setComponentProps(props);
}, []);

const getContent = () => {
if (React.isValidElement(children) && restoredState) {
return React.cloneElement(children, restoredState);
if (React.isValidElement(children) && (restoredState || componentProps)) {
return React.cloneElement(
children,
getProps(restoredState, componentProps)
);
}
return children;
};

const viewContextValue = useMemo(
const viewContextValue: ViewContextProps = useMemo(
() => ({
dispatch: dispatchViewAction,
id,
Expand All @@ -88,6 +109,7 @@ const View = forwardRef(function View(
purge,
save,
saveSession,
setComponentProps,
}),
[
dispatchViewAction,
Expand All @@ -99,6 +121,7 @@ const View = forwardRef(function View(
purge,
save,
saveSession,
setComponentProps,
title,
]
);
Expand Down
Loading

0 comments on commit 24e802b

Please sign in to comment.