Giter VIP home page Giter VIP logo

craft.js's People

Contributors

actions-user avatar ankri avatar azreenashah avatar boqiaok avatar brendanowens avatar clyer avatar dependabot[bot] avatar enva2712 avatar fengzilong avatar giliamverheide avatar github-actions[bot] avatar hahlh avatar hariombalhara avatar ismajl-ramadani avatar jefrydco avatar jmschneider avatar joshbalfour avatar k-yle avatar linxianxi avatar matdru avatar mresposito avatar prevwong avatar seang avatar sprabowo avatar timc1 avatar traveller23 avatar vjsrinath avatar vulcanoidlogic avatar wuichen avatar zaki-yama avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

craft.js's Issues

`actions.delete` isn't deleting all nodes within a canvas

Describe the bug
When I call actions.delete on a canvas, it is not deleting all of the nodes under that canvas. It appears to be deleting some but not all. The nodes disappear from the interface but still exists in the editor state.

To Reproduce
Steps to reproduce the behavior:

  1. Create a canvas with multiple nodes
  2. Call actions.delete(canvas_id)
  3. Call query.serialize()
  4. You should still see the deleted nodes in the serialized state

Expected behavior
Based on these lines, I think it is supposed to be deleting all the nodes within the canvas.

Your environment

Software Version(s)
craft.js 0.1.0-beta.5
React 16.13.1
TypeScript 3.8.3
Browser Firefox
npm/Yarn yarn
Operating System OS X

Absolute positioning and free dragging inside base canvas

Hello and thank you for a great work.
I'm looking for a simple web widget builder, with a functionality of graphic editor, so components are draggable over screen and their front-back position controlled by layer manager. Can i achive that with craft.js library and how can i do that?

Improve JSON documentation & output

I couldn't find any examples of what the JSON format looked like, and I wanted to be sure the format was relatively independent of Craft itself. However, the JSON uses node IDs instead of nesting the nodes themselves, which I find confusing.

For example, with these Frame contents:

<Frame>
  <Canvas is={CraftContainer}>
    <CraftComponent>
      <TextComponent text="my text here" />
    </CraftComponent>
  </Canvas>
</Frame>

The JSON output from query.serialize() is:

{
  "canvas-ROOT": {
    "type": {
      "resolvedName": "CraftContainer"
    },
    "isCanvas": true,
    "props": {},
    "displayName": "Canvas",
    "custom": {},
    "nodes": [
      "node-yxVcZGJj"
    ]
  },
  "node-yxVcZGJj": {
    "type": {
      "resolvedName": "CraftComponent"
    },
    "props": {
      "children": [
        {
          "type": {},
          "props": {
            "text": "text component"
          }
        }
      ]
    },
    "displayName": "CraftComponent",
    "custom": {},
    "parent": "canvas-ROOT",
    "_childCanvas": {
      "d02fab54-ddf2-4356-b79d-5660d54915df": "canvas-u-wv625gX"
    }
  },
  "canvas-u-wv625gX": {
    "type": {
      "resolvedName": "CraftContainer"
    },
    "isCanvas": true,
    "props": {},
    "displayName": "Canvas",
    "custom": {},
    "parent": "node-yxVcZGJj",
    "nodes": [
      "node-aKYJYqPZ0"
    ]
  },
  "node-aKYJYqPZ0": {
    "type": {},
    "props": {
      "text": "my text here"
    },
    "displayName": "TextComponent",
    "custom": {},
    "parent": "canvas-u-wv625gX"
  }
}

I would expect something more like:

[
  {
    "type": "CraftContainer",
    "nodes": [
      {
        "type": "CraftComponent",
        "nodes": [
          {
            "type": "TextComponent",
            "props": {
              "text": "my text here"
            }
          }
        ]
      }
    ]
  }
]

It would also be nice to have some examples of implementing custom serializers, like SlateJS does. I'm currently using SlateJS as a WYSIWYG React editor, but I'd love to switch to Craft for the nicer drag & drop functionality.

Deserialize does not honor the hidden attribute

Describe the bug
actions.deserialize does not honor the hidden attribute on nodes.

To Reproduce

  1. Hide a node
  2. Serialize the current state with something like state = query.serialize()
  3. Deserialize the state with actions.deserialize(state)
  4. Your hidden node will reappear

Expected behavior
If the hidden attribute is set in the serialized state, the node should be created in a hidden state.

Your environment

Software Version(s)
craft.js 0.1.0-beta.1
React 16.12.0
TypeScript 3.7.5
Browser Firefox 74.0b4
npm/Yarn Yarn 1.22.0
Operating System OS X 10.15.3

Merge values from collector function into Node

Is your feature request related to a problem? Please describe.

I want to use a value returned from useNode's hook collector function in a canDrag rule, but since it's available only in the hook's returned object, I am forced to declare and run the calculations at multiple places.

Describe the solution you'd like

The values returned from useNode's hook collector function are useful even outside of the components scope. It would be handy if they were merged into the Node itself.

