UNPKG

@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
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.")); };