Skip to content

Commit

Permalink
[master] Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
prevwong committed Jul 1, 2019
2 parents 62655c6 + cef2950 commit 505a896
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 30 deletions.
21 changes: 21 additions & 0 deletions packages/core/AnotherCanvas.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from "react";
import { connectNode } from "./nodes";
import { ConnectedNode, ConnectedPublicNode, Node } from "./interfaces";

type AnotherCanvas = {
children: React.ReactChildren
} & ConnectedPublicNode

const AnotherCanvas: React.FC<AnotherCanvas> = ({children, craft: {node, connectTarget}}: AnotherCanvas) => {
return connectTarget(
<hgroup style={{background:"#eee", padding:"20px 30px"}} className="another-canvas">{children}</hgroup>,
{
incoming: (incomingNode: Node) => {
// if ( incomingNode.data.props.children === 'Order1') return false;
return true;
}
}
)
}

export default connectNode(AnotherCanvas);
6 changes: 4 additions & 2 deletions packages/core/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ import { MsgBox } from "./MsgBox";
import { Heading } from "./Heading";
import { Renderer } from "./render/Renderer";
import { Craft } from "./Craft";
import AnotherCanvas from "./AnotherCanvas";

class App extends React.Component {
render() {
return (
<div className='app'>
<Craft>
<Renderer>
<Canvas is={AnotherCanvas}>
<p>Alright</p>
</Canvas>
<MsgBox text="hi" />
<MsgBox text="bye" />
<MsgBox text="lol" />
</Renderer>
</Craft>
</div>
Expand Down
7 changes: 4 additions & 3 deletions packages/core/MsgBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import React from "react";
import { Canvas } from "./nodes/Canvas";
import { Heading } from "./Heading";
import { PublicManagerMethods } from "./manager/methods";
import { connectNode, ConnectedPublicNode } from "./nodes/connectors";
import { connectNode } from "./nodes/connectors";
import MsgCanvas from "./MsgCanvas";
import { ConnectedPublicNode } from "./interfaces";

export type MsgBox = {
text: string
Expand All @@ -14,10 +15,10 @@ const Msg = ({craft:{node, manager, connectTarget}, text}: MsgBox) => {
return connectTarget(
<div className="message-box" >
<h2>MESSAGE{text}</h2>
{/* <Canvas id="Msgcanvas" is={MsgCanvas}>
<Canvas id="Msgcanvas" is={MsgCanvas}>
<p>Order1</p>
<p>Order2</p>
</Canvas> */}
</Canvas>
</div>
)
}
Expand Down
21 changes: 17 additions & 4 deletions packages/core/MsgCanvas.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
import React from "react";
import { connectNode } from "./nodes";
import { ConnectedNode, ConnectedPublicNode, Node } from "./interfaces";

export default function MsgCanvas({children}) {
return (
<hgroup className="msg-canvas">{children}</hgroup>
type MsgCanvas = {
children: React.ReactChildren
} & ConnectedPublicNode

const MsgCanvas: React.FC<MsgCanvas> = ({children, craft: {node, connectTarget}}: MsgCanvas) => {
return connectTarget(
<hgroup className="msg-canvas">{children}</hgroup>,
{
incoming: (incomingNode: Node) => {
return true;
}
}
)
}
}

export default connectNode(MsgCanvas);
37 changes: 28 additions & 9 deletions packages/core/events/EventsManager.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { connectManager, ConnectedManager } from "../manager";
import React, { useState, useRef, useCallback } from "react";
import { CanvasNode, PlaceholderInfo } from "../interfaces";
import { CanvasNode, PlaceholderInfo, Nodes, Node } from "../interfaces";
import { NodeId, DropAction } from "~types";
import { getDOMInfo, getDeepChildrenNodes } from "../utils";
import { getDOMInfo, getDeepChildrenNodes, getAllCanvas } from "../utils";
import findPosition from "./findPosition";
import movePlaceholder from "./movePlaceholder";
import { useEventListener } from "../utils/hooks";
Expand All @@ -17,8 +17,7 @@ export const EventsManager = connectManager(({ children, manager: [state, method
const placeholderRef = useRef<PlaceholderInfo>(null);
const placeBestPosition = (e: MouseEvent) => {
const { nodes } = state;
const nearestTargets = getNearestTarget(e),
nearestTargetId = nearestTargets.pop();
const [nearestTargetId, possibleNodes] = getNearestTarget(e);
if (nearestTargetId) {
const targetNode = nodes[nearestTargetId],
targetParent = (targetNode as CanvasNode).data.nodes ? targetNode as CanvasNode : nodes[targetNode.data.parent] as CanvasNode;
Expand All @@ -32,6 +31,8 @@ export const EventsManager = connectManager(({ children, manager: [state, method

const bestTarget = findPosition(targetParent, dimensionsInContainer, e.clientX, e.clientY);
const bestTargetNode = targetParent.data.nodes.length ? nodes[targetParent.data.nodes[bestTarget.index]] : targetParent;

if ( !possibleNodes.includes(bestTargetNode.id) ) return;

const output: PlaceholderInfo = {
position: movePlaceholder(
Expand All @@ -49,24 +50,42 @@ export const EventsManager = connectManager(({ children, manager: [state, method
}
}

const getNearestTarget = (e: MouseEvent) => {
const getNodesInAcceptedCanvas = (nodes: Nodes, incomingNode: Node): NodeId[] => {
const canvases = getAllCanvas(nodes);
const nodesToConsider = canvases.reduce((res: NodeId[], id) => {
const canvas = nodes[id] as CanvasNode;
if ( canvas.ref.incoming(incomingNode) ) {
if ( !res.includes(canvas.id) ) res = [...res, canvas.id];
res = [...res, ...canvas.data.nodes];

}
return res;
}, []);

return nodesToConsider;
}

const getNearestTarget = (e: MouseEvent): [NodeId, NodeId[]]=> {
const { nodes, events } = state;
const pos = { x: e.clientX, y: e.clientY };

const deepChildren = getDeepChildrenNodes(nodes, events.active.id);
const nodesWithinBounds = Object.keys(nodes).filter(nodeId => {
return nodeId !== "rootNode" && nodes[nodeId].ref.dom && !deepChildren.includes(nodeId)
const possibleNodeIds = getNodesInAcceptedCanvas(nodes, state.events.active);
const nodesWithinBounds = possibleNodeIds.filter(nodeId => {
return nodes[nodeId].ref.dom &&
!deepChildren.includes(nodeId)
});


return nodesWithinBounds.filter((nodeId: NodeId) => {
const nearestTargets = nodesWithinBounds.filter((nodeId: NodeId) => {
const { top, left, width, height } = getDOMInfo(nodes[nodeId].ref.dom);

return (
(pos.x >= left && pos.x <= left + width) &&
(pos.y >= top && pos.y <= top + height)
);
});

return [nearestTargets.length ? nearestTargets.pop() : null, possibleNodeIds]
};

const onDrag = useCallback((e: MouseEvent) => {
Expand Down
9 changes: 8 additions & 1 deletion packages/core/interfaces/nodes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,21 @@ export interface NodeData {

export interface CanvasNode extends Node {
data: CanvasNodeData;
ref: CanvasNodeRef
}

export interface CanvasNodeData extends NodeData {
nodes: NodeId[]
}

export type NodeRef = {
export interface NodeRef {
dom: HTMLElement;
canDrag(node: Node): void;
}

export interface CanvasNodeRef extends NodeRef {
incoming(incoming: Node): boolean;
outgoing(outgoing: Node): boolean;
}

export interface NodeEvent {
Expand Down
9 changes: 7 additions & 2 deletions packages/core/manager/methods.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { NodeId, Node, CanvasNode } from "../interfaces";
import { CallbacksFor } from "use-methods";
import { ManagerState } from "../interfaces";
import { isCanvas } from "../utils";

export const PublicManagerMethods = (state: ManagerState) => {
return {
Expand Down Expand Up @@ -33,11 +34,15 @@ export const PublicManagerMethods = (state: ManagerState) => {
};

const ManagerMethods = (state: ManagerState) => ({
setDOM: (id: NodeId, dom: HTMLElement) => {
state.nodes[id].ref.dom = dom;
setRef: (id: NodeId, ref: "dom" | "outgoing" | "incoming" | "canDrag", value: any) => {
if ( !["dom", "outgoing", "incoming", "canDrag"].includes(ref)) { throw new Error(); }
let node = state.nodes[id];
if ( isCanvas(node) ) (node as CanvasNode).ref[ref] = value;
else node.ref[ref as "dom" | "canDrag"] = value;
},
pushChildCanvas(id: NodeId, canvasName: string, newNode: Node) {
if (!state.nodes[id].data._childCanvas) state.nodes[id].data._childCanvas = {};
newNode.data.closestParent = id;
state.nodes[id].data._childCanvas[canvasName] = newNode.id;
state.nodes[newNode.id] = newNode;
},
Expand Down
13 changes: 9 additions & 4 deletions packages/core/nodes/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ const shortid = require("shortid");

export interface Canvas extends ConnectedInternalNode, React.Props<any> {
id?: NodeId,
style?: any,
className?: any,
is?: React.ElementType
children?: React.ReactNode
}

export const Canvas = connectInternalNode(({ craft: { node, manager }, children, is="div", id, ...props}: Canvas) => {
Expand All @@ -19,15 +20,19 @@ export const Canvas = connectInternalNode(({ craft: { node, manager }, children,
let canvasId = `canvas-${shortid.generate()}`;

if (node.data.type === Canvas) {
canvasId = internal.current.id = node.id;
const childNodes = mapChildrenToNodes(children, canvasId);
manager.add(node.id, childNodes);
if ( !(node as CanvasNode).data.nodes ) { // don't recreate nodes from children after initial hydration
canvasId = internal.current.id = node.id;
const childNodes = mapChildrenToNodes(children, canvasId);
manager.add(node.id, childNodes);
}
} else {
if (!id) throw new Error("Root Canvas cannot ommit `id` prop");
if (!node.data._childCanvas || (node.data._childCanvas && !node.data._childCanvas[id])) {
const rootNode = createNode(Canvas, { is, children } as any, canvasId, null);
internal.current.id = canvasId;
manager.pushChildCanvas(node.id, id, rootNode);
} else {
internal.current.id = node.data._childCanvas[id];
}
}
}, []);
Expand Down
12 changes: 10 additions & 2 deletions packages/core/nodes/useNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { NodeContext } from "./NodeContext";
import { ManagerContext } from "../manager";
import { ManagerMethods, PublicManagerMethods } from "../manager/methods";
import { CraftNodeAPI } from "../interfaces";
import { isCanvas } from "../utils";

const useNode = () : CraftNodeAPI<ManagerMethods> => {
const nodeContext = useContext(NodeContext);
Expand All @@ -23,15 +24,22 @@ const useNode = () : CraftNodeAPI<ManagerMethods> => {
)
}, [state.nodes[id]]);

const connectTarget = useCallback((render) => {
const connectTarget = useCallback((render, nodeMethods) => {
return cloneElement(render, {
onMouseDown: (e) => {
e.stopPropagation();
if ( node.id !== "rootNode" ) manager.setNodeEvent("active", node.id)
},
ref: (ref: any) => {
if ( ref ) {
manager.setDOM(id, ref);
manager.setRef(id, "dom", ref);
if ( nodeMethods ) {
if ( nodeMethods.canDrag ) manager.setRef(id, "canDrag", nodeMethods.canDrag);
if ( isCanvas(node) ) {
if ( nodeMethods.incoming ) manager.setRef(id, "incoming", nodeMethods.incoming)
if ( nodeMethods.outgoing ) manager.setRef(id, "outgoing", nodeMethods.outgoing);
}
}
}
if ( render.ref ) render.ref(ref);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/render/Renderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Canvas } from "../nodes/Canvas";
export const Renderer = ({ children }: any) => {
const [state, methods] = useContext(ManagerContext);
useEffect(() => {
let node = mapChildrenToNodes(<Canvas id="rootCanvas">{children}</Canvas>, null, "rootNode");
let node = mapChildrenToNodes(<Canvas id="rootCanvas" style={{background:"#ccc", padding:"20px 0"}}>{children}</Canvas>, null, "rootNode");
methods.add(null, node);
console.log("added root node");
}, []);
Expand Down
1 change: 0 additions & 1 deletion packages/core/utils/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { TextNode } from "./nodes";
export * from "./nodes";
export * from "./element";

export const isCanvas = (node: any) => !!node.nodes
export const isCraftComponent = (type: any): boolean => !!type.editor;

export const defineReactiveProperty = (obj: any, key: string, val?: string, cb?: Function) => {
Expand Down
17 changes: 16 additions & 1 deletion packages/core/utils/nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import produce from "immer";

const shortid = require("shortid");

export const isCanvas = (node: Node) => node.data.type === Canvas


export const createNode = (component: React.ElementType, props: React.Props<any>, id: NodeId, parent?: NodeId): Node => {
let node = produce({}, (node: Node) => {
node.id = id;
Expand All @@ -22,7 +25,12 @@ export const createNode = (component: React.ElementType, props: React.Props<any>
}
};
node.ref = {
dom: null
dom: null,
canDrag: () => true
};
if ( isCanvas(node) ) {
(node as CanvasNode).ref.incoming = () => true;
(node as CanvasNode).ref.outgoing = () => true;
}
}) as Node;

Expand Down Expand Up @@ -143,4 +151,11 @@ export const getAllParents = (nodes: Nodes, nodeId: NodeId, result:NodeId[] = []
}

return result;
}

export const getAllCanvas = (nodes: Nodes) => {
return Object.keys(nodes).filter(id => {
if (isCanvas(nodes[id]) ) return true;
return false;
})
}

0 comments on commit 505a896

Please sign in to comment.