@adaptabletools/adaptable
Version:
Powerful AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements
375 lines (374 loc) • 22 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import * as React from 'react';
import { CheckBox } from '../../../components/CheckBox';
import ErrorBox from '../../../components/ErrorBox';
import FormLayout, { FormRow } from '../../../components/FormLayout';
import { Tag } from '../../../components/Tag';
import { ColorPicker } from '../../../components/ColorPicker';
import { RangesComponent } from '../../Components/RangesComponent';
import { ColumnSelector } from '../../Components/Selectors/ColumnSelector';
import { getGraySwatchColor } from '../../UIHelper';
import { useOnePageAdaptableWizardContext } from '../../Wizard/OnePageAdaptableWizard';
import AdaptableInput from '../../Components/AdaptableInput';
import { Box, Flex } from '../../../components/Flex';
import { BulletRangesSummaryPreview } from './StyledColumnWizardStyleSection/Components/BulletRangesSummaryPreview';
import { SingleSelect } from '../../../components/NewSelect';
import { getCellFontStyleSummaryItems, StyledColumnFontStyleEditor, } from './StyledColumnSliceStyleEditors';
import { Card } from '../../../components/Card';
import { renderSummaryStringTags } from '../../Wizard/SummaryColorTag';
import { formatBarStyleCellTextLayoutSummary, getActiveBarStyleCellTextTokens, hasBarStyleCellTextConfigured, patchBarStyleCellTextPlacement, resolveBarStyleCellTextLayout, toggleBarStyleCellTextToken, } from '../../../Utilities/Helpers/StyledColumns/BarStylesHelper';
import { BarStyleCellTextLayoutEditor } from './StyledColumnWizardStyleSection/Components/BarStyleCellTextLayoutEditor';
const BULLET_STYLE_FORM_SIZES = ['200px', '1fr'];
const getTargetSummary = (target) => {
if (target == undefined) {
return null;
}
if (Array.isArray(target)) {
return target
.map((entry) => formatSingleBulletTargetSummary(entry))
.filter(Boolean)
.join(', ');
}
return formatSingleBulletTargetSummary(target);
};
const formatSingleBulletTargetSummary = (target) => {
if (typeof target === 'number') {
return String(target);
}
if (typeof target === 'string') {
return target;
}
const t = target;
return t.Label ?? String(t.Value);
};
const formatBulletOriginLabel = (origin) => {
if (origin == undefined) {
return 'Auto';
}
if (typeof origin === 'number') {
return `Custom (${origin})`;
}
return origin;
};
const formatBulletCellTextSummary = (cellText) => cellText?.map((t) => (t === 'CellValue' ? 'Cell Value' : 'Percent Value')).join(' + ') ?? '';
const formatBulletToolTipSummary = (toolTipText) => toolTipText?.map((t) => (t === 'CellValue' ? 'Cell Value' : 'Percent Value')).join(' + ') ?? '';
export const getStyledColumnBulletRangesViewValues = (data) => {
const bullet = data.BulletChartStyle;
if (!bullet) {
return [];
}
const bandCount = bullet.CellRanges?.length ?? 0;
const items = [`Bands: ${bandCount}`];
if (bullet.RangeValueType != null) {
items.push(`Range Value Type: ${bullet.RangeValueType}`);
}
return items;
};
const buildStyledColumnBulletStyleSummaryStrings = (bullet, options) => {
const items = [];
const targetProperties = bullet.TargetProperties;
const targetSummary = getTargetSummary(targetProperties?.Target);
if (targetSummary) {
items.push(`Target: ${targetSummary}`);
}
const marker = targetProperties?.Marker;
if (marker?.Shape && marker.Shape !== 'Line') {
items.push(`Target Marker Shape: ${marker.Shape}`);
}
if (marker?.Color) {
items.push(`Target Marker Colour: ${marker.Color}`);
}
if (marker?.Size != null) {
items.push(`Target Marker Size: ${marker.Size}px`);
}
if (bullet.Orientation === 'Vertical') {
items.push('Orientation: Vertical');
}
const barProperties = bullet.Bar;
if (barProperties?.Color) {
items.push(`Bar Colour: ${barProperties.Color}`);
}
if (barProperties?.Height != null) {
items.push(`${bullet.Orientation === 'Vertical' ? 'Bar Width' : 'Bar Height'}: ${barProperties.Height}px`);
}
items.push(`Origin: ${formatBulletOriginLabel(bullet.Origin)}`);
if (bullet.BackColor) {
items.push(`Back Colour: ${bullet.BackColor}`);
}
if (hasBarStyleCellTextConfigured(bullet.CellTextProperties)) {
const tokens = getActiveBarStyleCellTextTokens(bullet.CellTextProperties);
items.push(`Cell Text: ${formatBulletCellTextSummary(tokens)}`);
const layoutSummary = formatBarStyleCellTextLayoutSummary(resolveBarStyleCellTextLayout(bullet.CellTextProperties));
if (layoutSummary) {
items.push(`Placement: ${layoutSummary}`);
}
}
else if (options.includeEmptyCellText) {
items.push('Cell Text: None');
}
getCellFontStyleSummaryItems(bullet.Font).forEach(({ label, value }) => {
items.push(`${label}: ${value}`);
});
if (bullet.ToolTipText?.length) {
items.push(`Tooltip: ${formatBulletToolTipSummary(bullet.ToolTipText)}`);
}
else if (options.includeEmptyTooltip) {
items.push('Tooltip: No Tooltip');
}
return items;
};
export const getStyledColumnBulletStyleViewValues = (data) => {
const bullet = data.BulletChartStyle;
if (!bullet) {
return [];
}
return buildStyledColumnBulletStyleSummaryStrings(bullet, {
includeEmptyCellText: false,
includeEmptyTooltip: false,
});
};
export const renderStyledColumnBulletRangesSummary = (data) => {
const bullet = data.BulletChartStyle;
if (!bullet) {
return _jsx(Tag, { children: "No bands defined" });
}
const tagItems = getStyledColumnBulletRangesViewValues(data);
return (_jsxs(Flex, { flexDirection: "column", className: "twa:gap-2 twa:items-start", children: [renderSummaryStringTags(tagItems), _jsx(BulletRangesSummaryPreview, { bulletStyle: bullet })] }));
};
export const StyledColumnBulletRangesView = ({ data }) => renderStyledColumnBulletRangesSummary(data);
export const renderStyledColumnBulletStyleSummary = (data) => {
const bullet = data.BulletChartStyle;
if (!bullet) {
return _jsx(Tag, { children: "No Styling Defined" });
}
const items = buildStyledColumnBulletStyleSummaryStrings(bullet, {
includeEmptyCellText: true,
includeEmptyTooltip: true,
});
if (!items.length) {
return _jsx(Tag, { children: "No Style Defined" });
}
return renderSummaryStringTags(items);
};
export const renderStyledColumnBulletSummary = (data) => {
const bullet = data.BulletChartStyle;
if (!bullet) {
return _jsx(Tag, { children: "No Styling Defined" });
}
return (_jsxs(Flex, { flexDirection: "column", className: "twa:gap-3 twa:items-start", children: [renderStyledColumnBulletRangesSummary(data), renderStyledColumnBulletStyleSummary(data)] }));
};
const getSimpleTargetMode = (target) => {
if (target == undefined) {
return 'None';
}
const value = Array.isArray(target) ? extractTargetValue(target[0]) : extractTargetValue(target);
if (value === 'Col-Avg')
return 'Col-Avg';
if (value === 'Col-Median')
return 'Col-Median';
if (typeof value === 'number')
return 'Number';
if (typeof value === 'string')
return 'Column';
return 'None';
};
const extractTargetValue = (t) => {
if (t == undefined)
return undefined;
if (typeof t === 'object') {
return t.Value;
}
return t;
};
const MARKER_SHAPES = [
{ value: 'Line', label: 'Line' },
{ value: 'Triangle', label: 'Triangle' },
{ value: 'Dot', label: 'Dot' },
{ value: 'Diamond', label: 'Diamond' },
];
export const StyledColumnWizardBulletSection = (props) => {
const variant = props.variant ?? 'style';
const { data, api } = useOnePageAdaptableWizardContext();
const column = React.useMemo(() => {
const columnId = data.ColumnId;
if (!columnId) {
return undefined;
}
return api.columnApi.getColumnWithColumnId(columnId);
}, [data.ColumnId, api]);
const minMaxRangeValues = React.useMemo(() => {
if (!column) {
return null;
}
return {
min: api.styledColumnApi.internalApi.getMinValueForNumericColumn(column),
max: api.styledColumnApi.internalApi.getMaxValueForNumericColumn(column),
};
}, [column, api]);
const scope = { ColumnIds: [data.ColumnId] };
const bullet = data.BulletChartStyle ?? {};
const disabled = !data.ColumnId || !column;
const update = (patch) => {
props.onChange({
...data,
BulletChartStyle: {
...bullet,
...patch,
},
});
};
const onUpdateRanges = (ranges) => {
update({ CellRanges: ranges });
};
const onUpdateColumnComparison = (columnComparison) => {
update({ ColumnComparison: columnComparison });
};
const onRangeValueTypeChange = React.useCallback((rangeValueType) => {
const ranges = rangeValueType === 'Number'
? api.columnScopeApi.createCellColorRangesForScope(scope)
: [
{
Min: 0,
Max: 100,
Color: getGraySwatchColor(),
},
];
props.onChange({
...data,
BulletChartStyle: {
...bullet,
CellRanges: ranges,
RangeValueType: rangeValueType,
},
});
}, [data, bullet]);
const currentMode = getSimpleTargetMode(bullet.TargetProperties?.Target);
const updateTargetProperties = (patch) => {
const merged = {
...bullet.TargetProperties,
...patch,
};
const isEmpty = merged.Target == undefined && merged.Marker == undefined;
update({ TargetProperties: isEmpty ? undefined : merged });
};
const setTarget = (target) => updateTargetProperties({ Target: target });
const handleTargetModeChange = (mode) => {
if (mode === 'None') {
setTarget(undefined);
return;
}
if (mode === 'Col-Avg' || mode === 'Col-Median') {
setTarget(mode);
return;
}
if (mode === 'Number') {
setTarget(minMaxRangeValues?.max ?? 0);
return;
}
if (mode === 'Column') {
const candidate = api.columnApi.getNumericColumns().find((c) => c.columnId !== data.ColumnId);
setTarget(candidate?.columnId ?? '');
return;
}
};
const handleNumericTargetChange = (value) => {
setTarget(value == undefined || isNaN(value) ? 0 : value);
};
const handleColumnTargetChange = (columnId) => {
setTarget(columnId);
};
const updateBar = (patch) => {
const merged = {
...bullet.Bar,
...patch,
};
const isEmpty = merged.Color == undefined && merged.Height == undefined;
update({ Bar: isEmpty ? undefined : merged });
};
const updateMarker = (patch) => {
updateTargetProperties({
Marker: {
Shape: 'Line',
...bullet.TargetProperties?.Marker,
...patch,
},
});
};
const toggleCellText = (token, show) => {
update(toggleBarStyleCellTextToken(bullet.CellTextProperties, token, show));
};
const onCellTextPlacementChange = (token, patch) => {
update({
CellTextProperties: patchBarStyleCellTextPlacement(bullet.CellTextProperties, token, patch),
});
};
const toggleToolTipText = (token, checked) => {
const current = bullet.ToolTipText ?? [];
const next = checked
? Array.from(new Set([...current, token]))
: current.filter((t) => t !== token);
update({ ToolTipText: next });
};
if (!data.ColumnId || !column) {
return (_jsx(Box, { children: _jsx(ErrorBox, { className: "twa:mt-2", children: !data.ColumnId
? 'You need to select a column before styling.'
: `Column "${data.ColumnId}" was not found in the grid.` }) }));
}
if (variant === 'ranges') {
return (_jsx(Box, { children: _jsxs(Card, { shadow: false, children: [_jsxs(Card.Title, { children: [_jsx(Box, { className: "twa:font-medium", children: "Ranges" }), _jsx(Box, { className: "twa:text-xs twa:opacity-70 twa:font-normal twa:max-w-[520px]", children: "Define qualitative bands for the bullet scale" })] }), _jsx(Card.Body, { children: _jsx(RangesComponent, { disabled: disabled, minMaxRangeValues: minMaxRangeValues, api: api, scope: scope, ranges: bullet.CellRanges, rangeValueType: bullet.RangeValueType, onRangeValueTypeChange: onRangeValueTypeChange, columnComparison: bullet.ColumnComparison, updateRanges: onUpdateRanges, updateColumnComparison: onUpdateColumnComparison, hideColumnComparison: true }) })] }) }));
}
const primaryTarget = Array.isArray(bullet.TargetProperties?.Target)
? bullet.TargetProperties?.Target?.[0]
: bullet.TargetProperties?.Target;
const numericTargetValue = typeof primaryTarget === 'number'
? primaryTarget
: typeof primaryTarget?.Value === 'number'
? primaryTarget.Value
: undefined;
const columnTargetValue = typeof primaryTarget === 'string' &&
primaryTarget !== 'Col-Avg' &&
primaryTarget !== 'Col-Median'
? primaryTarget
: typeof primaryTarget?.Value === 'string'
? primaryTarget.Value
: undefined;
const markerCfg = bullet.TargetProperties?.Marker;
const marker = {
Shape: markerCfg?.Shape ?? 'Line',
Color: markerCfg?.Color ?? '',
Size: markerCfg?.Size ?? (markerCfg?.Shape === 'Line' ? 2 : 8),
};
const cellTextProperties = bullet.CellTextProperties;
const cellTextDisabled = !hasBarStyleCellTextConfigured(cellTextProperties) || disabled;
return (_jsxs(Box, { children: [_jsxs(Card, { shadow: false, className: "twa:mb-3", children: [_jsxs(Card.Title, { children: [_jsx(Box, { className: "twa:font-medium", children: "Target" }), _jsx(Box, { className: "twa:text-xs twa:opacity-70 twa:font-normal twa:max-w-[520px]", children: "Select a target value for the bullet scale" })] }), _jsx(Card.Body, { children: _jsxs(FormLayout, { sizes: [...BULLET_STYLE_FORM_SIZES], children: [_jsx(FormRow, { label: "Target:", children: _jsx(Box, { className: "twa:max-w-[180px]", children: _jsx(SingleSelect, { className: "twa:w-full", value: currentMode, onValueChange: (v) => handleTargetModeChange(v), items: [
{ value: 'None', label: 'None' },
{ value: 'Number', label: 'Fixed Number' },
{ value: 'Column', label: 'Column' },
{ value: 'Col-Avg', label: 'Column Average' },
{ value: 'Col-Median', label: 'Column Median' },
] }) }) }), currentMode === 'Number' && (_jsx(FormRow, { label: "Target Value:", children: _jsx(Box, { className: "twa:max-w-[160px]", children: _jsx(AdaptableInput, { type: "number", value: numericTargetValue ?? '', onChange: (e) => handleNumericTargetChange(e.target.value === '' ? undefined : Number(e.target.value)) }) }) })), currentMode === 'Column' && (_jsx(FormRow, { label: "Target Column:", children: _jsx(Box, { className: "twa:max-w-[220px]", children: _jsx(ColumnSelector, { type: "number", value: columnTargetValue ?? '', onChange: handleColumnTargetChange, filterColumn: (c) => c.columnId !== data.ColumnId, placeholder: "Select numeric column" }) }) })), currentMode !== 'None' && (_jsxs(_Fragment, { children: [_jsx(FormRow, { label: "Marker Shape:", children: _jsx(Box, { className: "twa:max-w-[160px]", children: _jsx(SingleSelect, { className: "twa:w-full", value: marker.Shape, onValueChange: (s) => updateMarker({ Shape: s }), items: MARKER_SHAPES.map((s) => ({ value: s.value, label: s.label })) }) }) }), _jsx(FormRow, { label: "Marker Colour:", children: _jsx(ColorPicker, { disabled: disabled, api: api, value: marker.Color || undefined, onChange: (c) => updateMarker({ Color: c }) }) }), _jsx(FormRow, { label: "Marker Size:", children: _jsx(Box, { className: "twa:max-w-[100px]", children: _jsx(AdaptableInput, { type: "number", min: 1, value: marker.Size, onChange: (e) => updateMarker({ Size: Number(e.target.value) }) }) }) })] }))] }) })] }), _jsxs(Card, { shadow: false, className: "twa:mb-3", children: [_jsxs(Card.Title, { children: [_jsx(Box, { className: "twa:font-medium", children: "Bar" }), _jsx(Box, { className: "twa:text-xs twa:opacity-70 twa:font-normal twa:max-w-[520px]", children: "Set the bar orientation and height" })] }), _jsx(Card.Body, { children: _jsxs(FormLayout, { sizes: [...BULLET_STYLE_FORM_SIZES], children: [_jsx(FormRow, { label: "Orientation:", children: _jsx(Box, { className: "twa:max-w-[160px]", children: _jsx(SingleSelect, { className: "twa:w-full", value: bullet.Orientation ?? 'Horizontal', onValueChange: (v) => update({ Orientation: v }), items: [
{ value: 'Horizontal', label: 'Horizontal' },
{ value: 'Vertical', label: 'Vertical' },
] }) }) }), bullet.Orientation === 'Vertical' && (_jsx(FormRow, { label: "", children: _jsxs(Box, { className: "twa:text-xs twa:text-neutral-500", children: ["Tip: vertical bullet charts need a tall row height to be readable (we recommend at least 60px). Set ", _jsx("code", { children: "rowHeight" }), " on ", _jsx("code", { children: " gridOptions " }), " or use", ' ', _jsx("code", { children: "getRowHeight" }), "."] }) })), _jsx(FormRow, { label: "Bar Colour:", children: _jsx(ColorPicker, { disabled: disabled, api: api, value: bullet.Bar?.Color, onChange: (c) => updateBar({ Color: c }) }) }), _jsx(FormRow, { label: bullet.Orientation === 'Vertical' ? 'Bar Width (px):' : 'Bar Height (px):', children: _jsx(Box, { className: "twa:max-w-[100px]", children: _jsx(AdaptableInput, { type: "number", min: 1, value: bullet.Bar?.Height ?? '', onChange: (e) => updateBar({
Height: e.target.value === '' ? undefined : Number(e.target.value),
}), placeholder: "8" }) }) }), _jsx(FormRow, { label: "Origin:", children: _jsx(Box, { className: "twa:max-w-[160px]", children: _jsx(SingleSelect, { className: "twa:w-full", value: typeof bullet.Origin === 'number' ? 'Number' : bullet.Origin ?? 'Auto', onValueChange: (v) => {
if (v === 'Auto' || v === 'Zero') {
update({ Origin: v });
}
else {
update({ Origin: 0 });
}
}, items: [
{ value: 'Auto', label: 'Auto' },
{ value: 'Zero', label: 'Zero' },
] }) }) })] }) })] }), _jsxs(Card, { shadow: false, className: "twa:mb-3", children: [_jsxs(Card.Title, { children: [_jsx(Box, { className: "twa:font-medium", children: "Cell Text" }), _jsx(Box, { className: "twa:text-xs twa:opacity-70 twa:font-normal twa:max-w-[520px]", children: "Pick which values to display and where each one sits around the bar" })] }), _jsxs(Card.Body, { children: [_jsx(FormLayout, { sizes: [...BULLET_STYLE_FORM_SIZES], children: _jsx(BarStyleCellTextLayoutEditor, { disabled: disabled, cellTextProperties: cellTextProperties, onToggle: toggleCellText, onPlacementChange: onCellTextPlacementChange }) }), _jsx(Box, { className: `twa:mt-3 twa:pt-3 twa:border-t twa:border-foreground/15 ${cellTextDisabled ? 'twa:opacity-50' : ''}`, children: _jsx(StyledColumnFontStyleEditor, { api: api, disabled: cellTextDisabled, value: bullet.Font, onChange: (next) => {
if (next) {
update({ Font: next });
}
else {
const cleaned = { ...bullet };
delete cleaned.Font;
props.onChange({ ...data, BulletChartStyle: cleaned });
}
} }) })] })] }), _jsxs(Card, { shadow: false, className: "twa:mb-3", children: [_jsxs(Card.Title, { children: [_jsx(Box, { className: "twa:font-medium", children: "Tooltip" }), _jsx(Box, { className: "twa:text-xs twa:opacity-70 twa:font-normal twa:max-w-[520px]", children: "Choose whether to display a tooltip" })] }), _jsx(Card.Body, { children: _jsx(FormLayout, { sizes: [...BULLET_STYLE_FORM_SIZES], children: _jsxs(FormRow, { label: "Tooltip Display:", children: [_jsx(CheckBox, { disabled: disabled, checked: bullet.ToolTipText?.includes('CellValue'), onChange: (checked) => toggleToolTipText('CellValue', checked), children: "Cell Value" }), ' ', _jsx(CheckBox, { disabled: disabled, className: "twa:ml-3", checked: bullet.ToolTipText?.includes('PercentageValue'), onChange: (checked) => toggleToolTipText('PercentageValue', checked), children: "Percent Value" })] }) }) })] })] }));
};