UNPKG

@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
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> ); }; */