const Hero = ({ title, ...rest }) => {
  const { connectors: { connect, drag }, setProp, tainted } = useNode(node => ({
    tainted: node.data.props.newTitle && title !== node.data.props.newTitle }
  }));
  return (
    <div>
      <h2>Edit me!</h2>
      <p contentEditable={true} onKeyUp={(e) => {
        setProp(props => {
          props.newTitle = e.target.innerText;
        })
      }}>{title}</p>
      <p style={{color: tainted ? 'black' : 'green'}}>{rest.newTitle}</p>
      <button ref={ref => connect(drag(ref))}>{tainted ? 'Drag me?' : "Don't touchme!"}</button>
    </div>
  )
}
Hero.craft = {
  defaultProps: {
    title: 'Sphinx of black quartz judge my vow!'
  },
  rules: {
    canDrag: node => node.data.someFittingPropertyName.tainted
  }
}

Additional context

At first I thought that node.data.custom might contain it, but it's always empty.

image

Connectors initialised after initial render fail to register properly

Describe the bug
Creating dynamic elements that use connectors ( for example loading extra elements that can be dragged onto editor ) dont register correctly. To get them to work editor needs one extra cycle and refreshed internal state ( for example due to hover state change ) to get these event handlers to work.

To Reproduce
Bug reproduced on this branch: https://github.com/matdru/craft.js/tree/matdru-dynamic-connector-issue
in basic example

Steps to reproduce the behavior:

  1. Add any dynamically mounted component that uses connectors
  2. Trigger that component to mount itself and try dragging onto editor
  3. This drag event won't work at all

Expected behavior
Dynamically added elements that use connectors should be available to use immediately.

Screenshots
https://share.getcloudapp.com/lluyLQkG

Additional context
After mounting a new connector, any change that trigger craft state update will fix it, which makes this issue hard to spot and debug. In specific cases, this is a roadblock issue, for example, if you have a panel with extra elements that mounts/unmounts on hover, which means it will unmount itself before craft registers proper event handlers

Your environment

Software Version(s)
craft.js beta.4
React 16
TypeScript
Browser chrome
npm/Yarn yarn
Operating System macOS Cataline

Integrate with Gatsby

Describe the bug
It needs to integrate with Gatsby.js to fetch Graphql endpoints on Server Side, with a plugin panel, to make complete websites using it.

To Reproduce
Steps to reproduce the behavior:

  1. Go to 'https://www.gatsbyjs.org/'
  2. Click on 'Documentation'
  3. Scroll down to 'Benefits'
  4. "Analyze the scope of craft.js core"

Expected behavior
Well, you can not still craft.js a fully-fledged modern react websites without disintegrating craft.js core to inject modern design system such as antd, antd-mobile, material-ui, & mapboxgl

Screenshots
Craft.js might have future but it needs to be extra thin editor leveraging the best of something called network effect in the age of internet.

Additional context
As of now, yours code type system is little too coupled for loosening those bounds.

Your environment

Web Computing

Software Version(s)
craft.js
React
TypeScript
Browser
npm/Yarn
Operating System Windows

Some bug about event binding

  1. The variable guarded in useHandlerGuard hooks is not wrapped by useMemo will create a new reference every time component render, and the next useMemo in line 23's cache ability is failed, so it will recalc every time component render and return a new object.

  2. After first time render finished(may be not first time, i means after craft finish all it's initialization process and ready to work), it bind 3 event listeners on every same element(i find it by chrome devtool), and after every time i do an operation on a element(such as move), craft will bind a another event listener on the element(some parent element also add an event listener).

I think the rebind bug's is caused by useHandlerGuard hook, but i can't find the real reason in it's long called chain(i haven't understood how craft work), can it would be resolved?
And no matter what, I believe use useMemo to wrap the guarded variable is necessary.

Duplicating a node twice fails due to error in `actions.setNodeEvent`

Describe the bug
A clear and concise description of what the bug is.

When trying to duplicate a node twice in a row, it fails due to the following error:

react-dom.development.js?61bb:11102 Uncaught TypeError: Cannot read property 'events' of undefined
    at Object.setNodeEvent (index.js?d4d4:1)
    at eval (index.js?b31b:1)
    at Immer.produce (immer.module.js?57f8:792)
    at eval (index.js?b31b:1)
    at updateReducer (react-dom.development.js?61bb:15135)
    at Object.useReducer (react-dom.development.js?61bb:15932)
    at Object.useReducer (react.development.js?72d0:1501)
    at Object.useMethods (index.js?b31b:1)
    at useEditorStore (index.js?d4d4:1)
    at Editor (index.js?d4d4:1)

