Skip to content

Commit

Permalink
feat(ssr): provide registerSSRDataGetter
Browse files Browse the repository at this point in the history
  • Loading branch information
Ovilia committed Jun 13, 2023
1 parent d7005b8 commit fb4c265
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 52 deletions.
21 changes: 16 additions & 5 deletions src/Element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,11 +368,6 @@ class Element<Props extends ElementProps = ElementProps> {
*/
__inHover: boolean

/**
* Any information to be binded on the element when rendering.
*/
__metaData: Record<string, string | number | boolean>

/**
* path to clip the elements and its children, if it is a group.
* @see http://www.w3.org/TR/2dcontext/#clipping-region
Expand Down Expand Up @@ -1022,6 +1017,22 @@ class Element<Props extends ElementProps = ElementProps> {
}
}

/**
* Return if el.silent or any ancestor element has silent true.
*/
isSilent() {
let isSilent = this.silent;
let ancestor = this.parent;
while (!isSilent && ancestor) {
if (ancestor.silent) {
isSilent = true;
break;
}
ancestor = ancestor.parent;
}
return isSilent;
}

/**
* Update animation targets when reference is changed.
*/
Expand Down
104 changes: 59 additions & 45 deletions src/svg/cssEmphasis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,52 +8,66 @@ export function createCSSEmphasis(
attrs: SVGVNodeAttrs,
scope: BrushScope
) {
if (!el.ignore && el.__metaData) {
const emphasisStyle = el.states.emphasis && el.states.emphasis.style
? el.states.emphasis.style
: {};
let fill = emphasisStyle.fill;
if (!fill) {
// No empahsis fill, lift color
const normalFill = el.style && el.style.fill;
const selectFill = el.states.select
&& el.states.select.style
&& el.states.select.style.fill;
const fromFill = el.currentStates.indexOf('select') >= 0
? (selectFill || normalFill)
: normalFill;
if (fromFill) {
fill = liftColor(fromFill);
}
}
let lineWidth = emphasisStyle.lineWidth;
if (lineWidth) {
// Symbols use transform to set size, so lineWidth
// should be divided by scaleX
const scaleX = (!emphasisStyle.strokeNoScale && el.transform)
? el.transform[0]
: 1;
lineWidth = lineWidth / scaleX;
}
const style = {
cursor: 'pointer', // TODO: Should this be customized?
} as any;
if (fill) {
style.fill = fill;
}
if (emphasisStyle.stroke) {
style.stroke = emphasisStyle.stroke;
if (!el.ignore) {
if (el.isSilent()) {
// If el is silent, it can not be hovered nor selected.
// So set pointer-events to pass through.
const style = {
'pointer-events': 'none'
};
setClassAttribute(style, attrs, scope, true);
}
if (lineWidth) {
style['stroke-width'] = lineWidth;
}
const styleKey = JSON.stringify(style);
let className = scope.cssStyleCache[styleKey];
if (!className) {
className = scope.zrId + '-cls-' + getClassId();
scope.cssStyleCache[styleKey] = className;
scope.cssNodes['.' + className + ':hover'] = style;
else {
const emphasisStyle = el.states.emphasis && el.states.emphasis.style
? el.states.emphasis.style
: {};
let fill = emphasisStyle.fill;
if (!fill) {
// No empahsis fill, lift color
const normalFill = el.style && el.style.fill;
const selectFill = el.states.select
&& el.states.select.style
&& el.states.select.style.fill;
const fromFill = el.currentStates.indexOf('select') >= 0
? (selectFill || normalFill)
: normalFill;
if (fromFill) {
fill = liftColor(fromFill);
}
}
let lineWidth = emphasisStyle.lineWidth;
if (lineWidth) {
// Symbols use transform to set size, so lineWidth
// should be divided by scaleX
const scaleX = (!emphasisStyle.strokeNoScale && el.transform)
? el.transform[0]
: 1;
lineWidth = lineWidth / scaleX;
}
const style = {
cursor: 'pointer', // TODO: Should this be customized?
} as any;
if (fill) {
style.fill = fill;
}
if (emphasisStyle.stroke) {
style.stroke = emphasisStyle.stroke;
}
if (lineWidth) {
style['stroke-width'] = lineWidth;
}
setClassAttribute(style, attrs, scope, true);
}
attrs.class = attrs.class ? (attrs.class + ' ' + className) : className;
}
}

function setClassAttribute(style: object, attrs: SVGVNodeAttrs, scope: BrushScope, withHover: boolean) {
const styleKey = JSON.stringify(style);
let className = scope.cssStyleCache[styleKey];
if (!className) {
className = scope.zrId + '-cls-' + getClassId();
scope.cssStyleCache[styleKey] = className;
scope.cssNodes['.' + className + (withHover ? ':hover' : '')] = style as any;
}
attrs.class = attrs.class ? (attrs.class + ' ' + className) : className;
}
13 changes: 11 additions & 2 deletions src/svg/graphic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { createCSSAnimation } from './cssAnimation';
import { hasSeparateFont, parseFontSize } from '../graphic/Text';
import { DEFAULT_FONT, DEFAULT_FONT_FAMILY } from '../core/platform';
import { createCSSEmphasis } from './cssEmphasis';
import { getElementSSRData } from '../zrender';

const round = Math.round;

Expand All @@ -62,6 +63,10 @@ function setStyleAttrs(attrs: SVGVNodeAttrs, style: AllStyleOption, el: Path | T
else if (isFillStroke && isPattern(val)) {
setPattern(el, attrs, key, scope);
}
else if (isFillStroke && val === 'none') {
// When is none, it cannot be interacted when ssr
attrs[key] = 'transparent';
}
else {
attrs[key] = val;
}
Expand All @@ -71,11 +76,15 @@ function setStyleAttrs(attrs: SVGVNodeAttrs, style: AllStyleOption, el: Path | T
}

function setMetaData(attrs: SVGVNodeAttrs, el: Path | TSpan | ZRImage) {
if (el.__metaData) {
each(el.__metaData, function (val, key) {
const metaData = getElementSSRData(el);
if (metaData) {
metaData.each((val, key) => {
attrs[(META_DATA_PREFIX + key).toLowerCase()]
= val + '';
});
if (el.isSilent()) {
attrs[META_DATA_PREFIX + 'silent'] = 'true';
}
}
}

Expand Down
17 changes: 17 additions & 0 deletions src/zrender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,23 @@ export function registerPainter(name: string, Ctor: PainterBaseCtor) {
painterCtors[name] = Ctor;
}

export type ElementSSRData = zrUtil.HashMap<unknown>;
export type ElementSSRDataGetter<T> = (el: Element) => zrUtil.HashMap<T>;

let ssrDataGetter = function (el: Element): ElementSSRData {
return null;
}

export function getElementSSRData(el: Element): ElementSSRData {
if (typeof ssrDataGetter === 'function') {
return ssrDataGetter(el);
}
}

export function registerSSRDataGetter<T>(getter: ElementSSRDataGetter<T>) {
ssrDataGetter = getter;
}

/**
* @type {string}
*/
Expand Down

0 comments on commit fb4c265

Please sign in to comment.