Skip to content

Commit 9261639

Browse files
fix right clicking issues on mobile
1 parent 94f5d67 commit 9261639

4 files changed

Lines changed: 116 additions & 49 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ check:
1717
uv run ty check
1818

1919
dev:
20-
uv run hiplot --port 8765
20+
uv run hiplot --port 8765 --host 0.0.0.0

src/component.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,20 @@ export class HiPlot extends React.Component<HiPlotProps, HiPlotState> {
426426
}.bind(this));
427427
contextmenu.append($('<div class="dropdown-divider"></div>'));
428428

429+
const ppRef = this.plugins_ref[DefaultPlugins.PARALLEL_PLOT];
430+
const pp = ppRef && ppRef.current ? ppRef.current as unknown as ParallelPlot : null;
431+
if (pp && pp.toggleInvertAxis) {
432+
var invert_axis = $('<a class="dropdown-item" href="#">Invert axis</a>');
433+
invert_axis.click(function(this: HiPlot, event) {
434+
const extent = pp.toggleInvertAxis(column);
435+
if (pp.update_ticks) {
436+
pp.update_ticks(column, extent);
437+
}
438+
event.preventDefault();
439+
}.bind(this));
440+
contextmenu.append(invert_axis);
441+
}
442+
429443
// Color by
430444
var link_colorize = $('<a class="dropdown-item" href="#">Use for coloring</a>');
431445
link_colorize.click(function(this: HiPlot, event) {
@@ -632,7 +646,7 @@ class DocAndCredits extends React.Component<DocsCreditsProps> {
632646
<strong>Brush</strong>: Drag vertically along an axis.<br/>
633647
<strong>Remove Brush</strong>: Tap the axis background.<br/>
634648
<strong>Reorder Axes</strong>: Drag a label horizontally.<br/>
635-
<strong>Invert Axis</strong>: Tap an axis label.<br/>
649+
<strong>Invert Axis</strong>: Click an axis label (desktop) or use the label menu (mobile).<br/>
636650
<strong>Remove Axis</strong>: Drag axis label to the left edge.<br/>
637651
</p>
638652
</div>

src/parallel/parallel.scss

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@
77
top: 0;
88
left: 0;
99
}
10-
.parallel-plot-chart {
10+
.parallel-plot-chart {
1111
position: relative;
1212
margin-top: 8px;
13-
}
13+
user-select: none;
14+
-webkit-user-select: none;
15+
-webkit-touch-callout: none;
16+
}
1417
.brush rect.extent {
1518
fill: rgba(100,100,100,0.15);
1619
stroke: #fff;
@@ -36,6 +39,11 @@
3639
cursor: move;
3740
font-size: 16px;
3841
}
42+
.pplot-label {
43+
user-select: none;
44+
-webkit-user-select: none;
45+
touch-action: none;
46+
}
3947
.axis text {
4048
fill: #111;
4149
text-anchor: right;

src/parallel/parallel.tsx

Lines changed: 90 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,64 @@ export class ParallelPlot extends React.Component<ParallelPlotData, ParallelPlot
113113
return text.length ? text : fallback;
114114
}
115115

116+
private normalizeBrushSelection(sel: d3.BrushSelection): [number, number] | null {
117+
if (!sel) {
118+
return null;
119+
}
120+
if (Array.isArray(sel[0])) {
121+
const box = sel as [[number, number], [number, number]];
122+
return [box[0][1], box[1][1]];
123+
}
124+
return sel as [number, number];
125+
}
126+
127+
private getBrushExtents() {
128+
const extents: {[key: string]: [number, number] | null} = {};
129+
if (!this.dimensions_dom) {
130+
return extents;
131+
}
132+
const self = this;
133+
this.dimensions_dom.selectAll("." + style.brush).each(function(this: SVGGElement, dim: string) {
134+
extents[dim] = self.normalizeBrushSelection(d3.brushSelection(this));
135+
});
136+
return extents;
137+
}
138+
139+
toggleInvertAxis(d: string) {
140+
// save extent before inverting
141+
const extents = this.getBrushExtents();
142+
const currentExtent = extents[d];
143+
const extent = currentExtent ? [this.h - currentExtent[1], this.h - currentExtent[0]] : null;
144+
const div = d3.select(this.root_ref.current);
145+
146+
if (this.state.invert.has(d)) {
147+
this.setState(function(prevState) {
148+
var newInvert = new Set(prevState.invert);
149+
newInvert.delete(d);
150+
return {
151+
invert: newInvert
152+
};
153+
});
154+
this.setScaleRange(d);
155+
div.selectAll("." + style.label)
156+
.filter(function(p) { return p == d; })
157+
.style("text-decoration", null);
158+
} else {
159+
this.setState(function(prevState) {
160+
var newInvert = new Set(prevState.invert);
161+
newInvert.add(d);
162+
return {
163+
invert: newInvert
164+
};
165+
});
166+
this.setScaleRange(d);
167+
div.selectAll("." + style.label)
168+
.filter(function(p) { return p == d; })
169+
.style("text-decoration", "underline");
170+
}
171+
return extent;
172+
}
173+
116174
constructor(props: ParallelPlotData) {
117175
super(props);
118176
this.state = {
@@ -389,8 +447,10 @@ export class ParallelPlot extends React.Component<ParallelPlotData, ParallelPlot
389447
.on("end", function(event, d: string) {
390448
if (!me.state.dragging.dragging) {
391449
// no movement, invert axis
392-
var extent = invert_axis(d);
393-
me.update_ticks(d, extent);
450+
if (!IS_MOBILE) {
451+
var extent = me.toggleInvertAxis(d);
452+
me.update_ticks(d, extent);
453+
}
394454
} else {
395455
// remove axis if dragged all the way left
396456
if (me.state.dragging.pos < 12 || me.state.dragging.pos > me.w-12) {
@@ -458,20 +518,42 @@ export class ParallelPlot extends React.Component<ParallelPlotData, ParallelPlot
458518
if (!me.props.context_menu_ref || !me.props.context_menu_ref.current) {
459519
return;
460520
}
521+
event.preventDefault();
522+
event.stopPropagation();
523+
(target as any).__longPressFired = false;
461524
const touch = (event as TouchEvent).touches[0];
462525
const timer = window.setTimeout(() => {
463526
me.props.context_menu_ref.current.show(touch.pageX, touch.pageY, dim);
527+
(target as any).__longPressFired = true;
464528
(target as any).__longPressTimer = null;
465529
}, 500);
466530
(target as any).__longPressTimer = timer;
467531
})
468-
.on("touchend touchcancel touchmove", function() {
532+
.on("touchend touchcancel touchmove", function(event, dim: string) {
469533
const target = this as SVGTextElement;
470534
const timer = (target as any).__longPressTimer;
471535
if (timer) {
472536
window.clearTimeout(timer);
473537
(target as any).__longPressTimer = null;
474538
}
539+
if ((target as any).__longPressFired) {
540+
event.preventDefault();
541+
event.stopPropagation();
542+
return;
543+
}
544+
if (event.type !== "touchend") {
545+
return;
546+
}
547+
const isDragging = me.state.dragging && me.state.dragging.dragging;
548+
if (isDragging) {
549+
return;
550+
}
551+
if (me.props.context_menu_ref && me.props.context_menu_ref.current) {
552+
const touch = (event as TouchEvent).changedTouches[0];
553+
me.props.context_menu_ref.current.show(touch.pageX, touch.pageY, dim);
554+
event.preventDefault();
555+
event.stopPropagation();
556+
}
475557
});
476558
me.updateAxisTitlesAnglesAndFontSize();
477559
me.dimensions_dom.selectAll(".pplot-label").call(create_drag_beh());
@@ -491,45 +573,8 @@ export class ParallelPlot extends React.Component<ParallelPlotData, ParallelPlot
491573
.text("Drag or resize this filter");
492574
};
493575

494-
function invert_axis(d: string) {
495-
// save extent before inverting
496-
var extents = brush_extends();
497-
var extent = extents[d] !== null ? [me.h - extents[d][1], me.h - extents[d][0]] : null;
498-
499-
if (me.state.invert.has(d)) {
500-
me.setState(function(prevState, props) {
501-
var newInvert = new Set(prevState.invert);
502-
newInvert.delete(d);
503-
return {
504-
invert: newInvert
505-
};
506-
});
507-
me.setScaleRange(d);
508-
div.selectAll("." + style.label)
509-
.filter(function(p) { return p == d; })
510-
.style("text-decoration", null);
511-
} else {
512-
me.setState(function(prevState, props) {
513-
var newInvert = new Set(prevState.invert);
514-
newInvert.add(d);
515-
return {
516-
invert: newInvert
517-
};
518-
});
519-
me.setScaleRange(d);
520-
div.selectAll("." + style.label)
521-
.filter(function(p) { return p == d; })
522-
.style("text-decoration", "underline");
523-
}
524-
return extent;
525-
}
526-
527576
function brush_extends() {
528-
var extents = {};
529-
me.dimensions_dom.selectAll("." + style.brush).each(function(this: SVGGElement, dim: string) {
530-
extents[dim] = d3.brushSelection(this);
531-
});
532-
return extents;
577+
return me.getBrushExtents();
533578
}
534579

535580
function brush() {
@@ -539,7 +584,7 @@ export class ParallelPlot extends React.Component<ParallelPlotData, ParallelPlot
539584
if (me.props.context_menu_ref !== undefined) {
540585
me.props.context_menu_ref.current.hide();
541586
}
542-
var extents = brush_extends();
587+
const extents: {[key: string]: [number, number] | null} = brush_extends();
543588
var actives = me.state.dimensions.filter(function(p) { return extents[p] !== null && extents[p] !== undefined; });
544589

545590
// hack to hide ticks beyond extent
@@ -580,7 +625,7 @@ export class ParallelPlot extends React.Component<ParallelPlotData, ParallelPlot
580625
// Get lines within extents
581626
var filters: Array<Filter> = actives.map(function(dimension) {
582627
const scale = me.yscale[dimension];
583-
var extent = extents[dimension];
628+
const extent = extents[dimension] as [number, number];
584629
const range = scale_pixels_range(scale, extent);
585630
if (range.type == ParamType.CATEGORICAL && !range.values) {
586631
// Select nothing
@@ -632,15 +677,15 @@ export class ParallelPlot extends React.Component<ParallelPlotData, ParallelPlot
632677
.forEach(function(d) {
633678
if (actives.every(function(dimension) {
634679
var scale = me.yscale[dimension];
635-
var extent = extents[dimension];
680+
var extent = extents[dimension] as [number, number];
636681
var value = d[dimension];
637682
return extent[0] + 1 <= scale(value) && scale(value) <= extent[1] - 1;
638683
})) {
639684
selected_pixels_minset.push(d);
640685
}
641686
if (actives.every(function(dimension) {
642687
var scale = me.yscale[dimension];
643-
var extent = extents[dimension];
688+
var extent = extents[dimension] as [number, number];
644689
var value = d[dimension];
645690
return extent[0] - 1 <= scale(value) && scale(value) <= extent[1] + 1;
646691
})) {

0 commit comments

Comments
 (0)