mirror of https://github.com/masayuki0812/c3.git
Ændrew Rininsland
8 years ago
23 changed files with 1745 additions and 1266 deletions
@ -1,770 +0,0 @@
|
||||
import { CLASS, |
||||
isValue, |
||||
isUndefined, |
||||
isDefined, |
||||
ChartInternal, |
||||
} from './chartinternal.js'; |
||||
|
||||
let c3_chart_fn; |
||||
|
||||
|
||||
function Chart(config) { |
||||
const $$ = this.internal = new ChartInternal(this); |
||||
$$.loadConfig(config); |
||||
|
||||
$$.beforeInit(config); |
||||
$$.init(); |
||||
$$.afterInit(config); |
||||
|
||||
// bind "this" to nested API
|
||||
(function bindThis(fn, target, argThis) { |
||||
Object.keys(fn).forEach((key) => { |
||||
target[key] = fn[key].bind(argThis); |
||||
if (Object.keys(fn[key]).length > 0) { |
||||
bindThis(fn[key], target[key], argThis); |
||||
} |
||||
}); |
||||
})(c3_chart_fn, this, this); |
||||
} |
||||
|
||||
c3_chart_fn = Chart.prototype; |
||||
|
||||
c3_chart_fn.focus = function (targetIds) { |
||||
let $$ = this.internal, candidates; |
||||
|
||||
targetIds = $$.mapToTargetIds(targetIds); |
||||
candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$))), |
||||
|
||||
this.revert(); |
||||
this.defocus(); |
||||
candidates.classed(CLASS.focused, true).classed(CLASS.defocused, false); |
||||
if ($$.hasArcType()) { |
||||
$$.expandArc(targetIds); |
||||
} |
||||
$$.toggleFocusLegend(targetIds, true); |
||||
|
||||
$$.focusedTargetIds = targetIds; |
||||
$$.defocusedTargetIds = $$.defocusedTargetIds.filter((id) => { |
||||
return targetIds.indexOf(id) < 0; |
||||
}); |
||||
}; |
||||
|
||||
c3_chart_fn.defocus = function (targetIds) { |
||||
let $$ = this.internal, candidates; |
||||
|
||||
targetIds = $$.mapToTargetIds(targetIds); |
||||
candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$))), |
||||
|
||||
candidates.classed(CLASS.focused, false).classed(CLASS.defocused, true); |
||||
if ($$.hasArcType()) { |
||||
$$.unexpandArc(targetIds); |
||||
} |
||||
$$.toggleFocusLegend(targetIds, false); |
||||
|
||||
$$.focusedTargetIds = $$.focusedTargetIds.filter((id) => { |
||||
return targetIds.indexOf(id) < 0; |
||||
}); |
||||
$$.defocusedTargetIds = targetIds; |
||||
}; |
||||
|
||||
c3_chart_fn.revert = function (targetIds) { |
||||
let $$ = this.internal, candidates; |
||||
|
||||
targetIds = $$.mapToTargetIds(targetIds); |
||||
candidates = $$.svg.selectAll($$.selectorTargets(targetIds)); // should be for all targets
|
||||
|
||||
candidates.classed(CLASS.focused, false).classed(CLASS.defocused, false); |
||||
if ($$.hasArcType()) { |
||||
$$.unexpandArc(targetIds); |
||||
} |
||||
if ($$.config.legend_show) { |
||||
$$.showLegend(targetIds.filter($$.isLegendToShow.bind($$))); |
||||
$$.legend.selectAll($$.selectorLegends(targetIds)) |
||||
.filter(function () { |
||||
return $$.d3.select(this).classed(CLASS.legendItemFocused); |
||||
}) |
||||
.classed(CLASS.legendItemFocused, false); |
||||
} |
||||
|
||||
$$.focusedTargetIds = []; |
||||
$$.defocusedTargetIds = []; |
||||
}; |
||||
|
||||
c3_chart_fn.show = function (targetIds, options) { |
||||
let $$ = this.internal, targets; |
||||
|
||||
targetIds = $$.mapToTargetIds(targetIds); |
||||
options = options || {}; |
||||
|
||||
$$.removeHiddenTargetIds(targetIds); |
||||
targets = $$.svg.selectAll($$.selectorTargets(targetIds)); |
||||
|
||||
targets.transition() |
||||
.style('opacity', 1, 'important') |
||||
.call($$.endall, () => { |
||||
targets.style('opacity', null).style('opacity', 1); |
||||
}); |
||||
|
||||
if (options.withLegend) { |
||||
$$.showLegend(targetIds); |
||||
} |
||||
|
||||
$$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); |
||||
}; |
||||
|
||||
c3_chart_fn.hide = function (targetIds, options) { |
||||
let $$ = this.internal, targets; |
||||
|
||||
targetIds = $$.mapToTargetIds(targetIds); |
||||
options = options || {}; |
||||
|
||||
$$.addHiddenTargetIds(targetIds); |
||||
targets = $$.svg.selectAll($$.selectorTargets(targetIds)); |
||||
|
||||
targets.transition() |
||||
.style('opacity', 0, 'important') |
||||
.call($$.endall, () => { |
||||
targets.style('opacity', null).style('opacity', 0); |
||||
}); |
||||
|
||||
if (options.withLegend) { |
||||
$$.hideLegend(targetIds); |
||||
} |
||||
|
||||
$$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); |
||||
}; |
||||
|
||||
c3_chart_fn.toggle = function (targetIds, options) { |
||||
let that = this, $$ = this.internal; |
||||
$$.mapToTargetIds(targetIds).forEach((targetId) => { |
||||
$$.isTargetToShow(targetId) ? that.hide(targetId, options) : that.show(targetId, options); |
||||
}); |
||||
}; |
||||
|
||||
c3_chart_fn.zoom = function (domain) { |
||||
const $$ = this.internal; |
||||
if (domain) { |
||||
if ($$.isTimeSeries()) { |
||||
domain = domain.map((x) => { return $$.parseDate(x); }); |
||||
} |
||||
$$.brush.extent(domain); |
||||
$$.redraw({ withUpdateXDomain: true, withY: $$.config.zoom_rescale }); |
||||
$$.config.zoom_onzoom.call(this, $$.x.orgDomain()); |
||||
} |
||||
return $$.brush.extent(); |
||||
}; |
||||
c3_chart_fn.zoom.enable = function (enabled) { |
||||
const $$ = this.internal; |
||||
$$.config.zoom_enabled = enabled; |
||||
$$.updateAndRedraw(); |
||||
}; |
||||
c3_chart_fn.unzoom = function () { |
||||
const $$ = this.internal; |
||||
$$.brush.clear().update(); |
||||
$$.redraw({ withUpdateXDomain: true }); |
||||
}; |
||||
|
||||
c3_chart_fn.zoom.max = function (max) { |
||||
let $$ = this.internal, config = $$.config, d3 = $$.d3; |
||||
if (max === 0 || max) { |
||||
config.zoom_x_max = d3.max([$$.orgXDomain[1], max]); |
||||
} |
||||
else { |
||||
return config.zoom_x_max; |
||||
} |
||||
}; |
||||
|
||||
c3_chart_fn.zoom.min = function (min) { |
||||
let $$ = this.internal, config = $$.config, d3 = $$.d3; |
||||
if (min === 0 || min) { |
||||
config.zoom_x_min = d3.min([$$.orgXDomain[0], min]); |
||||
} |
||||
else { |
||||
return config.zoom_x_min; |
||||
} |
||||
}; |
||||
|
||||
c3_chart_fn.zoom.range = function (range) { |
||||
if (arguments.length) { |
||||
if (isDefined(range.max)) { this.domain.max(range.max); } |
||||
if (isDefined(range.min)) { this.domain.min(range.min); } |
||||
} else { |
||||
return { |
||||
max: this.domain.max(), |
||||
min: this.domain.min(), |
||||
}; |
||||
} |
||||
}; |
||||
|
||||
c3_chart_fn.load = function (args) { |
||||
let $$ = this.internal, config = $$.config; |
||||
// update xs if specified
|
||||
if (args.xs) { |
||||
$$.addXs(args.xs); |
||||
} |
||||
// update names if exists
|
||||
if ('names' in args) { |
||||
c3_chart_fn.data.names.bind(this)(args.names); |
||||
} |
||||
// update classes if exists
|
||||
if ('classes' in args) { |
||||
Object.keys(args.classes).forEach((id) => { |
||||
config.data_classes[id] = args.classes[id]; |
||||
}); |
||||
} |
||||
// update categories if exists
|
||||
if ('categories' in args && $$.isCategorized()) { |
||||
config.axis_x_categories = args.categories; |
||||
} |
||||
// update axes if exists
|
||||
if ('axes' in args) { |
||||
Object.keys(args.axes).forEach((id) => { |
||||
config.data_axes[id] = args.axes[id]; |
||||
}); |
||||
} |
||||
// update colors if exists
|
||||
if ('colors' in args) { |
||||
Object.keys(args.colors).forEach((id) => { |
||||
config.data_colors[id] = args.colors[id]; |
||||
}); |
||||
} |
||||
// use cache if exists
|
||||
if ('cacheIds' in args && $$.hasCaches(args.cacheIds)) { |
||||
$$.load($$.getCaches(args.cacheIds), args.done); |
||||
return; |
||||
} |
||||
// unload if needed
|
||||
if ('unload' in args) { |
||||
// TODO: do not unload if target will load (included in url/rows/columns)
|
||||
$$.unload($$.mapToTargetIds((typeof args.unload === 'boolean' && args.unload) ? null : args.unload), () => { |
||||
$$.loadFromArgs(args); |
||||
}); |
||||
} else { |
||||
$$.loadFromArgs(args); |
||||
} |
||||
}; |
||||
|
||||
c3_chart_fn.unload = function (args) { |
||||
const $$ = this.internal; |
||||
args = args || {}; |
||||
if (args instanceof Array) { |
||||
args = { ids: args }; |
||||
} else if (typeof args === 'string') { |
||||
args = { ids: [args] }; |
||||
} |
||||
$$.unload($$.mapToTargetIds(args.ids), () => { |
||||
$$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); |
||||
if (args.done) { args.done(); } |
||||
}); |
||||
}; |
||||
|
||||
c3_chart_fn.flow = function (args) { |
||||
let $$ = this.internal, |
||||
targets, data, notfoundIds = [], |
||||
orgDataCount = $$.getMaxDataCount(), |
||||
dataCount, domain, baseTarget, baseValue, length = 0, |
||||
tail = 0, |
||||
diff, to; |
||||
|
||||
if (args.json) { |
||||
data = $$.convertJsonToData(args.json, args.keys); |
||||
} else if (args.rows) { |
||||
data = $$.convertRowsToData(args.rows); |
||||
} else if (args.columns) { |
||||
data = $$.convertColumnsToData(args.columns); |
||||
} else { |
||||
return; |
||||
} |
||||
targets = $$.convertDataToTargets(data, true); |
||||
|
||||
// Update/Add data
|
||||
$$.data.targets.forEach((t) => { |
||||
let found = false, |
||||
i, j; |
||||
for (i = 0; i < targets.length; i++) { |
||||
if (t.id === targets[i].id) { |
||||
found = true; |
||||
|
||||
if (t.values[t.values.length - 1]) { |
||||
tail = t.values[t.values.length - 1].index + 1; |
||||
} |
||||
length = targets[i].values.length; |
||||
|
||||
for (j = 0; j < length; j++) { |
||||
targets[i].values[j].index = tail + j; |
||||
if (!$$.isTimeSeries()) { |
||||
targets[i].values[j].x = tail + j; |
||||
} |
||||
} |
||||
t.values = t.values.concat(targets[i].values); |
||||
|
||||
targets.splice(i, 1); |
||||
break; |
||||
} |
||||
} |
||||
if (!found) { notfoundIds.push(t.id); } |
||||
}); |
||||
|
||||
// Append null for not found targets
|
||||
$$.data.targets.forEach((t) => { |
||||
let i, j; |
||||
for (i = 0; i < notfoundIds.length; i++) { |
||||
if (t.id === notfoundIds[i]) { |
||||
tail = t.values[t.values.length - 1].index + 1; |
||||
for (j = 0; j < length; j++) { |
||||
t.values.push({ |
||||
id: t.id, |
||||
index: tail + j, |
||||
x: $$.isTimeSeries() ? $$.getOtherTargetX(tail + j) : tail + j, |
||||
value: null, |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
|
||||
// Generate null values for new target
|
||||
if ($$.data.targets.length) { |
||||
targets.forEach((t) => { |
||||
let i, missing = []; |
||||
for (i = $$.data.targets[0].values[0].index; i < tail; i++) { |
||||
missing.push({ |
||||
id: t.id, |
||||
index: i, |
||||
x: $$.isTimeSeries() ? $$.getOtherTargetX(i) : i, |
||||
value: null, |
||||
}); |
||||
} |
||||
t.values.forEach((v) => { |
||||
v.index += tail; |
||||
if (!$$.isTimeSeries()) { |
||||
v.x += tail; |
||||
} |
||||
}); |
||||
t.values = missing.concat(t.values); |
||||
}); |
||||
} |
||||
$$.data.targets = $$.data.targets.concat(targets); // add remained
|
||||
|
||||
// check data count because behavior needs to change when it's only one
|
||||
dataCount = $$.getMaxDataCount(); |
||||
baseTarget = $$.data.targets[0]; |
||||
baseValue = baseTarget.values[0]; |
||||
|
||||
// Update length to flow if needed
|
||||
if (isDefined(args.to)) { |
||||
length = 0; |
||||
to = $$.isTimeSeries() ? $$.parseDate(args.to) : args.to; |
||||
baseTarget.values.forEach((v) => { |
||||
if (v.x < to) { length++; } |
||||
}); |
||||
} else if (isDefined(args.length)) { |
||||
length = args.length; |
||||
} |
||||
|
||||
// If only one data, update the domain to flow from left edge of the chart
|
||||
if (!orgDataCount) { |
||||
if ($$.isTimeSeries()) { |
||||
if (baseTarget.values.length > 1) { |
||||
diff = baseTarget.values[baseTarget.values.length - 1].x - baseValue.x; |
||||
} else { |
||||
diff = baseValue.x - $$.getXDomain($$.data.targets)[0]; |
||||
} |
||||
} else { |
||||
diff = 1; |
||||
} |
||||
domain = [baseValue.x - diff, baseValue.x]; |
||||
$$.updateXDomain(null, true, true, false, domain); |
||||
} else if (orgDataCount === 1) { |
||||
if ($$.isTimeSeries()) { |
||||
diff = (baseTarget.values[baseTarget.values.length - 1].x - baseValue.x) / 2; |
||||
domain = [new Date(+baseValue.x - diff), new Date(+baseValue.x + diff)]; |
||||
$$.updateXDomain(null, true, true, false, domain); |
||||
} |
||||
} |
||||
|
||||
// Set targets
|
||||
$$.updateTargets($$.data.targets); |
||||
|
||||
// Redraw with new targets
|
||||
$$.redraw({ |
||||
flow: { |
||||
index: baseValue.index, |
||||
length, |
||||
duration: isValue(args.duration) ? args.duration : $$.config.transition_duration, |
||||
done: args.done, |
||||
orgDataCount, |
||||
}, |
||||
withLegend: true, |
||||
withTransition: orgDataCount > 1, |
||||
withTrimXDomain: false, |
||||
withUpdateXAxis: true, |
||||
}); |
||||
}; |
||||
|
||||
c3_chart_fn.selected = function (targetId) { |
||||
let $$ = this.internal, d3 = $$.d3; |
||||
return d3.merge( |
||||
$$.main.selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(targetId)).selectAll('.' + CLASS.shape) |
||||
.filter(function () { return d3.select(this).classed(CLASS.SELECTED); }) |
||||
.map((d) => { return d.map((d) => { const data = d.__data__; return data.data ? data.data : data; }); }) |
||||
); |
||||
}; |
||||
c3_chart_fn.select = function (ids, indices, resetOther) { |
||||
let $$ = this.internal, d3 = $$.d3, config = $$.config; |
||||
if (!config.data_selection_enabled) { return; } |
||||
$$.main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape).each(function (d, i) { |
||||
let shape = d3.select(this), id = d.data ? d.data.id : d.id, |
||||
toggle = $$.getToggle(this, d).bind($$), |
||||
isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0, |
||||
isTargetIndex = !indices || indices.indexOf(i) >= 0, |
||||
isSelected = shape.classed(CLASS.SELECTED); |
||||
// line/area selection not supported yet
|
||||
if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) { |
||||
return; |
||||
} |
||||
if (isTargetId && isTargetIndex) { |
||||
if (config.data_selection_isselectable(d) && !isSelected) { |
||||
toggle(true, shape.classed(CLASS.SELECTED, true), d, i); |
||||
} |
||||
} else if (isDefined(resetOther) && resetOther) { |
||||
if (isSelected) { |
||||
toggle(false, shape.classed(CLASS.SELECTED, false), d, i); |
||||
} |
||||
} |
||||
}); |
||||
}; |
||||
c3_chart_fn.unselect = function (ids, indices) { |
||||
let $$ = this.internal, d3 = $$.d3, config = $$.config; |
||||
if (!config.data_selection_enabled) { return; } |
||||
$$.main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape).each(function (d, i) { |
||||
let shape = d3.select(this), id = d.data ? d.data.id : d.id, |
||||
toggle = $$.getToggle(this, d).bind($$), |
||||
isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0, |
||||
isTargetIndex = !indices || indices.indexOf(i) >= 0, |
||||
isSelected = shape.classed(CLASS.SELECTED); |
||||
// line/area selection not supported yet
|
||||
if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) { |
||||
return; |
||||
} |
||||
if (isTargetId && isTargetIndex) { |
||||
if (config.data_selection_isselectable(d)) { |
||||
if (isSelected) { |
||||
toggle(false, shape.classed(CLASS.SELECTED, false), d, i); |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
}; |
||||
|
||||
c3_chart_fn.transform = function (type, targetIds) { |
||||
let $$ = this.internal, |
||||
options = ['pie', 'donut'].indexOf(type) >= 0 ? { withTransform: true } : null; |
||||
$$.transformTo(targetIds, type, options); |
||||
}; |
||||
|
||||
c3_chart_fn.groups = function (groups) { |
||||
let $$ = this.internal, config = $$.config; |
||||
if (isUndefined(groups)) { return config.data_groups; } |
||||
config.data_groups = groups; |
||||
$$.redraw(); |
||||
return config.data_groups; |
||||
}; |
||||
|
||||
c3_chart_fn.xgrids = function (grids) { |
||||
let $$ = this.internal, config = $$.config; |
||||
if (!grids) { return config.grid_x_lines; } |
||||
config.grid_x_lines = grids; |
||||
$$.redrawWithoutRescale(); |
||||
return config.grid_x_lines; |
||||
}; |
||||
c3_chart_fn.xgrids.add = function (grids) { |
||||
const $$ = this.internal; |
||||
return this.xgrids($$.config.grid_x_lines.concat(grids ? grids : [])); |
||||
}; |
||||
c3_chart_fn.xgrids.remove = function (params) { // TODO: multiple
|
||||
const $$ = this.internal; |
||||
$$.removeGridLines(params, true); |
||||
}; |
||||
|
||||
c3_chart_fn.ygrids = function (grids) { |
||||
let $$ = this.internal, config = $$.config; |
||||
if (!grids) { return config.grid_y_lines; } |
||||
config.grid_y_lines = grids; |
||||
$$.redrawWithoutRescale(); |
||||
return config.grid_y_lines; |
||||
}; |
||||
c3_chart_fn.ygrids.add = function (grids) { |
||||
const $$ = this.internal; |
||||
return this.ygrids($$.config.grid_y_lines.concat(grids ? grids : [])); |
||||
}; |
||||
c3_chart_fn.ygrids.remove = function (params) { // TODO: multiple
|
||||
const $$ = this.internal; |
||||
$$.removeGridLines(params, false); |
||||
}; |
||||
|
||||
c3_chart_fn.regions = function (regions) { |
||||
let $$ = this.internal, config = $$.config; |
||||
if (!regions) { return config.regions; } |
||||
config.regions = regions; |
||||
$$.redrawWithoutRescale(); |
||||
return config.regions; |
||||
}; |
||||
c3_chart_fn.regions.add = function (regions) { |
||||
let $$ = this.internal, config = $$.config; |
||||
if (!regions) { return config.regions; } |
||||
config.regions = config.regions.concat(regions); |
||||
$$.redrawWithoutRescale(); |
||||
return config.regions; |
||||
}; |
||||
c3_chart_fn.regions.remove = function (options) { |
||||
let $$ = this.internal, config = $$.config, |
||||
duration, classes, regions; |
||||
|
||||
options = options || {}; |
||||
duration = $$.getOption(options, 'duration', config.transition_duration); |
||||
classes = $$.getOption(options, 'classes', [CLASS.region]); |
||||
|
||||
regions = $$.main.select('.' + CLASS.regions).selectAll(classes.map((c) => { return '.' + c; })); |
||||
(duration ? regions.transition().duration(duration) : regions) |
||||
.style('opacity', 0) |
||||
.remove(); |
||||
|
||||
config.regions = config.regions.filter((region) => { |
||||
let found = false; |
||||
if (!region.class) { |
||||
return true; |
||||
} |
||||
region.class.split(' ').forEach((c) => { |
||||
if (classes.indexOf(c) >= 0) { found = true; } |
||||
}); |
||||
return !found; |
||||
}); |
||||
|
||||
return config.regions; |
||||
}; |
||||
|
||||
c3_chart_fn.data = function (targetIds) { |
||||
const targets = this.internal.data.targets; |
||||
return typeof targetIds === 'undefined' ? targets : targets.filter((t) => { |
||||
return [].concat(targetIds).indexOf(t.id) >= 0; |
||||
}); |
||||
}; |
||||
c3_chart_fn.data.shown = function (targetIds) { |
||||
return this.internal.filterTargetsToShow(this.data(targetIds)); |
||||
}; |
||||
c3_chart_fn.data.values = function (targetId) { |
||||
let targets, values = null; |
||||
if (targetId) { |
||||
targets = this.data(targetId); |
||||
values = targets[0] ? targets[0].values.map((d) => { return d.value; }) : null; |
||||
} |
||||
return values; |
||||
}; |
||||
c3_chart_fn.data.names = function (names) { |
||||
this.internal.clearLegendItemTextBoxCache(); |
||||
return this.internal.updateDataAttributes('names', names); |
||||
}; |
||||
c3_chart_fn.data.colors = function (colors) { |
||||
return this.internal.updateDataAttributes('colors', colors); |
||||
}; |
||||
c3_chart_fn.data.axes = function (axes) { |
||||
return this.internal.updateDataAttributes('axes', axes); |
||||
}; |
||||
|
||||
c3_chart_fn.category = function (i, category) { |
||||
let $$ = this.internal, config = $$.config; |
||||
if (arguments.length > 1) { |
||||
config.axis_x_categories[i] = category; |
||||
$$.redraw(); |
||||
} |
||||
return config.axis_x_categories[i]; |
||||
}; |
||||
c3_chart_fn.categories = function (categories) { |
||||
let $$ = this.internal, config = $$.config; |
||||
if (!arguments.length) { return config.axis_x_categories; } |
||||
config.axis_x_categories = categories; |
||||
$$.redraw(); |
||||
return config.axis_x_categories; |
||||
}; |
||||
|
||||
// TODO: fix
|
||||
c3_chart_fn.color = function (id) { |
||||
const $$ = this.internal; |
||||
return $$.color(id); // more patterns
|
||||
}; |
||||
|
||||
c3_chart_fn.x = function (x) { |
||||
const $$ = this.internal; |
||||
if (arguments.length) { |
||||
$$.updateTargetX($$.data.targets, x); |
||||
$$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); |
||||
} |
||||
return $$.data.xs; |
||||
}; |
||||
c3_chart_fn.xs = function (xs) { |
||||
const $$ = this.internal; |
||||
if (arguments.length) { |
||||
$$.updateTargetXs($$.data.targets, xs); |
||||
$$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); |
||||
} |
||||
return $$.data.xs; |
||||
}; |
||||
|
||||
c3_chart_fn.axis = function () {}; |
||||
c3_chart_fn.axis.labels = function (labels) { |
||||
const $$ = this.internal; |
||||
if (arguments.length) { |
||||
Object.keys(labels).forEach((axisId) => { |
||||
$$.axis.setLabelText(axisId, labels[axisId]); |
||||
}); |
||||
$$.axis.updateLabels(); |
||||
} |
||||
// TODO: return some values?
|
||||
}; |
||||
c3_chart_fn.axis.max = function (max) { |
||||
let $$ = this.internal, config = $$.config; |
||||
if (arguments.length) { |
||||
if (typeof max === 'object') { |
||||
if (isValue(max.x)) { config.axis_x_max = max.x; } |
||||
if (isValue(max.y)) { config.axis_y_max = max.y; } |
||||
if (isValue(max.y2)) { config.axis_y2_max = max.y2; } |
||||
} else { |
||||
config.axis_y_max = config.axis_y2_max = max; |
||||
} |
||||
$$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); |
||||
} else { |
||||
return { |
||||
x: config.axis_x_max, |
||||
y: config.axis_y_max, |
||||
y2: config.axis_y2_max, |
||||
}; |
||||
} |
||||
}; |
||||
c3_chart_fn.axis.min = function (min) { |
||||
let $$ = this.internal, config = $$.config; |
||||
if (arguments.length) { |
||||
if (typeof min === 'object') { |
||||
if (isValue(min.x)) { config.axis_x_min = min.x; } |
||||
if (isValue(min.y)) { config.axis_y_min = min.y; } |
||||
if (isValue(min.y2)) { config.axis_y2_min = min.y2; } |
||||
} else { |
||||
config.axis_y_min = config.axis_y2_min = min; |
||||
} |
||||
$$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); |
||||
} else { |
||||
return { |
||||
x: config.axis_x_min, |
||||
y: config.axis_y_min, |
||||
y2: config.axis_y2_min, |
||||
}; |
||||
} |
||||
}; |
||||
c3_chart_fn.axis.range = function (range) { |
||||
if (arguments.length) { |
||||
if (isDefined(range.max)) { this.axis.max(range.max); } |
||||
if (isDefined(range.min)) { this.axis.min(range.min); } |
||||
} else { |
||||
return { |
||||
max: this.axis.max(), |
||||
min: this.axis.min(), |
||||
}; |
||||
} |
||||
}; |
||||
|
||||
c3_chart_fn.legend = function () {}; |
||||
c3_chart_fn.legend.show = function (targetIds) { |
||||
const $$ = this.internal; |
||||
$$.showLegend($$.mapToTargetIds(targetIds)); |
||||
$$.updateAndRedraw({ withLegend: true }); |
||||
}; |
||||
c3_chart_fn.legend.hide = function (targetIds) { |
||||
const $$ = this.internal; |
||||
$$.hideLegend($$.mapToTargetIds(targetIds)); |
||||
$$.updateAndRedraw({ withLegend: true }); |
||||
}; |
||||
|
||||
c3_chart_fn.resize = function (size) { |
||||
let $$ = this.internal, config = $$.config; |
||||
config.size_width = size ? size.width : null; |
||||
config.size_height = size ? size.height : null; |
||||
this.flush(); |
||||
}; |
||||
|
||||
c3_chart_fn.flush = function () { |
||||
const $$ = this.internal; |
||||
$$.updateAndRedraw({ withLegend: true, withTransition: false, withTransitionForTransform: false }); |
||||
}; |
||||
|
||||
c3_chart_fn.destroy = function () { |
||||
const $$ = this.internal; |
||||
|
||||
window.clearInterval($$.intervalForObserveInserted); |
||||
|
||||
if ($$.resizeTimeout !== undefined) { |
||||
window.clearTimeout($$.resizeTimeout); |
||||
} |
||||
|
||||
if (window.detachEvent) { |
||||
window.detachEvent('onresize', $$.resizeFunction); |
||||
} else if (window.removeEventListener) { |
||||
window.removeEventListener('resize', $$.resizeFunction); |
||||
} else { |
||||
const wrapper = window.onresize; |
||||
// check if no one else removed our wrapper and remove our resizeFunction from it
|
||||
if (wrapper && wrapper.add && wrapper.remove) { |
||||
wrapper.remove($$.resizeFunction); |
||||
} |
||||
} |
||||
|
||||
$$.selectChart.classed('c3', false).html(''); |
||||
|
||||
// MEMO: this is needed because the reference of some elements will not be released, then memory leak will happen.
|
||||
Object.keys($$).forEach((key) => { |
||||
$$[key] = null; |
||||
}); |
||||
|
||||
return null; |
||||
}; |
||||
|
||||
c3_chart_fn.tooltip = function () {}; |
||||
c3_chart_fn.tooltip.show = function (args) { |
||||
let $$ = this.internal, index, mouse; |
||||
|
||||
// determine mouse position on the chart
|
||||
if (args.mouse) { |
||||
mouse = args.mouse; |
||||
} |
||||
|
||||
// determine focus data
|
||||
if (args.data) { |
||||
if ($$.isMultipleX()) { |
||||
// if multiple xs, target point will be determined by mouse
|
||||
mouse = [$$.x(args.data.x), $$.getYScale(args.data.id)(args.data.value)]; |
||||
index = null; |
||||
} else { |
||||
// TODO: when tooltip_grouped = false
|
||||
index = isValue(args.data.index) ? args.data.index : $$.getIndexByX(args.data.x); |
||||
} |
||||
} |
||||
else if (typeof args.x !== 'undefined') { |
||||
index = $$.getIndexByX(args.x); |
||||
} |
||||
else if (typeof args.index !== 'undefined') { |
||||
index = args.index; |
||||
} |
||||
|
||||
// emulate mouse events to show
|
||||
$$.dispatchEvent('mouseover', index, mouse); |
||||
$$.dispatchEvent('mousemove', index, mouse); |
||||
|
||||
$$.config.tooltip_onshow.call($$, args.data); |
||||
}; |
||||
c3_chart_fn.tooltip.hide = function () { |
||||
// TODO: get target data by checking the state of focus
|
||||
this.internal.dispatchEvent('mouseout', 0); |
||||
|
||||
this.internal.config.tooltip_onhide.call(this); |
||||
}; |
||||
export { Chart }; |
||||
export default Chart; |
@ -0,0 +1,81 @@
|
||||
import { |
||||
CLASS, |
||||
isValue, |
||||
isFunction, |
||||
isString, |
||||
isUndefined, |
||||
isDefined, |
||||
ceil10, |
||||
asHalfPixel, |
||||
diffDomain, |
||||
isEmpty, |
||||
notEmpty, |
||||
getOption, |
||||
hasValue, |
||||
sanitise, |
||||
getPathBox, |
||||
ChartInternal |
||||
} from '../chartinternal.js'; |
||||
|
||||
const axis = function () {}; |
||||
axis.labels = function (labels) { |
||||
const $$ = this.internal; |
||||
if (arguments.length) { |
||||
Object.keys(labels).forEach((axisId) => { |
||||
$$.axis.setLabelText(axisId, labels[axisId]); |
||||
}); |
||||
$$.axis.updateLabels(); |
||||
} |
||||
// TODO: return some values?
|
||||
}; |
||||
axis.max = function (max) { |
||||
let $$ = this.internal, config = $$.config; |
||||
if (arguments.length) { |
||||
if (typeof max === 'object') { |
||||
if (isValue(max.x)) { config.axis_x_max = max.x; } |
||||
if (isValue(max.y)) { config.axis_y_max = max.y; } |
||||
if (isValue(max.y2)) { config.axis_y2_max = max.y2; } |
||||
} else { |
||||
config.axis_y_max = config.axis_y2_max = max; |
||||
} |
||||
$$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); |
||||
} else { |
||||
return { |
||||
x: config.axis_x_max, |
||||
y: config.axis_y_max, |
||||
y2: config.axis_y2_max, |
||||
}; |
||||
} |
||||
}; |
||||
axis.min = function (min) { |
||||
let $$ = this.internal, config = $$.config; |
||||
if (arguments.length) { |
||||
if (typeof min === 'object') { |
||||
if (isValue(min.x)) { config.axis_x_min = min.x; } |
||||
if (isValue(min.y)) { config.axis_y_min = min.y; } |
||||
if (isValue(min.y2)) { config.axis_y2_min = min.y2; } |
||||
} else { |
||||
config.axis_y_min = config.axis_y2_min = min; |
||||
} |
||||
$$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); |
||||
} else { |
||||
return { |
||||
x: config.axis_x_min, |
||||
y: config.axis_y_min, |
||||
y2: config.axis_y2_min, |
||||
}; |
||||
} |
||||
}; |
||||
axis.range = function (range) { |
||||
if (arguments.length) { |
||||
if (isDefined(range.max)) { this.axis.max(range.max); } |
||||
if (isDefined(range.min)) { this.axis.min(range.min); } |
||||
} else { |
||||
return { |
||||
max: this.axis.max(), |
||||
min: this.axis.min(), |
||||
}; |
||||
} |
||||
}; |
||||
|
||||
export { axis }; |
@ -0,0 +1,36 @@
|
||||
import { |
||||
CLASS, |
||||
isValue, |
||||
isFunction, |
||||
isString, |
||||
isUndefined, |
||||
isDefined, |
||||
ceil10, |
||||
asHalfPixel, |
||||
diffDomain, |
||||
isEmpty, |
||||
notEmpty, |
||||
getOption, |
||||
hasValue, |
||||
sanitise, |
||||
getPathBox, |
||||
ChartInternal, |
||||
} from '../chartinternal.js'; |
||||
|
||||
const category = function (i, category) { |
||||
let $$ = this.internal, config = $$.config; |
||||
if (arguments.length > 1) { |
||||
config.axis_x_categories[i] = category; |
||||
$$.redraw(); |
||||
} |
||||
return config.axis_x_categories[i]; |
||||
}; |
||||
const categories = function (categories) { |
||||
let $$ = this.internal, config = $$.config; |
||||
if (!arguments.length) { return config.axis_x_categories; } |
||||
config.axis_x_categories = categories; |
||||
$$.redraw(); |
||||
return config.axis_x_categories; |
||||
}; |
||||
|
||||
export { category, categories }; |
@ -0,0 +1,64 @@
|
||||
import { |
||||
CLASS, |
||||
isValue, |
||||
isFunction, |
||||
isString, |
||||
isUndefined, |
||||
isDefined, |
||||
ceil10, |
||||
asHalfPixel, |
||||
diffDomain, |
||||
isEmpty, |
||||
notEmpty, |
||||
getOption, |
||||
hasValue, |
||||
sanitise, |
||||
getPathBox, |
||||
ChartInternal |
||||
} from '../chartinternal.js'; |
||||
|
||||
const resize = function (size) { |
||||
let $$ = this.internal, config = $$.config; |
||||
config.size_width = size ? size.width : null; |
||||
config.size_height = size ? size.height : null; |
||||
this.flush(); |
||||
}; |
||||
|
||||
const flush = function () { |
||||
const $$ = this.internal; |
||||
$$.updateAndRedraw({ withLegend: true, withTransition: false, withTransitionForTransform: false }); |
||||
}; |
||||
|
||||
const destroy = function () { |
||||
const $$ = this.internal; |
||||
|
||||
window.clearInterval($$.intervalForObserveInserted); |
||||
|
||||
if ($$.resizeTimeout !== undefined) { |
||||
window.clearTimeout($$.resizeTimeout); |
||||
} |
||||
|
||||
if (window.detachEvent) { |
||||
window.detachEvent('onresize', $$.resizeFunction); |
||||
} else if (window.removeEventListener) { |
||||
window.removeEventListener('resize', $$.resizeFunction); |
||||
} else { |
||||
const wrapper = window.onresize; |
||||
// check if no one else removed our wrapper and remove our resizeFunction from it
|
||||
if (wrapper && wrapper.add && wrapper.remove) { |
||||
wrapper.remove($$.resizeFunction); |
||||
} |
||||
} |
||||
|
||||
$$.selectChart.classed('c3', false).html(''); |
||||
|
||||
// MEMO: this is needed because the reference of some elements will not be released,
|
||||
// then memory leak will happen.
|
||||
Object.keys($$).forEach((key) => { |
||||
$$[key] = null; |
||||
}); |
||||
|
||||
return null; |
||||
}; |
||||
|
||||
export { resize, flush, destroy }; |
@ -0,0 +1,26 @@
|
||||
import { |
||||
CLASS, |
||||
isValue, |
||||
isFunction, |
||||
isString, |
||||
isUndefined, |
||||
isDefined, |
||||
ceil10, |
||||
asHalfPixel, |
||||
diffDomain, |
||||
isEmpty, |
||||
notEmpty, |
||||
getOption, |
||||
hasValue, |
||||
sanitise, |
||||
getPathBox, |
||||
ChartInternal |
||||
} from '../chartinternal.js'; |
||||
|
||||
// TODO: fix
|
||||
const color = function (id) { |
||||
const $$ = this.internal; |
||||
return $$.color(id); // more patterns
|
||||
}; |
||||
|
||||
export { color }; |
@ -0,0 +1,53 @@
|
||||
import { |
||||
CLASS, |
||||
isValue, |
||||
isFunction, |
||||
isString, |
||||
isUndefined, |
||||
isDefined, |
||||
ceil10, |
||||
asHalfPixel, |
||||
diffDomain, |
||||
isEmpty, |
||||
notEmpty, |
||||
getOption, |
||||
hasValue, |
||||
sanitise, |
||||
getPathBox, |
||||
ChartInternal |
||||
} from '../chartinternal.js'; |
||||
|
||||
const data = function (targetIds) { |
||||
const targets = this.internal.data.targets; |
||||
return typeof targetIds === 'undefined' ? targets : targets.filter((t) => { |
||||
return [].concat(targetIds).indexOf(t.id) >= 0; |
||||
}); |
||||
}; |
||||
|
||||
data.shown = function (targetIds) { |
||||
return this.internal.filterTargetsToShow(this.data(targetIds)); |
||||
}; |
||||
|
||||
data.values = function (targetId) { |
||||
let targets, values = null; |
||||
if (targetId) { |
||||
targets = this.data(targetId); |
||||
values = targets[0] ? targets[0].values.map((d) => { return d.value; }) : null; |
||||
} |
||||
return values; |
||||
}; |
||||
|
||||
data.names = function (names) { |
||||
this.internal.clearLegendItemTextBoxCache(); |
||||
return this.internal.updateDataAttributes('names', names); |
||||
}; |
||||
|
||||
data.colors = function (colors) { |
||||
return this.internal.updateDataAttributes('colors', colors); |
||||
}; |
||||
|
||||
data.axes = function (axes) { |
||||
return this.internal.updateDataAttributes('axes', axes); |
||||
}; |
||||
|
||||
export { data }; |
@ -0,0 +1,164 @@
|
||||
import { |
||||
CLASS, |
||||
isValue, |
||||
isFunction, |
||||
isString, |
||||
isUndefined, |
||||
isDefined, |
||||
ceil10, |
||||
asHalfPixel, |
||||
diffDomain, |
||||
isEmpty, |
||||
notEmpty, |
||||
getOption, |
||||
hasValue, |
||||
sanitise, |
||||
getPathBox, |
||||
ChartInternal |
||||
} from '../chartinternal.js'; |
||||
|
||||
const flow = function (args) { |
||||
let $$ = this.internal, |
||||
targets, data, notfoundIds = [], |
||||
orgDataCount = $$.getMaxDataCount(), |
||||
dataCount, domain, baseTarget, baseValue, length = 0, |
||||
tail = 0, |
||||
diff, to; |
||||
|
||||
if (args.json) { |
||||
data = $$.convertJsonToData(args.json, args.keys); |
||||
} else if (args.rows) { |
||||
data = $$.convertRowsToData(args.rows); |
||||
} else if (args.columns) { |
||||
data = $$.convertColumnsToData(args.columns); |
||||
} else { |
||||
return; |
||||
} |
||||
targets = $$.convertDataToTargets(data, true); |
||||
|
||||
// Update/Add data
|
||||
$$.data.targets.forEach((t) => { |
||||
let found = false, |
||||
i, j; |
||||
for (i = 0; i < targets.length; i++) { |
||||
if (t.id === targets[i].id) { |
||||
found = true; |
||||
|
||||
if (t.values[t.values.length - 1]) { |
||||
tail = t.values[t.values.length - 1].index + 1; |
||||
} |
||||
length = targets[i].values.length; |
||||
|
||||
for (j = 0; j < length; j++) { |
||||
targets[i].values[j].index = tail + j; |
||||
if (!$$.isTimeSeries()) { |
||||
targets[i].values[j].x = tail + j; |
||||
} |
||||
} |
||||
t.values = t.values.concat(targets[i].values); |
||||
|
||||
targets.splice(i, 1); |
||||
break; |
||||
} |
||||
} |
||||
if (!found) { notfoundIds.push(t.id); } |
||||
}); |
||||
|
||||
// Append null for not found targets
|
||||
$$.data.targets.forEach((t) => { |
||||
let i, j; |
||||
for (i = 0; i < notfoundIds.length; i++) { |
||||
if (t.id === notfoundIds[i]) { |
||||
tail = t.values[t.values.length - 1].index + 1; |
||||
for (j = 0; j < length; j++) { |
||||
t.values.push({ |
||||
id: t.id, |
||||
index: tail + j, |
||||
x: $$.isTimeSeries() ? $$.getOtherTargetX(tail + j) : tail + j, |
||||
value: null, |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
|
||||
// Generate null values for new target
|
||||
if ($$.data.targets.length) { |
||||
targets.forEach((t) => { |
||||
let i, missing = []; |
||||
for (i = $$.data.targets[0].values[0].index; i < tail; i++) { |
||||
missing.push({ |
||||
id: t.id, |
||||
index: i, |
||||
x: $$.isTimeSeries() ? $$.getOtherTargetX(i) : i, |
||||
value: null, |
||||
}); |
||||
} |
||||
t.values.forEach((v) => { |
||||
v.index += tail; |
||||
if (!$$.isTimeSeries()) { |
||||
v.x += tail; |
||||
} |
||||
}); |
||||
t.values = missing.concat(t.values); |
||||
}); |
||||
} |
||||
$$.data.targets = $$.data.targets.concat(targets); // add remained
|
||||
|
||||
// check data count because behavior needs to change when it's only one
|
||||
dataCount = $$.getMaxDataCount(); |
||||
baseTarget = $$.data.targets[0]; |
||||
baseValue = baseTarget.values[0]; |
||||
|
||||
// Update length to flow if needed
|
||||
if (isDefined(args.to)) { |
||||
length = 0; |
||||
to = $$.isTimeSeries() ? $$.parseDate(args.to) : args.to; |
||||
baseTarget.values.forEach((v) => { |
||||
if (v.x < to) { length++; } |
||||
}); |
||||
} else if (isDefined(args.length)) { |
||||
length = args.length; |
||||
} |
||||
|
||||
// If only one data, update the domain to flow from left edge of the chart
|
||||
if (!orgDataCount) { |
||||
if ($$.isTimeSeries()) { |
||||
if (baseTarget.values.length > 1) { |
||||
diff = baseTarget.values[baseTarget.values.length - 1].x - baseValue.x; |
||||
} else { |
||||
diff = baseValue.x - $$.getXDomain($$.data.targets)[0]; |
||||
} |
||||
} else { |
||||
diff = 1; |
||||
} |
||||
domain = [baseValue.x - diff, baseValue.x]; |
||||
$$.updateXDomain(null, true, true, false, domain); |
||||
} else if (orgDataCount === 1) { |
||||
if ($$.isTimeSeries()) { |
||||
diff = (baseTarget.values[baseTarget.values.length - 1].x - baseValue.x) / 2; |
||||
domain = [new Date(+baseValue.x - diff), new Date(+baseValue.x + diff)]; |
||||
$$.updateXDomain(null, true, true, false, domain); |
||||
} |
||||
} |
||||
|
||||
// Set targets
|
||||
$$.updateTargets($$.data.targets); |
||||
|
||||
// Redraw with new targets
|
||||
$$.redraw({ |
||||
flow: { |
||||
index: baseValue.index, |
||||
length, |
||||
duration: isValue(args.duration) ? args.duration : $$.config.transition_duration, |
||||
done: args.done, |
||||
orgDataCount, |
||||
}, |
||||
withLegend: true, |
||||
withTransition: orgDataCount > 1, |
||||
withTrimXDomain: false, |
||||
withUpdateXAxis: true, |
||||
}); |
||||
}; |
||||
|
||||
export { flow }; |
@ -0,0 +1,81 @@
|
||||
import { |
||||
CLASS, |
||||
isValue, |
||||
isFunction, |
||||
isString, |
||||
isUndefined, |
||||
isDefined, |
||||
ceil10, |
||||
asHalfPixel, |
||||
diffDomain, |
||||
isEmpty, |
||||
notEmpty, |
||||
getOption, |
||||
hasValue, |
||||
sanitise, |
||||
getPathBox, |
||||
ChartInternal |
||||
} from '../chartinternal.js'; |
||||
|
||||
const focus = function (targetIds) { |
||||
let $$ = this.internal, candidates; |
||||
|
||||
targetIds = $$.mapToTargetIds(targetIds); |
||||
candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$))), |
||||
|
||||
this.revert(); |
||||
this.defocus(); |
||||
candidates.classed(CLASS.focused, true).classed(CLASS.defocused, false); |
||||
if ($$.hasArcType()) { |
||||
$$.expandArc(targetIds); |
||||
} |
||||
$$.toggleFocusLegend(targetIds, true); |
||||
|
||||
$$.focusedTargetIds = targetIds; |
||||
$$.defocusedTargetIds = $$.defocusedTargetIds.filter((id) => { |
||||
return targetIds.indexOf(id) < 0; |
||||
}); |
||||
}; |
||||
|
||||
const defocus = function (targetIds) { |
||||
let $$ = this.internal, candidates; |
||||
|
||||
targetIds = $$.mapToTargetIds(targetIds); |
||||
candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$))), |
||||
|
||||
candidates.classed(CLASS.focused, false).classed(CLASS.defocused, true); |
||||
if ($$.hasArcType()) { |
||||
$$.unexpandArc(targetIds); |
||||
} |
||||
$$.toggleFocusLegend(targetIds, false); |
||||
|
||||
$$.focusedTargetIds = $$.focusedTargetIds.filter((id) => { |
||||
return targetIds.indexOf(id) < 0; |
||||
}); |
||||
$$.defocusedTargetIds = targetIds; |
||||
}; |
||||
|
||||
const revert = function (targetIds) { |
||||
let $$ = this.internal, candidates; |
||||
|
||||
targetIds = $$.mapToTargetIds(targetIds); |
||||
candidates = $$.svg.selectAll($$.selectorTargets(targetIds)); // should be for all targets
|
||||
|
||||
candidates.classed(CLASS.focused, false).classed(CLASS.defocused, false); |
||||
if ($$.hasArcType()) { |
||||
$$.unexpandArc(targetIds); |
||||
} |
||||
if ($$.config.legend_show) { |
||||
$$.showLegend(targetIds.filter($$.isLegendToShow.bind($$))); |
||||
$$.legend.selectAll($$.selectorLegends(targetIds)) |
||||
.filter(function () { |
||||
return $$.d3.select(this).classed(CLASS.legendItemFocused); |
||||
}) |
||||
.classed(CLASS.legendItemFocused, false); |
||||
} |
||||
|
||||
$$.focusedTargetIds = []; |
||||
$$.defocusedTargetIds = []; |
||||
}; |
||||
|
||||
export { focus, defocus, revert }; |
@ -0,0 +1,56 @@
|
||||
import { |
||||
CLASS, |
||||
isValue, |
||||
isFunction, |
||||
isString, |
||||
isUndefined, |
||||
isDefined, |
||||
ceil10, |
||||
asHalfPixel, |
||||
diffDomain, |
||||
isEmpty, |
||||
notEmpty, |
||||
getOption, |
||||
hasValue, |
||||
sanitise, |
||||
getPathBox, |
||||
ChartInternal |
||||
} from '../chartinternal.js'; |
||||
|
||||
const xgrids = function (grids) { |
||||
let $$ = this.internal, config = $$.config; |
||||
if (!grids) { return config.grid_x_lines; } |
||||
config.grid_x_lines = grids; |
||||
$$.redrawWithoutRescale(); |
||||
return config.grid_x_lines; |
||||
}; |
||||
|
||||
xgrids.add = function (grids) { |
||||
const $$ = this.internal; |
||||
return this.xgrids($$.config.grid_x_lines.concat(grids ? grids : [])); |
||||
}; |
||||
|
||||
xgrids.remove = function (params) { // TODO: multiple
|
||||
const $$ = this.internal; |
||||
$$.removeGridLines(params, true); |
||||
}; |
||||
|
||||
const ygrids = function (grids) { |
||||
let $$ = this.internal, config = $$.config; |
||||
if (!grids) { return config.grid_y_lines; } |
||||
config.grid_y_lines = grids; |
||||
$$.redrawWithoutRescale(); |
||||
return config.grid_y_lines; |
||||
}; |
||||
|
||||
ygrids.add = function (grids) { |
||||
const $$ = this.internal; |
||||
return this.ygrids($$.config.grid_y_lines.concat(grids ? grids : [])); |
||||
}; |
||||
|
||||
ygrids.remove = function (params) { // TODO: multiple
|
||||
const $$ = this.internal; |
||||
$$.removeGridLines(params, false); |
||||
}; |
||||
|
||||
export { xgrids, ygrids }; |
@ -0,0 +1,28 @@
|
||||
import { |
||||
CLASS, |
||||
isValue, |
||||
isFunction, |
||||
isString, |
||||
isUndefined, |
||||
isDefined, |
||||
ceil10, |
||||
asHalfPixel, |
||||
diffDomain, |
||||
isEmpty, |
||||
notEmpty, |
||||
getOption, |
||||
hasValue, |
||||
sanitise, |
||||
getPathBox, |
||||
ChartInternal |
||||
} from '../chartinternal.js'; |
||||
|
||||
const groups = function (groups) { |
||||
let $$ = this.internal, config = $$.config; |
||||
if (isUndefined(groups)) { return config.data_groups; } |
||||
config.data_groups = groups; |
||||
$$.redraw(); |
||||
return config.data_groups; |
||||
}; |
||||
|
||||
export { groups }; |
@ -0,0 +1,34 @@
|
||||
import { |
||||
CLASS, |
||||
isValue, |
||||
isFunction, |
||||
isString, |
||||
isUndefined, |
||||
isDefined, |
||||
ceil10, |
||||
asHalfPixel, |
||||
diffDomain, |
||||
isEmpty, |
||||
notEmpty, |
||||
getOption, |
||||
hasValue, |
||||
sanitise, |
||||
getPathBox, |
||||
ChartInternal |
||||
} from '../chartinternal.js'; |
||||
|
||||
const legend = function () {}; |
||||
|
||||
legend.show = function (targetIds) { |
||||
const $$ = this.internal; |
||||
$$.showLegend($$.mapToTargetIds(targetIds)); |
||||
$$.updateAndRedraw({ withLegend: true }); |
||||
}; |
||||
|
||||
legend.hide = function (targetIds) { |
||||
const $$ = this.internal; |
||||
$$.hideLegend($$.mapToTargetIds(targetIds)); |
||||
$$.updateAndRedraw({ withLegend: true }); |
||||
}; |
||||
|
||||
export { legend }; |
@ -0,0 +1,82 @@
|
||||
import { |
||||
CLASS, |
||||
isValue, |
||||
isFunction, |
||||
isString, |
||||
isUndefined, |
||||
isDefined, |
||||
ceil10, |
||||
asHalfPixel, |
||||
diffDomain, |
||||
isEmpty, |
||||
notEmpty, |
||||
getOption, |
||||
hasValue, |
||||
sanitise, |
||||
getPathBox, |
||||
ChartInternal |
||||
} from '../chartinternal.js'; |
||||
|
||||
const load = function (args) { |
||||
let $$ = this.internal, config = $$.config; |
||||
// update xs if specified
|
||||
if (args.xs) { |
||||
$$.addXs(args.xs); |
||||
} |
||||
// update names if exists
|
||||
if ('names' in args) { |
||||
c3_chart_fn.data.names.bind(this)(args.names); |
||||
} |
||||
// update classes if exists
|
||||
if ('classes' in args) { |
||||
Object.keys(args.classes).forEach((id) => { |
||||
config.data_classes[id] = args.classes[id]; |
||||
}); |
||||
} |
||||
// update categories if exists
|
||||
if ('categories' in args && $$.isCategorized()) { |
||||
config.axis_x_categories = args.categories; |
||||
} |
||||
// update axes if exists
|
||||
if ('axes' in args) { |
||||
Object.keys(args.axes).forEach((id) => { |
||||
config.data_axes[id] = args.axes[id]; |
||||
}); |
||||
} |
||||
// update colors if exists
|
||||
if ('colors' in args) { |
||||
Object.keys(args.colors).forEach((id) => { |
||||
config.data_colors[id] = args.colors[id]; |
||||
}); |
||||
} |
||||
// use cache if exists
|
||||
if ('cacheIds' in args && $$.hasCaches(args.cacheIds)) { |
||||
$$.load($$.getCaches(args.cacheIds), args.done); |
||||
return; |
||||
} |
||||
// unload if needed
|
||||
if ('unload' in args) { |
||||
// TODO: do not unload if target will load (included in url/rows/columns)
|
||||
$$.unload($$.mapToTargetIds((typeof args.unload === 'boolean' && args.unload) ? null : args.unload), () => { |
||||
$$.loadFromArgs(args); |
||||
}); |
||||
} else { |
||||
$$.loadFromArgs(args); |
||||
} |
||||
}; |
||||
|
||||
const unload = function (args) { |
||||
const $$ = this.internal; |
||||
args = args || {}; |
||||
if (args instanceof Array) { |
||||
args = { ids: args }; |
||||
} else if (typeof args === 'string') { |
||||
args = { ids: [args] }; |
||||
} |
||||
$$.unload($$.mapToTargetIds(args.ids), () => { |
||||
$$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); |
||||
if (args.done) { args.done(); } |
||||
}); |
||||
}; |
||||
|
||||
export { load, unload }; |
@ -0,0 +1,63 @@
|
||||
import { |
||||
CLASS, |
||||
isValue, |
||||
isFunction, |
||||
isString, |
||||
isUndefined, |
||||
isDefined, |
||||
ceil10, |
||||
asHalfPixel, |
||||
diffDomain, |
||||
isEmpty, |
||||
notEmpty, |
||||
getOption, |
||||
hasValue, |
||||
sanitise, |
||||
getPathBox, |
||||
ChartInternal |
||||
} from '../chartinternal.js'; |
||||
|
||||
const regions = function (regions) { |
||||
let $$ = this.internal, config = $$.config; |
||||
if (!regions) { return config.regions; } |
||||
config.regions = regions; |
||||
$$.redrawWithoutRescale(); |
||||
return config.regions; |
||||
}; |
||||
|
||||
regions.add = function (regions) { |
||||
let $$ = this.internal, config = $$.config; |
||||
if (!regions) { return config.regions; } |
||||
config.regions = config.regions.concat(regions); |
||||
$$.redrawWithoutRescale(); |
||||
return config.regions; |
||||
}; |
||||
|
||||
regions.remove = function (options) { |
||||
let $$ = this.internal, config = $$.config, |
||||
duration, classes, regions; |
||||
|
||||
options = options || {}; |
||||
duration = $$.getOption(options, 'duration', config.transition_duration); |
||||
classes = $$.getOption(options, 'classes', [CLASS.region]); |
||||
|
||||
regions = $$.main.select('.' + CLASS.regions).selectAll(classes.map((c) => { return '.' + c; })); |
||||
(duration ? regions.transition().duration(duration) : regions) |
||||
.style('opacity', 0) |
||||
.remove(); |
||||
|
||||
config.regions = config.regions.filter((region) => { |
||||
let found = false; |
||||
if (!region.class) { |
||||
return true; |
||||
} |
||||
region.class.split(' ').forEach((c) => { |
||||
if (classes.indexOf(c) >= 0) { found = true; } |
||||
}); |
||||
return !found; |
||||
}); |
||||
|
||||
return config.regions; |
||||
}; |
||||
|
||||
export { regions }; |
@ -0,0 +1,77 @@
|
||||
import { |
||||
CLASS, |
||||
isValue, |
||||
isFunction, |
||||
isString, |
||||
isUndefined, |
||||
isDefined, |
||||
ceil10, |
||||
asHalfPixel, |
||||
diffDomain, |
||||
isEmpty, |
||||
notEmpty, |
||||
getOption, |
||||
hasValue, |
||||
sanitise, |
||||
getPathBox, |
||||
ChartInternal |
||||
} from '../chartinternal.js'; |
||||
|
||||
const selected = function (targetId) { |
||||
let $$ = this.internal, d3 = $$.d3; |
||||
return d3.merge( |
||||
$$.main.selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(targetId)).selectAll('.' + CLASS.shape) |
||||
.filter(function () { return d3.select(this).classed(CLASS.SELECTED); }) |
||||
.map((d) => { return d.map((d) => { const data = d.__data__; return data.data ? data.data : data; }); }) |
||||
); |
||||
}; |
||||
|
||||
const select = function (ids, indices, resetOther) { |
||||
let $$ = this.internal, d3 = $$.d3, config = $$.config; |
||||
if (!config.data_selection_enabled) { return; } |
||||
$$.main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape).each(function (d, i) { |
||||
let shape = d3.select(this), id = d.data ? d.data.id : d.id, |
||||
toggle = $$.getToggle(this, d).bind($$), |
||||
isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0, |
||||
isTargetIndex = !indices || indices.indexOf(i) >= 0, |
||||
isSelected = shape.classed(CLASS.SELECTED); |
||||
// line/area selection not supported yet
|
||||
if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) { |
||||
return; |
||||
} |
||||
if (isTargetId && isTargetIndex) { |
||||
if (config.data_selection_isselectable(d) && !isSelected) { |
||||
toggle(true, shape.classed(CLASS.SELECTED, true), d, i); |
||||
} |
||||
} else if (isDefined(resetOther) && resetOther) { |
||||
if (isSelected) { |
||||
toggle(false, shape.classed(CLASS.SELECTED, false), d, i); |
||||
} |
||||
} |
||||
}); |
||||
}; |
||||
|
||||
const unselect = function (ids, indices) { |
||||
let $$ = this.internal, d3 = $$.d3, config = $$.config; |
||||
if (!config.data_selection_enabled) { return; } |
||||
$$.main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape).each(function (d, i) { |
||||
let shape = d3.select(this), id = d.data ? d.data.id : d.id, |
||||
toggle = $$.getToggle(this, d).bind($$), |
||||
isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0, |
||||
isTargetIndex = !indices || indices.indexOf(i) >= 0, |
||||
isSelected = shape.classed(CLASS.SELECTED); |
||||
// line/area selection not supported yet
|
||||
if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) { |
||||
return; |
||||
} |
||||
if (isTargetId && isTargetIndex) { |
||||
if (config.data_selection_isselectable(d)) { |
||||
if (isSelected) { |
||||
toggle(false, shape.classed(CLASS.SELECTED, false), d, i); |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
}; |
||||
|
||||
export { selected, select, unselect }; |
@ -0,0 +1,71 @@
|
||||
import { |
||||
CLASS, |
||||
isValue, |
||||
isFunction, |
||||
isString, |
||||
isUndefined, |
||||
isDefined, |
||||
ceil10, |
||||
asHalfPixel, |
||||
diffDomain, |
||||
isEmpty, |
||||
notEmpty, |
||||
getOption, |
||||
hasValue, |
||||
sanitise, |
||||
getPathBox, |
||||
ChartInternal |
||||
} from '../chartinternal.js'; |
||||
|
||||
const show = function (targetIds, options) { |
||||
let $$ = this.internal, targets; |
||||
|
||||
targetIds = $$.mapToTargetIds(targetIds); |
||||
options = options || {}; |
||||
|
||||
$$.removeHiddenTargetIds(targetIds); |
||||
targets = $$.svg.selectAll($$.selectorTargets(targetIds)); |
||||
|
||||
targets.transition() |
||||
.style('opacity', 1, 'important') |
||||
.call($$.endall, () => { |
||||
targets.style('opacity', null).style('opacity', 1); |
||||
}); |
||||
|
||||
if (options.withLegend) { |
||||
$$.showLegend(targetIds); |
||||
} |
||||
|
||||
$$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); |
||||
}; |
||||
|
||||
const hide = function (targetIds, options) { |
||||
let $$ = this.internal, targets; |
||||
|
||||
targetIds = $$.mapToTargetIds(targetIds); |
||||
options = options || {}; |
||||
|
||||
$$.addHiddenTargetIds(targetIds); |
||||
targets = $$.svg.selectAll($$.selectorTargets(targetIds)); |
||||
|
||||
targets.transition() |
||||
.style('opacity', 0, 'important') |
||||
.call($$.endall, () => { |
||||
targets.style('opacity', null).style('opacity', 0); |
||||
}); |
||||
|
||||
if (options.withLegend) { |
||||
$$.hideLegend(targetIds); |
||||
} |
||||
|
||||
$$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); |
||||
}; |
||||
|
||||
const toggle = function (targetIds, options) { |
||||
let that = this, $$ = this.internal; |
||||
$$.mapToTargetIds(targetIds).forEach((targetId) => { |
||||
$$.isTargetToShow(targetId) ? that.hide(targetId, options) : that.show(targetId, options); |
||||
}); |
||||
}; |
||||
|
||||
export { show, hide, toggle }; |
@ -0,0 +1,60 @@
|
||||
import { |
||||
CLASS, |
||||
isValue, |
||||
isFunction, |
||||
isString, |
||||
isUndefined, |
||||
isDefined, |
||||
ceil10, |
||||
asHalfPixel, |
||||
diffDomain, |
||||
isEmpty, |
||||
notEmpty, |
||||
getOption, |
||||
hasValue, |
||||
sanitise, |
||||
getPathBox, |
||||
ChartInternal |
||||
} from '../chartinternal.js'; |
||||
|
||||
const tooltip = function () {}; |
||||
|
||||
tooltip.show = function (args) { |
||||
let $$ = this.internal, index, mouse; |
||||
|
||||
// determine mouse position on the chart
|
||||
if (args.mouse) { |
||||
mouse = args.mouse; |
||||
} |
||||
|
||||
// determine focus data
|
||||
if (args.data) { |
||||
if ($$.isMultipleX()) { |
||||
// if multiple xs, target point will be determined by mouse
|
||||
mouse = [$$.x(args.data.x), $$.getYScale(args.data.id)(args.data.value)]; |
||||
index = null; |
||||
} else { |
||||
// TODO: when tooltip_grouped = false
|
||||
index = isValue(args.data.index) ? args.data.index : $$.getIndexByX(args.data.x); |
||||
} |
||||
} else if (typeof args.x !== 'undefined') { |
||||
index = $$.getIndexByX(args.x); |
||||
} else if (typeof args.index !== 'undefined') { |
||||
index = args.index; |
||||
} |
||||
|
||||
// emulate mouse events to show
|
||||
$$.dispatchEvent('mouseover', index, mouse); |
||||
$$.dispatchEvent('mousemove', index, mouse); |
||||
|
||||
$$.config.tooltip_onshow.call($$, args.data); |
||||
}; |
||||
|
||||
tooltip.hide = function () { |
||||
// TODO: get target data by checking the state of focus
|
||||
this.internal.dispatchEvent('mouseout', 0); |
||||
|
||||
this.internal.config.tooltip_onhide.call(this); |
||||
}; |
||||
|
||||
export { tooltip }; |
@ -0,0 +1,26 @@
|
||||
import { |
||||
CLASS, |
||||
isValue, |
||||
isFunction, |
||||
isString, |
||||
isUndefined, |
||||
isDefined, |
||||
ceil10, |
||||
asHalfPixel, |
||||
diffDomain, |
||||
isEmpty, |
||||
notEmpty, |
||||
getOption, |
||||
hasValue, |
||||
sanitise, |
||||
getPathBox, |
||||
ChartInternal |
||||
} from '../chartinternal.js'; |
||||
|
||||
const transform = function (type, targetIds) { |
||||
let $$ = this.internal, |
||||
options = ['pie', 'donut'].indexOf(type) >= 0 ? { withTransform: true } : null; |
||||
$$.transformTo(targetIds, type, options); |
||||
}; |
||||
|
||||
export { transform }; |
@ -0,0 +1,38 @@
|
||||
import { |
||||
CLASS, |
||||
isValue, |
||||
isFunction, |
||||
isString, |
||||
isUndefined, |
||||
isDefined, |
||||
ceil10, |
||||
asHalfPixel, |
||||
diffDomain, |
||||
isEmpty, |
||||
notEmpty, |
||||
getOption, |
||||
hasValue, |
||||
sanitise, |
||||
getPathBox, |
||||
ChartInternal |
||||
} from '../chartinternal.js'; |
||||
|
||||
const x = function (x) { |
||||
const $$ = this.internal; |
||||
if (arguments.length) { |
||||
$$.updateTargetX($$.data.targets, x); |
||||
$$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); |
||||
} |
||||
return $$.data.xs; |
||||
}; |
||||
|
||||
const xs = function (xs) { |
||||
const $$ = this.internal; |
||||
if (arguments.length) { |
||||
$$.updateTargetXs($$.data.targets, xs); |
||||
$$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); |
||||
} |
||||
return $$.data.xs; |
||||
}; |
||||
|
||||
export { x, xs }; |
@ -0,0 +1,77 @@
|
||||
import { |
||||
CLASS, |
||||
isValue, |
||||
isFunction, |
||||
isString, |
||||
isUndefined, |
||||
isDefined, |
||||
ceil10, |
||||
asHalfPixel, |
||||
diffDomain, |
||||
isEmpty, |
||||
notEmpty, |
||||
getOption, |
||||
hasValue, |
||||
sanitise, |
||||
getPathBox, |
||||
ChartInternal |
||||
} from '../chartinternal.js'; |
||||
|
||||
const zoom = function (domain) { |
||||
const $$ = this.internal; |
||||
if (domain) { |
||||
if ($$.isTimeSeries()) { |
||||
domain = domain.map((x) => { return $$.parseDate(x); }); |
||||
} |
||||
$$.brush.extent(domain); |
||||
$$.redraw({ withUpdateXDomain: true, withY: $$.config.zoom_rescale }); |
||||
$$.config.zoom_onzoom.call(this, $$.x.orgDomain()); |
||||
} |
||||
return $$.brush.extent(); |
||||
}; |
||||
|
||||
const unzoom = function () { |
||||
const $$ = this.internal; |
||||
$$.brush.clear().update(); |
||||
$$.redraw({ withUpdateXDomain: true }); |
||||
}; |
||||
|
||||
zoom.enable = function (enabled) { |
||||
const $$ = this.internal; |
||||
$$.config.zoom_enabled = enabled; |
||||
$$.updateAndRedraw(); |
||||
}; |
||||
|
||||
zoom.max = function (max) { |
||||
let $$ = this.internal, config = $$.config, d3 = $$.d3; |
||||
if (max === 0 || max) { |
||||
config.zoom_x_max = d3.max([$$.orgXDomain[1], max]); |
||||
} |
||||
else { |
||||
return config.zoom_x_max; |
||||
} |
||||
}; |
||||
|
||||
zoom.min = function (min) { |
||||
let $$ = this.internal, config = $$.config, d3 = $$.d3; |
||||
if (min === 0 || min) { |
||||
config.zoom_x_min = d3.min([$$.orgXDomain[0], min]); |
||||
} |
||||
else { |
||||
return config.zoom_x_min; |
||||
} |
||||
}; |
||||
|
||||
zoom.range = function (range) { |
||||
if (arguments.length) { |
||||
if (isDefined(range.max)) { this.domain.max(range.max); } |
||||
if (isDefined(range.min)) { this.domain.min(range.min); } |
||||
} else { |
||||
return { |
||||
max: this.domain.max(), |
||||
min: this.domain.min(), |
||||
}; |
||||
} |
||||
}; |
||||
|
||||
export { zoom, unzoom }; |
@ -0,0 +1,77 @@
|
||||
import { ChartInternal } from '../chartinternal.js'; |
||||
|
||||
import { axis } from './api.axis'; |
||||
import { category, categories } from './api.category'; |
||||
import { resize, flush, destroy } from './api.chart'; |
||||
import { color } from './api.color'; |
||||
import { data } from './api.data'; |
||||
import { flow } from './api.flow'; |
||||
import { focus, defocus, revert } from './api.focus'; |
||||
import { xgrids, ygrids } from './api.grid'; |
||||
import { groups } from './api.group'; |
||||
import { legend } from './api.legend'; |
||||
import { load, unload } from './api.load'; |
||||
import { regions } from './api.region'; |
||||
import { selected, select, unselect } from './api.selection'; |
||||
import { show, hide, toggle } from './api.show'; |
||||
import { tooltip } from './api.tooltip'; |
||||
import { transform } from './api.transform'; |
||||
import { x, xs } from './api.x'; |
||||
import { zoom, unzoom } from './api.zoom'; |
||||
|
||||
let c3_chart_fn; |
||||
|
||||
function Chart(config) { |
||||
const $$ = this.internal = new ChartInternal(this); |
||||
$$.loadConfig(config); |
||||
|
||||
$$.beforeInit(config); |
||||
$$.init(); |
||||
$$.afterInit(config); |
||||
|
||||
// bind "this" to nested API
|
||||
(function bindThis(fn, target, argThis) { |
||||
Object.keys(fn).forEach((key) => { |
||||
target[key] = fn[key].bind(argThis); |
||||
if (Object.keys(fn[key]).length > 0) { |
||||
bindThis(fn[key], target[key], argThis); |
||||
} |
||||
}); |
||||
})(c3_chart_fn, this, this); |
||||
} |
||||
|
||||
c3_chart_fn = Chart.prototype; |
||||
|
||||
c3_chart_fn.axis = axis; |
||||
c3_chart_fn.category = category; |
||||
c3_chart_fn.categories = categories; |
||||
c3_chart_fn.resize = resize; |
||||
c3_chart_fn.flush = flush; |
||||
c3_chart_fn.destroy = destroy; |
||||
c3_chart_fn.color = color; |
||||
c3_chart_fn.data = data; |
||||
c3_chart_fn.flow = flow; |
||||
c3_chart_fn.focus = focus; |
||||
c3_chart_fn.defocus = defocus; |
||||
c3_chart_fn.revert = revert; |
||||
c3_chart_fn.xgrids = xgrids; |
||||
c3_chart_fn.ygrids = ygrids; |
||||
c3_chart_fn.groups = groups; |
||||
c3_chart_fn.legend = legend; |
||||
c3_chart_fn.load = load; |
||||
c3_chart_fn.unload = unload; |
||||
c3_chart_fn.regions = regions; |
||||
c3_chart_fn.selected = selected; |
||||
c3_chart_fn.select = select; |
||||
c3_chart_fn.unselect = unselect; |
||||
c3_chart_fn.show = show; |
||||
c3_chart_fn.hide = hide; |
||||
c3_chart_fn.toggle = toggle; |
||||
c3_chart_fn.tooltip = tooltip; |
||||
c3_chart_fn.transform = transform; |
||||
c3_chart_fn.x = x; |
||||
c3_chart_fn.xs = xs; |
||||
c3_chart_fn.zoom = zoom; |
||||
c3_chart_fn.unzoom = unzoom; |
||||
|
||||
export { Chart }; |
Loading…
Reference in new issue