In the previous post we explored API to select events from streams using RegExp-like syntax, e.g.:
const D = fromEvent(element, 'mousedown');
const M = fromEvent(document, 'mousemove');
const U = fromEvent(document, 'mouseup');
exec(
'DM*U' // <- regular expression
, { D, M, U } // <- streams that will be used
)
.pipe(…)
.subscribe(console.log);
In this post we'll review it's functional API counterpart:
query(D, some(M), U) // select some Ms between Ds and Us
.pipe(…)
.subscribe(console.log)
But first, we'll recap the thought process. If you're already familiar with the idea — hop here
tl;dr: package @ https://github.com/erql/rx-rql 📦
💡 Recap the idea
Imagine we need to implement a drag-n-drop behavior.
For that, we have three event streams: mousemove$
, mousedown$
, mouseup$
. So we want to capture mousemove$
events after mousedown$
emitted and before mouseup$
.
Let's draw a marble diagram of the event streams we have:
For better readability let's substitute all the o
s to respective letters of the streams:
Now that we have distinct event names, we can simplify our diagram to a single line of events:
events$ -mdm-m-m-m-mum-
Let's remove the time -
signs as well, we don't them:
events$ mdmmmmmum
Okay, to rephrase our task in terms of the new diagram: we need to capture the m
events between d
and u
emissions.
🤔
Hmm...
"we need
m
s betweend
andu
"...
Sounds familiar...
Ah! If that was a string, we could easily do it with a regular expression:
/dm*u/.exec('mdmmmum')
Would give us the needed dmmmu
without trailing mouse-move m
events...
Right?
If only we had a library to select events from streams with regexes...
🚀 Solution
query(D, some(M), U)
.pipe(…)
.subscribe(console.log)
Rx-RQL 📦 package provides following API to make such selections:
-
query(…)
— root of your selection -
A
— select 1 emission from the stream -
some(A)
— select 0 to ∞ emissions from A -
maybe(A)
— select 0 or 1 emission from A -
many(n,m)(A)
— select from n to m emissions from A -
mute(A)
— select emission from A & mute it
And you can group them as you like:
-
some(A, some(B), mute(C))
— select as many emissions from: select as many Bs as possible between emissions from A and muted C
Here's how to create a simple drag-n-drop behavior using this package:
And here's a Mr. Potato-Head DnD 🥔 — a more sophisticated example based on this amazing article by @dailydevtips1 ! Thx, Chris 🙏
👋 Outro
Thank you for reading this article! Stay reactive and have a nice day 🙂
If you enjoyed reading — please, indicate that with ❤️ 🦄 📘 buttons
And in case you're not yet following me here and on twitter — then you've probably missed my recent experiments: Rx + Proxy, Rx Autorun, React + Rx
Now I'd love to hear your thoughts! 👂
Top comments (3)
Look very cool. I will try it out.
Thanks! 😊
Be cautious: although it's well covered with tests, it's still very experimental!
If community would find this library useful, I'm thinking it could be extended with:
query(A, many(B).pipe(…), C)
that would be applied to particular group outputAnyhow, please ping me back here or on github if you have additional thoughts or ideas. Thx!
I think it would be interesting to create predefined example to solve some common scenarios. Things like Drag and Drop, Hotkeys etc. I can see many possibilities here.