To duplicate, we use the following function:

  const { actions, nodes, selectedNodeId } = useEditor((editorState) => ({
       selectedNodeId: editorState.events.selected,
       nodes: editorState.nodes,
    }));

    const duplicateNode = (nodeIdToDuplicate: string) => {
      if (!selectedNodeId || !nodeIdToDuplicate || nodeIdToDuplicate === CRAFT_ROOT_ID) {
        return;
      }
      // we check that the node still exists here because it might have been deleted.
      const selectedNode = nodes[selectedNodeId];
      const parentId = selectedNode.data.parent || CRAFT_ROOT_ID;
      const nodeToCopy = { ...nodes[nodeIdToDuplicate], id: newId() };
      if (!nodeToCopy) {
        return;
      }
      actions.add(nodeToCopy, selectedNode.data.parent || CRAFT_ROOT_ID);
      actions.selectNode(nodeToCopy.id);
      // move the node to be right next to the current node.
      const newIndex = nodes[parentId].data.nodes.indexOf(selectedNodeId) + 1;
      actions.move(nodeToCopy.id, parentId, newIndex);
    },

To Reproduce
Steps to reproduce the behavior:

When duplicating a node using the above function, try to copy it twice in a row and it will throw an error

Expected behavior
A clear and concise description of what you expected to happen.
it shouldn't throw an error

Screenshots
If applicable, add screenshots to help explain your problem.
captured

Additional context
Add any other context about the problem here.

Your environment

Software Version(s)
craft.js beta.4
React 16.13
TypeScript yes
Browser chrome
npm/Yarn yarn
Operating System osx high sierra

node.toNodeTree() does not include linked nodes

I'm trying to create an editor for multiple pages using CraftJS. I have a single root node that has "page" nodes as children, and then I have components that can be put into pages.

I want to save each page separately, so I tried using node.toNodeTree() to extract the tree for a single page. However, it doesn't include linked nodes from named canvases. Do I need to recursively check for node.data.linkedNodes and add those to my tree? It seems like they should be included in node.toNodeTree(), but maybe I'm missing something.

Thanks!

Ability to save json on edit

Is your feature request related to a problem? Please describe.
I want to save the json to the database when the canvas is changed for loading the page in the future. Right now the only way to do it is requesting the user to manual press the button

  <MaterialButton
            className="copy-state-btn"
            size="small"
            variant="outlined"
            color="secondary"
            onClick={() => {
              const json = query.serialize();
            //sent the json the server
              setSnackbarMessage("Saved");
            }}
            style={{ marginRight: "10px" }}
          >
           Save
          </MaterialButton>

Describe the solution you'd like
Solution 1

   <Frame json={JSON.stringify(value)} onChange={query=>{}} >
 const { actions, query, enabled } = useEditor(state => ({
    enabled: state.options.enabled
  }));
query.onChange().

Again, what a great library there!

Why use Canvas is prop instead of nesting Canvas?

All examples in documentation use Canvas with this pattern:
note: the code is invalid and D&D hook is omitted for simplicity

const Container = () => ( 
  <div>
    {children}
  </div>
)
// later
<Canvas is={Container} />

Why not use

const CanvasContainer = () => (
  <div>
    <Canvas>
      {children}
    </Canvas>
  </div>
)
// later
<CanvasContainer />

I think the second one is more explicit (better at fine-tuning the exact position of Canvas) and DRY as it doesn't need that one uses the "<Canvas is" pattern every time they want to use the container.

The difference is that the first needs to be used differently than other components every time it is used, and even in the Toolbox:

<button ref={ref => connectors.create(ref, <Canvas is={Container}/>)}>Container</button>
<button ref={ref => connectors.create(ref, <CanvasContainer/>)}> CanvasContainer</button>

One reason I would see to not put Canvas in the Container directly is for cases where one wants to use Container without being droppable (i.e. not accepting dragged items). But I already use containers vs special droppable components that are meant to always accept items.

Could you please mention the reasoning behind this in the Basic Tutorial?

I like the second pattern more as every component is used the same way and I can actually move the droppable region within the Container or see where exactly it is. It also doesn't happen that I accidentaly forget to use the Container with <Canvas is.

//edit
Allright, an empty nested Canvas introduces additional html markup (as seen in the image below) which might not be desirable, anyway, this can also be worked around.
note: the canvas uses blue border, container uses red border.
image

The workaround
// edit2 My mistake, this actually doesn't work, the div isn't droppable, though I wonder why.

export const CanvasContainer = ({children = null, ...props}) => {
  const { connectors: { connect, drag } } = useNode();
  return (
      <Canvas is={() =>
        <div
          style={{ border: "1px dashed red" }}
          id={id}
          ref={ref => connect(drag(ref))}
          {...props}
        >
          {children}
        </div>
      } />
  )
}

// later
<CanvasContainer />

which is equivalent to

export const Container = ({children = null, ...props}) => {
  const { connectors: { connect, drag } } = useNode();
  return (
    <div
      style={{ border: "1px dashed red" }}
      id={id}
      ref={ref => connect(drag(ref))}
    >
      {children}
    </div>
  )
}

// later
<Canvas is={Container} />

I still prefer the <CanvasContainer/>, please let me know if that's wrong idea and understanding of the concept.

PS

