/** @jsxImportSource react */ import { VDOM } from "../ui/Widget"; import { StyledContainerBase, StyledContainerConfig } from "../ui/Container"; import { tooltipMouseMove, tooltipMouseLeave, TooltipParentInstance, } from "../widgets/overlay/tooltip-ops"; import { Selection } from "../ui/selection/Selection"; import { withHoverSync } from "../ui/HoverSync"; import { Prop, BooleanProp, NumberProp, StringProp } from "../ui/Prop"; import { Instance } from "../ui/Instance"; import { RenderingContext } from "../ui/RenderingContext"; import { Rect } from "../svg/util/Rect"; import type { ChartRenderingContext } from "./Chart"; export interface ColumnBarBaseConfig extends StyledContainerConfig { /** The `x` value binding or expression. */ x?: Prop; /** The `y` value binding or expression. */ y?: Prop; disabled?: BooleanProp; /** Index of a color from the standard palette of colors. 0-15. */ colorIndex?: NumberProp; /** Used to automatically assign a color based on the `name` and the contextual `ColorMap` widget. */ colorMap?: StringProp; /** Name used to resolve the color. If not provided, `name` is used instead. */ colorName?: StringProp; /** Name of the item as it will appear in the legend. */ name?: StringProp; /** Used to indicate if an item is active or not. Inactive items are shown only in the legend. */ active?: BooleanProp; /** Indicate that columns should be stacked on top of the other columns. Default value is `false`. */ stacked?: BooleanProp; /** Name of the stack. If multiple stacks are used, each should have a unique name. Default value is `stack`. */ stack?: StringProp; /** Of center offset of the column. Use this in combination with `size` to align multiple series on the same chart. */ offset?: NumberProp; /** Border radius of the column/bar. */ borderRadius?: NumberProp; /** * Name of the horizontal axis. The value should match one of the horizontal axes set * in the `axes` configuration of the parent `Chart` component. Default value is `x`. */ xAxis?: string; /** * Name of the vertical axis. The value should match one of the vertical axes set * in the `axes` configuration if the parent `Chart` component. Default value is `y`. */ yAxis?: string; /** Name of the legend to be used. Default is `legend`. Set to `false` to hide the legend entry. */ legend?: string | false; legendAction?: string; legendShape?: string; /** A value used to identify the group of components participating in hover effect synchronization. */ hoverChannel?: string; /** A value used to uniquely identify the record within the hover sync group. */ hoverId?: StringProp | NumberProp; /** Hide the bar/column rect. Used for stacked series where only top bar should be visible. */ hidden?: BooleanProp; selection?: any; tooltip?: any; } export interface ColumnBarBaseInstance extends Instance, TooltipParentInstance { axes: Record; xAxis: any; yAxis: any; hoverSync: any; colorMap: any; bounds: Rect; } export class ColumnBarBase extends StyledContainerBase { declare baseClass: string; declare xAxis: string; declare yAxis: string; declare offset: number; declare legend: string | false; declare legendAction: string; declare active: boolean; declare stacked: boolean; declare stack: string; declare legendShape: string; declare hoverChannel: string; declare borderRadius: number; declare hidden: boolean; declare selection: Selection; declare tooltip: any; constructor(config: ColumnBarBaseConfig) { super(config); } init(): void { this.selection = Selection.create(this.selection); super.init(); } declareData(...args: any[]): any { var selection = this.selection.configureWidget(this); return super.declareData( selection, { x: undefined, y: undefined, style: { structured: true }, class: { structured: true }, className: { structured: true }, disabled: undefined, colorIndex: undefined, colorMap: undefined, colorName: undefined, name: undefined, active: true, stacked: undefined, stack: undefined, offset: undefined, hoverId: undefined, borderRadius: undefined, hidden: undefined, }, ...args, ); } prepareData( context: ChartRenderingContext, instance: ColumnBarBaseInstance, ): void { instance.axes = context.axes!; instance.xAxis = context.axes![this.xAxis]; instance.yAxis = context.axes![this.yAxis]; instance.hoverSync = context.hoverSync; var { data } = instance; data.valid = this.checkValid(data); if (!data.colorName && data.name) data.colorName = data.name; super.prepareData(context, instance); } checkValid(data: any): boolean { return true; } prepare( context: ChartRenderingContext, instance: ColumnBarBaseInstance, ): void { let { data, colorMap } = instance; if (colorMap && data.colorName) { data.colorIndex = colorMap.map(data.colorName); if (instance.cache("colorIndex", data.colorIndex)) instance.markShouldUpdate(context); } if (!data.valid) return; if (data.active) { instance.bounds = this.calculateRect(instance); instance.cache("bounds", instance.bounds); if (!instance.bounds.isEqual(instance.cached.bounds)) instance.markShouldUpdate(context); context.push("parentRect", instance.bounds); if (instance.xAxis.shouldUpdate || instance.yAxis.shouldUpdate) instance.markShouldUpdate(context); } if (data.name && context.addLegendEntry) context.addLegendEntry(this.legend, { name: data.name, active: data.active, colorIndex: data.colorIndex, disabled: data.disabled, selected: this.selection.isInstanceSelected(instance), style: data.style, shape: this.legendShape, onClick: (e: MouseEvent) => { this.onLegendClick(e, instance); }, }); } prepareCleanup( context: ChartRenderingContext, instance: ColumnBarBaseInstance, ): void { let { data } = instance; if (data.valid && data.active) context.pop("parentRect"); } onLegendClick(e: MouseEvent, instance: ColumnBarBaseInstance): void { var allActions = this.legendAction == "auto"; var { data } = instance; if (allActions || this.legendAction == "toggle") if (instance.set("active", !data.active)) return; if (allActions || this.legendAction == "select") this.handleClick(e, instance); } calculateRect(instance: ColumnBarBaseInstance): Rect { throw new Error("Abstract method."); } render( context: RenderingContext, instance: ColumnBarBaseInstance, key: string, ): React.ReactNode { let { data, bounds } = instance; if (!data.active || !data.valid) return null; return withHoverSync( key, instance.hoverSync, this.hoverChannel, data.hoverId, ({ hover, onMouseMove, onMouseLeave, key }: any) => { var stateMods: Record = { selected: this.selection.isInstanceSelected(instance), disabled: data.disabled, selectable: !this.selection.isDummy, ["color-" + data.colorIndex]: data.colorIndex != null, hover, }; return ( {!data.hidden && ( { onMouseMove(e, instance); tooltipMouseMove(e, instance, this.tooltip); }} onMouseLeave={(e) => { onMouseLeave(e, instance); tooltipMouseLeave(e, instance, this.tooltip); }} onClick={(e) => { this.handleClick(e, instance); }} /> )} {this.renderChildren(context, instance)} ); }, ); } handleClick( e: MouseEvent | React.MouseEvent, instance: ColumnBarBaseInstance, ): void { if (!this.selection.isDummy) { this.selection.selectInstance(instance, { toggle: e.ctrlKey, }); e.stopPropagation(); e.preventDefault(); } } } ColumnBarBase.prototype.xAxis = "x"; ColumnBarBase.prototype.yAxis = "y"; ColumnBarBase.prototype.offset = 0; ColumnBarBase.prototype.legend = "legend"; ColumnBarBase.prototype.legendAction = "auto"; ColumnBarBase.prototype.active = true; ColumnBarBase.prototype.stacked = false; ColumnBarBase.prototype.stack = "stack"; ColumnBarBase.prototype.legendShape = "rect"; ColumnBarBase.prototype.styled = true; ColumnBarBase.prototype.hoverChannel = "default"; ColumnBarBase.prototype.borderRadius = 0; ColumnBarBase.prototype.hidden = false;