Hello house, how would you suggest a nested db like {:id id :children [{:id id :children []...} {...} {...} ]... (with uncertain depth) be updated, assuming I have the id of a deep child?
I am currently using the DB format {id1 {:id I'd children [childID1 childID1 childID1...]...} id2 {:id2 id2 :children [id3....]}....
In the second (current) approach, all the nodes are in same depth except that the parent node takes the ids of the children. So to render them, I just need to find the node from the ID and render in its parent. Which is lot easier, but rerenders all the components when any node is added or removed. The system will become slow when the nodes are large (by this approach). I just feel changing to the second approach will fix this issue. Or what's your suggestions?..
I think the first approach is the once used in re-com but component-wise
OK. I'll do, I just need to take care of some things first.
Thank you
> So how do i create a simple function that will render them other than this that does that recursively? The general pattern is something like this:
(defn item [{:keys [id children-ids]}]
(let [data @(rf/subscribe [:item-data id])]
[:div {:key id}
data
(for [child-id children-ids]
^{:key child-id}
[item {:id child-id}])]))
Of course, that last item can be some other component, doesn't have to be the same one.
The main things here are that all the unchanging IDs are passed as arguments, all the actual data is gathered via subs only in the components that need that data (it's perfectly fine to call the same sub multiple times from multiple places - they're cached anyway), and you never call view functions as functions, you always use them as items in Hiccup vectors.
> Another reason why i'm creating the IDs in the view is because, i discovered that if i create same node (plugin) multiple times (withe ID in the structure), they all tend to have the same IDs when rendered
That just means you were doing something incorrectly.Yes and thank you for the input. I understood this pattern actually, but why I used the "giant hiccup" one was because, i wanted to set a master view that already has all the default DOM functions in place, and should not be altered by anybody who'll create his own plugin in the future. So by this, all the plugins will follow a similar and simple pattern, instead of looping withing them for children all the time. So, the idea is that, people can be able to make their own plugin in the future, without paying attention to how the children are rendered (cos I've Done that somewhere and should not be changed by any plugin) It will be like an open source platform where other devs can just create their own plugins (only) but not touch the main code.
I don't see how the approach that I outline contradicts the desire for the system to be pluggable. You can create DOM functions in the parent component and pass them down - that's fine, you don't need to create the whole tree in a single function. Behavior of particular components can easily be made customizable with multimethods, for example. And at the same time, the same pattern will still be followed by all plugins. None of the plugins will have to loop over the children.
OK. Lemme switch to the pattern and see how iy goes. But what about the current db map pattern, do you see any need to change it to the deep depth method I explained earlier? Currently they are all at the same level
No, avoid nesting in the data. It brings nothing but problems, unless you never have duplication, never need to update anything, never need to get some deeply nested item's data without caring about its parents.
> I am currently using the DB format [...] That's the way to do, normalized data is much better. > Which is lot easier, but rerenders all the components when any node is added or removed. Restructure your subscriptions.
Subscriptions can have other subscriptions as their input signals.
I use the map to render as different components recursively. It's not about subs, the logic of the recursive function and it's reliability is what I want suggestions for
Currently, I'm thinking to use clojure.walk but I feel it will rerender the components like the current one for every changes this time instead of node addition or subtraction alone
Uhm, if you send all the data to a view, if will be re-rendered if any piece of that data changes, no matter what.
That's the issue. In the current setting, it only rerenders the current component only when it changes... But the systems freezes sometimes when the nodes gets heavy
The desire to send all the data to the top level component (exactly what you're doing, if I understood you correctly) is not compatible with the hard reality of components re-rendering when the input data changes. You have to make a sacrifice here and pass only limited data, even to the top-level component. All the children components can derive their data from subscriptions based on the input IDs that won't be changing.
Currently, every node has its :key value in the map that feeds the particular component when it renders.
So are you suggesting I have a separate map for those data and another nested map for the components?
Besides, the nodes doesn't hold the same data for all.
I need a code sample to provide further comments.
OK. In some time. I'm on mobile phone now
Here's the current approach map:
{"ce-136c62fa-abde-46aa-a232-b80c936db36e"
{:id "ce-136c62fa-abde-46aa-a232-b80c936db36e",
:type :paragraph,
:text "",
:focus? false,
:typing?
{:nodeTree {:start [], :end []}, :position {:start 0, :end 0}}},
"ce-082d4905-0321-4b5c-9d53-1373cfcec60a"
{:id "ce-082d4905-0321-4b5c-9d53-1373cfcec60a",
:type :block,
:children ["ce-7bc1f3ce-631d-4bdf-ad48-40f9fcf66f1c"],
:size {},
:focus? true},
"ce-558b764a-e83c-48b3-9277-cd4f58da991d"
{:id "ce-558b764a-e83c-48b3-9277-cd4f58da991d",
:type :paragraph,
:text "",
:focus? false},
"ce-ea43a461-a3ad-4a7d-a06d-79a230cd324d"
{:id "ce-ea43a461-a3ad-4a7d-a06d-79a230cd324d",
:type :checklist,
:text "sghghs vdgds",
:check? true,
:group :default,
:replaced? true,
:focus? false,
:typing?
{:nodeTree {:start [0], :end [0]}, :position {:start 12, :end 12}}},
"ce-5b58e6d9-07f7-4faf-a09c-92708ad0e1ee"
{:id "ce-5b58e6d9-07f7-4faf-a09c-92708ad0e1ee",
:type :checklist,
:text "sddskjsdjsdjkdkj",
:check? false,
:group :default,
:focus? false,
:typing?
{:nodeTree {:start [0], :end [0]}, :position {:start 16, :end 16}}},
"ce-beeddd2c-7714-4b56-ba5d-f322ee8484bc"
{:id "ce-beeddd2c-7714-4b56-ba5d-f322ee8484bc",
:type :frame,
:orientation "horizontal",
:children ["ce-ff46dae8-fef6-4f2f-a750-112578c965ad"],
:style nil,
:focus? true},
"ce-631e1047-9202-404c-8bbe-e87bd8ff0b49"
{:id "ce-631e1047-9202-404c-8bbe-e87bd8ff0b49",
:type :paragraph,
:text "nbdshsdhdshhjds",
:focus? false,
:typing?
{:nodeTree {:start [0], :end [0]}, :position {:start 15, :end 15}}},
"ce-b074774a-0cb7-48f7-b30d-07fb619977c9"
{:id "ce-b074774a-0cb7-48f7-b30d-07fb619977c9",
:type :frame,
:orientation "horizontal",
:children ["ce-90e3ab4a-49e4-4ec9-8048-b0f6a12bf2f5"],
:style nil,
:focus? true},
"ce-ff46dae8-fef6-4f2f-a750-112578c965ad"
{:id "ce-ff46dae8-fef6-4f2f-a750-112578c965ad",
:type :block,
:children ["ce-0f2538d7-fb02-4465-ae70-297d8f7ba76e"],
:size {},
:focus? true},
"ce-391d8be6-bef4-45cc-b23c-38255398584b"
{:id "ce-391d8be6-bef4-45cc-b23c-38255398584b",
:type :frame,
:orientation "vertical",
:children
["ce-0561f7f1-7ea4-4b9d-9773-560b1f302b6a"
"ce-082d4905-0321-4b5c-9d53-1373cfcec60a"
"ce-5508fc1f-5c4b-4fb0-848a-205fb58458b7"],
:style nil,
:focus? true},
"ce-0f2538d7-fb02-4465-ae70-297d8f7ba76e"
{:id "ce-0f2538d7-fb02-4465-ae70-297d8f7ba76e",
:type :paragraph,
:text "hello world<br><br>",
:focus? true,
:typing?
{:nodeTree {:start [0], :end [0]}, :position {:start 11, :end 11}}},
"ce-943f54db-6d18-41a3-8f81-0eacbf7dbe69"
{:id "ce-943f54db-6d18-41a3-8f81-0eacbf7dbe69",
:type :paragraph,
:text "",
:focus? true,
:typing?
{:nodeTree {:start [], :end []}, :position {:start 0, :end 0}}},
"ce-9a77fe09-9df9-49fe-99b1-c1817c74da15"
{:id "ce-9a77fe09-9df9-49fe-99b1-c1817c74da15",
:type :paragraph,
:text "<br>",
:focus? true,
:typing?
{:nodeTree {:start [], :end []}, :position {:start 0, :end 0}}},
"ce-7d559404-fdd2-48cd-b02f-b1002ddee87b"
{:id "ce-7d559404-fdd2-48cd-b02f-b1002ddee87b",
:type :frame,
:orientation "vertical",
:children
["ce-0561f7f1-7ea4-4b9d-9773-560b1f302b6a"
"ce-e8429709-2dcb-48d5-9c8d-d536c98597a8"
"ce-082d4905-0321-4b5c-9d53-1373cfcec60a"
"ce-5508fc1f-5c4b-4fb0-848a-205fb58458b7"],
:style nil,
:focus? true},
"ce-5508fc1f-5c4b-4fb0-848a-205fb58458b7"
{:id "ce-5508fc1f-5c4b-4fb0-848a-205fb58458b7",
:type :block,
:children ["ce-5b58e6d9-07f7-4faf-a09c-92708ad0e1ee"],
:size {},
:focus? true},
"ce-0561f7f1-7ea4-4b9d-9773-560b1f302b6a"
{:id "ce-0561f7f1-7ea4-4b9d-9773-560b1f302b6a",
:type :block,
:children ["ce-ea43a461-a3ad-4a7d-a06d-79a230cd324d"],
:size {},
:focus? true},
"ce-e7990dba-448b-4430-9e26-8a9960897cb2"
{:id "ce-e7990dba-448b-4430-9e26-8a9960897cb2",
:type :paragraph,
:text "dsghhdhgdsnms<br>",
:focus? false,
:typing?
{:nodeTree {:start [0], :end [0]}, :position {:start 13, :end 13}}},
"ce-a186684c-5f62-4d4a-a33d-e1b7ecb94c23"
{:id "ce-a186684c-5f62-4d4a-a33d-e1b7ecb94c23",
:type :block,
:children ["ce-e7990dba-448b-4430-9e26-8a9960897cb2"],
:size {},
:focus? true},
"title" {:focus? false},
"ce-7bc1f3ce-631d-4bdf-ad48-40f9fcf66f1c"
{:id "ce-7bc1f3ce-631d-4bdf-ad48-40f9fcf66f1c",
:type :checklist,
:text "smnjjsdjkjks",
:check? false,
:group :default,
:focus? false,
:typing?
{:nodeTree {:start [0], :end [0]}, :position {:start 12, :end 12}}},
"ce-839f14f7-85a8-4c48-a9ff-3ace54634096"
{:id "ce-839f14f7-85a8-4c48-a9ff-3ace54634096",
:type :paragraph,
:text "",
:focus? true,
:typing?
{:nodeTree {:start [], :end []}, :position {:start 0, :end 0}}},
"ce-f715000a-75e3-409f-96cf-f9fe7edc6e5a"
{:id "ce-f715000a-75e3-409f-96cf-f9fe7edc6e5a",
:type :frame,
:orientation "horizontal",
:children
["ce-7d559404-fdd2-48cd-b02f-b1002ddee87b"
"ce-391d8be6-bef4-45cc-b23c-38255398584b"],
:style nil,
:focus? true},
"ce-e8429709-2dcb-48d5-9c8d-d536c98597a8"
{:id "ce-e8429709-2dcb-48d5-9c8d-d536c98597a8",
:type :block,
:children ["ce-631e1047-9202-404c-8bbe-e87bd8ff0b49"],
:size {},
:focus? true},
"Untitled-d73e9656-8a91-4acb-a626-659c1a1117bf" {:title ""},
"ce-acc62480-5102-41f0-92e8-84c08030685c"
{:id "ce-acc62480-5102-41f0-92e8-84c08030685c",
:type :paragraph,
:text "",
:focus? true}}all the nodes are on the same level, but the parents have their children IDs as a vector. So by this, i can search for the node map, and render them (recursively).
So, by this, if any node is added, it re-renders all of them instead of just the node that will parent it.
So i'm thinking of moving the approach to putting every children map into a vector of the key :children contained in the parent node, so if i add a new node, the to-be parent will be the only node to be re-rendered.
Now, the logic to update a certain node in an uncertain depth without triggering a whole db update is the problem now
I'm more interested in an approximate structure of your views and relevant subscriptions.
This is an event that updates a node
(reg-event-db
:nodes/update
(undoable "A node was updated")
(fn [db [_ page-id node-id field & value]]
(if value
(assoc-in db [:pages page-id :nodes node-id field] (first value))
(update-in db [:pages page-id :nodes node-id] conj field))))*event, not a subscription
The render code here:
you can preview the db here: https://www.flatter.to/editor
Sorry but your code is as far from what it's supposed to be in the realm of re-frame/Reagent as possible.
• Don't pass all the data around (hard to see what's going on exactly on the video, but seems like that's the case), use very specific subscriptions instead
• Don't call functions everywhere, turn most of them into Reagent components
• Don't use random :key values
• Don't generate random data on render
There may be other issues, I haven't looked past these. There's a lot of work that code needs before it'd become something I'd approve if it were in some PR that I had to review.
The functions are utility functions that are used many times in the whole project, also, they don't return anything other than map except for this:
(defn render-elements [node & child]
(let [click-fn (fn []
(.trigger (js/$ "#settings-btn") "click"))
mouse-on-fn (set-active-node)
mouse-out-fn (remove-active-node)
db-click-fn (db-click-fn)
drag-start-fn (drag-start-fn)
drag-over-fn (drag-over-fn)
drop-fn (drag-end-fn)
enter-fn (ENTER-fn)
key-down-fn (key-down-fn)
paste-fn (paste-fn)
toggle-click-fn (handle-click-fn)
;; select-fn (selection-fn)
;; node (if (= @(subscribe [:nodes/focus]) (:id node)) (assoc node :focus? true) (assoc node :focus? false))
node (assoc node
:click-fn click-fn
:mouse-on-fn mouse-on-fn
:mouse-out-fn mouse-out-fn
:db-click-fn db-click-fn
;; :drop-fn drop-fn
:drag-over-fn drag-over-fn
:enter-fn enter-fn
:key-down-fn key-down-fn
:paste-fn paste-fn
:toggle-click-fn toggle-click-fn
;; :select-fn select-fn
)
render-fn (fn [ele node]
(let [id (:id node)
parent-id (get-node-parent-id id)
has-parent? (if (or parent-id (seq? parent-id)) true false)
block_id (str "ce-" (random-uuid))
content_id (str "ce-" (random-uuid))]
(if-not has-parent?
[:div {:key (rand 9876543567)
:id block_id
:class "ce-block"
:on-mouse-over (remove-active-node)}
[:div {:key (rand-int 98765435678)
:id content_id
:class "ce-block__content"} ele]]
ele)))
element (render-options node child)]
(render-fn element node)))
The (let []) bindings are functions for the DOM, passed as props into each plugin (node).
This:
[:div {:key (rand 9876543567)
:id block_id
:class "ce-block"
:on-mouse-over (remove-active-node)}
[:div {:key (rand-int 98765435678)
:id content_id
:class "ce-block__content"} ele]]
is generated for every new block, but doesn't go to the DB, its just for proper styling only. I have nodes that are parents, and those that are children as shown in the db map above. I do render them as Reagent components (all of them). This is the simplest of the plugins (components) i am referring to:
(def menu
{:name "Frame"
:type :frame
:icon icons/IconTableWithoutHeadings
:class ""})
(def structure
{:id (str "ce-" (random-uuid))
:type :frame
:orientation "horizontal" ;; or horizontal
:children []
:style nil})
(defn element [node & children]
(let [{id :id
orientation :orientation
db-click-fn :db-click-fn
style :style} node]
[:div {:key (rand-int 324567890)
:id id
:style (or style {:flex 1 :min-width "20%" :max-width "100%"})
:class (str "frame cdx-block__frame__" orientation)
:on-click db-click-fn}
children]))
only [element] is rendered , structure goes to the db as a mapEvery plugins have different structure and element Also, the events and subs, are fundamental to all the plugins except for plugins that requires specific event and subscription.
Ids must be randomly and uniquely generated cos, when you drop a plugin after D'n'D, it copies /moves the plugin to a new parent, which requires a new unique ID, i.e a checkbox will check its identical node when clicked cos they have same ID
I didn't mean utility functions, I meant functions that return Hiccup.
Your code ends up with a single giant Hiccup structure. Instead, that structure should be small. All the inner components will generate their own structure.
In other words, in many places, (f) should actually be [f].
> Ids must be randomly and uniquely generated That's fine to have random IDs. But you should not generate them in views - instead, generate them in whatever ends there are that populate the data.
Ok. So how do i create a simple function that will render them other than this that does that recursively? It is a
parent -> children -> grand-children ->>>
DOM, and the map is a single level map. Hw do i render them in small small pieces that will not be as this (recursive)?
Would you suggest a simpler pattern or code for this?Another reason why i'm creating the IDs in the view is because, i discovered that if i create same node (plugin) multiple times (withe ID in the structure), they all tend to have the same IDs when rendered
The map method i am using, is same as used by Figma i guess, cos i've coded a page that renders Figma JSON into HTML & CSS in the past. Thats where the Idea of same level nodes came from.
Now, I'm thinking of switching to the Webflow JSON pattern (which i've also worked with before) of having nodes in their depth and hierarchy (parent -> children) and not same level
Hello @p-himik, I've implemented the children loops using the (for...) loop (i.e every component loop through to render their own children instead of the one big recursive component as before), but the issue now is, as the loop goes like 4 children deep, it becomes infinite and freezes the system. I don't really know what's going on.
It actually works fine when the loop is <4 children deep.
> I don't really know what's going on. Well, neither do I - I'm not an oracle, unfortunately.
😂 Actually... I'll try to figure it, then I'll post the code if I had to. Thanks
Please do you have any idea how I can memoize a component? I can't find reagent.core/memo or if you can suggest a better way to do it.
Here's my code:
(defn render-children [children funcs]
(when-not (empty? children)
(doall (for [child-id children
index (range (count children))]
(let [child (search-node child-id)]
[render-options child funcs])))))
(defn render-options [node functions]
(let [{type :type} node]
(case type
:frame [frame/element node functions]
:block [block/element node functions]
:paragraph [paragraph/element node functions]
:header [headers/element node functions]
:list [list/element node functions]
:image [image/element node functions]
:checklist [checklist/element node functions]
:toggle-list [toggle-list/element node functions]
:delimiter [delimiter/element node]
:link [link/element node functions]
:page [page/element node functions]
:quote [quote/element node functions]
nil)))
list/element
(defn element [node funcs]
(let [{id :id
text :text
tag :tag
children :children} node
{key-down-fn :key-down-fn
set-active-fn :mouse-on-fn
remove-active-fn :mouse-out-fn
render-children :render-children
list? :list?} funcs
class (if (= tag :ol)
" cdx-nested-list--ordered"
" cdx-nested-list--unordered")
list-type (if (= tag :ol)
{:list-style "number"}
{:list-style "disc"})
parent-id (atom nil)]
(r/create-class {:component-did-mount (fn [comp]
;; (.addEventListener (.querySelector (rdom/dom-node comp) ".tt") "selectstart" (fn []
;; (.addEventListener js/document "selectionchange" (selection-start-fn))))
(.addEventListener (rdom/dom-node comp) "focusin" (fn [evt]
(set-active-fn evt)
(dispatch [:nodes/set-focus id])))
(let [_this (rdom/dom-node comp)
ancestor _this.parentNode.parentNode]
(reset! parent-id ancestor.id)))
:reagent-render (fn []
(if (and list? (empty? children))
[:span.list
{:id id
:key id
:class (str "cdx-nested-list cdx-block" class)
:contentEditable true
:style (merge {:border-right "30px" :width "100%"})
:on-key-down key-down-fn
:data-parent @parent-id
:data-type "List"
:data-placeholder "Press 'TAB' or '/' for Menus..."
:suppressContentEditableWarning true
:dangerouslySetInnerHTML {:__html text}}]
[:div.list.frame.cdx-block__frame__vertical
{:key id
:id id}
[:span.list
{:id id
:class (str "cdx-nested-list cdx-block" class)
:contentEditable true
:style (merge {:border-right "30px" :width "100%"})
:on-key-down key-down-fn
:data-parent @parent-id
:data-type "List"
:data-placeholder "Press 'TAB' or '/' for Menus..."
:suppressContentEditableWarning true
:dangerouslySetInnerHTML {:__html text}}]
(render-children children (assoc funcs :list? true))]))})))
children is a vector of ids> Please do you have any idea how I can memoize a component?
Why would you want to do that in the first place?
Some general comments on your code:
• That for at the top results in N^2 items. You don't use index there so I don't know whether it's intended or not
• Don't use rdom/dom-node - it has been deprecated. Instead, use React refs
• The list/element component will not pass a proper parent-id after the first render. I don't know why you need the :data-parent attribute at all, but if you're in control of the code that consumes that attribute then I'd suggest restructuring the code in such a way so that children components don't need a direct reference to one of their parents
• Use :keys destructuring to avoid name repetition, e.g. (let [{:keys [id text tag children]} node] ...)
• No need to concatenate strings yourself when composing classes for Reagent components - use reagent.core/class-names instead, it will filter out nils and expand collections for you, and classes can be keywords
• The :data-parent and the parent-id isn't useful, i tried using it to test something (Which node is it's parent in the map, not the in the DOM) but i saw no need to use it, i just left it there as i was trying to show the code.
• For the class, noted. But the class isn't useful here too, i just used it in a previous structure, so i won't be needing it in this (will probably remove it)
• So how do i deal with the for ? (I think this is the main issue)
The index, i intended using it as a :key for the loop though
I don't see anything in your code that would cause an infinite loop. If you create a publicly available minimal reproducible example, I can take a look.
(defn render-children [children funcs]
(when-not (empty? children)
(doall (for [child-id children
index (range (count children))]
(let [child (search-node child-id)]
^{:key index}
(render-options child funcs))))))
(defn render-options [node functions]
(let [{type :type} node]
(case type
:frame [frame/element node functions]
:block [block/element node functions]
:paragraph [paragraph/element node functions]
:header [headers/element node functions]
:list [list/element node functions]
:image [image/element node functions]
:checklist [checklist/element node functions]
:toggle-list [toggle-list/element node functions]
:delimiter [delimiter/element node]
:link [link/element node functions]
:page [page/element node functions]
:quote [quote/element node functions]
nil)))
(defn render []
(let [blocks (fetch [:nodes/get-blocks])
blocks-node (map #(search-node %) blocks)]
(if (not blocks)
(put [:initialize-db!])
(doall (for [block blocks-node
:let [{id :id} block
_id (str "ce-" (random-uuid))
click-fn (fn []
(.trigger (js/$ "#settings-btn") "click"))
funcs {:click-fn click-fn
:mouse-on-fn (set-active-node)
:mouse-out-fn (remove-active-node)
:db-click-fn (db-click-fn)
;; :drop-fn drop-fn
:drag-over-fn (drag-over-fn)
:key-down-fn (key-down-fn)
:key-up-fn (key-up-fn)
:paste-fn (paste-fn)
:toggle-click-fn (handle-click-fn)
:render-children render-children
;; :select-fn select-fn
:add-block add-block
:add-node add-node}]]
[:div {:key id
:id id
:class "ce-block"}
[:div {:key (str id (rand-int 98765435678))
:id _id
:on-mouse-leave remove-active-node
:on-mouse-out remove-active-node
:class "ce-block__content"} (render-options block funcs)]])))))
list/element
(defn element [node funcs]
(let [{id :id
text :text
tag :tag
children :children} node
{key-down-fn :key-down-fn
set-active-fn :mouse-on-fn
remove-active-fn :mouse-out-fn
render-children :render-children
list? :list?} funcs
class (if (= tag :ol)
" cdx-nested-list--ordered"
" cdx-nested-list--unordered")
list-type (if (= tag :ol)
{:list-style "number"}
{:list-style "disc"})]
(r/create-class {:component-did-mount (fn [comp]
;; (.addEventListener (.querySelector (rdom/dom-node comp) ".tt") "selectstart" (fn []
;; (.addEventListener js/document "selectionchange" (selection-start-fn))))
(.addEventListener (rdom/dom-node comp) "focusin" (fn [evt]
(set-active-fn evt)
(dispatch [:nodes/set-focus id]))))
:reagent-render (fn []
(if (and list? (empty? children))
[:span.list
{:id id
:key id
:class (str "cdx-nested-list cdx-block" class)
:contentEditable true
:style (merge {:border-right "30px" :width "100%"})
:on-key-down key-down-fn
:data-type "List"
:data-placeholder "Press 'TAB' or '/' for Menus..."
:suppressContentEditableWarning true
:dangerouslySetInnerHTML {:__html text}}]
[:div.list.frame.cdx-block__frame__vertical
{:key id
:id id}
[:span.list
{:id id
:class (str "cdx-nested-list cdx-block" class)
:contentEditable true
:style (merge {:border-right "30px" :width "100%"})
:on-key-down key-down-fn
:data-type "List"
:data-placeholder "Press 'TAB' or '/' for Menus..."
:suppressContentEditableWarning true
:dangerouslySetInnerHTML {:__html text}}]
(render-children children (assoc funcs :list? true))]))})))
A typical (minimal) DB
{:nodes
{"ce-bbeb5451-ec21-45df-bfc6-33ee6062cc25"
{:id "ce-bbeb5451-ec21-45df-bfc6-33ee6062cc25",
:type :frame,
:orientation "horizontal",
:children ["ce-811a09ef-1ca8-4b20-bace-cb65d62a528a"],
:style nil,
:focus? true},
"ce-811a09ef-1ca8-4b20-bace-cb65d62a528a"
{:id "ce-811a09ef-1ca8-4b20-bace-cb65d62a528a",
:type :block,
:children ["ce-0b482160-d0b7-402a-8337-c38e1bde48ef"],
:size {},
:level 0,
:focus? true},
"ce-906c045f-d591-4270-a2cc-a051a85baa7c"
{:id "ce-906c045f-d591-4270-a2cc-a051a85baa7c",
:type :paragraph,
:text "sdjksdkjdsk",
:typing?
{:nodeTree {:start [0], :end [0]}, :position {:start 11, :end 11}}},
"ce-0b482160-d0b7-402a-8337-c38e1bde48ef"
{:id "ce-0b482160-d0b7-402a-8337-c38e1bde48ef",
:type :list,
:tag :ol,
:text "",
:size {},
:focus? true,
:children ("ce-92391c6c-5057-4f25-940a-d0b5d55f246b")},
"ce-f495277e-ac8d-4b6a-af28-0b56a7b7442a"
{:id "ce-f495277e-ac8d-4b6a-af28-0b56a7b7442a",
:type :block,
:children ["ce-906c045f-d591-4270-a2cc-a051a85baa7c"],
:size {}},
"ce-92391c6c-5057-4f25-940a-d0b5d55f246b"
{:id "ce-92391c6c-5057-4f25-940a-d0b5d55f246b",
:type :block,
:children ["ce-59dc71af-1652-43a2-afa1-4995255b454f"],
:size {},
:level 0,
:focus? true},
"ce-59dc71af-1652-43a2-afa1-4995255b454f"
{:id "ce-59dc71af-1652-43a2-afa1-4995255b454f",
:type :list,
:tag :ol,
:text "",
:size {},
:focus? true,
:typing?
{:nodeTree {:start [0], :end [0]}, :position {:start 0, :end 0}}},
"ce-9a4d47bc-74fa-4109-9131-317d544bad0b"
{:id "ce-9a4d47bc-74fa-4109-9131-317d544bad0b",
:type :frame,
:orientation "horizontal",
:children ["ce-f495277e-ac8d-4b6a-af28-0b56a7b7442a"],
:style nil}},
:blocks
["ce-9a4d47bc-74fa-4109-9131-317d544bad0b" "ce-bbeb5451-ec21-45df-bfc6-33ee6062cc25"],
:focus "ce-59dc71af-1652-43a2-afa1-4995255b454f",
}
This is the code the renders the component in the editor. The components get their data (props) from the db based on their :type
Don't know if this will helpThat's not a reproducible example, it's just a bunch of code blocks that I'd have to somehow string together in order to make them work. Can you create a public repo with all the right files and instructions on how to reproduce the error?