Is there an API for droppable similar to how draggable is made?
I would totally prefer to use
<div ref=(ref => connect(drop(ref)) />
instead of wrapping my component in <Canvas is

setProps doesn't cascade to child Canvas element like I'd expect

minimalrepro.zip

Describe the bug
I'm trying to create a 'container' component, that is basically a styled div with a canvas inside it. The goal is for the user so select the container component and be able to modify properties on it using a properties panel. This works great if the prop changes something on the rendered div. However, if the prop is passed through to my custom Canvas, no changes are visible.

To Reproduce
Steps to reproduce the behavior:

  1. I've attached a minimal repro of what I'm describing.
  2. yarn and yarn start to execute the application
  3. open browser to localhost:3000
  4. The container is blue with a blue frame; this shows that the prop is settings the styled div (frame color) as well as the custom Canvas (background color)
  5. Select the container element. The 'Change Color' button appears.
  6. Click the button.

Expected behavior
The button sets the 'color' prop to yellow, so I would expect both the frame and the background color to become yellow.

Actual behavior
The frame turns yellow, so the prop is being changed on the container. However the background remains blue, indicating the prop change did not cause the canvas to re-render despite the prop being passed into it.

Screenshots
image

Additional context
Add any other context about the problem here.

Your environment

Software Version(s)
craft.js 0.1.0-beta.1
React 16.12.0
TypeScript 3.7.2
Browser Firefox
npm/Yarn yarn 1.19.1
Operating System windows 10

Add targetNode to canDrag rule

I have a BoxListContainer and BoxListItem

The BoxListContainer is set to only accept BoxListItems

BoxListContainer.craft.rules.canMoveIn = incomingNode => incomingNode.data.type === BoxListItem

Now I need to also set that BoxListItem can be dragged only into BoxListContainer. How do I do that?

canDrag(currentNode: Node, helpers: NodeHelpers) => boolean
canMoveIn(incomingNode: Node, currentNode: Node, helpers: NodeHelpers) => boolean
canMoveout(outgoingNode: Node, currentNode: Node, helpers: NodeHelpers) => boolean

I am missing targetNode in canDrag, the other two have both nodes available.

this:

canDrag(currentNode: Node, targetNode: Node, helpers: NodeHelpers) => boolean

BoxListItem.craft.rules.canMoveIn = (_currentNode, targetNode) => targetNode.data.type === BoxListContainer

Every canvas is draggable

Describe the bug
It seems every <Canvas /> element is draggable, regardless of whether or not it is an immediate child of another <Canvas />. While this may be hard to notice if the surface of every canvas is completely covered by other elements, like in a simple example, it causes lots of issues otherwise.

To Reproduce

This seems to happen to any <Canvas /> element not an immediate child of another Canvas, and becomes noticeable when that Canvas has a padding or a minimum size of some sort. This is noticeable with the root Canvas element immediately below the Frame element, as well as a Canvas element used to implement a simple styled container. A simplified version of the code from my project is as follows:

export const Container = ({children}) => {
	const { connectors: {connect, drag} } = useNode();

	return (
		<div className="container" ref={ref => connect(drag(ref))}>
			<Canvas id="content">
				{children}
			</Canvas>
		</div>
	)
}
.container {
	background: #0001;
	padding: 10px;
}

.container #content {
	padding: 10px;
}

In the above code, I expected half of the total 20px padding to act as the active region for placing elements inside the container, and the other half to act as the active region for placing elements outside the container. This part works, but the inside of the container becomes a draggable non-User Element, and dragging the container around only works as expected with the outer 10px of the padding. Additionally, dragging this inner 10px without activating the indicator and then letting go seems to crash the editor entirely.

Expected behavior
I expected Canvas nodes that are not immediate children of other Canvas elements to not be draggable, as mentioned in the documentation on the website.

Your environment

Software Version(s)
craft.js 0.1.0-beta.6
React 16.13.1
TypeScript 3.8.3
Browser Firefox 75.0
npm/Yarn 6.14.4
Operating System Solus 4.1

Improve connectors

Currently the usage is this

const { connectors: { connect, drag } } = useNode();

// and then
   return <div ref={connect}>

// which is basically this
   return <div ref={ref => connect(ref)}>

// or with drag
   return <div ref={ref => connect(drag(ref))}>

Could we instead use this pattern, so that in future, when there are more methods, we can combine them simpler?

   return <div ref={ref => connect(ref, drag, drop)}>
  
// instead of
   return <div ref={ref => connect(drag(drop(ref)))}/>

or even let connect() return curried itself if it doesn't detect a ref for these cases

   return <div ref={connect}> // this is OK already
   return <div ref={ref => connect(ref)}> // this is OK already

   return <div ref={connect()}>
   return <div ref={ref => connect()(ref)}>
   return <div ref={connect(drag)}>
   return <div ref={connect(drag, drop)}>
   return <div ref={ref => connect(drag, drop)(ref)}>

Support for Frame inside iframe / react-frame-component / react-styled-frame

