prevwong / craft.js Goto Github PK
View Code? Open in Web Editor NEW๐ A React Framework for building extensible drag and drop page editors
Home Page: https://craft.js.org
License: MIT License
๐ A React Framework for building extensible drag and drop page editors
Home Page: https://craft.js.org
License: MIT License
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:
actions.delete(canvas_id)
query.serialize()
Expected behavior
Based on these lines, I think it is supposed to be deleting all the nodes within the canvas.
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 |
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?
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.
Describe the bug
actions.deserialize
does not honor the hidden
attribute on nodes.
To Reproduce
state = query.serialize()
actions.deserialize(state)
Expected behavior
If the hidden attribute is set in the serialized state, the node should be created in a hidden state.
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 |
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.
I want to create a component similar to 3 columns
component in grapejs https://grapesjs.com/demo.html
I want the container and all inside elements to be draggable, droppable and deletable, I've tried different options but none meet all the above requirements.
is this achievable?
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:
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
Software | Version(s) |
---|---|
craft.js | beta.4 |
React | 16 |
TypeScript | |
Browser | chrome |
npm/Yarn | yarn |
Operating System | macOS Cataline |
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:
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.
Web Computing
Software | Version(s) |
---|---|
craft.js | |
React | |
TypeScript | |
Browser | |
npm/Yarn | |
Operating System | Windows |
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.
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.
In fact, our company did such a project based on antd3. X components. The biggest difficulty is mixture dev. How to transfer the data between craft component and developer's local code
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.
Additional context
Add any other context about the problem here.
Software | Version(s) |
---|---|
craft.js | beta.4 |
React | 16.13 |
TypeScript | yes |
Browser | chrome |
npm/Yarn | yarn |
Operating System | osx high sierra |
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!
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!
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.
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.
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
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:
yarn
and yarn start
to execute the applicationlocalhost:3000
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.
Additional context
Add any other context about the problem here.
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 |
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
onClick event not working in user components but onMouseDown event it works!
e.g. Text component in basic examples
Software | Version(s) |
---|---|
Material-UI | 4.10.2 |
craft.js | 0.1.0-beta.7 |
React | 16.8.6 |
Chrome | 83 |
npm | 6.14.4 |
macOS | 10.15.4 |
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.
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 |
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)}>
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.
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
Software | Version(s) |
---|---|
craft.js | 0.1.0-beta.5 |
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
Node
is a direct child of a Canvas
.canMoveIn(incomingNode: Node, currentNode: Node, helpers: NodeHelpers) => boolean
Node
is a Canvas
.canMoveout(outgoingNode: Node, currentNode: Node, helpers: NodeHelpers) => boolean
Node
is a Canvas
.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.
This looks like grapesjs what is the motivation behind the project, it looks interesting though
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
Software | Version(s) |
---|---|
craft.js | |
React | |
TypeScript | |
Browser | |
npm/Yarn | |
Operating System | macos, chrome 80 |
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
The code
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 |
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 Canvas
es (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. ๐ค
I don't know how to export my page
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)
Additional context
I read the source code about https://github.com/prevwong/craft.js/blob/develop/packages/core/src/editor/actions.ts#L174
Is there a better way or how I avoid it?
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 |
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
?
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!
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>
)
}
}
I am looking for file uploading for react application.
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,
},
};
<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?
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:
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
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 .
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.
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.
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:
Notice how the new element does not get rendered until dragging ends. This should be a rather simple fix at the EventManager
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>
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.
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:
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.
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 |
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>
}
Software | Version(s) |
---|---|
craft.js | v0.1.0-beta-3 |
/edit Title changed from
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.
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.
Allow Nodes to be moveable within and across Canvas
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
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",
}}
/>
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:
Expected behavior
Allow to drop the user component if the moveIn rule matches
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 |
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
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:
Expected behavior
use the connectors.create for create new nodes
Screenshots
If applicable, add screenshots to help explain your problem.
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
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 |
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.