Fixed the node labels bug on Firefox.

This commit is contained in:
Filip Barl
2017-02-03 17:06:53 +01:00
parent c9ff15f7f3
commit 2b4ba3239c
11 changed files with 55 additions and 55 deletions
+3 -4
View File
@@ -4,7 +4,7 @@ import { connect } from 'react-redux';
import { Motion, spring } from 'react-motion';
import { NODES_SPRING_ANIMATION_CONFIG } from '../constants/animation';
import { NODE_BASE_SIZE, NODE_BLUR_OPACITY } from '../constants/styles';
import { NODE_BLUR_OPACITY } from '../constants/styles';
import Node from './node';
const transformedNode = (otherProps, { x, y, k }) => (
@@ -13,10 +13,9 @@ const transformedNode = (otherProps, { x, y, k }) => (
class NodeContainer extends React.Component {
render() {
const { dx, dy, isAnimated, magnified, blurred } = this.props;
const forwardedProps = omit(this.props, 'dx', 'dy', 'isAnimated', 'magnified', 'blurred');
const { dx, dy, isAnimated, scale, blurred } = this.props;
const forwardedProps = omit(this.props, 'dx', 'dy', 'isAnimated', 'scale', 'blurred');
const opacity = blurred ? NODE_BLUR_OPACITY : 1;
const scale = magnified * NODE_BASE_SIZE;
// NOTE: Controlling blurring from here seems to re-render faster
// than adding a CSS class and controlling it from there.
@@ -3,15 +3,17 @@ import { scaleBand } from 'd3-scale';
import { List as makeList } from 'immutable';
import { getNetworkColor } from '../utils/color-utils';
import { isContrastMode } from '../utils/contrast-utils';
import { NODE_BASE_SIZE } from '../constants/styles';
// Min size is about a quarter of the width, feels about right.
const minBarWidth = 0.25;
const barHeight = 0.08;
const innerPadding = 0.04;
const borderRadius = 0.01;
const offset = 0.67;
const x = scaleBand();
function NodeNetworksOverlay({offset, stack, networks = makeList()}) {
function NodeNetworksOverlay({ stack, networks = makeList() }) {
const barWidth = Math.max(1, minBarWidth * networks.size);
const yPosition = offset - (barHeight * 0.5);
@@ -37,7 +39,7 @@ function NodeNetworksOverlay({offset, stack, networks = makeList()}) {
const translateY = stack && isContrastMode() ? 0.15 : 0;
return (
<g transform={`translate(0, ${translateY})`}>
<g transform={`translate(0, ${translateY}) scale(${NODE_BASE_SIZE})`}>
{bars.toJS()}
</g>
);
@@ -4,17 +4,18 @@ import {
NODE_SHAPE_BORDER_RADIUS,
NODE_SHAPE_SHADOW_RADIUS,
NODE_SHAPE_DOT_RADIUS,
NODE_BASE_SIZE,
} from '../constants/styles';
// This path is already normalized so no rescaling is needed.
const CLOUD_PATH = 'M-1.25 0.233Q-1.25 0.44-1.104 0.587-0.957 0.733-0.75 0.733H0.667Q0.908 '
+ '0.733 1.079 0.562 1.25 0.391 1.25 0.15 1.25-0.022 1.158-0.164 1.065-0.307 0.914-0.377q'
+ '0.003-0.036 0.003-0.056 0-0.276-0.196-0.472-0.195-0.195-0.471-0.195-0.206 0-0.373 0.115'
+ '-0.167 0.115-0.244 0.299-0.091-0.081-0.216-0.081-0.138 0-0.236 0.098-0.098 0.098-0.098 '
+ '0.236 0 0.098 0.054 0.179-0.168 0.039-0.278 0.175-0.109 0.136-0.109 0.312z';
// This path is already normalized so no dynamic rescaling is needed.
const CLOUD_PATH = 'M-125 23.333Q-125 44.036-110.352 58.685-95.703 73.333-75 73.333H66.667Q90.755 '
+ '73.333 107.878 56.211 125 39.089 125 15 125-2.188 115.755-16.445 106.51-30.703 91.406-37.734q'
+ '0.26-3.646 0.261-5.599 0-27.604-19.532-47.136-19.531-19.531-47.135-19.531-20.573 0-37.305 '
+ '11.458-16.732 11.458-24.414 29.948-9.115-8.073-21.614-8.073-13.802 0-23.568 9.766-9.766 9.766-'
+ '9.766 23.568 0 9.766 5.339 17.968-16.797 3.906-27.735 17.513-10.938 13.607-10.937 31.185z';
export default function NodeShapeCloud({highlighted, color}) {
const pathProps = r => ({ d: CLOUD_PATH, transform: `scale(${r})` });
const pathProps = r => ({ d: CLOUD_PATH, transform: `scale(${r / NODE_BASE_SIZE})` });
return (
<g className="shape shape-cloud">
+18 -16
View File
@@ -7,6 +7,7 @@ import { clickNode, enterNode, leaveNode } from '../actions/app-actions';
import { getNodeColor } from '../utils/color-utils';
import MatchedText from '../components/matched-text';
import MatchedResults from '../components/matched-results';
import { NODE_BASE_SIZE } from '../constants/styles';
import NodeShapeCircle from './node-shape-circle';
import NodeShapeStack from './node-shape-stack';
@@ -17,11 +18,7 @@ import NodeShapeCloud from './node-shape-cloud';
import NodeNetworksOverlay from './node-networks-overlay';
function stackedShape(Shape) {
const factory = React.createFactory(NodeShapeStack);
return props => factory(Object.assign({}, props, {shape: Shape}));
}
const labelWidth = 1.4 * NODE_BASE_SIZE;
const nodeShapes = {
circle: NodeShapeCircle,
hexagon: NodeShapeHexagon,
@@ -30,6 +27,11 @@ const nodeShapes = {
cloud: NodeShapeCloud
};
function stackedShape(Shape) {
const factory = React.createFactory(NodeShapeStack);
return props => factory(Object.assign({}, props, {shape: Shape}));
}
function getNodeShape({ shape, stack }) {
const nodeShape = nodeShapes[shape];
if (!nodeShape) {
@@ -65,9 +67,9 @@ class Node extends React.Component {
renderSvgLabels(labelClassName, subLabelClassName, labelOffsetY) {
const { label, subLabel } = this.props;
return (
<g className="node-labels-container" y={labelOffsetY}>
<text className={labelClassName} y={13} textAnchor="middle">{label}</text>
<text className={subLabelClassName} y={30} textAnchor="middle">
<g className="node-labels-container">
<text className={labelClassName} y={13 + labelOffsetY} textAnchor="middle">{label}</text>
<text className={subLabelClassName} y={30 + labelOffsetY} textAnchor="middle">
{subLabel}
</text>
</g>
@@ -81,7 +83,12 @@ class Node extends React.Component {
const matchedNodeDetails = matchedMetadata.concat(matchedParents);
return (
<foreignObject className="node-labels-container" y={labelOffsetY}>
<foreignObject
className="node-labels-container"
y={labelOffsetY}
x={-0.5 * labelWidth}
width={labelWidth}
height="5em">
<div className="node-label-wrapper" {...mouseEvents}>
<div className={labelClassName}>
<MatchedText text={label} match={matches.get('label')} />
@@ -103,7 +110,6 @@ class Node extends React.Component {
const color = getNodeColor(rank, label, pseudo);
const truncate = !focused && !hovered;
const labelOffsetY = (showingNetworks && networks) ? 40 : 28;
const networkOffset = 0.67;
const nodeClassName = classnames('node', {
highlighted,
@@ -126,7 +132,7 @@ class Node extends React.Component {
return (
<g className={nodeClassName} transform={transform}>
{useSvgLabels || false ?
{useSvgLabels ?
this.renderSvgLabels(labelClassName, subLabelClassName, labelOffsetY) :
this.renderStandardLabels(labelClassName, subLabelClassName, labelOffsetY, mouseEvents)}
@@ -134,11 +140,7 @@ class Node extends React.Component {
<NodeShapeType color={color} {...this.props} />
</g>
{showingNetworks && <NodeNetworksOverlay
offset={networkOffset}
networks={networks}
stack={stack}
/>}
{showingNetworks && <NodeNetworksOverlay networks={networks} stack={stack} />}
</g>
);
}
@@ -71,7 +71,7 @@ class NodesChartNodes extends React.Component {
metric={metric(node)}
rank={node.get('rank')}
isAnimated={isAnimated}
magnified={node.get('focused') ? selectedScale : 1}
scale={node.get('focused') ? selectedScale : 1}
dx={node.get('x')}
dy={node.get('y')}
/>)}
+11 -11
View File
@@ -9,8 +9,6 @@ export const DETAILS_PANEL_MARGINS = {
export const DETAILS_PANEL_OFFSET = 8;
export const CANVAS_METRIC_FONT_SIZE = 0.19;
export const CANVAS_MARGINS = {
top: 160,
left: 40,
@@ -19,16 +17,18 @@ export const CANVAS_MARGINS = {
};
// Node shapes
export const NODE_SHAPE_HIGHLIGHT_RADIUS = 0.7;
export const NODE_SHAPE_BORDER_RADIUS = 0.5;
export const NODE_SHAPE_SHADOW_RADIUS = 0.45;
export const NODE_SHAPE_DOT_RADIUS = 0.125;
export const NODE_SHAPE_HIGHLIGHT_RADIUS = 70;
export const NODE_SHAPE_BORDER_RADIUS = 50;
export const NODE_SHAPE_SHADOW_RADIUS = 45;
export const NODE_SHAPE_DOT_RADIUS = 10;
export const NODE_BLUR_OPACITY = 0.2;
// NOTE: Modifying this value shouldn't actually change much in the way
// nodes are rendered, as long as its kept >> 1. The idea was to draw all
// the nodes in a unit scale and control their size just through scaling
// transform, but the problem is that dagre only works with integer coordinates,
// so this constant basically serves as a precision factor for dagre.
// NOTE: This value represents the node unit radius (in pixels). Since zooming is
// controlled at the top level now, this renormalization would be obsolete (i.e.
// value 1 could be used instead), if it wasn't for the following factors:
// 1. `dagre` library only works with integer coordinates,
// so >> 1 value is used to increase layout precision.
// 2. Fonts don't behave nicely (especially on Firefox) if they
// are given on a small unit scale as foreign objects in SVG.
export const NODE_BASE_SIZE = 100;
// Node details table constants
@@ -48,9 +48,9 @@ const defaultZoomSelector = createSelector(
// Maximal allowed zoom will always be such that a node covers 1/5 of the viewport.
const maxZoomScale = Math.min(width, height) / NODE_BASE_SIZE / 5;
// Initial zoom is such that the graph covers 90% of either
// the viewport, respecting the maximal zoom constraint.
const zoomScale = Math.min(xFactor, yFactor, maxZoomScale) * 0.9;
// Initial zoom is such that the graph covers 90% of either the viewport,
// or one half of maximal zoom constraint, whichever is smaller.
const zoomScale = Math.min(xFactor, yFactor, maxZoomScale / 2) * 0.9;
// Finally, we always allow zooming out exactly 5x compared to the initial zoom.
const minZoomScale = zoomScale / 5;
+2 -2
View File
@@ -2,14 +2,14 @@ import { includes } from 'lodash';
import { scaleLog } from 'd3-scale';
import React from 'react';
import { NODE_SHAPE_DOT_RADIUS } from '../constants/styles';
import { NODE_BASE_SIZE, NODE_SHAPE_DOT_RADIUS } from '../constants/styles';
import { formatMetricSvg } from './string-utils';
import { colors } from './color-utils';
export function getClipPathDefinition(clipId, height) {
return (
<defs>
<clipPath id={clipId}>
<clipPath id={clipId} transform={`scale(${NODE_BASE_SIZE})`}>
<rect width={1} height={1} x={-0.5} y={0.5 - height} />
</clipPath>
</defs>
-3
View File
@@ -319,9 +319,6 @@
.node-labels-container {
transform: scale($node-text-scale);
pointer-events: none;
height: 5em;
x: -0.5 * $node-labels-max-width;
width: $node-labels-max-width;
}
.node-label-wrapper {
+2 -2
View File
@@ -14,8 +14,8 @@ $white: white;
$node-opacity-blurred: 0.6;
$node-highlight-fill-opacity: 0.3;
$node-highlight-stroke-opacity: 0.5;
$node-highlight-stroke-width: 0.06;
$node-border-stroke-width: 0.1;
$node-highlight-stroke-width: 8;
$node-border-stroke-width: 10;
$node-pseudo-opacity: 1;
$edge-highlight-opacity: 0.3;
$edge-opacity-blurred: 0;
+3 -4
View File
@@ -33,11 +33,10 @@ $terminal-header-height: 44px;
$node-opacity-blurred: 0.25;
$node-highlight-fill-opacity: 0.1;
$node-highlight-stroke-opacity: 0.4;
$node-highlight-stroke-width: 0.02;
$node-border-stroke-width: 0.06;
$node-highlight-stroke-width: 2;
$node-border-stroke-width: 6;
$node-pseudo-opacity: 0.8;
$node-text-scale: 0.02;
$node-labels-max-width: 120px;
$node-text-scale: 2;
$edge-highlight-opacity: 0.1;
$edge-opacity-blurred: 0.2;
$edge-opacity: 0.5;