-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handler/Sink refactor, added Pipe trait #91
Changes from all commits
6c1de88
ddc89b9
7c58cb4
c7bbad7
2392bec
565b318
6cb14cb
5e1ba29
4846f0f
d21fceb
669583c
279704b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package outwatch | ||
|
||
import cats.effect.IO | ||
import outwatch.dom.Observable | ||
|
||
object Handler { | ||
private[outwatch] def apply[T](sink: Sink[T], source: Observable[T]): Handler[T] = Pipe(sink, source) | ||
|
||
/** | ||
* This function also allows you to create initial values for your newly created Handler. | ||
* This is equivalent to calling `startWithMany` with the given values. | ||
* | ||
* @param seeds a sequence of initial values that the Handler will emit. | ||
* @tparam T the type parameter of the elements | ||
* @return the newly created Handler. | ||
*/ | ||
def create[T](seeds: T*): IO[Handler[T]] = Pipe.create[T](seeds: _*) | ||
|
||
def create[T]: IO[Handler[T]] = Pipe.create[T] | ||
|
||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package outwatch | ||
|
||
import cats.effect.IO | ||
import outwatch.Sink.{ObservableSink, SubjectSink} | ||
import rxscalajs.Observable | ||
|
||
object Pipe { | ||
private[outwatch] def apply[I, O](sink: Sink[I], source: Observable[O]): Pipe[I, O] = | ||
new ObservableSink[I, O](sink, source) | ||
|
||
/** | ||
* This function also allows you to create initial values for your newly created Pipe. | ||
* This is equivalent to calling `startWithMany` with the given values. | ||
* | ||
* @param seeds a sequence of initial values that the Pipe will emit. | ||
* @tparam T the type parameter of the elements | ||
* @return the newly created Pipe. | ||
*/ | ||
def create[T](seeds: T*): IO[Pipe[T, T]] = create[T].map { pipe => | ||
if (seeds.nonEmpty) { | ||
pipe.transformSource(_.startWithMany(seeds: _*)) | ||
} | ||
else { | ||
pipe | ||
} | ||
} | ||
|
||
def create[T]: IO[Pipe[T, T]] = IO { | ||
val subjectSink = SubjectSink[T]() | ||
Pipe(subjectSink, subjectSink) | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package outwatch.dom | ||
|
||
import cats.effect.IO | ||
import org.scalajs.dom.{ClipboardEvent, DragEvent, KeyboardEvent, MouseEvent} | ||
import outwatch.dom.helpers.InputEvent | ||
|
||
/** | ||
* Trait containing event handlers, so they can be mixed in to other objects if needed. | ||
*/ | ||
trait HandlerFactories { | ||
|
||
@deprecated("Use Handler.inputEvents instead", "0.11.0") | ||
def createInputHandler() = Handler.create[InputEvent] | ||
@deprecated("Use Handler.mouseEvents instead", "0.11.0") | ||
def createMouseHandler() = Handler.create[MouseEvent] | ||
@deprecated("Use Handler.keyboardEvents instead", "0.11.0") | ||
def createKeyboardHandler() = Handler.create[KeyboardEvent] | ||
@deprecated("Use Handler.dragEvents instead", "0.11.0") | ||
def createDragHandler() = Handler.create[DragEvent] | ||
@deprecated("Use Handler.clipboardEvents instead", "0.11.0") | ||
def createClipboardHandler() = Handler.create[ClipboardEvent] | ||
|
||
@deprecated("Use Handler.create[String] instead", "0.11.0") | ||
def createStringHandler(defaultValues: String*) = Handler.create[String](defaultValues: _*) | ||
@deprecated("Use Handler.create[Boolean] instead", "0.11.0") | ||
def createBoolHandler(defaultValues: Boolean*) = Handler.create[Boolean](defaultValues: _*) | ||
@deprecated("Use Handler.create[Double] instead", "0.11.0") | ||
def createNumberHandler(defaultValues: Double*) = Handler.create[Double](defaultValues: _*) | ||
|
||
@deprecated("Use Handler.create[T] instead", "0.11.0") | ||
def createHandler[T](defaultValues: T*): IO[Pipe[T, T]] = Handler.create[T](defaultValues: _*) | ||
|
||
|
||
implicit class HandlerCreateHelpers(handler: Handler.type) { | ||
lazy val inputEvents = Handler.create[InputEvent] | ||
lazy val mouseEvents = Handler.create[MouseEvent] | ||
lazy val keyboardEvents = Handler.create[KeyboardEvent] | ||
lazy val dragEvents = Handler.create[DragEvent] | ||
lazy val clipboardEvents = Handler.create[ClipboardEvent] | ||
} | ||
|
||
} | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,13 @@ | ||
package outwatch.dom.helpers | ||
|
||
import org.scalajs.dom.raw.{Event, HTMLInputElement} | ||
import org.scalajs.dom.Event | ||
import org.scalajs.dom.html | ||
|
||
import scala.scalajs.js.annotation.ScalaJSDefined | ||
|
||
@ScalaJSDefined | ||
class InputEvent() extends Event { | ||
override def target = { | ||
super.target.asInstanceOf[HTMLInputElement] | ||
super.target.asInstanceOf[html.Input] | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import rxscalajs.Observable | ||
|
||
package object outwatch { | ||
type Pipe[-I, +O] = Observable[O] with Sink[I] | ||
type Handler[T] = Pipe[T, T] | ||
|
||
implicit class PipeOps[-I, +O](val self: Pipe[I, O]) extends AnyVal { | ||
|
||
def mapSink[I2](f: I2 => I): Pipe[I2, O] = Pipe(self.redirectMap(f), self) | ||
|
||
def mapSource[O2](f: O => O2): Pipe[I, O2] = Pipe(self, self.map(f)) | ||
|
||
def mapPipe[I2, O2](f: I2 => I)(g: O => O2): Pipe[I2, O2] = Pipe(self.redirectMap(f), self.map(g)) | ||
|
||
|
||
def collectSink[I2](f: PartialFunction[I2, I]): Pipe[I2, O] = Pipe(self.redirect(_.collect(f)), self) | ||
|
||
def collectSource[O2](f: PartialFunction[O, O2]): Pipe[I, O2] = Pipe(self, self.collect(f)) | ||
|
||
def collectPipe[I2, O2](f: PartialFunction[I2, I])(g: PartialFunction[O, O2]): Pipe[I2, O2] = Pipe( | ||
self.redirect(_.collect(f)), self.collect(g) | ||
) | ||
|
||
|
||
def filterSource(f: O => Boolean): Pipe[I, O] = Pipe(self, self.filter(f)) | ||
|
||
|
||
def transformSink[I2](f: Observable[I2] => Observable[I]): Pipe[I2, O] = Pipe(self.redirect(f), self) | ||
|
||
def transformSource[O2](f: Observable[O] => Observable[O2]): Pipe[I, O2] = Pipe(self, f(self)) | ||
|
||
def transformPipe[I2, O2](f: Observable[I2] => Observable[I])(g: Observable[O] => Observable[O2]): Pipe[I2, O2] = | ||
Pipe(self.redirect(f), g(self)) | ||
} | ||
|
||
// This is not in PipeOps, because I is contravariant | ||
implicit class FilterSink[I, +O](val self: Pipe[I, O]) extends AnyVal { | ||
def filterSink(f: I => Boolean): Pipe[I, O] = Pipe(self.redirect(_.filter(f)), self) | ||
} | ||
|
||
implicit class HandlerOps[T](val self: Handler[T]) extends AnyVal { | ||
|
||
def imap[S](read: T => S)(write: S => T): Handler[S] = self.mapPipe(write)(read) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should have the sink/source transformation order consistent between There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch! Yes this should be consistent. My motivation for this order was that you can easily rewrite a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interestingly
And 😅 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's interesting. In that case, let's leave them as they are. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great. Merge? |
||
|
||
def lens[S](seed: T)(read: T => S)(write: (T, S) => T): Handler[S] = { | ||
val redirected = self.redirect[S](_.withLatestFrom(self.startWith(seed)).map { case (a, b) => write(b, a) }) | ||
Handler(redirected, self.map(read)) | ||
} | ||
|
||
def transformHandler[S](read: Observable[T] => Observable[S])(write: Observable[S] => Observable[T]): Handler[S] = | ||
self.transformPipe(write)(read) | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like the direction of outwatch becoming frp-backend agnostic. 👍