-
Notifications
You must be signed in to change notification settings - Fork 4
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
add MutationObserver
#72
Changes from all commits
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,66 @@ | ||
/* | ||
* Copyright 2022 Arman Bilge | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package fs2.dom | ||
|
||
import cats.syntax.all._ | ||
import org.scalajs.dom | ||
import cats.effect.kernel.Async | ||
import cats.effect.std.Dispatcher | ||
import cats.effect.kernel.Resource | ||
import cats.effect.kernel.Sync | ||
|
||
abstract class MutationObserver[F[_]] private[dom] { | ||
|
||
def observe(target: Node[F], options: dom.MutationObserverInit): F[Unit] | ||
|
||
def disconnect: F[Unit] | ||
|
||
def takeRecords: F[List[MutationRecord[F]]] | ||
|
||
} | ||
|
||
object MutationObserver { | ||
|
||
def apply[F[_]]( | ||
callback: (List[dom.MutationRecord], MutationObserver[F]) => F[Unit] | ||
)(implicit F: Async[F]): Resource[F, MutationObserver[F]] = for { | ||
dispatcher <- Dispatcher.parallel[F] | ||
jsObserver <- Resource.make( | ||
F.delay( | ||
new dom.MutationObserver((a, b) => | ||
dispatcher.unsafeRunAndForget(callback(a.toList, fromJS(b))) | ||
) | ||
) | ||
)(obs => F.delay(obs.disconnect())) | ||
} yield fromJS(jsObserver) | ||
|
||
private def fromJS[F[_]]( | ||
jsObserver: dom.MutationObserver | ||
)(implicit F: Sync[F]): MutationObserver[F] = | ||
new MutationObserver[F] { | ||
|
||
def observe(target: Node[F], options: dom.MutationObserverInit): F[Unit] = | ||
F.delay(jsObserver.observe(target.asInstanceOf[dom.Node], options)) | ||
|
||
def disconnect: F[Unit] = F.delay(jsObserver.disconnect()) | ||
|
||
def takeRecords: F[List[MutationRecord[F]]] = | ||
F.delay(jsObserver.takeRecords()).map(_.toList.map(MutationRecord.fromJS)) | ||
|
||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/* | ||
* Copyright 2022 Arman Bilge | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package fs2.dom | ||
|
||
import org.scalajs.dom | ||
|
||
abstract class MutationRecord[F[_]] private[dom] { | ||
|
||
def `type`: String | ||
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. I think in scala-js-dom we should make this an enum (which is a compatible change) but it might become an incompatible change for us here. Too bad, we'll deal with it when it happens 🙃 |
||
|
||
def target: Node[F] | ||
|
||
def addedNodes: List[Node[F]] | ||
|
||
def removedNodes: List[Node[F]] | ||
|
||
def previousSibling: Option[Node[F]] | ||
|
||
def nextSibling: Option[Node[F]] | ||
|
||
def attributeName: Option[String] | ||
|
||
def attributeNamespace: Option[String] | ||
|
||
def oldValue: Option[String] | ||
|
||
} | ||
|
||
object MutationRecord { | ||
|
||
private[dom] def fromJS[F[_]](record: dom.MutationRecord) = new MutationRecord[F] { | ||
|
||
override def `type`: String = record.`type` | ||
|
||
override def target: Node[F] = record.target.asInstanceOf[Node[F]] | ||
|
||
override def addedNodes: List[Node[F]] = | ||
record.addedNodes.toList.asInstanceOf[List[Node[F]]] | ||
|
||
override def removedNodes: List[Node[F]] = | ||
record.removedNodes.toList.asInstanceOf[List[Node[F]]] | ||
|
||
override def previousSibling: Option[Node[F]] = Option( | ||
record.previousSibling.asInstanceOf[Node[F]] | ||
) | ||
|
||
override def nextSibling: Option[Node[F]] = | ||
Option(record.nextSibling.asInstanceOf[Node[F]]) | ||
|
||
override def attributeName: Option[String] = | ||
Option(record.attributeName) | ||
|
||
override def attributeNamespace: Option[String] = | ||
Option(record.attributeNamespace) | ||
|
||
override def oldValue: Option[String] = Option(record.oldValue) | ||
|
||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/* | ||
* Copyright 2022 Arman Bilge | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package fs2.dom | ||
|
||
import org.scalajs.dom | ||
|
||
abstract class ResizeObserverEntry[F[_]] private[dom] { | ||
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. Perhaps something like this? 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. I'll take a more detailed look later but this is exactly what I was imagining!! Thank you so much, really appreciate it :) 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. Cool, I can add something similar for |
||
|
||
def target: Element[F] | ||
|
||
def borderBoxSize: List[dom.ResizeObserverSize] | ||
|
||
def contentBoxSize: List[dom.ResizeObserverSize] | ||
|
||
def contentRect: dom.DOMRectReadOnly | ||
|
||
} | ||
|
||
object ResizeObserverEntry { | ||
|
||
private[dom] def fromJS[F[_]](entry: dom.ResizeObserverEntry) = new ResizeObserverEntry[F] { | ||
|
||
def target: Element[F] = entry.target.asInstanceOf[Element[F]] | ||
|
||
def borderBoxSize: List[dom.ResizeObserverSize] = entry.borderBoxSize.toList | ||
|
||
def contentBoxSize: List[dom.ResizeObserverSize] = entry.contentBoxSize.toList | ||
|
||
def contentRect: dom.DOMRectReadOnly = entry.contentRect | ||
|
||
} | ||
|
||
} |
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.
Shall we also provide a wrapper for
dom.MutationObserverInit
?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.
And
dom.ResizeObserverOptions
?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.
Good question :) ok, so usual disclaimer about arbitrariness ...
I've been trying to just reuse these
*Init
and*Options
types whenever possible, so that we don't have to wrap everything. They are mutable, which is not great, but I think their usual usage pattern is as builders local to the method call, rather than passing them all over the place. So probably fine. And all their member types are simple stuff (strings, booleans, enums). So IMO the trade-off is not worth it: if we wrapped it, it would be more annoying for us to maintain, and more annoying for the user to use, so lose-lose.