I'd like to use Craft for a drag-and-drop React editor, where the preview is resizable to show responsive behavior.

I tried using react-styled-frame like this:

<Editor resolver={allComponents}>
    <StyledFrame>
        <Frame>
            <Canvas>
                <TextComponent text="Hello world" />
            </Canvas>
        </Frame>
    </StyledFrame>
</Editor>

...but the drag-and-drop indicators show up in the wrong space, probably using the global window coordinates instead of the coordinates within the iframe.

Indicator width over the line

Describe the bug
when i try to add Layers on left. i see that indicator over the line (offside)

To Reproduce
please review this #70

Expected behavior
it should be the same width

Screenshots
Screen Shot 2020-05-06 at 14 34 26

Your environment

Software Version(s)
craft.js 0.1.0-beta.5

Improve rules documentation

Add to UserComponent's rule's documentation when is each rule triggered. Meaning, when and how often is each callback called.
https://craft.js.org/r/docs/api/user-component

for example: (note, I don't know if my assumptions are correct)

rules?

  • canDrag(currentNode: Node, helpers: NodeHelpers) => boolean
    Specifies if a component can be dragged. Applicable only to components whose corresponding Node is a direct child of a Canvas.
    This callback is called when the current component is being dragged.
  • canMoveIn(incomingNode: Node, currentNode: Node, helpers: NodeHelpers) => boolean
    Decides if an incoming Node can be dropped into the current component. Applicable only to components whose corresponding Node is a Canvas.
    This callback is called on a target Canvas component, when a dragged (incoming) component hovers over it.
  • canMoveout(outgoingNode: Node, currentNode: Node, helpers: NodeHelpers) => boolean
    Decides if a child Node can be dragged out of the current component. Applicable only to components whose corresponding Node is a Canvas.
    This callback is called on a Canvas component (current), when dragging its (outgoing) child component.

Each callback is called for initial drag and then for every mouse event (ie. every pixel traveled).

If we can agree on the text here, I am happy to create a PR for it.

Undo editing ?

Describe the bug

In basic example, https://craft.js.org/examples/basic/ do any acting ,then undo it, not works

To Reproduce

Expected behavior
Action can undo

Screenshots

Additional context

Your environment

Software Version(s)
craft.js
React
TypeScript
Browser
npm/Yarn
Operating System macos, chrome 80

Initialising a new Editor, Frame and Canvas with some children results in an empty canvas

I use the basic tutorial guide available at this link
https://craft.js.org/r/docs/guides/basic-tutorial#overview

I just create a text component to try out the library and when I run initially my Canvas is empty even if I entered in some TextComponents

Screenshots

The result

Schermata 2020-04-26 alle 22 39 44

The code

1
2
3

Your environment

Software Version(s)
craft.js 0.1.0-beta.5
React 16.13.1
TypeScript 3.7.2
Browser Chrome
npm/Yarn 6.13.4
Operating System MacOS Catalina 10.15.2

Question: Skipping the Canvas in hover hierarchy

tl;dr: Example on CodeSandbox

In my project I make heavy use of the onRender callback. For example I add an indicator to every component which shows the name of the component.

This works fine for all Components but Connected Container Components (like Card in the Codesandbox example). Those components do not show the Components name "Card" but they show "Canvas" in the indicator.

I found a workaround by adding pointer-events: none to all the Canvases (uncheck the checkbox in the Codesandbox example).

The workaround works and I didn't find any drawbacks, yet. Do you have an idea on how to effectively hover the UserComponent and not the Canvas without my workaround?

It feels hacky to have the Canvas connected via JS and then disconnected by CSS. ๐Ÿค”

Move Exception handling

Describe the bug
Img cannot move in Root
Img can move in Container

rules: { canMoveIn: (incomingNode, currentNode, helpers ) => { return incomingNode.data.name !== 'Img' } }

To Reproduce

When I drag Img between Conainer and Root, I drop Img and throw error

index.js:15 Uncaught Error: Error: Invariant failed: Target parent rejects incoming node
at index.js:15
at Object.isDroppable (index.js:15)
at Object.move (index.js:15)
at index.js:1
at Immer.produce (immer.module.js:792)
at index.js:1
at updateReducer (react-dom.development.js:16737)
at Object.useReducer (react-dom.development.js:17492)
at useReducer (react.development.js:1711)
at D (index.js:1)

Screenshots

switch6

Additional context
I read the source code about https://github.com/prevwong/craft.js/blob/develop/packages/core/src/editor/actions.ts#L174

Expected behavior

Is there a better way or how I avoid it?

Your environment

Software Version(s)
craft.js 0.1.0-beta.6
React v16.10.2
TypeScript
Browser 83.0.4103.61
npm/Yarn v10.13.0
Operating System

useEditor actions.setProp documentation is incorrect

When I try to use actions.setProp from useEditor, it expects the second argument to be a function, not an object. I tried passing a function that returned an object with the props I wanted to change, but that doesn't appear to work either.

What is the proper usage for actions.setProp?

How to override the default drag event handler?

I'm trying to understand if there is an easy way to implement a different drag event handler.
I want it to be free dragging.
I saw issue #32 , but this implementation doesn't provide a way to drop the element on a different canvas.

P.S
Thank you for the hard work you put into this library!

Feature: Programmatically render Nodes

Allows users to define how nodes should be rendered, and attach click/drag handlers on the desired DOM elements. Eg:

const RenderMyNodes extends React.Component {

  render() {
    const { nodeState, handlers, Component } = this.props;
    return (
       <React.Fragment>
           <Component ref={
               if ( ref ) handlers.click(ReactDOM.findDOMNode(this))
           } />
           {nodeState.active && <a ref={(dom) => handlers.drag(dom)}>Move</a>
       </React.Fragment>
    )
  }
}

Dynamically add props to resolver

Screenshot 2020-04-22 at 11 11 51 AM

I want to allow people to edit the index page for dropping the slider in the admin portal which

export const UpcomingAuction = (props) => {
  const { enabled } = useEditor((state) => ({
    enabled: state.options.enabled,
  }));
  const {
    connectors: { connect },
  } = useNode((node) => ({
    selected: node.events.selected,
  }));

  const {} = props;

  return (
    <ADiv ref={connect}>
      <img
        style={{ width: "100%" }}
        src="https://via.placeholder.com/728x300.png?text=Upcoming_Auction"
        enabled={enabled}
      ></img>
    </ADiv>
  );
};

UpcomingAuction.craft = {
  name: "UpcomingAuction",
  defaultProps: {},
  related: {
    toolbar: UpcomingAuctionSettings,
  },
};

And use it in front end as
Screenshot 2020-04-22 at 11 15 56 AM

 <div className={className}>
       <CarouselProvider
              visibleSlides={1}
              totalSlides={upcomingAuctions.length}
              step={1}
              naturalSlideWidth={1600}
              naturalSlideHeight={1200}
              // isPlaying={true}
              lockOnWindowScroll={true}
              infinite={true}
            >
              <div style={Style.container}>
                <Slider style={Style.slider}>
                  {upcomingAuctions.map((auction, index) => (
                    <Slide
                      key={index}
                      index={index}
                      style={{ maxWidth: "100%" }}
                    >
                      <Link to={`/auction/${auction.id}`}>
                        <SmartMedia
                          options={{
                            maxWidth: 1600,
                            controls: false,
                            autoPlay: true,
                            originalElement: true
                          }}
                          mediaId={auction.landscapeMediaId}
                        />
                      </Link>
                    </Slide>
                  ))}
                </Slider>
                <ButtonBack style={Style.buttonBack}>
                  <NavigateBeforeIcon fontSize={"large"} />
                </ButtonBack>
                <ButtonNext style={Style.buttonNext}>
                  <NavigateNextIcon fontSize={"large"} />
                </ButtonNext>
              </div>
              <DotGroup style={Style.dotGroup} />
            </CarouselProvider>
      
      </div>

They are achieved by giving different resolver with the same name Upcoming Auction like

export function renderMarkup({base64,upcomingAuctions,pastAuctions}) {
  let json = lz.decompress(lz.decodeBase64(base64));

  let markup = (
 <Editor
      enabled={false}
      resolver={{
        Container,
        Text,
        Custom1,
        Custom2,
        Custom2VideoDrop,
        Custom3,
        Custom3BtnDrop,
        OnlyButtons,
        Button,
        Video,
        Image,
        UpcomingAuction,
        PastAuction,
      }}
    >
      <Frame json={json} />
    </Editor>
  return markup;
}

Describe the solution you'd like
But it bothers me how I pass props to the Upcoming Auctions?

Deserializing a Canvas with empty children raises an unhandled exception and fails

Hi,
First of all, I'd like to point out that this package is amazing.

We've been making use of it and encountered an issue when trying to serialize/deserialize our nodes.

A sample of the serialized output is:

{"canvas-ROOT":{"type":"div","isCanvas":true,"props":{"className":"page null"},"displayName":"Canvas","custom":{},"nodes":["canvas-jdl3oRkem"]},"canvas-jdl3oRkem":{"type":{"resolvedName":"Container"},"isCanvas":true,"props":{"children":null,"padding":20,"alignment":"column"},"displayName":"Canvas","custom":{},"index":0,"parent":"canvas-ROOT","nodes":["node-BbFEKEMN7"]},"node-BbFEKEMN7":{"type":{"resolvedName":"TextField"},"props":{"text":"Talks","fontSize":20},"displayName":"TextField","custom":{},"index":0,"parent":"canvas-jdl3oRkem"}}

When trying to deserialize it, we receive the following error:

Uncaught TypeError: Cannot read property 'resolvedName' of null

I have trace the issue to the following line:

if (typeof prop === "object" && prop.resolvedName) {

What I believe that happens, is that as we can see, the node canvas-jdl3oRkem has a null children prop, null is truthy for typeof null === "object" however, it does not allow accessing it's attributes.

A possible solution would be to check if the prop value is null before trying to access it.

I'm also trying to understand why the null children prop is there to begin with.

What do you think?
@prevwong

Possible to overlap components, and move freely around canvas?

This might sound like a strange request, though I am wondering if it is possible to freely move components around the entire container, including overlapping other components. Though I do understand the reason for doing it like this. What I mean is like this project, which is using vue rather than react.

https://vuegg.now.sh/
https://github.com/vuegg/vuegg

The reason I ask, is because the designer I am making is more or less a basic desktop mockup editor - looking to have the editor being able to move components around like VS.

The only other thing is maybe use something like react-canvas with craft.js and create new components for it so they can be drawn on the .

actions.setNodeSelection in useEditor

Is your feature request related to a problem? Please describe.
When trying out craft.js the first time I needed to select a node when clicking something. I expected it to work like

const { setNodeSelection } = useEditor();
const { parent } = useNode(node => ({ parent: node.data.parent }));
....
setNodeSelection(parent, true);

Thanks to the documentation I found the way to select nodes. ๐Ÿ‘

Describe the solution you'd like
If you are open to API changes I would like to open a PR (I have already implemented it in my fork) so that useEditor exposes setNodeSelection(id: NodeId, isSelected: boolean). The exposed method would make it possible to programmatically select nodes without relying on ref and the mousedown handler.

Improve creation of new elements

Currently when an element with connector.create is dragged to the <Frame />, it creates and appends a new Node (and thus renders a new instance of the User Element) to the first Canvas that cursor reaches.

ezgif com-video-to-gif (5)

I think that this "breaks" the user experience a little bit as it feels rather unnatural/unintuitive.

I would prefer a solution similar to how Grape.js handles it:
ezgif com-optimize (10)

Notice how the new element does not get rendered until dragging ends. This should be a rather simple fix at the EventManager

Attempting to add a node with duplicated id

Unhandled Runtime Error
Error: Error: Invariant failed: Attempting to add a node with duplicated id

Environment

Software Version(s)
craft.js 0.1.0-beta.6
React 16.8.6
Browser Chrome 83
npm/Yarn 6.14.4
Operating System MacOS 10.15.4

Screen Shot 2020-06-09 at 2 44 52 PM

Feature: Allow canvas to programmatically allow/reject incoming and outgoing nodes

Canvas needs to be able to check whether the incoming/outgoing component is allowed.

Current proposal:

<Canvas
  incoming={(node: Node, children: Array<Node>) => {
    // Logic happens here
    if (node.name === "Button") {
      const count = children.filter(child => child.name === "Button").length;
      if (count > 3) { // too many buttons in container already
        return false; 
      }
    }
    return true;
  }}
  outgoing={(node: Node, children: Array<Node>) => {
    // Logic happens here
    if (node.name === "Button") {
      const count = children.filter(child => child.name === "Button").length;
      if (count <= 1) { // too few buttons, so prevent current button from being moved out
        return false; 
      }
    }
    return true;
  }}
>
...
</Canvas>

Rich Text Editor - Is there an example?

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Is it possible to use a Rich Text Editor for editing the text content. Is there an example on how this can be done?

Describe the solution you'd like
A clear and concise description of what you want to happen.

I would like to see Rich Text Editor added to examples and documentation.

Nodes reset on hot module replacement (HMR)

First of all, this is an awesome project! It's exactly what I was looking for and it is so well-built! Thank you very much.

Describe the bug
I am building a craft.js-based editor using Gatsby, which uses Webpack's hot module replacement (HMR) feature to update the editor as I save changes to project source files. When HMR takes place (actually, after--see below), the editor loses all nodes from the state except for the root node. Other aspects of the state (eg. selection) are preserved.

To Reproduce
Steps to reproduce the behavior:

  1. Host a sample editor locally using Gatsby or at least Webpack's hot module replacement feature.
  2. Add some Nodes to the root Node so that the state is nontrivial.
  3. Make a trivial change to a source file (such as adding an empty line) and save to trigger HMR.
  4. When the web editor updates, the nodes vanish.

Expected behavior
When the web editor updates, the nodes should remain.

Additional context
This might be a quick fix, but I don't have enough context to know for sure. To debug, I wrote the state to the console on render, and found that the state is indeed preserved after HMR, but a second render happens immediately after the first where the non-root nodes have been removed from the first render. I believe the cause is the replaceNodes() call in useEffect() in Frame.tsx. I'm not sure if there's a simple logic change here or if it is a deeper problem, but again, the state seems to be clobbered in replaceNodes() and not by the HMR itself.

Your environment

Software Version(s)
craft.js 0.1.0-beta.3
React 16.12.0
TypeScript 3.5.1
Browser Chrome 78.0.3904.108
npm/Yarn npm 6.4.1
Operating System OS X 10.14.6

Connectors will not work when the Editor is initially disabled

Describe the bug

When the enabled prop in <Editor /> is initially set to false, all connectors fail to work even after the prop is set to true.

const App = () => {
   const [enabled, setEnabled] = useState(false);

   return (
      <Editor enabled={enabled}
         ....
      </Editor>
   )
}

const Btn = () => {
    const { connectors } = useNode();
     // connectors wont work even after the Editor is enabled
    return <button ref={connectors.connect}>Click</button>
}

Your environment

Software Version(s)
craft.js v0.1.0-beta-3

Add draggedOver to state.events

/edit Title changed from

Why is dragged item also hovered when dragging over another item?

This is not a bug per se, but still an unexpected and weird behaviour.
I am dragging an item from its former location to another location, my cursor is above another Container, but the original dragged item still remains hovered even though that doesn't really make sense.
image

I have discovered this weird behaviour while trying to work around the limitation in #26.
I desperately need to tell which element am I currently dragging over. I used this custom proxy hack to achieve that, maybe it's wrong and I am getting stale value there?

const BoxListItem: UserComponent<BoxListItemProps> = props => {
  const { id, connectors: { connect, drag } } = useNode();
  const { actions: { setCustom }, hovered } = useEditor(state => console.debug('hovered', state.events.hovered) || ({ hovered: state.nodes[state.events.hovered].data.type }) )

  setCustom(id, custom => custom.hovered = hovered)
 
  // return JSX
BoxListItem.craft = {
  rules: {
    canDrag: node => console.debug('node.data.custom.hovered === ', node.data.custom.hovered === BoxList, node.data.custom.hovered) || node.data.custom.hovered === BoxList
  }
}
// this is where the state keeps printing
export const PropertiesPanel = () => {
  const {  PropsPanel, name } = useEditor(state => {
    if (!state.events.selected) return;
    console.debug('state', state.events, state)
    // return collector

Even though I will work on the canDrop rule as mentioned in #26 to implement the functionality, I believe that the editor state should better reflect what events are happening.

mobile interaction

Is your feature request related to a problem? Please describe.
I would like to use craft.js on mobile. however, it currently doesnt support drag and drop event on mobile

Describe the solution you'd like
I'm willing to contribute to this feature. i need someone to point me to where in the repo i should look for to make this happen.

This is an awesome library! thank you very much for all the work

Text with react-rte

Screenshot 2020-04-20 at 4 38 52 PM

Basically I am creating a textarea component react-rte for the better format, but don't know a good way to implement it that causing me to lost cursor each type I type wording

 <RichTextEditor
            value={RichTextEditor.createValueFromString(value, "html")}
            onChange={(richVal) => {
              console.log("new value", richVal.toString("html"));
              setProp((props) => {
                props[propKey] = richVal.toString("html");
              });
            }} // use true to disable editing
            style={{
              margin: "27px",
              border: "1px solid lightgray",
            }}
          />

Error: Target parent rejects incoming node

Element canvas with id is rejecting the user class component
I have two element canvas with id and a rule that says only certain user component can move in.
However, when I try to move component from one canvas to another the incomingNode changes to Proxy.

To Reproduce
Steps to reproduce the behavior:

  1. Create two canvas elements with id
  2. Add rule to only allow a user component
  3. Create a new user class component
  4. Drop user class component to first editor
  5. Drag the component to another editor
  6. Error occurs and app crashes.

Expected behavior
Allow to drop the user component if the moveIn rule matches

Screenshots
Screen Shot 2020-06-22 at 1 43 27 pm

Your environment

Software Version(s)
craft.js ^0.1.0-beta.7
React ^16.11.0
TypeScript
Browser Chrome 83.0.4103.106
npm/Yarn 1.22.4
Operating System macOS 10.15.5

Undefined error when selecting object then deleting.

Describe the bug
Cannot read property 'data' of undefined at Object.setCustom

To Reproduce
Steps to reproduce the behavior:
This can be replicated at https://craft.js.org/. Select a text element then press the delete trash can.

This error only occurs when you explicitly select the object. Hovering over the object then pressing delete will still work.

This error seems to only happen to child components, you can successfully delete the outer most parent after clicking it, but not its inner components

Expected behavior
Select object -> press delete -> object deletes

image

Error calling useEditor hook or connectedEditor

Describe the bug
I'm just trying to create an example app but crashes, if I send the collector function as param says that some variable (I can't see which because everything in minified) is not a function

with connectedEditor HOC I get the same issue

To Reproduce
Steps to reproduce the behavior:

  1. npx create-react-app
  2. render Editor Frame and Canvas
  3. create a component with useEditor
  4. call useEditor inside of the component
  5. crash

Expected behavior
use the connectors.create for create new nodes

Screenshots
If applicable, add screenshots to help explain your problem.
image

image

Additional context
I can drag elements inside of the editor but when I want drag an external node it just crashes with just call the hook

Your environment

Software Version(s)
craft.js ^0.1.0-beta.6
React 16.13.1 using cra 3.4.1
TypeScript
Browser
npm/Yarn
Operating System

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.