@adaptabletools/adaptable
Version:
Powerful data-agnostic HTML5 AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements
443 lines (442 loc) • 27.3 kB
JavaScript
import * as React from 'react';
import { Box, Flex, Text } from 'rebass';
import uniq from 'lodash/uniq';
import clamp from 'lodash/clamp';
import HelpBlock from '../../../components/HelpBlock';
import Input from '../../../components/Input';
import SimpleButton from '../../../components/SimpleButton';
import { CheckBox } from '../../../components/CheckBox';
import FormLayout, { FormRow } from '../../../components/FormLayout';
import { AdaptableObjectRow } from '../../Components/AdaptableObjectRow';
import Radio from '../../../components/Radio';
import { useOnePageAdaptableWizardContext } from '../../Wizard/OnePageAdaptableWizard';
import { Tabs } from '../../../components/Tabs';
import StringExtensions from '../../../Utilities/Extensions/StringExtensions';
import { Tag } from '../../../components/Tag';
import { useAdaptable } from '../../AdaptableContext';
import FormatHelper from '../../../Utilities/Helpers/FormatHelper';
import { Toggle, ToggleGroup } from '../../../components/Toggle';
import { DEFAULT_DOUBLE_DISPLAY_VALUE, DEFAULT_STRING_DISPLAY_VALUE, } from '../../../Utilities/Constants/GeneralConstants';
import Textarea from '../../../components/Textarea';
import { ButtonInfo } from '../../Components/Buttons/ButtonInfo';
import { FormatColumnPlaceholderDocsLink } from '../../../Utilities/Constants/DocumentationLinkConstants';
const DOLLAR_OPTIONS = {
FractionDigits: 2,
FractionSeparator: '.',
IntegerDigits: undefined,
IntegerSeparator: ',',
Prefix: '$',
Suffix: '',
Multiplier: 1,
Parentheses: false,
};
const STERLING_OPTIONS = {
FractionDigits: 2,
FractionSeparator: '.',
IntegerDigits: undefined,
IntegerSeparator: ',',
Prefix: '£',
Suffix: '',
Multiplier: 1,
Parentheses: false,
};
const MILLION_OPTIONS = {
FractionDigits: undefined,
FractionSeparator: '.',
IntegerDigits: undefined,
IntegerSeparator: ',',
Prefix: '',
Suffix: 'M',
Multiplier: 0.000001,
Parentheses: false,
};
const THOUSAND_OPTIONS = {
FractionDigits: undefined,
FractionSeparator: '.',
IntegerDigits: undefined,
IntegerSeparator: ',',
Prefix: '',
Suffix: 'K',
Multiplier: 0.001,
Parentheses: false,
};
const PERCENT_OPTIONS = {
FractionDigits: 2,
FractionSeparator: '.',
IntegerDigits: undefined,
IntegerSeparator: ',',
Prefix: '',
Suffix: '%',
Multiplier: 100,
Parentheses: false,
};
const DateFormatPresets = [
'MM/dd/yyyy',
'dd-MM-yyyy',
'MMMM do yyyy, h:mm:ss a',
'EEEE',
'MMM do yyyy',
'yyyyMMdd',
'HH:mm:ss',
];
export const getFormatColumnFormatSummaryValue = (data) => {
let content = 'N/A';
if (!data.DisplayFormat) {
content = 'N/A';
}
else {
if (data.DisplayFormat.Formatter === 'NumberFormatter') {
content = FormatHelper.NumberFormatter(DEFAULT_DOUBLE_DISPLAY_VALUE, data.DisplayFormat.Options);
}
if (data.DisplayFormat.Formatter === 'DateFormatter') {
content = FormatHelper.DateFormatter(new Date(), data.DisplayFormat.Options);
}
if (data.DisplayFormat.Formatter === 'StringFormatter') {
content = FormatHelper.StringFormatter(DEFAULT_STRING_DISPLAY_VALUE, data.DisplayFormat.Options);
}
}
return content;
};
const renderCustomFormatter = (data, customFormatter, setFormatOption) => (React.createElement(FormRow, { key: customFormatter.id, label: customFormatter.label ?? customFormatter.id },
React.createElement(CheckBox, { "data-name": customFormatter.id, checked: data.DisplayFormat.Options.CustomDisplayFormats?.some?.((item) => item === customFormatter.id), onChange: (checked) => {
let newCustomFormats = data?.DisplayFormat?.Options?.CustomDisplayFormats ?? [];
if (checked) {
newCustomFormats = [...newCustomFormats, customFormatter.id];
}
else {
newCustomFormats = newCustomFormats.filter((item) => item !== customFormatter.id);
}
setFormatOption('CustomDisplayFormats', newCustomFormats);
} })));
export const renderFormatColumnFormatSummary = (data) => {
return React.createElement(Tag, null, getFormatColumnFormatSummaryValue(data));
};
export const getFormatDisplayTypeForScope = (scope, api) => {
if (scope == undefined) {
return undefined;
}
if ('All' in scope) {
return undefined;
}
// need to see if all columns are numeric or date
if ('ColumnIds' in scope) {
const columns = scope.ColumnIds.map((c) => api.columnApi.getColumnWithColumnId(c)).filter(Boolean);
const columnDataTypes = uniq(columns.map((c) => c.dataType));
if (columnDataTypes.length == 1 && columnDataTypes[0] == 'number') {
return 'number';
}
if (columnDataTypes.length == 1 && columnDataTypes[0] == 'date') {
return 'date';
}
if (columnDataTypes.length == 1 && columnDataTypes[0] == 'text') {
return 'text';
}
return undefined;
}
if ('DataTypes' in scope && scope.DataTypes.length == 1 && scope.DataTypes[0] == 'number') {
return 'number';
}
if ('DataTypes' in scope && scope.DataTypes.length == 1 && scope.DataTypes[0] == 'date') {
return 'date';
}
if ('DataTypes' in scope && scope.DataTypes.length == 1 && scope.DataTypes[0] == 'text') {
return 'text';
}
if ('ColumnTypes' in scope && scope.ColumnTypes.length) {
// need to check if all column with this column type has the same data type and return that particular one
const columns = scope.ColumnTypes.flatMap((columnType) => {
return api.columnApi.getColumnsByColumnType(columnType);
});
// check if all have the same type
if (columns.length &&
columns.every((column) => column.dataType === columns[0].dataType) &&
// supported data types
['number', 'date', 'text'].includes(columns[0].dataType)) {
return columns[0].dataType;
}
}
return undefined;
};
const renderDateFormat = (data, _onChange, setFormatOption, scopedCustomFormatters) => {
if (data.DisplayFormat.Formatter !== 'DateFormatter') {
return null;
}
return (React.createElement(Box, { padding: 2 },
React.createElement(Tabs, null,
React.createElement(Tabs.Tab, null, "Format"),
React.createElement(Tabs.Content, null,
React.createElement(Text, { padding: 2, fontSize: 2 },
"Either create your own date pattern, or select one of the preset date formats (check out",
' ',
React.createElement("a", { href: "https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table", target: "_blank" }, "available symbols"),
")."),
React.createElement(FormLayout, null,
React.createElement(FormRow, { label: "Pattern" },
React.createElement(Input, { "data-name": "pattern", value: data.DisplayFormat.Options.Pattern ?? '', onChange: (e) => setFormatOption('Pattern', e.currentTarget.value), mr: 2 }),
React.createElement("span", null, data.DisplayFormat.Options.Pattern &&
FormatHelper.DateFormatter(new Date(), data.DisplayFormat.Options, true)))))),
scopedCustomFormatters.length > 0 && (React.createElement(Tabs, { marginTop: 2, keyboardNavigation: false },
React.createElement(Tabs.Tab, null, "Custom Formats"),
React.createElement(Tabs.Content, null,
React.createElement(FormLayout, null, scopedCustomFormatters.map((formatter) => renderCustomFormatter(data, formatter, setFormatOption)))))),
React.createElement(Tabs, { marginTop: 2, keyboardNavigation: false },
React.createElement(Tabs.Tab, null, "Presets"),
React.createElement(Tabs.Content, null,
React.createElement(AdaptableObjectRow, { style: { fontWeight: 'bold' }, colItems: [
{ Content: 'Pattern', Size: 1 },
{ Content: 'Formatted Date', Size: 1 },
{ Content: '', Size: 1 },
] }),
DateFormatPresets.map((Pattern, index) => (React.createElement(AdaptableObjectRow, { key: index, colItems: [
{ Content: Pattern, Size: 1 },
{
Content: FormatHelper.DateFormatter(new Date(), { Pattern }),
Size: 1,
},
{
Content: (React.createElement(SimpleButton, { "data-name": "apply-format-pattern", "data-value": Pattern, onClick: () => setFormatOption('Pattern', Pattern) }, "Apply")),
Size: 1,
},
] })))))));
};
const renderNumberFormat = (data, onChange, setFormatOption, scopedCustomFormatters, api) => {
if (data.DisplayFormat.Formatter !== 'NumberFormatter') {
return null;
}
const setPercentPreset = () => {
onChange({
DisplayFormat: {
Formatter: 'NumberFormatter',
Options: PERCENT_OPTIONS,
},
});
};
const setDivideThousandPreset = () => {
onChange({
DisplayFormat: {
Formatter: 'NumberFormatter',
Options: THOUSAND_OPTIONS,
},
});
};
const setDivideMillionPreset = () => {
onChange({
DisplayFormat: {
Formatter: 'NumberFormatter',
Options: MILLION_OPTIONS,
},
});
};
const setDollarPreset = () => {
onChange({
DisplayFormat: {
Formatter: 'NumberFormatter',
Options: DOLLAR_OPTIONS,
},
});
};
const setSterlingPreset = () => {
onChange({
DisplayFormat: {
Formatter: 'NumberFormatter',
Options: STERLING_OPTIONS,
},
});
};
const IS_PERCENT = data.DisplayFormat.Options.Suffix === '%' &&
data.DisplayFormat.Options.Multiplier === PERCENT_OPTIONS.Multiplier; //isEqual(data.DisplayFormat.Options, PERCENT_OPTIONS);
const IS_THOUSAND = data.DisplayFormat.Options.Suffix === 'K' &&
data.DisplayFormat.Options.Multiplier === THOUSAND_OPTIONS.Multiplier; // isEqual(data.DisplayFormat.Options, THOUSAND_OPTIONS);
const IS_MILLION = data.DisplayFormat.Options.Suffix === 'M' &&
data.DisplayFormat.Options.Multiplier === MILLION_OPTIONS.Multiplier; //isEqual(data.DisplayFormat.Options, MILLION_OPTIONS);
const IS_DOLLAR = data.DisplayFormat.Options.Prefix === '$'; //isEqual(data.DisplayFormat.Options, DOLLAR_OPTIONS);
const IS_STERLING = data.DisplayFormat.Options.Prefix === '£'; //isEqual(data.DisplayFormat, STERLING_OPTIONS);
const showDocumentationLinks = api.internalApi.isDocumentationLinksDisplayed();
return (React.createElement(Box, { "data-name": 'format-column-display-format', padding: 2 },
React.createElement(Tabs, null,
React.createElement(Tabs.Tab, null, "Format"),
React.createElement(Tabs.Content, null,
React.createElement(Flex, { flexDirection: "row" },
React.createElement(FormLayout, { mr: 3 },
React.createElement(FormRow, { label: "Fraction Separator" },
React.createElement(Input, { "data-name": "fraction-separator", value: data.DisplayFormat.Options.FractionSeparator ?? '', onChange: (e) => setFormatOption('FractionSeparator', e.currentTarget.value) }),
' '),
React.createElement(FormRow, { label: "Integer Separator" },
React.createElement(Input, { "data-name": "integer-separator", value: data.DisplayFormat.Options.IntegerSeparator ?? '', onChange: (e) => setFormatOption('IntegerSeparator', e.currentTarget.value) })),
React.createElement(FormRow, { label: "Prefix" },
React.createElement(Input, { "data-name": "prefix", value: data.DisplayFormat.Options.Prefix ?? '', onChange: (e) => setFormatOption('Prefix', e.currentTarget.value) })),
React.createElement(FormRow, { label: "Suffix" },
React.createElement(Input, { "data-name": "suffix", value: data.DisplayFormat.Options.Suffix ?? '', onChange: (e) => setFormatOption('Suffix', e.currentTarget.value) })),
React.createElement(FormRow, { label: "Truncate" },
React.createElement(CheckBox, { "data-name": "truncate-checkbox", checked: data.DisplayFormat.Options.Truncate, onChange: (checked) => setFormatOption('Truncate', checked) })),
React.createElement(FormRow, { label: "Ceiling" },
React.createElement(CheckBox, { "data-name": "ceiling-checkbox", checked: data.DisplayFormat.Options.Ceiling, onChange: (checked) => setFormatOption('Ceiling', checked) })),
' ',
React.createElement(FormRow, { label: "Absolute" },
React.createElement(CheckBox, { "data-name": "abs-checkbox", checked: data.DisplayFormat.Options.Abs, onChange: (checked) => setFormatOption('Abs', checked) }))),
React.createElement(FormLayout, null,
React.createElement(FormRow, { label: "Fraction Digits" },
React.createElement(Input, { "data-name": "fraction-digits", type: "number", min: "0",
// max="20"
value: typeof data.DisplayFormat.Options.FractionDigits === 'number'
? data.DisplayFormat.Options.FractionDigits
: '', onChange: (e) => setFormatOption('FractionDigits', StringExtensions.IsNumeric(e.currentTarget.value)
? clamp(Number(e.currentTarget.value), 0, 20)
: undefined) })),
React.createElement(FormRow, { label: "Integer Digits" },
React.createElement(Input, { "data-name": "integer-digits", type: "number", min: "0", value: data.DisplayFormat.Options.IntegerDigits, onChange: (e) => setFormatOption('IntegerDigits', StringExtensions.IsNumeric(e.currentTarget.value)
? clamp(Number(e.currentTarget.value), 0, 20)
: undefined) })),
React.createElement(FormRow, { label: "Multiplier" },
React.createElement(Input, { "data-name": "multiplier", type: "number", value: data.DisplayFormat.Options.Multiplier, onChange: (e) => setFormatOption('Multiplier', Number(e.currentTarget.value)) })),
' ',
React.createElement(FormRow, { label: "Parentheses" },
React.createElement(CheckBox, { "data-name": "parentheses-checkbox", checked: data.DisplayFormat.Options.Parentheses, onChange: (checked) => setFormatOption('Parentheses', checked) })),
React.createElement(FormRow, { label: "Floor" },
React.createElement(CheckBox, { "data-name": "floor-checkbox", checked: data.DisplayFormat.Options.Floor, onChange: (checked) => setFormatOption('Floor', checked) })),
React.createElement(FormRow, { label: "Round" },
React.createElement(CheckBox, { "data-name": "round-checkbox", checked: data.DisplayFormat.Options.Round, onChange: (checked) => setFormatOption('Round', checked) })))))),
scopedCustomFormatters.length > 0 && (React.createElement(Tabs, { marginTop: 2, keyboardNavigation: false },
React.createElement(Tabs.Tab, null, "Custom Formats"),
React.createElement(Tabs.Content, null,
React.createElement(Flex, { flexDirection: "row" },
React.createElement(FormLayout, null, scopedCustomFormatters.map((formatter) => renderCustomFormatter(data, formatter, setFormatOption))))))),
React.createElement(Tabs, { marginTop: 2, autoFocus: false, keyboardNavigation: false },
React.createElement(Tabs.Tab, null, "Presets"),
React.createElement(Tabs.Content, null,
React.createElement(Text, { padding: 2, fontSize: 2 }, "Select a preset for common use cases"),
React.createElement(Flex, { flexDirection: "row" },
React.createElement(FormLayout, { margin: 2 },
React.createElement(FormRow, { label: "Show As:" },
React.createElement(Radio, { "data-name": "preset-percentage", marginLeft: 2, checked: IS_PERCENT, onChange: () => setPercentPreset() }, "Percentage"),
React.createElement(Radio, { "data-name": "preset-thousand", marginLeft: 3, checked: IS_THOUSAND, onChange: () => setDivideThousandPreset() }, "K (Thousand)"),
React.createElement(Radio, { "data-name": "preset-million", marginLeft: 3, checked: IS_MILLION, onChange: () => setDivideMillionPreset() }, "M (Million)"),
React.createElement(Radio, { "data-name": "preset-dollar", marginLeft: 3, checked: IS_DOLLAR, onChange: () => setDollarPreset() }, "Dollar"),
React.createElement(Radio, { "data-name": "preset-sterling", marginLeft: 3, checked: IS_STERLING, onChange: () => setSterlingPreset() }, "Sterling")))))),
React.createElement(Tabs, { marginTop: 2, autoFocus: false, keyboardNavigation: false },
React.createElement(Tabs.Tab, null, "Dynamic Content"),
React.createElement(Tabs.Content, null,
React.createElement(Text, { padding: 2, fontSize: 2 }, "Provide dynamic content through the use of Placeholders"),
React.createElement(FormLayout, { margin: 2 },
React.createElement(FormRow, { label: "" },
React.createElement(Textarea, { minWidth: 300, rows: 3, placeholder: "", marginTop: 2, type: 'text', autoFocus: false, value: data.DisplayFormat.Options.Content?.toString() ?? '', onChange: (e) => setFormatOption('Content', e.currentTarget.value) }),
showDocumentationLinks && (React.createElement(HelpBlock, { "data-name": "query-documentation", mt: 3, mb: 2, style: {
fontSize: 'var(--ab-font-size-3)',
padding: 0,
} },
React.createElement(ButtonInfo, { mr: 2, onClick: () => window.open(FormatColumnPlaceholderDocsLink, '_blank') }),
"Learn more about using placeholders"))),
' '))),
React.createElement(Tabs, { marginTop: 2, autoFocus: false, keyboardNavigation: false },
React.createElement(Tabs.Tab, null, "Examples"),
React.createElement(Tabs.Content, null,
React.createElement(AdaptableObjectRow, { style: { fontWeight: 'bold' }, colItems: [
{ Content: 'Raw Number', Size: 1 },
{ Content: 'Formatted Number', Size: 1 },
] }),
React.createElement(AdaptableObjectRow, { colItems: [
{ Content: DEFAULT_DOUBLE_DISPLAY_VALUE, Size: 1 },
{
Content: FormatHelper.NumberFormatter(DEFAULT_DOUBLE_DISPLAY_VALUE, data.DisplayFormat.Options),
Size: 1,
},
] }),
React.createElement(AdaptableObjectRow, { colItems: [
{ Content: '-' + DEFAULT_DOUBLE_DISPLAY_VALUE, Size: 1 },
{
Content: FormatHelper.NumberFormatter(-DEFAULT_DOUBLE_DISPLAY_VALUE, data.DisplayFormat.Options),
Size: 1,
},
] }),
React.createElement(AdaptableObjectRow, { colItems: [
{ Content: '0.123', Size: 1 },
{
Content: FormatHelper.NumberFormatter(0.123, data.DisplayFormat.Options),
Size: 1,
},
] })))));
};
const renderStringFormat = (data, _onChange, setFormatOption, scopedCustomFormatters, api) => {
if (data.DisplayFormat.Formatter !== 'StringFormatter') {
return null;
}
const showDocumentationLinks = api.internalApi.isDocumentationLinksDisplayed();
return (React.createElement(Box, { "data-name": 'format-column-display-format', padding: 2 },
React.createElement(Tabs, null,
React.createElement(Tabs.Tab, null, "Format"),
React.createElement(Tabs.Content, null,
React.createElement(Flex, { flexDirection: "column" },
React.createElement(FormLayout, { mr: 3 },
React.createElement(FormRow, { label: "Case:" },
React.createElement(Flex, null,
React.createElement(ToggleGroup, null,
React.createElement(Toggle, { pressed: data.DisplayFormat.Options.Case === 'Upper', onPressedChange: (pressed) => setFormatOption('Case', pressed ? 'Upper' : undefined), icon: "case-upper" }),
React.createElement(Toggle, { pressed: data.DisplayFormat.Options.Case === 'Lower', onPressedChange: (pressed) => setFormatOption('Case', pressed ? 'Lower' : undefined), icon: "case-lower" }),
React.createElement(Toggle, { pressed: data.DisplayFormat.Options.Case === 'Sentence', onPressedChange: (pressed) => setFormatOption('Case', pressed ? 'Sentence' : undefined), icon: "case-sentence" })),
React.createElement(CheckBox, { "data-name": "trim-checkbox", marginLeft: 5, checked: data.DisplayFormat.Options.Trim, onChange: (checked) => setFormatOption('Trim', checked) }, "Trim"))),
React.createElement(FormRow, { label: "Prefix" },
React.createElement(Input, { "data-name": "prefix", value: data.DisplayFormat.Options.Prefix ?? '', onChange: (e) => setFormatOption('Prefix', e.currentTarget.value) })),
React.createElement(FormRow, { label: "Suffix" },
React.createElement(Input, { "data-name": "suffix", value: data.DisplayFormat.Options.Suffix ?? '', onChange: (e) => setFormatOption('Suffix', e.currentTarget.value) })),
React.createElement(FormRow, { label: "Content" },
React.createElement(Textarea, { minWidth: 300, rows: 3, placeholder: "", marginTop: 2, type: 'text', autoFocus: false, value: data.DisplayFormat.Options.Content ?? '', onChange: (e) => setFormatOption('Content', e.currentTarget.value) }),
showDocumentationLinks && (React.createElement(HelpBlock, { "data-name": "query-documentation", mt: 3, mb: 2, style: {
fontSize: 'var(--ab-font-size-3)',
padding: 0,
} },
React.createElement(ButtonInfo, { mr: 2, onClick: () => window.open(FormatColumnPlaceholderDocsLink, '_blank') }),
"See how to create dynamic Display Format using placeholders"))),
React.createElement(FormRow, { label: "Empty" },
React.createElement(CheckBox, { "data-name": "empty-checkbox", checked: data.DisplayFormat.Options.Empty, onChange: (checked) => setFormatOption('Empty', checked) })))))),
scopedCustomFormatters.length > 0 && (React.createElement(Tabs, { marginTop: 2, keyboardNavigation: false },
React.createElement(Tabs.Tab, null, "Custom Formats"),
React.createElement(Tabs.Content, null,
React.createElement(Flex, { flexDirection: "column" },
React.createElement(FormLayout, { mr: 3 }, scopedCustomFormatters.map((formatter) => renderCustomFormatter(data, formatter, setFormatOption))))))),
React.createElement(Tabs, { marginTop: 2, keyboardNavigation: false },
React.createElement(Tabs.Tab, null, "Example"),
React.createElement(Tabs.Content, null,
React.createElement(AdaptableObjectRow, { style: { fontWeight: 'bold' }, colItems: [
{ Content: 'Raw String', Size: 1 },
{ Content: 'Formatted String', Size: 1 },
] }),
React.createElement(AdaptableObjectRow, { colItems: [
{ Content: '"' + DEFAULT_STRING_DISPLAY_VALUE + '"', Size: 1 },
{
Content: '"' +
FormatHelper.StringFormatter(DEFAULT_STRING_DISPLAY_VALUE, data.DisplayFormat.Options) +
'"',
Size: 1,
},
] })))));
};
export const FormatColumnFormatWizardSection = (props) => {
const { data } = useOnePageAdaptableWizardContext();
const adaptable = useAdaptable();
const formatColumnApi = adaptable.api.formatColumnApi;
const customDisplayFormatters = adaptable.api.optionsApi.getFormatColumnOptions().customDisplayFormatters ?? [];
const update = (updated) => {
props.onChange({ ...data, ...updated });
};
const setFormatOption = (key, value) => {
const DisplayFormat = { ...data.DisplayFormat };
// @ts-ignore
DisplayFormat.Options = { ...DisplayFormat.Options, [key]: value };
update({ DisplayFormat });
};
const Type = data.DisplayFormat && data.DisplayFormat.Formatter;
const customScopedFormatters = customDisplayFormatters.filter((displayFormatter) => adaptable.api.columnScopeApi.isScopeInScope(data.Scope, displayFormatter.scope));
if (Type === 'NumberFormatter') {
return renderNumberFormat(data, update, setFormatOption, customScopedFormatters, adaptable.api);
}
if (Type === 'DateFormatter') {
return renderDateFormat(data, update, setFormatOption, customScopedFormatters);
}
if (Type === 'StringFormatter') {
return renderStringFormat(data, update, setFormatOption, customScopedFormatters, adaptable.api);
}
return (React.createElement(HelpBlock, { margin: 3 },
"Setting a Display Format is only possible if ",
React.createElement("b", null, "all"),
" the columns in Scope are Numeric, String or Date."));
};