Skip to content
This repository was archived by the owner on Mar 12, 2024. It is now read-only.

Commit 074826a

Browse files
authored
Hack-fix labels on safari when scrolling (#222)
Co-authored-by: danthe3rd <[email protected]>
1 parent c7f0cd4 commit 074826a

File tree

4 files changed

+49
-6
lines changed

4 files changed

+49
-6
lines changed

src/component.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { ErrorDisplay, HeaderBar } from "./header";
2121
import { HiPlotPluginData, DataProviderClass } from "./plugin";
2222
import { StaticDataProvider } from "./dataproviders/static";
2323
import { uncompress } from "./lib/compress";
24-
24+
import { setupBrowserCompat } from "./lib/browsercompat";
2525

2626
//@ts-ignore
2727
import LogoSVG from "../hiplot/static/logo.svg";
@@ -152,6 +152,7 @@ export function createDefaultPlugins(): PluginsMap {
152152
export class HiPlot extends React.Component<HiPlotProps, HiPlotState> {
153153
// React refs
154154
contextMenuRef = React.createRef<ContextMenu>();
155+
rootRef = React.createRef<HTMLDivElement>();
155156

156157
plugins_window_state: {[plugin: string]: any} = {};
157158

@@ -322,6 +323,8 @@ export class HiPlot extends React.Component<HiPlotProps, HiPlotState> {
322323
this.callFilteredUidsHooks.cancel();
323324
}
324325
componentDidMount() {
326+
setupBrowserCompat(this.rootRef.current);
327+
325328
// Setup contextmenu when we right-click a parameter
326329
this.contextMenuRef.current.addCallback(this.columnContextMenu.bind(this), this);
327330

@@ -532,7 +535,7 @@ export class HiPlot extends React.Component<HiPlotProps, HiPlotState> {
532535
};
533536
}.bind(this);
534537
return (
535-
<div className={`hip_thm--${this.state.dark ? "dark" : "light"}`}>
538+
<div ref={this.rootRef} className={`hip_thm--${this.state.dark ? "dark" : "light"}`}>
536539
<div className={style.hiplot}>
537540
<SelectedCountProgressBar {...controlProps} />
538541
<HeaderBar

src/lib/browsercompat.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
export const IS_SAFARI = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
3+
4+
export function redrawObject(fo: SVGForeignObjectElement) {
5+
const parent = fo.parentNode;
6+
parent.removeChild(fo);
7+
parent.appendChild(fo);
8+
9+
}
10+
export function redrawAllForeignObjectsIfSafari() {
11+
if (!IS_SAFARI) {
12+
return;
13+
}
14+
const fo = document.getElementsByTagName("foreignObject");
15+
Array.from(fo).forEach(redrawObject);
16+
}
17+
18+
export function setupBrowserCompat(root: HTMLDivElement) {
19+
/**
20+
* Safari has a lot of trouble with foreignObjects inside canvas. Especially when we apply rotations, etc...
21+
* As it considers the parent of the objects inside the FO to be the canvas origin, and not the FO.
22+
* See https://stackoverflow.com/questions/51313873/svg-foreignobject-not-working-properly-on-safari
23+
* Applying the fix in the link above fixes their position upon scroll - we don't want that, so we
24+
* manually force-redraw them upon scroll.
25+
*/
26+
if (IS_SAFARI) {
27+
root.addEventListener("wheel", redrawAllForeignObjectsIfSafari);
28+
}
29+
}

src/lib/svghelpers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { ParamDef } from "../infertypes";
1111
import style from "../hiplot.scss";
1212
import { ContextMenu } from "../contextmenu";
1313

14+
1415
function leftPos(anchor: string, w: number, minmax?: [number, number]): number {
1516
var left = {
1617
end: -w,
@@ -51,8 +52,7 @@ export function foCreateAxisLabel(pd: ParamDef, cm?: React.RefObject<ContextMenu
5152
var fo = document.createElementNS('http://www.w3.org/2000/svg',"foreignObject");
5253
const span = d3.select(fo).append("xhtml:div")
5354
.classed(style.tooltipContainer, true)
54-
.classed(style.label, true)
55-
.style("position", "fixed"); // BUGFIX for transforms in Safari (https://stackoverflow.com/questions/51313873/svg-foreignobject-not-working-properly-on-safari)
55+
.classed(style.label, true);
5656
span.append("xhtml:span")
5757
.attr("class", pd.label_css)
5858
.classed("label-name", true)

src/parallel/parallel.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { HiPlotPluginData } from "../plugin";
2020
import { ResizableH } from "../lib/resizable";
2121
import { Filter, FilterType, apply_filters } from "../filters";
2222
import { foDynamicSizeFitContent, foCreateAxisLabel } from "../lib/svghelpers";
23+
import { IS_SAFARI, redrawAllForeignObjectsIfSafari } from "../lib/browsercompat";
2324

2425
interface StringMapping<V> { [key: string]: V; };
2526

@@ -152,10 +153,11 @@ export class ParallelPlot extends React.Component<ParallelPlotData, ParallelPlot
152153
this.xscale.domain(this.state.dimensions);
153154
this.dimensions_dom.filter(function(this: ParallelPlot, p) { return this.state.dimensions.indexOf(p) == -1; }.bind(this)).remove();
154155
this.dimensions_dom = this.dimensions_dom.filter(function(this: ParallelPlot, p) { return this.state.dimensions.indexOf(p) !== -1; }.bind(this));
155-
if (!this.state.dragging) {
156+
if (!this.state.dragging && !IS_SAFARI) {
156157
g = g.transition();
157158
}
158159
g.attr("transform", function(this: ParallelPlot, p) { return "translate(" + this.position(p) + ")"; }.bind(this));
160+
redrawAllForeignObjectsIfSafari();
159161
this.update_ticks();
160162
this.updateAxisTitlesAnglesAndFontSize();
161163
}
@@ -349,14 +351,19 @@ export class ParallelPlot extends React.Component<ParallelPlotData, ParallelPlot
349351
me.setState({dimensions: new_dimensions});
350352
}
351353
me.dimensions_dom.attr("transform", function(d) { return "translate(" + me.position(d) + ")"; });
354+
redrawAllForeignObjectsIfSafari();
352355
})
353356
.on("end", function(d: string) {
354357
if (!me.state.dragging.dragging) {
355358
// no movement, invert axis
356359
var extent = invert_axis(d);
357360
} else {
358361
// reorder axes
359-
d3.select(this).transition().attr("transform", "translate(" + me.xscale(d) + ")");
362+
var drag: any = d3.select(this);
363+
if (!IS_SAFARI) {
364+
drag = drag.transition();
365+
}
366+
drag.attr("transform", "translate(" + me.xscale(d) + ")");
360367
var extents = brush_extends();
361368
extent = extents[d];
362369
}
@@ -612,6 +619,7 @@ export class ParallelPlot extends React.Component<ParallelPlotData, ParallelPlot
612619
.each(function(d: string) {
613620
d3.select(this).call(me.axis.scale(me.yscale[d]));
614621
});
622+
me.updateAxisTitlesAnglesAndFontSize();
615623

616624
// render data
617625
this.setState(function(prevState) { return { brush_count: prevState.brush_count + 1}; });
@@ -682,6 +690,9 @@ export class ParallelPlot extends React.Component<ParallelPlotData, ParallelPlot
682690
));
683691
this.style.fontSize = newFontSize + "px";
684692
this.style.transform = "rotate(" + (360 - ROTATION_ANGLE_RADS * 180 / Math.PI) + "deg)";
693+
if (IS_SAFARI) {
694+
this.parentElement.style.position = "fixed";
695+
}
685696
const fo = this.parentElement.parentElement as any as SVGForeignObjectElement;
686697
fo.setAttribute("y", -newFontSize + "");
687698
});

0 commit comments

Comments
 (0)