From 79d100e824e5947285784b832271b46c1112cdb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E8=88=B8?= Date: Wed, 5 Nov 2025 20:52:05 +0800 Subject: [PATCH] feat: support legend render --- src/ui/legend/category.ts | 40 ++++++++++++++++++++++++++++----- src/ui/legend/category/items.ts | 1 + src/ui/legend/constant.ts | 1 + 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/ui/legend/category.ts b/src/ui/legend/category.ts index e903540b..faf63db8 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) as Selection<Title>; + } + private renderItems(container: Selection, bbox: DOMRect) { const { x, y, width, height } = bbox; const style = subStyleProps(this.attributes, 'title', true); @@ -84,14 +110,18 @@ export class Category extends Component<CategoryStyleProps> { } render(attributes: Required<CategoryStyleProps>, container: Group) { - const { width, height, x = 0, y = 0 } = this.attributes; + const { width, height, x = 0, y = 0, render } = this.attributes; const ctn = select(container); 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 e10e7a4d..10f331b3 100644 --- a/src/ui/legend/category/items.ts +++ b/src/ui/legend/category/items.ts @@ -60,6 +60,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',