-
Notifications
You must be signed in to change notification settings - Fork 189
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tool bar above zoom bar and shift-clicking on chart #717
Changes from 250 commits
8aa950b
ad2f115
e903a6a
3b7413e
d974b60
ac6ed57
fa1ac37
80324a2
3ccef0d
68993ec
1b0b962
93cce4a
eebb780
2e76f0a
c04d0db
563f680
1ff4343
6845aff
5efec0d
b8d7d38
2208434
aed57ea
fc1fccc
c2c700e
d5d512e
48a313d
fd250d5
15020af
fc20325
22a9478
feea0d9
89e8af6
b45a8dd
a803433
3c23df6
4575327
c7770e3
4f3cc99
85b056e
604577e
75c8d36
338f298
708a1cb
e71c7db
beefd10
ed3bf3f
a753521
7d2a57a
9373977
8247969
c8f3f2e
874e7c2
4fd15a0
7a23808
9359888
529ecb5
af8519c
1dbd716
ca01a85
bf98e62
8faf441
d1a0697
5a1372e
f5574f6
fad73e6
7b83c72
4e6097e
6604563
4b2bcce
b4399d9
53d1ee2
5826383
877c013
b7239cd
8a94716
21d2d28
d3483b1
7821866
274799d
df80f1c
e74427f
e05fc0c
e25b8a3
3cdad63
55c4fa2
20a3dca
a1252ba
3f3092d
9b34ff6
6eb41d2
9388463
a01f087
3ece1ba
0fd3d07
114dbee
30fc426
384cd30
1ee5869
5927de0
b381d2d
481b0f3
98e7749
8929eed
f947ec9
cf42561
0b50223
7b8e17e
f6f685a
d973aeb
868b8a3
c5dec99
27e00e3
c486eb9
baba478
c46af66
c442885
8259517
8755c86
ee50ab6
c4c3613
0b0c407
a3a1291
9a767b6
b749d99
2df63ff
1095fc4
0c1501e
8c8c598
0b7eea7
74bf969
2326608
bcfdbd9
aa437b8
278e5e2
fac1a44
4465f27
79f912b
6d8a6a8
b17b390
60afec8
fd032a9
5a9cc62
826ee56
dfbf0df
5a7c898
767af06
a1da889
7ea54d2
8a54d4f
6b66c77
51cd780
2bbd953
3f8eebd
310471f
39c22d9
25c7298
15fe502
88ccc92
21cfda4
82d336e
fd51108
62c6f23
c9ad27d
731c973
819bcd8
1f94df4
16c3736
60b4266
5c218c2
8267039
afd99ed
a881220
2410b31
a426d4d
5053075
a4b6e3d
f079264
a786d12
369fda7
24e6721
5e3c12e
9b1e851
7543850
b4e4304
e11c0e8
ae56130
ff3b006
ed35d47
78c7216
6580558
1ae7f65
9807801
4d01a2e
92d2d95
43b84c9
2cc538f
cd77fd3
fe16282
77439e2
a5767ee
23833a7
29d292b
77a511c
57eebda
1d9348f
e95e964
5278029
4c6f0ec
de5d8d5
a830e42
87afa1b
dc533a7
925b9b4
247f2cf
0af7b5b
d1298df
82746d9
a4c5489
69fe6f2
10d9604
b5bdb9c
20627d7
53e62c6
cd3fbaa
50a9137
a734ef0
b9d0398
be16376
b9be31d
5ebf4a8
3eaade9
94e05e0
bc342d7
1e6ff2a
ba21aeb
ad2c4fb
a956942
d979d09
c3d15a6
e4e07b9
4594c1b
f1b5747
fa0f2fe
a4060bb
2b07934
9732c8a
61f3f76
bc597f1
a2832f8
0bee757
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,12 +24,23 @@ const definedZoomBarData = [ | |
{ date: new Date(2019, 0, 19), value: 21300 } | ||
]; | ||
|
||
const defaultToolBarOptions = { | ||
enabled: true, | ||
toolBarMenuItems: { | ||
resetZoom: { | ||
enabled: true, | ||
text: "Reset zoom" | ||
} | ||
} | ||
}; | ||
|
||
// utility function to update title and enable zoomBar option | ||
const addZoomBarToOptions = (options, includeDefinedZoomBarData = false) => { | ||
options["experimental"] = true; | ||
if (includeDefinedZoomBarData) { | ||
options["title"] = options["title"] + " - Defined zoom bar enabled"; | ||
options["zoomBar"] = { | ||
zoomRatio: 0.4, | ||
top: { | ||
enabled: true, | ||
data: definedZoomBarData | ||
|
@@ -38,11 +49,13 @@ const addZoomBarToOptions = (options, includeDefinedZoomBarData = false) => { | |
} else { | ||
options["title"] = options["title"] + " - Zoom bar enabled"; | ||
options["zoomBar"] = { | ||
zoomRatio: 0.4, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. isn't this set by default? why are we specifying it in code that the user will copy later on to create a chart? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was hoping to provide a clear demo configuration, but I have no concern to remove it. |
||
top: { | ||
enabled: true | ||
} | ||
}; | ||
} | ||
options["toolBar"] = defaultToolBarOptions; | ||
return options; | ||
}; | ||
|
||
|
@@ -104,6 +117,12 @@ export const zoomBarLineTimeSeriesInitDomainOptions = addZoomBarToOptions( | |
zoomBarLineTimeSeriesInitDomainOptions["title"] += " (initial zoomed domain)"; | ||
zoomBarLineTimeSeriesInitDomainOptions.zoomBar.top.initialZoomDomain = initialZoomDomain; | ||
|
||
export const zoomBarEmptyStateData = barChart.stackedBarEmptyStateData; | ||
export const zoomBarEmptyStateOptions = addZoomBarToOptions( | ||
Object.assign({}, barChart.stackedBarTimeSeriesOptions) | ||
); | ||
zoomBarEmptyStateOptions["title"] = "Zoom bar (empty state)"; | ||
|
||
// assume no data set while loading is true | ||
export const zoomBarSkeletonData = []; | ||
export const zoomBarSkeletonOptions = addZoomBarToOptions( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,10 +2,11 @@ | |
import { Component } from "../component"; | ||
import { Events, ScaleTypes } from "../../interfaces"; | ||
import { DOMUtils } from "../../services"; | ||
import { Tools } from "../../tools"; | ||
|
||
// D3 Imports | ||
import { brushX } from "d3-brush"; | ||
import { event } from "d3-selection"; | ||
import { event, mouse } from "d3-selection"; | ||
import { scaleTime } from "d3-scale"; | ||
|
||
// This class is used for handle brush events in chart | ||
|
@@ -18,7 +19,11 @@ export class ChartBrush extends Component { | |
|
||
frontSelectionSelector = "rect.frontSelection"; // needs to match the class name in _grid-brush.scss | ||
|
||
zoomService = this.services.zoom; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should move this into a variable inside There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated in 1e6ff2a. |
||
|
||
render(animate = true) { | ||
const zoomRatio = this.zoomService.getZoomRatio(); | ||
|
||
const svg = this.parent; | ||
// use this area to display selection above all graphs | ||
const frontSelectionArea = this.getContainerSVG(); | ||
|
@@ -29,6 +34,14 @@ export class ChartBrush extends Component { | |
// use this area to handle d3 brush events | ||
const brushArea = DOMUtils.appendOrSelect(backdrop, `g.${this.type}`); | ||
|
||
if ( | ||
this.zoomService.isDataLoading() || | ||
this.zoomService.isEmptyState() | ||
) { | ||
brushArea.html(null); | ||
return; | ||
} | ||
|
||
// set an id for rect.selection to be referred | ||
const d3Selection = DOMUtils.appendOrSelect( | ||
brushArea, | ||
|
@@ -38,8 +51,17 @@ export class ChartBrush extends Component { | |
const { width, height } = DOMUtils.getSVGElementSize(backdrop, { | ||
useAttrs: true | ||
}); | ||
// initialization is not completed yet | ||
if (width === 0 || height === 0) { | ||
return; | ||
} | ||
|
||
const { cartesianScales } = this.services; | ||
let axesLeftMargin = 0; | ||
const axesMargins = this.model.get("axesMargins"); | ||
if (axesMargins && axesMargins.left) { | ||
axesLeftMargin = axesMargins.left; | ||
} | ||
const mainXScaleType = cartesianScales.getMainXScaleType(); | ||
const mainXScale = cartesianScales.getMainXScale(); | ||
const [xScaleStart, xScaleEnd] = mainXScale.range(); | ||
|
@@ -49,13 +71,19 @@ export class ChartBrush extends Component { | |
this.frontSelectionSelector | ||
); | ||
|
||
const setDomain = (newDomain) => { | ||
this.model.set({ zoomDomain: newDomain }, { animate: false }); | ||
}; | ||
|
||
if (mainXScale && mainXScaleType === ScaleTypes.TIME) { | ||
// get current zoomDomain | ||
let zoomDomain = this.model.get("zoomDomain"); | ||
if (zoomDomain === undefined) { | ||
// default to full range with extended domain | ||
zoomDomain = this.services.zoom.getDefaultZoomBarDomain(); | ||
this.model.set({ zoomDomain: zoomDomain }, { animate: false }); | ||
if (zoomDomain) { | ||
setDomain(zoomDomain); | ||
} | ||
} | ||
|
||
const updateSelectionDash = (selection) => { | ||
|
@@ -127,37 +155,44 @@ export class ChartBrush extends Component { | |
}); | ||
} | ||
}; | ||
|
||
const handleZoomDomain = (startPoint, endPoint) => { | ||
// create xScale based on current zoomDomain | ||
const xScale = scaleTime() | ||
.range([axesLeftMargin, width]) | ||
.domain(zoomDomain); | ||
|
||
let newDomain = [ | ||
xScale.invert(startPoint), | ||
xScale.invert(endPoint) | ||
]; | ||
|
||
// if selected start time and end time are the same | ||
// reset to default full range | ||
if (newDomain[0].valueOf() === newDomain[1].valueOf()) { | ||
// same as d3 behavior and zoom bar behavior: set to default full range | ||
newDomain = this.services.zoom.getDefaultZoomBarDomain(); | ||
} | ||
|
||
// only if zoomDomain needs update | ||
if ( | ||
zoomDomain[0].valueOf() !== newDomain[0].valueOf() || | ||
zoomDomain[1].valueOf() !== newDomain[1].valueOf() | ||
) { | ||
// only dispatch zoom domain change event for triggering api call when event type equals to end | ||
this.services.events.dispatchEvent( | ||
Events.ZoomDomain.CHANGE, | ||
{ newDomain } | ||
); | ||
setDomain(newDomain); | ||
} | ||
}; | ||
|
||
const brushed = () => { | ||
const selection = event.selection; | ||
|
||
if (selection !== null) { | ||
// create xScale based on current zoomDomain | ||
const xScale = scaleTime() | ||
.range([0, width]) | ||
.domain(zoomDomain); | ||
|
||
let newDomain = [ | ||
xScale.invert(selection[0]), | ||
xScale.invert(selection[1]) | ||
]; | ||
|
||
// if selected start time and end time are the same | ||
// reset to default full range | ||
if (newDomain[0].valueOf() === newDomain[1].valueOf()) { | ||
// same as d3 behavior and zoom bar behavior: set to default full range | ||
newDomain = this.services.zoom.getDefaultZoomBarDomain(); | ||
} | ||
|
||
// only if zoomDomain needs update | ||
if ( | ||
zoomDomain[0].valueOf() !== newDomain[0].valueOf() || | ||
zoomDomain[1].valueOf() !== newDomain[1].valueOf() | ||
) { | ||
this.model.set( | ||
{ zoomDomain: newDomain }, | ||
{ animate: false } | ||
); | ||
} | ||
handleZoomDomain(selection[0], selection[1]); | ||
|
||
// clear brush selection | ||
brushArea.call(brush.move, null); | ||
|
@@ -176,6 +211,26 @@ export class ChartBrush extends Component { | |
.on("end.brushed", brushed); | ||
|
||
brushArea.call(brush); | ||
|
||
backdrop.on("click", function () { | ||
if (event.shiftKey) { | ||
const coords = mouse(this); | ||
const currentClickLocation = coords[0] - axesLeftMargin; | ||
|
||
let leftPoint = | ||
currentClickLocation - (width * zoomRatio) / 2; | ||
if (leftPoint < 0) { | ||
leftPoint = 0; | ||
} | ||
let rightPoint = | ||
currentClickLocation + (width * zoomRatio) / 2; | ||
if (rightPoint > width) { | ||
rightPoint = width; | ||
} | ||
|
||
handleZoomDomain(leftPoint, rightPoint); | ||
} | ||
}); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you shouldn't need to provide the
text
anymore. maybe for localization purposes you'd want to translate the string etc. but in our demo it should just show the default textThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was hoping to provide a clear demo configuration, but I have no concern to remove it.
Updated in bc342d7.