@adaptabletools/adaptable
Version:
Powerful data-agnostic HTML5 AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements
633 lines (585 loc) • 19.8 kB
JavaScript
export {};
/*
This is no longer required since we cannot build FDC3 columns
But keeping it like this as it was rather useful ...
import { ColDef } from 'ag-grid-enterprise';
import * as React from 'react';
import { Box, BoxProps, Flex } from 'rebass';
import { CheckBox } from '../../../../components/CheckBox';
import DropdownButton, { DropdownButtonProps } from '../../../../components/DropdownButton';
import FormLayout, { FormRow } from '../../../../components/FormLayout';
import HelpBlock from '../../../../components/HelpBlock';
import Input from '../../../../components/Input';
import SimpleButton from '../../../../components/SimpleButton';
import {
AdaptableConfig,
AdaptableOptions,
ContactColumn,
CountryColumn,
FDC3Column_Depr,
FinancePluginOptions,
InstrumentColumn,
OrganizationColumn,
PositionColumn,
} from '../../../../types';
import StringExtensions from '../../../../Utilities/Extensions/StringExtensions';
import { NocodeWizardFormBox } from '../Components/FormBox';
const LocalDropdownButton = (props: DropdownButtonProps) => (
<DropdownButton {...props} width="100%" columns={['label']} />
);
const AlertMessage: React.FunctionComponent<React.PropsWithChildren<BoxProps>> = (props) => (
<Box color="var(--ab-color-error)" {...props} />
);
type FDC3ColumnsKey = Exclude<
keyof FinancePluginOptions['fdc3Columns'],
'customFDC3Columns' | 'customFDC3Intents'
>;
type AnyFDC3Column =
| InstrumentColumn
| PositionColumn
| ContactColumn
| CountryColumn
| OrganizationColumn;
interface FDC3ColumnField<COLUMN_TYPE, INTENTS> {
fields: {
label: string;
id: keyof COLUMN_TYPE;
value?: string;
required?: boolean;
}[];
intents: INTENTS;
}
const fdc3ColumnDefinitions: {
instrumentColumns: FDC3ColumnField<
Omit<InstrumentColumn, 'intents'>,
InstrumentColumn['intents']
>;
positionColumns: FDC3ColumnField<Omit<PositionColumn, 'intents'>, PositionColumn['intents']>;
contactColumns: FDC3ColumnField<Omit<ContactColumn, 'intents'>, ContactColumn['intents']>;
countryColumns: FDC3ColumnField<Omit<CountryColumn, 'intents'>, CountryColumn['intents']>;
organizationColumns: FDC3ColumnField<
Omit<OrganizationColumn, 'intents'>,
OrganizationColumn['intents']
>;
} = {
instrumentColumns: {
fields: [
{ label: 'Ticker', id: 'tickerColumnId' },
{ label: 'ISIN', id: 'isinColumnId' },
{ label: 'CUSIP', id: 'cusipColumnId' },
{ label: 'SEDOL', id: 'sedolColumnId' },
{ label: 'RIC', id: 'ricColumnId' },
{ label: 'BBG', id: 'bbgColumnId' },
{ label: 'PERMID', id: 'permidColumnId' },
{ label: 'FIGI', id: 'figiColumnId' },
],
intents: ['ViewChart', 'ViewQuote', 'ViewNews', 'ViewAnalysis', 'ViewInstrument'],
},
positionColumns: {
fields: [{ label: 'Instrument', id: 'instrumentColumnId', required: true }],
intents: ['ViewChart', 'ViewNews', 'ViewAnalysis'],
},
contactColumns: {
fields: [
{ label: 'Email', id: 'emailColumnId' },
{ label: 'FDS_ID', id: 'fds_idColumnId' },
],
intents: ['StartCall', 'StartChat', 'ViewContact'],
},
countryColumns: {
fields: [
{ label: 'ISOALPHA2', id: 'isoalpha2ColumnId' },
{ label: 'ISOALPHA3', id: 'isoalpha3ColumnId' },
],
intents: ['ViewChart'],
},
organizationColumns: {
fields: [
{ label: 'LEI', id: 'leiColumnId' },
{ label: 'PERM ID', id: 'permidColumnId' },
{ label: 'FDS ID', id: 'fds_idColumnId' },
],
intents: ['ViewNews', 'ViewAnalysis'],
},
};
const FDC3ColumnSelector: React.FunctionComponent<{
label: string;
id: string;
value?: string;
required?: boolean;
onChange: (columnId: string) => void;
columnOptions: { label: string; value: string }[];
}> = (props) => {
const options = React.useMemo(() => {
return props.columnOptions.map((columnOption) => ({
...columnOption,
onClick: () => props.onChange(columnOption.value),
}));
}, [props.onChange]);
return (
<FormRow
label={
<>
{props.label} {props.required && <AlertMessage as="span">*</AlertMessage>}{' '}
</>
}
>
<LocalDropdownButton items={options}>
{StringExtensions.Humanize(props.value) || 'Select Column'}
</LocalDropdownButton>
</FormRow>
);
};
const FDC3InstrumentColumnBuilder: React.FunctionComponent<{
column: InstrumentColumn;
columnOptions: { label: string; value: string }[];
onColumnFieldChange: (fieldId: string, columnId: string) => void;
onIntentToggle: (intent: string) => void;
}> = (props) => {
const fields = fdc3ColumnDefinitions.instrumentColumns.fields;
return (
<Flex>
<FormLayout mr={2}>
{fields.slice(0, 4).map((field) => (
<FDC3ColumnSelector
{...field}
key={field.id}
value={(props.column[field.id] as string) ?? 'Select Column'}
onChange={(columnId) => props.onColumnFieldChange(field.id, columnId)}
columnOptions={props.columnOptions}
/>
))}
</FormLayout>
<FormLayout>
{fields.slice(4).map((field) => (
<FDC3ColumnSelector
{...field}
key={field.id}
value={(props.column[field.id] as string) ?? 'Select Column'}
onChange={(columnId) => props.onColumnFieldChange(field.id, columnId)}
columnOptions={props.columnOptions}
/>
))}
</FormLayout>
<Flex ml={3} flexDirection="column">
{fdc3ColumnDefinitions.instrumentColumns.intents.map((intent) => (
<CheckBox
checked={props.column.intents?.includes?.(intent)}
onClick={() => props.onIntentToggle(intent)}
key={intent}
>
{intent}
</CheckBox>
))}
</Flex>
</Flex>
);
};
const FDC3BaseColumnBuilder: React.FunctionComponent<{
fdc3ColumnType: FDC3ColumnsKey;
columnOptions: { label: string; value: string }[];
column: AnyFDC3Column;
onColumnFieldChange: (fieldId: string, columnId?: string) => void;
onIntentToggle: (intent: string) => void;
}> = (props) => {
const fields = fdc3ColumnDefinitions[props.fdc3ColumnType].fields;
const intents = fdc3ColumnDefinitions[props.fdc3ColumnType].intents;
return (
<Flex>
<FormLayout>
{fields.map((field) => (
<FDC3ColumnSelector
{...field}
key={field.id}
columnOptions={props.columnOptions}
onChange={(columnId) => props.onColumnFieldChange(field.id, columnId)}
value={(props.column as any)?.[field.id] ?? ''}
/>
))}
<FormRow label="">
<Flex flexDirection="column">
{intents.map((intent: string) => (
<CheckBox
checked={((props.column?.intents as string[]) ?? []).includes(intent)}
onClick={() => props.onIntentToggle(intent)}
key={intent}
>
{intent}
</CheckBox>
))}
</Flex>
</FormRow>
</FormLayout>
</Flex>
);
};
const FDC3ColumnBuilder: React.FunctionComponent<{
columnOptions: { label: string; value: string }[];
fdc3Column: FDC3Column_Depr;
fdc3ColumnType?: FDC3ColumnsKey;
onChange: (fdc3Column: FDC3Column_Depr) => void;
beforeContent?: React.ReactElement;
disabled?: boolean;
footer: React.ReactElement;
}> = (props) => {
const handleColumnFieldChange = React.useCallback(
(fieldId: string, columnId?: string) => {
props.onChange({
...props.fdc3Column,
[fieldId]: columnId,
});
},
[props.fdc3Column, props.onChange]
);
const handleIntentToggle = React.useCallback(
(intent: string) => {
const newFDC3Column = { ...props.fdc3Column };
newFDC3Column.intents = newFDC3Column.intents ?? [];
if ((newFDC3Column.intents as string[]).includes(intent)) {
(newFDC3Column.intents as string[]) = (newFDC3Column.intents as string[]).filter(
(i: string) => i !== intent
);
} else {
(newFDC3Column.intents as string[]).push(intent);
}
props.onChange(newFDC3Column);
},
[props.fdc3Column]
);
const columnOptions = React.useMemo(() => {
return props.columnOptions.map((columnOption) => ({
...columnOption,
onClick: () => {
props.onChange({
...props.fdc3Column,
columnId: columnOption.value,
});
},
}));
}, [props.fdc3Column]);
const handleNameColumnIdChange = React.useCallback(
(event: any) => {
props.onChange({
...props.fdc3Column,
nameColumnId: event.target.value ?? '',
});
},
[props.fdc3Column]
);
const handleShowBroadcastToggle = React.useCallback(() => {
props.onChange({
...props.fdc3Column,
showBroadcastContextMenu: !props.fdc3Column?.showBroadcastContextMenu,
});
}, [props.fdc3Column]);
const handleBroadcastLabelChange = React.useCallback(
(event: any) => {
props.onChange({
...props.fdc3Column,
broadcastContextMenuLabel: event.target.value ?? '',
});
},
[props.fdc3Column]
);
return (
<NocodeWizardFormBox>
<Flex flexWrap="wrap">
<FormLayout mr={3} mb={3}>
{props.beforeContent}
<FormRow
label={
<>
Column <AlertMessage as="span">*</AlertMessage>{' '}
</>
}
>
<LocalDropdownButton disabled={props.disabled} items={columnOptions}>
{columnOptions.find((item) => item.value === props.fdc3Column?.columnId)?.label ??
'Select Column'}
</LocalDropdownButton>
</FormRow>
<FormRow label="FDC3 Name">
<Input
disabled={props.disabled}
onChange={handleNameColumnIdChange}
value={props.fdc3Column?.nameColumnId ?? ''}
/>
</FormRow>
<FormRow label="">
<CheckBox
disabled={props.disabled}
checked={Boolean(props.fdc3Column?.showBroadcastContextMenu)}
onChange={handleShowBroadcastToggle}
>
Show Broadcast <br /> Context Menu
</CheckBox>
</FormRow>
<FormRow label="Broadcast Label">
<Input
disabled={props.disabled || !Boolean(props.fdc3Column?.showBroadcastContextMenu)}
onChange={handleBroadcastLabelChange}
value={
typeof props.fdc3Column?.broadcastContextMenuLabel === 'string'
? props.fdc3Column?.broadcastContextMenuLabel
: ''
}
/>
</FormRow>
</FormLayout>
<Box flex={1}>
{props.fdc3ColumnType === 'instrumentColumns' ? (
<FDC3InstrumentColumnBuilder
onIntentToggle={handleIntentToggle}
onColumnFieldChange={(...args) => handleColumnFieldChange(...args)}
column={props.fdc3Column as InstrumentColumn}
columnOptions={props.columnOptions}
/>
) : (
props.fdc3ColumnType && (
<FDC3BaseColumnBuilder
onColumnFieldChange={(...args) => handleColumnFieldChange(...args)}
onIntentToggle={handleIntentToggle}
column={props.fdc3Column}
fdc3ColumnType={props.fdc3ColumnType}
columnOptions={props.columnOptions}
/>
)
)}
</Box>
</Flex>
{props.footer}
</NocodeWizardFormBox>
);
};
const FDC3ColumnEditor: React.FunctionComponent<{
onChange: (fdc3Column: AnyFDC3Column) => void;
columnOptions: { label: string; value: string }[];
fdc3Column: AnyFDC3Column;
fdc3ColumnType: FDC3ColumnsKey;
footer: React.ReactElement;
}> = (props) => {
return (
<FDC3ColumnBuilder
columnOptions={props.columnOptions}
fdc3Column={props.fdc3Column}
fdc3ColumnType={props.fdc3ColumnType}
onChange={props.onChange}
footer={props.footer}
/>
);
};
const FDC3ColumnCreator: React.FunctionComponent<{
onNew: (fdc3ColumnType: FDC3ColumnsKey, fdc3Column: FDC3Column_Depr) => void;
columnOptions: { label: string; value: string }[];
}> = (props) => {
const [fdc3Column, setFDC3Column] = React.useState<AnyFDC3Column>();
const [fdc3ColumnType, setFDC3ColumnType] = React.useState<FDC3ColumnsKey>();
const handleOnNew = React.useCallback(() => {
props.onNew(fdc3ColumnType, fdc3Column);
setFDC3Column(null);
setFDC3ColumnType(null);
}, [fdc3Column]);
const fdc3ColumnOptions: { label: string; columnField: FDC3ColumnsKey; onClick: () => void }[] =
React.useMemo(() => {
return [
{
label: 'Instrument',
columnField: 'instrumentColumns',
},
{
label: 'Position',
columnField: 'positionColumns',
},
{
label: 'Contact',
columnField: 'contactColumns',
},
{
label: 'Country',
columnField: 'countryColumns',
},
{
label: 'Organization',
columnField: 'organizationColumns',
},
].map((item: { label: string; columnField: FDC3ColumnsKey }) => ({
...item,
onClick: () => {
setFDC3ColumnType(item.columnField);
setFDC3Column({ columnId: '' });
},
}));
}, []);
const typeSelector = (
<FormRow label="FDC3 Type">
<LocalDropdownButton items={fdc3ColumnOptions}>
{fdc3ColumnOptions.find((item) => item.columnField === fdc3ColumnType)?.label ??
'Select Column'}
</LocalDropdownButton>
</FormRow>
);
const fieldsDisabled = !fdc3ColumnType;
const errors: string[] = [];
if (fdc3Column && !fdc3Column.columnId) {
errors.push('Column is required.');
}
if (fdc3Column) {
const fields = fdc3ColumnDefinitions[fdc3ColumnType].fields;
fields.forEach((field) => {
if (field.required && !(fdc3Column as any)[field.id]) {
errors.push(`${field.label} is required.`);
}
});
}
return (
<FDC3ColumnBuilder
disabled={fieldsDisabled}
columnOptions={props.columnOptions}
fdc3Column={fdc3Column}
fdc3ColumnType={fdc3ColumnType}
onChange={(newFDC3Column) => setFDC3Column(newFDC3Column)}
beforeContent={typeSelector}
footer={
<Flex>
<Box flex={1} />
<Flex alignItems="center" mr={3}>
{errors.slice(0, 1).map((error) => (
<AlertMessage key={error} ml={2}>
{error}
</AlertMessage>
))}
</Flex>
<SimpleButton
disabled={fieldsDisabled || !fdc3Column.columnId}
onClick={handleOnNew}
icon="plus"
variant="raised"
>
Add FDC3 Column
</SimpleButton>
</Flex>
}
/>
);
};
const FinanceFormSectionTitle: React.FunctionComponent<React.PropsWithChildren<BoxProps>> = (
props
) => (
<Box {...props} mb={2}>
<HelpBlock>{props.children}</HelpBlock>
</Box>
);
export interface FinanceFormProps {
adaptableConfig: AdaptableConfig;
onChangedAdaptableConfig: (adaptableConfig: AdaptableConfig) => void;
}
export const FinanceForm: React.FunctionComponent<React.PropsWithChildren<FinanceFormProps>> = (
props
) => {
const columnOptions = props.adaptableConfig.gridOptions?.columnDefs?.map?.((item: ColDef) => ({
label: StringExtensions.Humanize(item.headerName ?? item.field),
value: item.field,
}));
const financePlugin = props.adaptableConfig.adaptableOptions?.plugins?.find(
(item) => item.pluginId === 'finance'
);
const financePluginOptions = (financePlugin?.options ?? {}) as FinancePluginOptions;
const fdc3Columns = financePluginOptions?.fdc3Columns ?? {};
const handleSetFdc3Columns = (key: FDC3ColumnsKey, newFDC3Columns: any) => {
// Unfurtunatly finance plugin options need to be mutated.
// It is the only was without instanciating the plugin again.
// But that would mean to inport the finance plugin inside the nocode plugin.
financePluginOptions.fdc3Columns = financePluginOptions.fdc3Columns ?? {};
financePluginOptions.fdc3Columns[key] = newFDC3Columns;
// on change is called with a new array of plugins so the component refreshes.
props.onChangedAdaptableConfig({
...props.adaptableConfig,
adaptableOptions: {
...props.adaptableConfig.adaptableOptions,
plugins: [...props.adaptableConfig.adaptableOptions.plugins],
},
});
};
const renderEditor = (fdc3ColumnType: FDC3ColumnsKey, fdc3ColumnsItems: AnyFDC3Column[]) => {
return fdc3ColumnsItems.map((fdc3Column, currentFDCColumnId) => (
<Box key={currentFDCColumnId} mb={3}>
<FDC3ColumnEditor
columnOptions={columnOptions}
fdc3Column={fdc3Column}
fdc3ColumnType={fdc3ColumnType}
onChange={(editedFDC3Column) => {
const newFDC3Columns = fdc3ColumnsItems.map((fdc3Column, index) => {
if (index === currentFDCColumnId) {
return editedFDC3Column;
}
return fdc3Column;
});
handleSetFdc3Columns(fdc3ColumnType, newFDC3Columns);
}}
footer={
<Flex>
<Box flex={1} />
<SimpleButton
onClick={() => {
const newFDC3Columns = fdc3ColumnsItems.filter(
(_, index) => index !== currentFDCColumnId
);
handleSetFdc3Columns(fdc3ColumnType, newFDC3Columns);
}}
tone="error"
variant="raised"
icon="delete"
>
Delete
</SimpleButton>
</Flex>
}
/>
</Box>
));
};
return (
<Box p={2} className="ab-FinanceForm">
<Box mb={3}>
<HelpBlock mb={3}>Create FDC3 Column</HelpBlock>
<FDC3ColumnCreator
columnOptions={columnOptions}
onNew={(type, fdc3Column) => {
handleSetFdc3Columns(type, [...(fdc3Columns[type] ?? []), fdc3Column]);
}}
/>
</Box>
{Boolean(fdc3Columns.instrumentColumns?.length) && (
<Box>
<FinanceFormSectionTitle>Instrument Columns</FinanceFormSectionTitle>
{renderEditor('instrumentColumns', fdc3Columns.instrumentColumns)}
</Box>
)}
{Boolean(fdc3Columns.positionColumns?.length) && (
<Box>
<FinanceFormSectionTitle>Position Columns</FinanceFormSectionTitle>
{renderEditor('positionColumns', fdc3Columns.positionColumns)}
</Box>
)}
{Boolean(fdc3Columns.contactColumns?.length) && (
<Box>
<FinanceFormSectionTitle>Contact Columns</FinanceFormSectionTitle>
{renderEditor('contactColumns', fdc3Columns.contactColumns)}
</Box>
)}
{Boolean(fdc3Columns.countryColumns?.length) && (
<Box>
<FinanceFormSectionTitle>Country Columns</FinanceFormSectionTitle>
{renderEditor('countryColumns', fdc3Columns.countryColumns)}
</Box>
)}
{Boolean(fdc3Columns.organizationColumns?.length) && (
<Box>
<FinanceFormSectionTitle>Organization Columns</FinanceFormSectionTitle>
{renderEditor('organizationColumns', fdc3Columns.organizationColumns)}
</Box>
)}
</Box>
);
};
*/