Skip to content

Commit

Permalink
feat: html label supported
Browse files Browse the repository at this point in the history
  • Loading branch information
hustcc committed Jul 28, 2023
1 parent 66c58af commit ab43232
Show file tree
Hide file tree
Showing 10 changed files with 132 additions and 20 deletions.
41 changes: 41 additions & 0 deletions __tests__/plots/static/alphabet-interval-html-label.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { G2Spec } from '../../../src';

export function alphabetIntervalHtmlLabel(): G2Spec {
return {
type: 'interval',
transform: [{ type: 'sortX', by: 'y', reverse: true }],
data: {
type: 'fetch',
value: 'data/alphabet.csv',
},
axis: {
y: { labelFormatter: '.0%' },
},
encode: {
x: 'letter',
y: 'frequency',
color: 'steelblue',
},
labels: [
{
text: 'frequency',
transform: [
{
type: 'overlapHide',
},
],
className: 'alphabet-labels',
render: (
_,
datum,
) => `<div style="left:-50%;top:-16px;position:relative;font-size:12px;">
<span style="color: blue">${
datum.letter
}</span>:<span style="color: black">${datum.frequency.toFixed(
2,
)}</span>
</div>`,
},
],
};
}
1 change: 1 addition & 0 deletions __tests__/plots/static/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { alphabetInterval } from './alphabet-interval';
export { alphabetIntervalHtmlLabel } from './alphabet-interval-html-label';
export { alphabetIntervalMaxWidth } from './alphabet-interval-max-width';
export { alphabetIntervalMinWidth } from './alphabet-interval-min-width';
export { alphabetIntervalMaxWidthTransposed } from './alphabet-interval-max-width-transposed';
Expand Down
9 changes: 7 additions & 2 deletions src/runtime/plot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1189,6 +1189,7 @@ function createLabelShapeFunction(
formatter = (d) => `${d}`,
transform,
style: abstractStyle,
render,
...abstractOptions
} = options;
const visualOptions = mapObject(
Expand All @@ -1197,10 +1198,14 @@ function createLabelShapeFunction(
);
const { shape = defaultLabelShape, text, ...style } = visualOptions;
const f = typeof formatter === 'string' ? format(formatter) : formatter;
const value = { ...style, text: f(text, datum, index, abstractData) };
const value = {
...style,
text: f(text, datum, index, abstractData),
datum,
};

// Params for create shape.
const shapeOptions = { type: `label.${shape}`, ...style };
const shapeOptions = { type: `label.${shape}`, render, ...style };
const shapeFunction = useShape(shapeOptions, shapeContext);
const defaults = getDefaultsStyle(theme, 'label', shape, 'label');

Expand Down
1 change: 1 addition & 0 deletions src/runtime/types/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ type ComponentTheme = {
legendContinuous?: any;
label?: LabelStyleProps;
innerLabel?: LabelStyleProps;
htmlLabel?: any;
slider?: any;
scrollbar?: any;
title?: any; // @todo
Expand Down
22 changes: 20 additions & 2 deletions src/shape/label/label.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import { Advance } from '../text/advance';
import { LabelPosition } from './position/default';
import * as PositionProcessor from './position';

export type LabelOptions = Record<string, any>;
export type LabelOptions = {
/**
* Customize label with html string or element.
*/
render: (text: string, datum: any, index: number) => string | HTMLElement;
[key: string]: any;
};

function inferPosition(position: LabelPosition, coordinate: Coordinate) {
if (position !== undefined) return position;
Expand All @@ -27,8 +33,14 @@ function getDefaultStyle(
// For non-series mark, calc position for label based on
// position and the bounds of shape.
const { position } = value;
const { render } = options;
const p = inferPosition(position, coordinate);
const t = theme[p === 'inside' ? 'innerLabel' : 'label'];
const labelType = render
? 'htmlLabel'
: p === 'inside'
? 'innerLabel'
: 'label';
const t = theme[labelType];
const v = Object.assign({}, t, value);
const processor = PositionProcessor[camelCase(p)];
if (!processor) {
Expand All @@ -46,13 +58,17 @@ function getDefaultStyle(
*/
export const Label: SC<LabelOptions> = (options, context) => {
const { coordinate, theme } = context;
const { render } = options;
return (points, value) => {
const {
text,
x,
y,
transform: specifiedTS = '',
transformOrigin,
className = '',
datum,
index,
...overrideStyle
} = value;
const {
Expand All @@ -64,6 +80,8 @@ export const Label: SC<LabelOptions> = (options, context) => {
return select(new Advance())
.call(applyStyle, defaultStyle)
.style('text', `${text}`)
.style('className', `${className} g2-label`)
.style('innerHTML', render ? render(text, datum, index) : undefined)
.style(
'labelTransform',
`${transform} rotate(${+rotate}) ${specifiedTS}`.trim(),
Expand Down
53 changes: 38 additions & 15 deletions src/shape/text/advance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ type TextShapeStyleProps = Omit<TextStyleProps, 'text'> &
BackgroundStyleProps &
MarkerStyleProps<'startMarker'> &
MarkerStyleProps<'endMarker'> & {
id: string;
className?: string;
x0?: number; // x0 represents the x position of relative point, default is equal to x
y0?: number;
coordCenter?: Vector2; // center of coordinate
Expand All @@ -44,6 +46,8 @@ type TextShapeStyleProps = Omit<TextStyleProps, 'text'> &
labelTransform?: string;
labelTransformOrigin?: string;
rotate?: number;
innerHTML?: string | HTMLElement;
text?: string;
};

function getConnectorPoint(shape: GText | Rect) {
Expand Down Expand Up @@ -126,8 +130,8 @@ function inferConnectorPath(

export const Advance = createElement((g) => {
const {
// Do not pass className
class: className,
id,
className,
transform,
rotate,
labelTransform,
Expand All @@ -136,11 +140,13 @@ export const Advance = createElement((g) => {
y,
x0 = x,
y0 = y,
text,
background,
connector,
startMarker,
endMarker,
coordCenter,
innerHTML,
...rest
} = g.attributes as TextShapeStyleProps;

Expand All @@ -157,26 +163,43 @@ export const Advance = createElement((g) => {
[+x0, +y0],
[+x, +y],
];
const shape1 = select(g)
.maybeAppend('text', 'text')
.style('zIndex', 0)
.call(applyStyle, {
textBaseline: 'middle',
transform: labelTransform,
transformOrigin: labelTransformOrigin,
...rest,
})
.node();

const shape2 = select(g)
let textShape;
// Use html to customize advance text.
if (innerHTML) {
textShape = select(g)
.maybeAppend(id, 'html', className)
.style('zIndex', 0)
.style('innerHTML', innerHTML)
.call(applyStyle, {
transform: labelTransform,
transformOrigin: labelTransformOrigin,
...rest,
})
.node();
} else {
textShape = select(g)
.maybeAppend(id, 'text')
.style('zIndex', 0)
.style('text', text)
.call(applyStyle, {
textBaseline: 'middle',
transform: labelTransform,
transformOrigin: labelTransformOrigin,
...rest,
})
.node();
}

const rect = select(g)
.maybeAppend('background', 'rect')
.style('zIndex', -1)
.call(applyStyle, inferBackgroundBounds(shape1, padding))
.call(applyStyle, inferBackgroundBounds(textShape, padding))
.call(applyStyle, background ? backgroundStyle : {})
.node();

const connectorPath = inferConnectorPath(
shape2,
rect,
endPoints,
points,
coordCenter,
Expand Down
6 changes: 6 additions & 0 deletions src/theme/academy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,12 @@ export const Academy: TC<AcademyOptions> = (options) => {
stroke: undefined,
offset: 0,
},
htmlLabel: {
fontSize: 12,
opacity: 0.65,
color: COLORS.BLACK,
fontWeight: 'normal',
},
slider: {
trackSize: 16,
trackFill: COLORS.STROKE,
Expand Down
6 changes: 6 additions & 0 deletions src/theme/classic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,12 @@ export const Classic: TC<ClassicOptions> = (options) => {
stroke: undefined,
offset: 0,
},
htmlLabel: {
fontSize: 12,
opacity: 0.65,
color: COLORS.BLACK,
fontWeight: 'normal',
},
slider: {
trackSize: 16,
trackFill: COLORS.STROKE,
Expand Down
6 changes: 6 additions & 0 deletions src/theme/classicDark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,12 @@ export const ClassicDark: TC<ClassicDarkOptions> = (options) => {
stroke: undefined,
offset: 0,
},
htmlLabel: {
fontSize: 12,
opacity: 0.65,
color: COLORS.BLACK,
fontWeight: 'normal',
},
slider: {
trackSize: 16,
trackFill: COLORS.STROKE,
Expand Down
7 changes: 6 additions & 1 deletion src/utils/selection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,11 @@ export class Selection<T = any> {
}
}

maybeAppend(id: string, node: string | (() => G2Element)) {
maybeAppend(
id: string,
node: string | (() => G2Element),
className?: string,
) {
const element = this._elements[0];
const child = element.getElementById(id) as G2Element;
if (child) {
Expand All @@ -175,6 +179,7 @@ export class Selection<T = any> {
const newChild =
typeof node === 'string' ? this.createElement(node) : node();
newChild.id = id;
if (className) newChild.className = className;
element.appendChild(newChild);
return new Selection([newChild], null, this._parent, this._document);
}
Expand Down

0 comments on commit ab43232

Please sign in to comment.