Skip to content
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

[WIP] Schema for configuring layers in plexus graphs #1

Merged
merged 7 commits into from
Feb 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/plexus/src/DirectedGraph/DirectedGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import PureEdges from './builtins/PureEdges';
import PureNodes from './builtins/PureNodes';
import MiniMap from './MiniMap';
import classNameIsSmall from './prop-factories/classNameIsSmall';
import mergePropSetters, { mergeClassNameAndStyle } from './prop-factories/mergePropSetters';
import mergePropSetters, { assignMergeCss } from './prop-factories/mergePropSetters';
import scaledStrokeWidth from './prop-factories/scaledStrokeWidth';
import { TDirectedGraphProps, TDirectedGraphState } from './types';
import { TCancelled, TLayoutDone, TPositionsDone, TSizeVertex } from '../types';
Expand Down Expand Up @@ -236,7 +236,7 @@ export default class DirectedGraph<T> extends React.PureComponent<
// const { current: rootElm } = this.rootRef;
const haveEdges = phase === PHASE_DONE;

const nodesContainerProps = mergeClassNameAndStyle(
const nodesContainerProps = assignMergeCss(
(setOnNodesContainer && setOnNodesContainer(this.state)) || {},
{
style: {
Expand All @@ -248,14 +248,14 @@ export default class DirectedGraph<T> extends React.PureComponent<
className: `${classNamePrefix}-DirectedGraph--nodeContainer`,
}
);
const edgesContainerProps = mergeClassNameAndStyle(
const edgesContainerProps = assignMergeCss(
(setOnEdgesContainer && setOnEdgesContainer(this.state)) || {},
{
style: { minHeight: '100%', minWidth: '100%' },
className: `${classNamePrefix}-DirectedGraph--nodeContainer`,
}
);
const rootProps = mergeClassNameAndStyle((setOnRoot && setOnRoot(this.state)) || {}, {
const rootProps = assignMergeCss((setOnRoot && setOnRoot(this.state)) || {}, {
style: zoomEnabled ? WRAPPER_STYLE_ZOOM : WRAPPER_STYLE,
className: `${classNamePrefix}-DirectedGraph ${className}`,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import { TPropsFactoryFn } from '../types';

function merge(a: Record<string, any>, b: Record<string, any>) {
function reduce(a: Record<string, any>, b: Record<string, any>) {
// eslint-disable-next-line prefer-const
let { className, style, ...rest } = a;
const { className: bClassName, style: bStyle, ...bRest } = b;
Expand All @@ -29,8 +29,8 @@ function merge(a: Record<string, any>, b: Record<string, any>) {
return { className, style, ...rest, ...bRest };
}

export function mergeClassNameAndStyle(...objs: Record<string, any>[]) {
return objs.reduce(merge);
export function assignMergeCss(...objs: Record<string, any>[]) {
return objs.reduce(reduce);
}

export default function mergePropSetters<U>(...fns: TPropsFactoryFn<U>[]): TPropsFactoryFn<U> {
Expand All @@ -44,6 +44,6 @@ export default function mergePropSetters<U>(...fns: TPropsFactoryFn<U>[]): TProp
propsList.push(props);
}
}
return propsList.reduce(merge);
return propsList.reduce(reduce);
};
}
143 changes: 143 additions & 0 deletions packages/plexus/src/LayeredDigraph/types.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Copyright (c) 2019 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import * as React from 'react';

import { TEdge, TLayoutEdge, TLayoutGraph, TLayoutVertex, TVertex } from '../types';
import { TOneOfFour, TOneOfTwo } from '../types/TOneOf';
import { ZoomTransform } from '../ZoomManager';

export enum ELayoutPhase {
NoData = 'NoData',
CalcSizes = 'CalcSizes',
CalcPositions = 'CalcPositions',
CalcEdges = 'CalcEdges',
Done = 'Done',
}

export type TAnyProps = Record<string, unknown> & {
className?: string;
style?: React.CSSProperties;
};

export type TPropFactoryFn = (...args: any[]) => TAnyProps | null;

export type TSetProps<TFactoryFn extends TPropFactoryFn> =
| TAnyProps
| TFactoryFn
| (TAnyProps | TFactoryFn)[];

// TODO(joe): consider getting rid of this type
// type TAesthetics = {
// className?: string;
// style?: React.CSSProperties;
// };

export type TRendererUtils = {
getLocalId: (name: string) => string;
getZoomTransform: () => ZoomTransform;
};

export type TExposedGraphState<T = {}, U = {}> = {
edges: TEdge<U>[];
zoomTransform: ZoomTransform;
layoutEdges: TLayoutEdge<U>[] | null;
layoutGraph: TLayoutGraph | null;
layoutPhase: ELayoutPhase;
layoutVertices: TLayoutVertex<T>[] | null;
renderUtils: TRendererUtils;
vertices: TVertex<T>[];
};

export type TFromGraphStateFn<T = {}, U = {}> = (input: TExposedGraphState<T, U>) => TAnyProps | null;

export type TSetOnContainer<T = {}, U = {}> = {
setOnContainer?: TSetProps<TFromGraphStateFn<T, U>>;
};

type TKeyed = { key: string };

export type TElemType = TOneOfTwo<{ html: true }, { svg: true }>;

export type TNodeRenderFn<T = {}> = (vertex: TVertex<T>, utils: TRendererUtils) => React.ReactNode;

export type TMeasurableNodeRenderFn<T = {}> = (
vertex: TVertex<T>,
utils: TRendererUtils,
layoutVertex: TLayoutVertex<T> | null
) => React.ReactNode;

export type TMeasurableNodeRenderer<T = {}> = {
measurable: true;
nodeRender: TMeasurableNodeRenderFn<T>;
setOnNode?: TSetProps<
(vertex: TVertex<T>, utils: TRendererUtils, layoutVertex: TLayoutVertex<T> | null) => TAnyProps | null
>;
};

type TNodeRenderer<T = {}> = {
nodeRender: TNodeRenderFn<T>;
setOnNode?: TSetProps<(layoutVertex: TLayoutVertex<T>, utils: TRendererUtils) => TAnyProps | null>;
};

type TNodesLayer<T = {}, U = {}> = TKeyed &
TOneOfTwo<TNodeRenderer<T>, TMeasurableNodeRenderer<T>> &
TSetOnContainer<T, U>;

type TStandaloneNodesLayer<T = {}, U = {}> = TNodesLayer<T, U> & TElemType;

type TMarkerDef<T = {}, U = {}> = TKeyed & {
type: React.Component;
localId: string;
setOnMarker?: TSetProps<TFromGraphStateFn<T, U>>;
};

type TEdgesLayer<T = {}, U = {}> = TKeyed &
TSetOnContainer<T, U> & {
edges: true;
setOnEdge?: TSetProps<(edges: TLayoutEdge<U>, utils: TRendererUtils) => TAnyProps | null>;
};

type TStandaloneEdgesLayer<T = {}, U = {}> = TEdgesLayer<T, U> &
TElemType & {
defs?: TMarkerDef<T, U>[];
};

type THtmlLayersGroup<T = {}, U = {}> = TKeyed &
TSetOnContainer<T, U> & {
html: true;
layers: (TNodesLayer<T, U> | TEdgesLayer<T, U>)[];
};

type TSvgLayersGroup<T = {}, U = {}> = TKeyed &
TSetOnContainer<T, U> & {
svg: true;
defs?: TMarkerDef<T, U>[];
layers: (TNodesLayer<T, U> | TEdgesLayer<T, U>)[];
};

export type TLayer<T = {}, U = {}> = TOneOfFour<
THtmlLayersGroup<T, U>,
TSvgLayersGroup<T, U>,
TStandaloneNodesLayer<T, U>,
TStandaloneEdgesLayer<T, U>
>;

export type TMeasurableNodeProps<T = {}> = Pick<TMeasurableNodeRenderer<T>, 'nodeRender' | 'setOnNode'> & {
classNamePrefix: string;
hidden?: boolean;
layoutVertex: TLayoutVertex<T> | null;
renderUtils: TRendererUtils;
vertex: TVertex<T>;
};
29 changes: 29 additions & 0 deletions packages/plexus/src/LayeredDigraph/utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) 2019 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { TPropFactoryFn, TSetProps } from './types';
import { assignMergeCss } from '../DirectedGraph/prop-factories/mergePropSetters';

// eslint-disable-next-line import/prefer-default-export
export function getProps<TFactoryFn extends TPropFactoryFn>(
propSpec: TSetProps<TFactoryFn> | void,
...args: Parameters<TFactoryFn>
) {
if (!propSpec) {
return {};
}
const specs = Array.isArray(propSpec) ? propSpec : [propSpec];
const propsList = specs.map(spec => (typeof spec === 'function' ? spec(...args) || {} : spec));
return assignMergeCss(...propsList);
}
40 changes: 40 additions & 0 deletions packages/plexus/src/types/TOneOf.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) 2018 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

export type TOneOf<A, B, C = {}, D = {}, E = {}> =
| { [P in Exclude<keyof (B & C & D & E), keyof A>]?: null } & A
| { [P in Exclude<keyof (A & C & D & E), keyof B>]?: null } & B
| { [P in Exclude<keyof (A & B & D & E), keyof C>]?: null } & C
| { [P in Exclude<keyof (A & B & C & E), keyof D>]?: null } & D
| { [P in Exclude<keyof (A & B & C & D), keyof E>]?: null } & E;

// eslint-disable-next-line no-undef
export default TOneOf;

export type TOneOfTwo<A, B> =
| { [P in Exclude<keyof B, keyof A>]?: null } & A
| { [P in Exclude<keyof A, keyof B>]?: null } & B;

export type TOneOfThree<A, B, C> =
| { [P in Exclude<keyof (B & C), keyof A>]?: null } & A
| { [P in Exclude<keyof (A & C), keyof B>]?: null } & B
| { [P in Exclude<keyof (A & B), keyof C>]?: null } & C;

export type TOneOfFour<A, B, C, D> =
| { [P in Exclude<keyof (B & C & D), keyof A>]?: null } & A
| { [P in Exclude<keyof (A & C & D), keyof B>]?: null } & B
| { [P in Exclude<keyof (A & B & D), keyof C>]?: null } & C
| { [P in Exclude<keyof (A & B & C), keyof D>]?: null } & D;

export type TOneOfFive<A, B, C, D, E> = TOneOf<A, B, C, D, E>;
6 changes: 3 additions & 3 deletions packages/plexus/src/types/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2017 Uber Technologies, Inc.
// Copyright (c) 2018 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -46,8 +46,8 @@ export type TEdge<T = any> = {
data?: T;
};

export type TLayoutEdge = {
edge: TEdge;
export type TLayoutEdge<T = any> = {
edge: TEdge<T>;
pathPoints: [number, number][];
};

Expand Down