UNPKG

billboard.js

Version:

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

206 lines (173 loc) 5.54 kB
/** * Copyright (c) 2017 ~ present NAVER Corp. * billboard.js project is licensed under the MIT license */ import { mouse as d3Mouse, select as d3Select } from "d3-selection"; import {drag as d3Drag} from "d3-drag"; import CLASS from "../../config/classes"; import {KEY} from "../../module/Cache"; import {emulateEvent, isNumber, isObject} from "../../module/util"; export default { selectRectForSingle(context, eventRect, index: number): void { const $$ = this; const {config, $el: {main}} = $$; const isSelectionEnabled = config.data_selection_enabled; const isSelectionGrouped = config.data_selection_grouped; const isTooltipGrouped = config.tooltip_grouped; const selectedData = $$.getAllValuesOnIndex(index); if (isTooltipGrouped) { $$.showTooltip(selectedData, context); $$.showGridFocus && $$.showGridFocus(selectedData); if (!isSelectionEnabled || isSelectionGrouped) { return; } } main.selectAll(`.${CLASS.shape}-${index}`) .each(function() { d3Select(this).classed(CLASS.EXPANDED, true); if (isSelectionEnabled) { eventRect.style("cursor", isSelectionGrouped ? "pointer" : null); } if (!isTooltipGrouped) { $$.hideGridFocus && $$.hideGridFocus(); $$.hideTooltip(); !isSelectionGrouped && $$.expandCirclesBars(index); } }) .filter(function(d) { return $$.isWithinShape(this, d); }) .call(selected => { const d = selected.data(); if (isSelectionEnabled && (isSelectionGrouped || config.data_selection_isselectable.bind($$.api)(d)) ) { eventRect.style("cursor", "pointer"); } if (!isTooltipGrouped) { $$.showTooltip(d, context); $$.showGridFocus && $$.showGridFocus(d); $$.unexpandCircles(); selected.each(d => $$.expandCirclesBars(index, d.id)); } }); }, expandCirclesBars(index: number, id: string, reset: boolean): void { const $$ = this; const {config, $el: {bar, circle}} = $$; circle && config.point_focus_expand_enabled && $$.expandCircles(index, id, reset); bar && $$.expandBars(index, id, reset); }, /** * Handle data.onover/out callback options * @param {boolean} isOver Over or not * @param {number|object} d data object * @private */ setOverOut(isOver: boolean, d): void { const $$ = this; const {config, state: {hasRadar}, $el: {main}} = $$; const isArc = isObject(d); // Call event handler if (isArc || d !== -1) { let callback = config[isOver ? "data_onover" : "data_onout"].bind($$.api); config.color_onover && $$.setOverColor(isOver, d, isArc); if (isArc) { callback(d, main.select(`.${CLASS.arc}${$$.getTargetSelectorSuffix(d.id)}`).node()); } else if (!config.tooltip_grouped) { let last = $$.cache.get(KEY.setOverOut) || []; const shape = main.selectAll(`.${CLASS.shape}-${d}`) .filter(function(d) { return $$.isWithinShape(this, d); }); shape .each(function(d) { if (last.length === 0 || last.every(v => v !== this)) { callback(d, this); last.push(this); } }); if (last.length > 0 && shape.empty()) { callback = config.data_onout.bind($$.api); last.forEach(v => callback(d3Select(v).datum(), v)); last = []; } $$.cache.add(KEY.setOverOut, last); } else { if (isOver) { config.point_focus_only && hasRadar ? $$.showCircleFocus($$.getAllValuesOnIndex(d, true)) : $$.expandCirclesBars(d, null, true); } !$$.isMultipleX() && main.selectAll(`.${CLASS.shape}-${d}`) .each(function(d) { callback(d, this); }); } } }, /** * Call data.onover/out callback for touch event * @param {number|object} d target index or data object for Arc type * @private */ callOverOutForTouch(d): void { const $$ = this; const last = $$.cache.get(KEY.callOverOutForTouch); if (isObject(d) && last ? d.id !== last.id : (d !== last)) { (last || isNumber(last)) && $$.setOverOut(false, last); (d || isNumber(d)) && $$.setOverOut(true, d); $$.cache.add(KEY.callOverOutForTouch, d); } }, /** * Return draggable selection function * @returns {Function} * @private */ getDraggableSelection(): Function { const $$ = this; const {config} = $$; return config.interaction_enabled && config.data_selection_draggable && $$.drag ? d3Drag() .on("drag", function() { // @ts-ignore $$.drag(d3Mouse(this)); }) .on("start", function() { // @ts-ignore $$.dragstart(d3Mouse(this)); }) .on("end", () => { $$.dragend(); }) : () => {}; }, /** * Dispatch a mouse event. * @private * @param {string} type event type * @param {number} index Index of eventRect * @param {Array} mouse x and y coordinate value */ dispatchEvent(type: string, index: number, mouse): void { const $$ = this; const {state: {hasRadar}, $el: {main, radar}} = $$; const isMultipleX = $$.isMultipleX(); const selector = hasRadar ? `.${CLASS.axis}-${index} text` : `.${isMultipleX ? CLASS.eventRect : `${CLASS.eventRect}-${index}`}`; const eventRect = (hasRadar ? radar.axes : main).select(selector).node(); const {width, left, top} = eventRect.getBoundingClientRect(); const x = left + (mouse ? mouse[0] : 0) + ( isMultipleX || $$.config.axis_rotated ? 0 : (width / 2) ); const y = top + (mouse ? mouse[1] : 0); const params = { screenX: x, screenY: y, clientX: x, clientY: y }; emulateEvent[/^(mouse|click)/.test(type) ? "mouse" : "touch"](eventRect, type, params); } };