UNPKG

billboard.js

Version:

Re-usable easy interface JavaScript chart library, based on D3 v4+

132 lines (109 loc) 3.44 kB
/** * Copyright (c) 2017 ~ present NAVER Corp. * billboard.js project is licensed under the MIT license */ import {namespaces as d3Namespaces, select as d3Select} from "d3-selection"; import {document} from "../../module/browser"; import {isFunction, isObjectType, notEmpty, toArray} from "../../module/util"; /** * Check if point draw methods are valid * @param {string} point point type * @returns {boolean} * @private */ function hasValidPointDrawMethods(point: string): boolean { return isObjectType(point) && isFunction(point.create) && isFunction(point.update); } /** * Insert point info defs element * @param {string} point Point element * @param {string} id Point id * @private */ function insertPointInfoDefs(point: string, id: string): void { const $$ = this; const copyAttr = (from, target) => { const attribs = from.attributes; for (let i = 0, name; (name = attribs[i]); i++) { name = name.name; target.setAttribute(name, from.getAttribute(name)); } }; const doc = new DOMParser().parseFromString(point, "image/svg+xml"); const node = doc.documentElement; const clone = document.createElementNS(d3Namespaces.svg, node.nodeName.toLowerCase()); clone.id = id; clone.style.fill = "inherit"; clone.style.stroke = "inherit"; copyAttr(node, clone); if (node.childNodes?.length) { const parent = d3Select(clone); if ("innerHTML" in clone) { parent.html(node.innerHTML); } else { toArray(node.childNodes).forEach(v => { copyAttr(v, parent.append(v.tagName).node()); }); } } $$.$el.defs.node().appendChild(clone); } export default { /** * Check if point type option is valid * @param {string} type point type * @returns {boolean} * @private */ hasValidPointType(type?: string): boolean { return /^(circle|rect(angle)?|polygon|ellipse|use)$/i.test(type || this.config.point_type); }, /** * Check if pattern point is set to be used on legend * @returns {boolean} * @private */ hasLegendDefsPoint(): boolean { const {config} = this; return config.legend_show && config.point_pattern?.length && config.legend_usePoint; }, getDefsPointId(id: string): string { const {state: {datetimeId}} = this; return `${datetimeId}-point${id}`; }, /** * Get generate point function * @returns {Function} * @private */ generatePoint(): Function { const $$ = this; const {$el, config} = $$; const ids: string[] = []; const pattern = notEmpty(config.point_pattern) ? config.point_pattern : [config.point_type]; return function(method, context, ...args) { return function(d) { const id: string = $$.getTargetSelectorSuffix(d.id || d.data?.id || d); const element = d3Select(this); ids.indexOf(id) < 0 && ids.push(id); let point = pattern[ids.indexOf(id) % pattern.length]; if ($$.hasValidPointType(point)) { point = $$[point]; } else if (!hasValidPointDrawMethods(point || config.point_type)) { const pointId = $$.getDefsPointId(id); const defsPoint = $el.defs.select(`#${pointId}`); if (defsPoint.size() < 1) { insertPointInfoDefs.bind($$)(point, pointId); } if (method === "create") { return $$.custom?.create.bind(context)(element, pointId, ...args); } else if (method === "update") { return $$.custom?.update.bind(context)(element, ...args); } } return point[method]?.bind(context)(element, ...args); }; }; } };