mirror of
https://github.com/xodio/xod.git
synced 2026-03-23 00:56:54 +01:00
feat(xod-client): new patch board look with bound values
This commit is contained in:
@@ -91,7 +91,7 @@ describe('creating blink patch', () => {
|
||||
await clockNode.drag(150, 10);
|
||||
|
||||
const { x, y } = await clockNode.getBoundingClientRect();
|
||||
assert.deepEqual({ x, y }, { x: 389, y: 114 });
|
||||
assert.deepEqual({ x, y }, { x: 399.5, y: 128.5 });
|
||||
});
|
||||
|
||||
it('adds rest of the nodes needed for blink patch', async () => {
|
||||
|
||||
@@ -19,7 +19,7 @@ $light-grey: #777;
|
||||
$light-grey-bright: #999;
|
||||
|
||||
$chalk: #ccc;
|
||||
$chalk-light: #aaa;
|
||||
$chalk-light: #ddd;
|
||||
$chalk-bright: #eee;
|
||||
|
||||
// Colored
|
||||
|
||||
@@ -14,7 +14,7 @@ $color-success: #579205;
|
||||
$color-canvas-background: $dark-deep;
|
||||
$color-canvas-background-gridlines: $dark-light;
|
||||
$color-canvas-face: #646464;
|
||||
$color-canvas-face-light: #A7A7A7;
|
||||
$color-canvas-face-light: $chalk-light;
|
||||
$color-canvas-face-text: #a7a7a7;
|
||||
$color-canvas-selected: #4ed5ed;
|
||||
$color-highlight: #eeeeee;
|
||||
@@ -30,7 +30,7 @@ $color-datatype-custom: $terracotta;
|
||||
$color-datatype-dead: $error;
|
||||
|
||||
$color-node-fill: #222222;
|
||||
$color-node-outline: #bbbbbb;
|
||||
$color-node-outline: $light-grey-bright;
|
||||
$color-pin-fill: #262626;
|
||||
|
||||
$color-watch-node-text: $color-canvas-face-text;
|
||||
@@ -44,7 +44,7 @@ $color-watch-node-highlight-outline: #bce88e;
|
||||
$color-comment-text: #d0ccc0;
|
||||
$color-comment-handle: #605f5b;
|
||||
|
||||
$color-pinlabel: $color-canvas-face-text; //#8b8b8b;
|
||||
$color-pinlabel: $grey-bright;
|
||||
|
||||
$color-toolbar-background: #b3b3b3;
|
||||
$color-toolbar-foreground: #000;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
.outline {
|
||||
fill: transparent;
|
||||
stroke-width: 2px;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
.container {
|
||||
|
||||
@@ -199,7 +199,7 @@
|
||||
.PinIcon {
|
||||
pointer-events: none;
|
||||
float: left;
|
||||
margin-top: 4px;
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
label {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
.outline {
|
||||
fill: transparent;
|
||||
stroke: $color-node-outline;
|
||||
stroke-width: 2px;
|
||||
stroke-width: 1px;
|
||||
|
||||
// For terminals:
|
||||
&.string { stroke: $color-datatype-string; }
|
||||
@@ -76,6 +76,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
.terminal-node, .bus-node {
|
||||
.outline {
|
||||
stroke-width: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.jumper-line {
|
||||
stroke: $color-node-outline;
|
||||
stroke-width: 2px;
|
||||
@@ -117,7 +123,7 @@
|
||||
|
||||
// move from under pins
|
||||
// leave 1px margin on the sides to avoid "gluing" of two nearest labels
|
||||
margin: 12px 1px;
|
||||
margin: 11px 1px;
|
||||
height: calc(100% - 24px);
|
||||
}
|
||||
|
||||
@@ -125,9 +131,10 @@
|
||||
display: block;
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
font-size: 12px;
|
||||
font-family: $font-family-condensed;
|
||||
color: $color-canvas-face-text;
|
||||
font-size: 13px;
|
||||
font-family: $font-family-normal;
|
||||
font-weight: 400;
|
||||
color: $color-canvas-face-light;
|
||||
white-space: pre;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -144,11 +151,11 @@
|
||||
}
|
||||
|
||||
.nodeLabel {
|
||||
color: $color-highlight;
|
||||
color: $color-canvas-face-light;
|
||||
}
|
||||
|
||||
.VariadicHandle--grip {
|
||||
stroke: $color-highlight;
|
||||
fill: $color-highlight;
|
||||
}
|
||||
|
||||
.NodeResizeHandle {
|
||||
@@ -173,7 +180,7 @@
|
||||
}
|
||||
|
||||
.VariadicHandle--grip {
|
||||
stroke: $color-canvas-selected !important;
|
||||
fill: $color-canvas-selected !important;
|
||||
}
|
||||
|
||||
.NodeResizeHandle {
|
||||
@@ -185,9 +192,12 @@
|
||||
pointer-events: none;
|
||||
opacity: 0.3;
|
||||
|
||||
.outline, .VariadicHandle--grip {
|
||||
.outline {
|
||||
stroke: $color-node-outline !important;
|
||||
}
|
||||
.VariadicHandle--grip {
|
||||
fill: $color-node-outline !important;
|
||||
}
|
||||
.nodeLabel {
|
||||
visibility: hidden;
|
||||
}
|
||||
@@ -201,7 +211,7 @@
|
||||
color: $color-highlight;
|
||||
}
|
||||
.VariadicHandle--grip {
|
||||
stroke: $color-highlight;
|
||||
fill: $color-highlight;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,8 +233,7 @@
|
||||
}
|
||||
&--grip {
|
||||
pointer-events: none;
|
||||
stroke-width: 5px;
|
||||
stroke-linecap: round;
|
||||
stroke: $color-node-outline;
|
||||
fill: $color-node-outline;
|
||||
stroke-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ $PinInfo-padding-bottom: 10px;
|
||||
min-height: $node-height;
|
||||
|
||||
&--no-inputs {
|
||||
min-height: 75px;
|
||||
min-height: 77px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
.symbol {
|
||||
fill: $color-pin-fill;
|
||||
stroke: $color-datatype-custom;
|
||||
stroke-width: 2px;
|
||||
stroke-width: 1.5px;
|
||||
|
||||
&.t1, &.t2, &.t3 {
|
||||
@include symbol-coloring($color-datatype-generic);
|
||||
@@ -71,9 +71,12 @@
|
||||
}
|
||||
|
||||
.generic-pin-marker {
|
||||
fill: none;
|
||||
stroke: $color-datatype-generic;
|
||||
stroke-width: 2px;
|
||||
fill: $color-datatype-generic;
|
||||
stroke-width: 0;
|
||||
}
|
||||
|
||||
.hotspotHelper {
|
||||
fill: transparent;
|
||||
}
|
||||
|
||||
.linkingHighlight {
|
||||
@@ -100,7 +103,7 @@
|
||||
}
|
||||
|
||||
.PinOverlay {
|
||||
.symbol {
|
||||
.linkingHighlight {
|
||||
fill: white;
|
||||
stroke: white;
|
||||
stroke-width: 2px;
|
||||
@@ -108,14 +111,13 @@
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.symbol {
|
||||
.linkingHighlight {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.linkingHighlight {
|
||||
display: none;
|
||||
opacity: 0; // just acts as a hotspot
|
||||
.hotspot {
|
||||
fill: transparent;
|
||||
}
|
||||
|
||||
&.is-accepting-links, &.is-selected {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
.PinLabel {
|
||||
font-family: $font-family-condensed;
|
||||
font-weight: 500;
|
||||
font-size: 10px;
|
||||
dominant-baseline: central;
|
||||
fill: $color-pinlabel;
|
||||
font-size: 11px;
|
||||
color: $color-pinlabel;
|
||||
user-select: none;
|
||||
|
||||
&.outline {
|
||||
stroke: #262626;
|
||||
stroke-linejoin: round;
|
||||
stroke-width: 5;
|
||||
}
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
24
packages/xod-client/src/core/styles/components/PinValue.scss
Normal file
24
packages/xod-client/src/core/styles/components/PinValue.scss
Normal file
@@ -0,0 +1,24 @@
|
||||
.PinValue {
|
||||
font-family: $font-family-condensed;
|
||||
font-weight: 400;
|
||||
font-size: 11px;
|
||||
color: $color-datatype-generic;
|
||||
user-select: none;
|
||||
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
line-height: 1;
|
||||
|
||||
// For terminals:
|
||||
&.string { color: $color-datatype-string; }
|
||||
&.number { color: $color-datatype-number; }
|
||||
&.boolean { color: $color-datatype-boolean; }
|
||||
&.pulse { color: $color-datatype-pulse; }
|
||||
&.byte { color: $color-datatype-byte; }
|
||||
&.port { color: $color-datatype-port; }
|
||||
&.dead, &.conflicting { color: $color-datatype-dead; }
|
||||
&.self, &.custom { color: $color-datatype-custom; }
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
margin: 7px 0;
|
||||
|
||||
.propertyLabel {
|
||||
color: $color-canvas-face-light;
|
||||
color: $color-canvas-face-text;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
'components/PatchWrapper',
|
||||
'components/Pin',
|
||||
'components/PinLabel',
|
||||
'components/PinValue',
|
||||
'components/PopupPublishProject',
|
||||
'components/ProjectBrowser',
|
||||
'components/Sidebar',
|
||||
|
||||
@@ -12,11 +12,11 @@ import { SLOT_SIZE } from '../../project/nodeLayout';
|
||||
|
||||
const NODE_POSITION_IN_PREVIEW = {
|
||||
x: 3,
|
||||
y: 20, // compensate for labels outside the node
|
||||
y: 10, // compensate for bound values outside the node
|
||||
};
|
||||
|
||||
const MAX_NODE_WIDTH = 245 - NODE_POSITION_IN_PREVIEW.x * 2;
|
||||
const NODE_PREVIEW_HEIGHT = 93;
|
||||
const NODE_PREVIEW_HEIGHT = 85;
|
||||
|
||||
const formatPinType = R.when(XP.isGenericType, type => `generic ${type}`);
|
||||
const getPinTypeClassName = R.when(XP.isGenericType, R.always('generic'));
|
||||
@@ -152,13 +152,6 @@ const PatchDocs = ({ patch, minimal }) => {
|
||||
|
||||
const distanceBetweenPins = minimal ? 0 : scaleFactor * SLOT_SIZE.WIDTH - 1;
|
||||
|
||||
// because we never draw labels for terminal nodes
|
||||
const position = R.when(
|
||||
() => XP.isTerminalPatchPath(nodeType),
|
||||
R.assoc('y', XP.isInputTerminalPath(nodeType) ? 32 : 8),
|
||||
NODE_POSITION_IN_PREVIEW
|
||||
);
|
||||
|
||||
const deprecatedReason = XP.getDeprecationReason(patch);
|
||||
|
||||
const cls = cn('PatchDocs', {
|
||||
@@ -200,10 +193,16 @@ const PatchDocs = ({ patch, minimal }) => {
|
||||
width={scaledNodeWidth}
|
||||
height={scaledNodePreviewHeight}
|
||||
>
|
||||
<rect className="bg" width="100%" height="100%" />
|
||||
<g transform={`scale(${scaleFactor})`}>
|
||||
<Node {...nodeProps} position={position} noEvents />
|
||||
</g>
|
||||
<svg x="0.5" y="0.5">
|
||||
<rect className="bg" width="100%" height="100%" />
|
||||
<g transform={`scale(${scaleFactor})`}>
|
||||
<Node
|
||||
{...nodeProps}
|
||||
position={NODE_POSITION_IN_PREVIEW}
|
||||
noEvents
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,7 @@ import * as XP from 'xod-project';
|
||||
|
||||
import Pin from './Pin';
|
||||
import PinLabel from './PinLabel';
|
||||
import PinValue from './PinValue';
|
||||
import { noop } from '../../utils/ramda';
|
||||
import { isPinSelected } from '../../editor/utils';
|
||||
|
||||
@@ -207,12 +208,11 @@ class Node extends React.Component {
|
||||
<g className={pinsCls} id={`nodePins_${id}`}>
|
||||
{pinsArr.map(pin => (
|
||||
<g key={pin.key}>
|
||||
{pin.isConnected || XP.isOutputPin(pin) ? null : (
|
||||
<PinValue {...pin} key={`pinValue_${pin.key}`} />
|
||||
)}
|
||||
{isTerminalNode ? null : (
|
||||
<PinLabel
|
||||
{...pin}
|
||||
keyName={pin.key}
|
||||
key={`pinlabel_${pin.key}`}
|
||||
/>
|
||||
<PinLabel {...pin} key={`pinlabel_${pin.key}`} />
|
||||
)}
|
||||
<Pin
|
||||
{...pin}
|
||||
|
||||
@@ -26,10 +26,13 @@ const PatchSVG = ({
|
||||
ref={svgRef}
|
||||
{...restProps}
|
||||
>
|
||||
<defs>
|
||||
<DraggedNodeShadowFilter />
|
||||
</defs>
|
||||
{children}
|
||||
{/* Nested svg to compensate bluring of strokes */}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" x="0.5" y="0.5">
|
||||
<defs>
|
||||
<DraggedNodeShadowFilter />
|
||||
</defs>
|
||||
{children}
|
||||
</svg>
|
||||
</svg>
|
||||
);
|
||||
|
||||
|
||||
@@ -12,35 +12,6 @@ import {
|
||||
PIN_HIGHLIGHT_RADIUS,
|
||||
} from '../nodeLayout';
|
||||
|
||||
/**
|
||||
* An outline of half of the circle, that indicated that Pin is generic
|
||||
* even it has a deduced type.
|
||||
*/
|
||||
// :: Number -> Number -> Boolean -> ReactElement
|
||||
const genericPinMarker = (pinCircleCenter, output) => {
|
||||
// To avoid using complex paths we draw just a circle
|
||||
// but draw only half of the outline by CSS.
|
||||
|
||||
// To do it with CSS we have to:
|
||||
// 1. Calculate the length of the circle (circumference).
|
||||
const circumference = Math.PI * PIN_RADIUS * 2;
|
||||
// 2. Calculate distance beetween dashes
|
||||
const dasharray = 0.5 * circumference;
|
||||
// 3. And then set `dasharray` to draw a half of outline
|
||||
// 4. And set `dashoffset` to draw another half (for output Pin)
|
||||
return (
|
||||
<circle
|
||||
className="generic-pin-marker"
|
||||
{...pinCircleCenter}
|
||||
r={PIN_RADIUS}
|
||||
style={{
|
||||
strokeDashoffset: output ? dasharray : 0,
|
||||
strokeDasharray: dasharray,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const Pin = props => {
|
||||
const isOutput = props.direction === PIN_DIRECTION.OUTPUT;
|
||||
|
||||
@@ -104,9 +75,9 @@ const Pin = props => {
|
||||
r={PIN_INNER_RADIUS}
|
||||
/>
|
||||
) : null}
|
||||
{isGenericType(props.type)
|
||||
? genericPinMarker(pinCircleCenter, isOutput)
|
||||
: null}
|
||||
{isGenericType(props.type) ? (
|
||||
<circle className="generic-pin-marker" {...pinCircleCenter} r={1} />
|
||||
) : null}
|
||||
{variadicDots}
|
||||
</g>
|
||||
);
|
||||
|
||||
@@ -1,36 +1,28 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { PIN_DIRECTION } from 'xod-project';
|
||||
|
||||
import { PIN_RADIUS, TEXT_OFFSET_FROM_PIN_BORDER } from '../nodeLayout';
|
||||
import { PINLABEL_WIDTH, getPinLabelProps } from '../nodeLayout';
|
||||
|
||||
const Pin = ({ keyName, label, direction, position }) => {
|
||||
const textVerticalOffset = PIN_RADIUS + TEXT_OFFSET_FROM_PIN_BORDER;
|
||||
const isInput = direction === PIN_DIRECTION.INPUT;
|
||||
|
||||
const textProps = {
|
||||
x: position.x,
|
||||
y: position.y + textVerticalOffset * (isInput ? -1 : 1),
|
||||
textAnchor: 'middle',
|
||||
};
|
||||
const PinLabel = ({ label, direction, position }) => {
|
||||
const textProps = getPinLabelProps(direction, position);
|
||||
|
||||
return label ? (
|
||||
<g key={`pinText_${keyName}`}>
|
||||
<text className="PinLabel outline" {...textProps}>
|
||||
<foreignObject {...textProps}>
|
||||
<div
|
||||
className="PinLabel"
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
style={{ width: PINLABEL_WIDTH }}
|
||||
>
|
||||
{label}
|
||||
</text>
|
||||
<text className="PinLabel" {...textProps}>
|
||||
{label}
|
||||
</text>
|
||||
</g>
|
||||
</div>
|
||||
</foreignObject>
|
||||
) : null;
|
||||
};
|
||||
|
||||
Pin.propTypes = {
|
||||
keyName: PropTypes.string.isRequired,
|
||||
PinLabel.propTypes = {
|
||||
label: PropTypes.string,
|
||||
direction: PropTypes.string.isRequired,
|
||||
position: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default Pin;
|
||||
export default PinLabel;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { PIN_RADIUS, PIN_HIGHLIGHT_RADIUS } from '../nodeLayout';
|
||||
import { PIN_HOTSPOT_RADIUS, PIN_HOVER_HIGHLIGHT_RADIUS } from '../nodeLayout';
|
||||
|
||||
import deepSCU from '../../utils/deepSCU';
|
||||
|
||||
@@ -45,9 +45,13 @@ export default class PinOverlay extends React.Component {
|
||||
<circle
|
||||
className="linkingHighlight"
|
||||
{...pinCircleCenter}
|
||||
r={PIN_HIGHLIGHT_RADIUS}
|
||||
r={PIN_HOVER_HIGHLIGHT_RADIUS}
|
||||
/>
|
||||
<circle
|
||||
className="hotspot"
|
||||
{...pinCircleCenter}
|
||||
r={PIN_HOTSPOT_RADIUS}
|
||||
/>
|
||||
<circle className="symbol" {...pinCircleCenter} r={PIN_RADIUS} />
|
||||
</g>
|
||||
);
|
||||
}
|
||||
|
||||
60
packages/xod-client/src/project/components/PinValue.jsx
Normal file
60
packages/xod-client/src/project/components/PinValue.jsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import cls from 'classnames';
|
||||
import { PIN_TYPE, INPUT_PULSE_PIN_BINDING_OPTIONS } from 'xod-project';
|
||||
import { unquote } from 'xod-func-tools';
|
||||
|
||||
import { PINVALUE_WIDTH, getPinValueProps } from '../nodeLayout';
|
||||
import { getRenderablePinType } from '../utils';
|
||||
|
||||
const formatPulsePinValue = value => {
|
||||
switch (value) {
|
||||
case INPUT_PULSE_PIN_BINDING_OPTIONS.CONTINUOUSLY:
|
||||
return 'Loop';
|
||||
case INPUT_PULSE_PIN_BINDING_OPTIONS.ON_BOOT:
|
||||
return 'Boot';
|
||||
default:
|
||||
case INPUT_PULSE_PIN_BINDING_OPTIONS.NEVER:
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
const formatPinValue = (type, value) => {
|
||||
switch (type) {
|
||||
case PIN_TYPE.STRING:
|
||||
return unquote(value);
|
||||
case PIN_TYPE.PULSE:
|
||||
return formatPulsePinValue(value);
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
const PinValue = ({ value, type, deducedType, direction, position }) => {
|
||||
const textProps = getPinValueProps(direction, position);
|
||||
|
||||
const pinType = getRenderablePinType({ type, deducedType });
|
||||
const className = cls('PinValue', pinType);
|
||||
|
||||
return value ? (
|
||||
<foreignObject {...textProps}>
|
||||
<div
|
||||
className={className}
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
style={{ width: PINVALUE_WIDTH }}
|
||||
>
|
||||
{formatPinValue(pinType, value)}
|
||||
</div>
|
||||
</foreignObject>
|
||||
) : null;
|
||||
};
|
||||
|
||||
PinValue.propTypes = {
|
||||
value: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
deducedType: PropTypes.object,
|
||||
direction: PropTypes.string.isRequired,
|
||||
position: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default PinValue;
|
||||
@@ -4,19 +4,32 @@ import { noop } from 'xod-func-tools';
|
||||
|
||||
import { SLOT_SIZE, NODE_HEIGHT } from '../../nodeLayout';
|
||||
|
||||
// Add 0.5 to compensate blurring of pattern
|
||||
const COMPENSATE_BLUR = 0.5;
|
||||
|
||||
const NodeSlotPattern = ({ offset }) => (
|
||||
<pattern
|
||||
id="patch_bg_pattern"
|
||||
x={Math.round(offset.x)}
|
||||
y={Math.round(offset.y)}
|
||||
x={Math.round(offset.x) - COMPENSATE_BLUR}
|
||||
y={Math.round(offset.y) - COMPENSATE_BLUR}
|
||||
width={SLOT_SIZE.WIDTH}
|
||||
height={SLOT_SIZE.HEIGHT}
|
||||
patternUnits="userSpaceOnUse"
|
||||
>
|
||||
<g stroke="none" fill="none">
|
||||
<line x1={1} y1={1} x2={1} y2={NODE_HEIGHT} />
|
||||
<line x1={0} y1={1} x2={SLOT_SIZE.WIDTH} y2={1} />
|
||||
<line x1={0} y1={NODE_HEIGHT} x2={SLOT_SIZE.WIDTH} y2={NODE_HEIGHT} />
|
||||
<line x1={COMPENSATE_BLUR} y1={1} x2={COMPENSATE_BLUR} y2={NODE_HEIGHT} />
|
||||
<line
|
||||
x1={0}
|
||||
y1={COMPENSATE_BLUR}
|
||||
x2={SLOT_SIZE.WIDTH}
|
||||
y2={COMPENSATE_BLUR}
|
||||
/>
|
||||
<line
|
||||
x1={0}
|
||||
y1={NODE_HEIGHT + COMPENSATE_BLUR}
|
||||
x2={SLOT_SIZE.WIDTH}
|
||||
y2={NODE_HEIGHT + COMPENSATE_BLUR}
|
||||
/>
|
||||
</g>
|
||||
</pattern>
|
||||
);
|
||||
|
||||
@@ -38,7 +38,7 @@ const BusNodeBody = ({ type, size, pins, label }) => {
|
||||
)(pins);
|
||||
|
||||
return (
|
||||
<g>
|
||||
<g className="bus-node">
|
||||
<polygon className="body" {...polygonProps} />
|
||||
<NodeLabel
|
||||
text={label}
|
||||
|
||||
@@ -2,27 +2,19 @@ import R from 'ramda';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { foldEither, foldMaybe, maybePath } from 'xod-func-tools';
|
||||
import { NODE_CORNER_RADIUS } from '../../nodeLayout';
|
||||
import { getRenderablePinType } from '../../utils';
|
||||
|
||||
const INPUT_PINKEY = '__in__';
|
||||
const OUTPUT_PINKEY = '__out__';
|
||||
|
||||
const getDeducedTypeOfPin = R.curry((pinkey, pins) =>
|
||||
R.compose(
|
||||
foldMaybe('generic', R.identity),
|
||||
R.map(foldEither('error', R.identity)),
|
||||
maybePath([INPUT_PINKEY, 'deducedType'])
|
||||
)(pins)
|
||||
);
|
||||
|
||||
const JumperNodeBody = ({ pins }) => {
|
||||
const inConnected = R.path([INPUT_PINKEY, 'isConnected'], pins);
|
||||
const outConnected = R.path([OUTPUT_PINKEY, 'isConnected'], pins);
|
||||
|
||||
const type = R.cond([
|
||||
[() => inConnected, getDeducedTypeOfPin(INPUT_PINKEY)],
|
||||
[() => outConnected, getDeducedTypeOfPin(OUTPUT_PINKEY)],
|
||||
[() => inConnected, R.pipe(R.prop(INPUT_PINKEY), getRenderablePinType)],
|
||||
[() => outConnected, R.pipe(R.prop(OUTPUT_PINKEY), getRenderablePinType)],
|
||||
[R.T, R.always('generic')],
|
||||
])(pins);
|
||||
|
||||
@@ -30,7 +22,7 @@ const JumperNodeBody = ({ pins }) => {
|
||||
inConnected || outConnected ? 'is-connected' : 'not-connected';
|
||||
|
||||
return (
|
||||
<g>
|
||||
<g className="jumper-node">
|
||||
<rect
|
||||
className="clickable-area"
|
||||
width="100%"
|
||||
|
||||
@@ -21,7 +21,7 @@ const TerminalNodeBody = props => {
|
||||
);
|
||||
|
||||
return (
|
||||
<g>
|
||||
<g className="terminal-node">
|
||||
<circle className="body" {...circleProps} />
|
||||
<NodeLabel
|
||||
text={terminalLabel}
|
||||
|
||||
@@ -1,7 +1,40 @@
|
||||
import * as R from 'ramda';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { NODE_CORNER_RADIUS } from '../../nodeLayout';
|
||||
import {
|
||||
NODE_CORNER_RADIUS,
|
||||
VARIADIC_HANDLE_WIDTH,
|
||||
VARIADIC_HANDLE_HEIGHT,
|
||||
} from '../../nodeLayout';
|
||||
|
||||
const COMPENSATE_BLURING = 0.5;
|
||||
|
||||
/**
|
||||
* Outputs a string with coordinates for four points of variadic handle:
|
||||
* 0○
|
||||
* 3○
|
||||
*
|
||||
* 2○
|
||||
* 1○
|
||||
*
|
||||
* :: { width, height } -> String
|
||||
*/
|
||||
const getVariadicPoints = size => {
|
||||
const xRight = size.width - COMPENSATE_BLURING;
|
||||
const xLeft = size.width - VARIADIC_HANDLE_WIDTH - COMPENSATE_BLURING;
|
||||
const yTop = (size.height - VARIADIC_HANDLE_HEIGHT) / 2;
|
||||
const yBottom = yTop + VARIADIC_HANDLE_HEIGHT;
|
||||
|
||||
const points = [
|
||||
[xRight, yTop], // 0
|
||||
[xRight, yBottom], // 1
|
||||
[xLeft, yBottom - VARIADIC_HANDLE_WIDTH], // 2
|
||||
[xLeft, yTop + VARIADIC_HANDLE_WIDTH], // 3
|
||||
];
|
||||
|
||||
return R.compose(R.join(' '), R.unnest)(points);
|
||||
};
|
||||
|
||||
const VariadicHandle = props => (
|
||||
<g className="VariadicHandle">
|
||||
@@ -9,19 +42,16 @@ const VariadicHandle = props => (
|
||||
className="VariadicHandle--clickArea"
|
||||
rx={NODE_CORNER_RADIUS}
|
||||
ry={NODE_CORNER_RADIUS}
|
||||
x={props.size.width - 8}
|
||||
x={props.size.width - VARIADIC_HANDLE_WIDTH * 2}
|
||||
y={-1}
|
||||
width={10}
|
||||
height={props.size.height + 2}
|
||||
width={VARIADIC_HANDLE_WIDTH * 2 + 2}
|
||||
height={props.size.height + 1}
|
||||
fill="rgba(255,0,0,.5)"
|
||||
onMouseDown={props.onMouseDown}
|
||||
/>
|
||||
<line
|
||||
<polygon
|
||||
className="VariadicHandle--grip"
|
||||
x1={props.size.width - 1.5}
|
||||
x2={props.size.width - 1.5}
|
||||
y1={props.size.height / 2 - 5}
|
||||
y2={props.size.height / 2 + 5}
|
||||
points={getVariadicPoints(props.size)}
|
||||
/>
|
||||
</g>
|
||||
);
|
||||
|
||||
@@ -3,14 +3,20 @@ import { Maybe } from 'ramda-fantasy';
|
||||
import * as XP from 'xod-project';
|
||||
import { foldMaybe } from 'xod-func-tools';
|
||||
|
||||
const BASE_SIZE_UNIT = 17;
|
||||
const BASE_SIZE_UNIT = 5;
|
||||
|
||||
export const NODE_HEIGHT = BASE_SIZE_UNIT * 13;
|
||||
|
||||
export const SLOT_SIZE = {
|
||||
WIDTH: BASE_SIZE_UNIT * 2,
|
||||
HEIGHT: BASE_SIZE_UNIT * 6,
|
||||
WIDTH: BASE_SIZE_UNIT * 8 + 4,
|
||||
HEIGHT: NODE_HEIGHT + BASE_SIZE_UNIT * 8,
|
||||
};
|
||||
|
||||
export const NODE_HEIGHT = BASE_SIZE_UNIT * 3;
|
||||
const PINVALUE_GAP = 4;
|
||||
export const PINVALUE_WIDTH = SLOT_SIZE.WIDTH - PINVALUE_GAP / 2;
|
||||
|
||||
const PINLABEL_GAP = 8;
|
||||
export const PINLABEL_WIDTH = SLOT_SIZE.WIDTH - PINLABEL_GAP / 2;
|
||||
|
||||
export const DEFAULT_PANNING_OFFSET = {
|
||||
x: NODE_HEIGHT,
|
||||
@@ -21,18 +27,22 @@ export const LINK_HOTSPOT_SIZE = {
|
||||
WIDTH: 8,
|
||||
};
|
||||
|
||||
export const NODE_CORNER_RADIUS = 5;
|
||||
export const NODE_CORNER_RADIUS = 3;
|
||||
|
||||
export const RESIZE_HANDLE_SIZE = 12;
|
||||
export const VARIADIC_HANDLE_WIDTH = 4;
|
||||
export const VARIADIC_HANDLE_HEIGHT = BASE_SIZE_UNIT * 5;
|
||||
|
||||
export const PIN_RADIUS = 6;
|
||||
export const PIN_RADIUS = 4;
|
||||
export const PIN_INNER_RADIUS = PIN_RADIUS - 2;
|
||||
export const PIN_RADIUS_WITH_OUTER_STROKE = PIN_RADIUS + 3;
|
||||
export const PIN_RADIUS_WITH_SHADOW = PIN_RADIUS + 4; // TODO: rename
|
||||
export const PIN_HIGHLIGHT_RADIUS = 15;
|
||||
export const PIN_OFFSET_FROM_NODE_EDGE = 3;
|
||||
export const PIN_HOVER_HIGHLIGHT_RADIUS = PIN_RADIUS;
|
||||
export const PIN_HOTSPOT_RADIUS = PIN_HIGHLIGHT_RADIUS;
|
||||
export const PIN_OFFSET_FROM_NODE_EDGE = 0;
|
||||
|
||||
export const TEXT_OFFSET_FROM_PIN_BORDER = 10;
|
||||
export const TEXT_OFFSET_FROM_PIN_BORDER = 3;
|
||||
|
||||
// :: { input: Number, output: Number } -> Size
|
||||
const nodeSizeInSlots = pinCountByDirection => ({
|
||||
@@ -221,3 +231,27 @@ export const getBusNodePositionForPin = (node, pin) => {
|
||||
SLOT_SIZE.HEIGHT * (pinDirection === XP.PIN_DIRECTION.INPUT ? -1 : 1),
|
||||
};
|
||||
};
|
||||
|
||||
// :: Number -> Boolean -> PIN_DIRECTION -> { x: Number, y: Number } -> { x, y, width, height }
|
||||
const getPinTextProps = (width, isLabel, direction, position) => {
|
||||
const FONT_HEIGHT = 11;
|
||||
const isAboveNodeBorder = direction === XP.PIN_DIRECTION.OUTPUT || !isLabel;
|
||||
const compensateFontHeight = isAboveNodeBorder ? FONT_HEIGHT : 0;
|
||||
const offsetFromNodeBorder =
|
||||
(PIN_RADIUS + TEXT_OFFSET_FROM_PIN_BORDER) * (isAboveNodeBorder ? -1 : 1);
|
||||
|
||||
return {
|
||||
x: position.x - width / 2,
|
||||
y: position.y - compensateFontHeight + offsetFromNodeBorder,
|
||||
width,
|
||||
height: FONT_HEIGHT,
|
||||
};
|
||||
};
|
||||
|
||||
// :: PIN_DIRECTION -> { x: Number, y: Number } -> { x, y, width, height }
|
||||
export const getPinValueProps = (pinDirection, position) =>
|
||||
getPinTextProps(PINVALUE_WIDTH, false, pinDirection, position);
|
||||
|
||||
// :: PIN_DIRECTION -> { x: Number, y: Number } -> { x, y, width, height }
|
||||
export const getPinLabelProps = (pinDirection, position) =>
|
||||
getPinTextProps(PINLABEL_WIDTH, true, pinDirection, position);
|
||||
|
||||
Reference in New Issue
Block a user