This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-10-05
Channels
- # announcements (19)
- # babashka (28)
- # beginners (62)
- # biff (3)
- # calva (19)
- # cider (24)
- # clj-kondo (8)
- # cljdoc (15)
- # clojure (32)
- # clojure-europe (16)
- # clojure-nl (1)
- # clojure-norway (17)
- # clojure-uk (8)
- # clojuredesign-podcast (26)
- # cursive (64)
- # datomic (43)
- # deps-new (1)
- # fulcro (4)
- # honeysql (1)
- # hyperfiddle (46)
- # kaocha (16)
- # lsp (15)
- # missionary (51)
- # music (1)
- # nbb (4)
- # off-topic (55)
- # pedestal (11)
- # podcasts-discuss (1)
- # polylith (7)
- # practicalli (1)
- # releases (4)
- # shadow-cljs (120)
- # tools-build (34)
- # vscode (1)
- # xtdb (2)
Here’s a way to detect key presses, and stop detecting them when the app loses focus. It emits a key state if the currently pressed keys, but if some other tab or window becomes active or something like find-in-page happens, the key state will be cleared. Another thing handled is that you can tell the flow to preventDefault on some particular keys. NB: It doesn’t handle the case that you might want to register a key that is pressed when the app gains focus. Thoughts?
Hi, first time trying Electric, and I got stuck on building a recursive tree in UI. Is there a way to do a forward declaration for e/defn? Similar to declare
in Clojure?
Oh, nevermind
e/def
+ binding
work, I just forgot to call it with new
.
best workaround is probably letfn
yeah, binding a lambda into dynamic scope also works
Thanks!
Do I have to use e/fn
in dom handlers? I'm trying to handle drop
event and it has a very noticeable delay (about 0.5 seconds) before firing? My assumption is that the delay is due to some propagation of reactive events through graph? Ideally, I'd like to skip the whole electric graph for this handler, as it is a pure UI handler.
dom/on! is idiomatic, not sure what’s up with the delay, that is unexpected would need to see more context
dom/on! takes a clojure fn
with all the drag events as dom/on! things work as expected.
I think I pinned down the problem. "dragover" handler with e/fn
probably fired too many events and it was processing it. As soon as I switched that to e/on!
delay disappeared. dragover fires non-stop (at least every animation frame) while dragging, so it makes sense that it may create a long queue of events to propagate through graph.
But nice to know about e/on! I'll use it when I don't need reactivity
Thanks for the quick response!
hmm maybe, we relieve backpressure in these event apis so that shouldn’t be the issue, however the event apis that take e/fn have FSMs inside them for dealing with possibility of latency they are all super complicated so i expect it’s an interaction with that. This is all getting paved but not until next spring at this point
Makes sense. Glad there is a workaround for now!
I’m experiencing the same delay for on drop.
the drag over dom/on "drop"
areas seems to go in slow motion, because I use dom/on "dragenter"
and e/on-unmount
in "drop"
to update the dragging states in atoms. on!
did not fix it.
The UI feels laggy while dragging
Probably try to debounce some of the events?
@U4EFBUCUE you mean using debounce of missionary?
If I understand correct what happening, some events spams a bunch of events and then delay is happening while those events propagate through dag over and over again. If you reduce number of those events it should help reduce delay.
how would I reduce the events? I need to update the drag state so that ui and logic can adapt to the change
Is it possible for you to put logic into only dragstart/drop? Those fire once.
I have some logic in dragenter/dragleave but that only setting some classes on DOM so I can move it easily into dom/on!
(dom/on "dragover" (e/fn [e] (.preventDefault e)))
(dom/on! "dragenter" (fn [_] (reset! !stage-dragged-to id)))
(dom/on "drop" (e/fn [_]
(when (= stage-dragged-to id)
(e/server
(e/discard
(d/transact! !conn
[{:db/id lead-dragging
:lead/stage id}]))))
(e/on-unmount (fn []
(reset! !lead-dragging nil)
(reset! !stage-dragged-to nil)))))
the state is used so switch css classes as well, I could add those using js but then its less declarative.
I suggest you get target where it dragged within the drop event. I have "droppable" class on allowable target and then do the following in drop:
(dom/on! "drop" (fn [evt]
(.stopPropagation evt)
(js/console.log "Handle Drop" evt "tree-state" tree-state)
(let [target (.closest (gobj/get evt "target") ".droppable")
block-id (.getAttribute target "data-block-id")
block-type (:dragged tree-state)]
#?(:cljs (do (trigger-tree-command target
{:block-id block-id
:command :add-item
:block-type block-type})
(trigger-tree-command target
{:command :set-dragged
:target nil}
))))
false))
look at the first let binding.
just find a closest droppable to where it dropped.
This way you can drop dragenter logic and I hope remove the delay
Try to make dragover
a dom/on!
command and return false from it after preventing default.
Trigger-tree-command is just firing a custom js event.
#?(:cljs (defn trigger-tree-command
[el data]
(let [command (js/CustomEvent. "treeCommand" #js {"detail" data "bubbles" true})]
(.dispatchEvent el command))))
I capture it within the root div to localize all state updates.
(dom/on "treeCommand" (e/fn [evt]
(let [data (gobj/get evt "detail")]
(case (:command data)
:toggle-opened (swap! !state update :opened (fn [opened]
(if (contains? opened (:block-id data))
(disj opened (:block-id data))
(conj opened (:block-id data)))))
:set-dragged (swap! !state assoc :dragged (:target data))
:add-item (e/server
(page-model/block-command (merge {:builder/page-id page-id} e/*http-request*)
{:command :add-item
:block-id (:block-id data)
:item-type (:block-type data)}))))))
interesting. rebuilding reframe haha if you have one !state atom wouldn’t e/watch on !state signal everytime anything in the state changes? how do you listen for only parts of the !state on the client
!state is a client-side only atom and I guess it's updates every time something changes, but it's not that often. My whole Electric implementation is for one very interactive form, not for the whole application.
I also believe that even when it signals it won't update parts of the graph that haven't changed, but I'm not 100% sure about this.
It's my first day with Electric, so take everything I say with a grain of salt :))
send us a working gist or repo that is slow and we will make it fast for you
> if you have one !state atom wouldn’t e/watch on !state signal everytime anything in the state changes?
yes, the !state
watch will trigger. But it might be less costly than expected. E.g. in (expensive (:bar state))
• there's an (swap! !state update :foo inc)
somewhere in the app
• the state
watch fires
• state
changed, so (:bar state)
re-runs
• (:bar state)
returns the same value
• (expensive (:bar state))
doesn't re-run because the arguments didn't change
how do you listen for only parts of the !state on the client1 atom should have 1 e/watch
to prevent FRP glitches. Passing part of the state means selecting a part of the watched value
(let [!state (atom {}), state (e/watch !state)]
(Foo (:foo state)))
the (dom/on! "dragenter" (fn [_] (reset! !stage-dragged-to id)))
still causes the multiple changes causing the ui to lag behind if I drag over multiple elements. would need a way do discard reset! if a new one is triggered
we don't advertise any state management solution. Single atom, many atoms, datascript db, rama... All work and have their tradeoffs. I'm just pointing out a global atom is a viable option in many scenarios so if it is one's preference one is fine to try it out
maybe there is an easy way to use missionary to queue up client state updates and only signal updates to the e/watch when no other state update is queued
i still suspect something wrong in userland, dom/on!
relieves backpressure, it is not slow
@U010L3S1XHS can you try wrap your state reset! in some simple debouncer? You can write one in pure js with setTimeout/clearTimeout. While it won't solve the delay UI problem, it will at least help figure out if that's related to Electric or something else. With debouncer all of your handlers should fire immediately once you timeout interval passes, if the problem was too many events scheduled by dragenter/dragover. Put something like 16ms. Or even simpler comment those handlers and see if drop fires immediately and then return them one by one to see where the problem is.
@U4EFBUCUE thanks for the suggestion. even when removing on! dragenter
the on drop
still is delayed for a second. not sure why because there are no state changes before.
we haven't seen a case where electric would lag behind one second. If there's a clear repro we can take a quick look