diff --git a/src/ui/legend/category.ts b/src/ui/legend/category.ts index d1313650..e427ea65 100644 --- a/src/ui/legend/category.ts +++ b/src/ui/legend/category.ts @@ -1,5 +1,5 @@ import { Component } from '../../core'; -import type { Group } from '../../shapes'; +import { HTML, type Group } from '../../shapes'; import { BBox, select, Selection, splitStyle, subStyleProps } from '../../util'; import type { TitleStyleProps } from './title'; import { getBBox, Title } from './title'; @@ -9,6 +9,12 @@ import type { CategoryOptions, CategoryStyleProps } from './types'; export type { CategoryOptions, CategoryStyleProps }; +class HtmlLegend extends HTML { + update(options: any) { + this.attr(options); + } +} + export class Category extends Component { constructor(options: CategoryOptions) { super(options, CATEGORY_DEFAULT_OPTIONS); @@ -35,6 +41,26 @@ export class Category extends Component { .update(finalTitleStyle) as Selection; } + private renderCustom(container: Selection) { + const { data } = this.attributes; + + const style = { + innerHTML: this.attributes.render(data), + pointerEvents: 'auto' as const, + }; + + container + .maybeAppendByClassName( + CLASS_NAMES.html, + () => + new HtmlLegend({ + className: CLASS_NAMES.html.name, + style, + }) + ) + .update(style); + } + private renderItems(container: Selection, bbox: DOMRect) { const { x, y, width, height } = bbox; const style = subStyleProps(this.attributes, 'title', true); @@ -84,7 +110,7 @@ export class Category extends Component<CategoryStyleProps> { } render(attributes: Required<CategoryStyleProps>, container: Group) { - const { width, height, x = 0, y = 0, classNamePrefix } = this.attributes; + const { width, height, x = 0, y = 0, classNamePrefix, render } = this.attributes; const ctn = select(container); // Set root container className @@ -97,10 +123,14 @@ export class Category extends Component<CategoryStyleProps> { container.style.transform = `translate(${x}, ${y})`; - this.renderTitle(ctn, width!, height!); + if (render as CategoryStyleProps['render']) { + this.renderCustom(ctn); + } else { + this.renderTitle(ctn, width!, height!); - this.renderItems(ctn, this.availableSpace); + this.renderItems(ctn, this.availableSpace); - this.adjustLayout(); + this.adjustLayout(); + } } } diff --git a/src/ui/legend/category/items.ts b/src/ui/legend/category/items.ts index 5e8ca740..f93f23f9 100644 --- a/src/ui/legend/category/items.ts +++ b/src/ui/legend/category/items.ts @@ -65,6 +65,7 @@ export type CategoryItemsStyleProps = GroupStyleProps & focus?: boolean; focusMarkerSize?: number; classNamePrefix?: string; + render?: (data: CategoryItemsDatum[]) => string | HTMLElement; }; export type CategoryItemsOptions = ComponentOptions<CategoryItemsStyleProps>; diff --git a/src/ui/legend/constant.ts b/src/ui/legend/constant.ts index 0f59a217..5690be9f 100644 --- a/src/ui/legend/constant.ts +++ b/src/ui/legend/constant.ts @@ -62,6 +62,7 @@ export const NAME_VALUE_RATIO = 0.5; export const CLASS_NAMES = classNames( { title: 'title', + html: 'html', titleGroup: 'title-group', items: 'items', itemsGroup: 'items-group',