Skip to content

Commit 3975259

Browse files
committed
update
1 parent 464083a commit 3975259

File tree

12 files changed

+1280
-1279
lines changed

12 files changed

+1280
-1279
lines changed

.scalafmt.conf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ align.multiline = false
77
rewrite.rules = [RedundantBraces, RedundantParens]
88
rewrite.scala3.convertToNewSyntax = true
99
rewrite.scala3.removeOptionalBraces = true
10-
docstrings.wrapMaxColumn = 80
10+
docstrings.wrapMaxColumn = 80
11+
rewrite.trailingCommas.style = preserve

build.sbt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import org.scalajs.linker.interface.ModuleSplitStyle
12
import sbtcrossproject.CrossPlugin.autoImport.crossProject
23

34
inThisBuild(
@@ -86,7 +87,10 @@ lazy val animus = crossProject(JSPlatform)
8687
)
8788
)
8889
.jsSettings(
89-
scalaJSLinkerConfig ~= { _.withSourceMap(false) },
90+
scalaJSLinkerConfig ~= {
91+
_.withModuleKind(ModuleKind.ESModule)
92+
.withModuleSplitStyle(ModuleSplitStyle.SmallModulesFor(List("animus")))
93+
},
9094
libraryDependencies ++= Seq("com.raquo" %%% "laminar" % laminarVersion)
9195
)
9296

@@ -99,7 +103,10 @@ lazy val example = project
99103
.settings(commonSettings)
100104
.settings(
101105
scalaJSUseMainModuleInitializer := true,
102-
scalaJSLinkerConfig ~= { _.withModuleKind(ModuleKind.ESModule) },
106+
scalaJSLinkerConfig ~= {
107+
_.withModuleKind(ModuleKind.ESModule)
108+
.withModuleSplitStyle(ModuleSplitStyle.SmallModulesFor(List("example")))
109+
},
103110
skip / publish := true,
104111
libraryDependencies ++= Seq(
105112
"dev.zio" %%% "zio" % zioVersion,

example/src/main/scala/example/Main.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,11 @@ object Main:
209209
div(
210210
cls("text-lg mt-4 items-center max-w-lg italic"),
211211
div(
212-
cls("items-center flex whitespace-nowrap"),
212+
cls("items-center flex whitespace-nowrap overflow-hidden"),
213213
"is a",
214214
children <-- article.splitOneTransition(identity) { (_, string, _, t) =>
215215
div(
216+
cls("overflow-hidden"),
216217
string,
217218
t.width
218219
)
@@ -281,9 +282,10 @@ object Main:
281282
fontFamily("Bebas Neue"),
282283
fontSize("24px"),
283284
div(
284-
cls("flex whitespace-nowrap text-neutral-900 bg-neutral-300 px-1.5 py-1 relative top-1"),
285+
cls("flex whitespace-nowrap text-neutral-900 bg-neutral-300 px-1.5 py-1 relative top-1 overflow-hidden"),
285286
children <-- adjective.splitOneTransition(identity) { (_, string, _, t) =>
286287
div(
288+
cls("overflow-hidden"),
287289
string,
288290
t.width,
289291
t.opacity,

modules/core/js/src/main/scala/animus/Animator.scala

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@ final class Animator[V](
1010
val epsilon: Double = 0.001,
1111
val speed: Double = 1
1212
)(implicit V: VectorArithmetic[V]):
13-
14-
var isDone: Boolean = false
15-
var callback: V => Unit = _ => ()
13+
var isDone: Boolean = false
14+
var onChange: V => Unit = _ => ()
15+
var onComplete: () => Unit = () => ()
1616

1717
def update(deltaSeconds0: Double): Unit =
18-
// val deltaSeconds = (1.0 / 60.0) * speed
1918
val deltaSeconds = deltaSeconds0 * speed
2019
val displacement = V.subtract(value, targetValue)
2120
val springForce = V.scaledBy(displacement, -stiffness)
@@ -26,10 +25,12 @@ final class Animator[V](
2625
velocity = V.add(velocity, V.scaledBy(acceleration, deltaSeconds))
2726
value = V.add(value, V.scaledBy(velocity, deltaSeconds))
2827

29-
// Return true if the animation is "settled"
30-
callback(value)
31-
isDone = V.magnitudeSquared(displacement) < epsilon &&
28+
onChange(value)
29+
if V.magnitudeSquared(displacement) < epsilon &&
3230
V.magnitudeSquared(velocity) < epsilon
31+
then
32+
isDone = true
33+
onComplete()
3334

3435
def setTarget(newTarget: V): Unit =
3536
this.targetValue = newTarget
@@ -49,6 +50,7 @@ final class Animator[V](
4950
def default: Animator[V] = copy(stiffness = 170, damping = 26)
5051
def gentle: Animator[V] = copy(stiffness = 120, damping = 14)
5152
def wobbly: Animator[V] = copy(stiffness = 180, damping = 12)
53+
def bouncy: Animator[V] = copy(stiffness = 280, damping = 25)
5254
def stiff: Animator[V] = copy(stiffness = 210, damping = 20)
5355
def slow: Animator[V] = copy(stiffness = 280, damping = 60)
5456
def molasses: Animator[V] = copy(stiffness = 280, damping = 120)

modules/core/js/src/main/scala/animus/ResizeObserver.scala

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,11 @@ class ResizeListener(callback: DOMRect => Unit) extends Binder[ReactiveElement.B
3030
})
3131

3232
val subscribe = (ctx: MountContext[ReactiveElement.Base]) =>
33-
println("SUBSCRIBE")
3433
observer.observe(element.ref)
34+
callback(ctx.thisNode.ref.getBoundingClientRect())
3535
new Subscription(
3636
ctx.owner,
37-
cleanup = () =>
38-
println("CLEANUP")
39-
observer.unobserve(element.ref)
37+
cleanup = () => observer.unobserve(element.ref)
4038
)
4139

4240
ReactiveElement.bindSubscriptionUnsafe(element)(subscribe)

modules/core/js/src/main/scala/animus/Transitions.scala

Lines changed: 94 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import com.raquo.laminar.api.L
88
import scala.collection.mutable
99
import scala.collection.mutable.ListBuffer
1010
import scala.scalajs.js
11-
import scala.scalajs.js.timers.SetTimeoutHandle
11+
import scala.scalajs.js.timers.{SetTimeoutHandle, setTimeout}
1212

1313
case class Transition(signal: Signal[TransitionStatus]):
1414
lazy val $isActive: Signal[Boolean] = signal.map(_.isActive)
@@ -19,55 +19,129 @@ case class Transition(signal: Signal[TransitionStatus]):
1919
lazy val width: Mod[HtmlElement] =
2020
Transitions.width($isActive)
2121

22+
def width(speed: Double): Mod[HtmlElement] =
23+
Transitions.width($isActive, speed)
24+
2225
lazy val height: Mod[HtmlElement] =
2326
Transitions.height($isActive)
2427

28+
def height(speed: Double): Mod[HtmlElement] =
29+
Transitions.height($isActive, speed)
30+
2531
lazy val blur: Mod[HtmlElement] =
2632
Transitions.blur($isActive)
2733

34+
def blur(closedBlur: Double): Mod[HtmlElement] =
35+
Transitions.blur($isActive, closedBlur)
36+
37+
lazy val scale: Mod[HtmlElement] =
38+
Transitions.scale($isActive)
39+
40+
def scale(closedScale: Double, speed: Double = 1): Mod[HtmlElement] =
41+
Transitions.scale($isActive, closedScale, speed)
42+
43+
def offset(closedX: Double = 0, closedY: Double = 0): Mod[HtmlElement] =
44+
Transitions.offset($isActive, closedX, closedY)
45+
2846
object Transitions:
2947
def opacity($visible: Signal[Boolean]): Mod[HtmlElement] =
3048
L.opacity <-- $visible.map(if _ then 1.0 else 0).spring
3149

32-
def height($open: Signal[Boolean]): Mod[HtmlElement] =
50+
enum Status:
51+
case Inserting, Active, Removing
52+
53+
def height($open: Signal[Boolean], speed: Double = 1): Mod[HtmlElement] =
3354
val scrollHeightVar = Var(0.0)
55+
56+
val $scrollHeight = $open.distinct
57+
.flatMap {
58+
if _ then scrollHeightVar.signal
59+
else Val(0.0)
60+
}
61+
.spring(_.withSpeed(speed))
62+
.px
63+
64+
val signalVar = Var($scrollHeight)
65+
var setTimeoutHandle: SetTimeoutHandle = null
66+
3467
Seq(
3568
overflowY.hidden,
3669
onMountBind { ctx =>
37-
ResizeObserver --> { _ =>
70+
EventStream.periodic(10) --> { _ =>
3871
scrollHeightVar.set(ctx.thisNode.ref.scrollHeight.toDouble)
3972
}
4073
},
41-
onMountBind { (el: MountContext[HtmlElement]) =>
42-
L.maxHeight <-- $open.flatMap {
43-
if _ then scrollHeightVar.signal
44-
else Val(0.0)
45-
}.spring.px
46-
}
74+
onMountBind { ctx =>
75+
$open.distinct --> { open =>
76+
if setTimeoutHandle != null then js.timers.clearTimeout(setTimeoutHandle)
77+
if open then
78+
signalVar.set($scrollHeight)
79+
setTimeoutHandle = setTimeout(700) {
80+
signalVar.set(Val("none"))
81+
}
82+
else signalVar.set($scrollHeight)
83+
}
84+
},
85+
$scrollHeight --> { _ => () },
86+
maxHeight <-- signalVar.signal.flatten
4787
)
4888

49-
def width($open: Signal[Boolean]): Mod[HtmlElement] =
89+
def width($open: Signal[Boolean], speed: Double = 1): Mod[HtmlElement] =
5090
val scrollWidthVar = Var(0.0)
91+
92+
val $scrollWidth = $open.distinct
93+
.flatMap {
94+
if _ then scrollWidthVar.signal
95+
else Val(0.0)
96+
}
97+
.spring(_.withSpeed(speed))
98+
.px
99+
100+
val signalVar = Var($scrollWidth)
101+
var setTimeoutHandle: SetTimeoutHandle = null
102+
51103
Seq(
52-
overflowX.hidden,
104+
overflowY.hidden,
53105
onMountBind { ctx =>
54-
ResizeObserver --> { _ =>
106+
EventStream.periodic(10) --> { _ =>
55107
scrollWidthVar.set(ctx.thisNode.ref.scrollWidth.toDouble)
56108
}
57109
},
58-
onMountBind { (ctx: MountContext[HtmlElement]) =>
59-
L.maxWidth <-- $open.flatMap {
60-
if _ then scrollWidthVar.signal
61-
else Val(0.0)
62-
}.spring.px
63-
}
110+
onMountBind { ctx =>
111+
$open.distinct --> { open =>
112+
if setTimeoutHandle != null then js.timers.clearTimeout(setTimeoutHandle)
113+
if open then
114+
signalVar.set($scrollWidth)
115+
setTimeoutHandle = setTimeout(700) {
116+
signalVar.set(Val("none"))
117+
}
118+
else signalVar.set($scrollWidth)
119+
}
120+
},
121+
$scrollWidth --> { _ => () },
122+
maxWidth <-- signalVar.signal.flatten
64123
)
65124

66-
def blur($open: Signal[Boolean]): Mod[HtmlElement] =
67-
styleProp("filter") <-- $open.map(if _ then 0.0 else 5.0).spring.map { blur =>
125+
def scale($open: Signal[Boolean], closedScale: Double = 0.0, speed: Double = 1): Mod[HtmlElement] =
126+
List(
127+
styleProp("transform") <-- $open.map(if _ then 1.0 else closedScale).spring(_.withSpeed(speed)).map { scale =>
128+
s"scale($scale)"
129+
},
130+
transformOrigin("top left")
131+
)
132+
133+
def blur($open: Signal[Boolean], closedBlur: Double = 5.0): Mod[HtmlElement] =
134+
styleProp("filter") <-- $open.map(if _ then 0.0 else closedBlur).spring(_.withSpeed(0.8)).map { blur =>
68135
s"blur(${blur}px)"
69136
}
70137

138+
def offset($open: Signal[Boolean], closedX: Double = 0.0, closedY: Double = 0.0): Mod[HtmlElement] =
139+
List(
140+
position.relative,
141+
top <-- $open.map(if _ then 0.0 else closedY).spring.px,
142+
left <-- $open.map(if _ then 0.0 else closedX).spring.px
143+
)
144+
71145
def transitionList[A, Key, Output](
72146
$items: Signal[Seq[A]]
73147
)(getKey: A => Key)(project: (Key, A, Signal[A], Transition) => Output): Signal[Seq[Output]] =

modules/core/js/src/main/scala/com/raquo/airstream/core/AnimationManager.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ object AnimationManager:
77
private val animations = scalajs.js.Map.empty[Int, Animator[_]]
88
private var animating = false
99
private var animationId: Int = 0
10+
1011
private def nextAnimationId(): Int =
1112
animationId += 1
1213
animationId

modules/core/js/src/main/scala/com/raquo/airstream/core/SpringSignal.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class SpringSignal[A](
3333
val value = parent.tryNow()
3434
value.foreach { a =>
3535
spring = configureSpring(Animator.make(a))
36-
spring.callback = fireQuick
36+
spring.onChange = fireQuick
3737
}
3838
value
3939

modules/core/js/src/main/scala/com/raquo/airstream/core/TransitioningSignal.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,13 @@ class TransitioningSignal[Input, Output, Key](
4141

4242
def refireMemoized(): Unit = fireValue(ordered.toList.map(memoized(_)._1), null)
4343

44-
var transitionSet: TransitionSet[Input, Input] = TransitionSet.empty[Input]
45-
4644
private[this] def memoizedProject(nextInputs: Seq[Input], first: Boolean = false): Seq[Output] =
47-
val nextKeysDict = mutable.HashSet.empty[Key] // HashSet has desirable performance tradeoffs
45+
val nextKeys = mutable.HashSet.empty[Key] // HashSet has desirable performance tradeoffs
4846

4947
val nextOutputs = nextInputs.map { input =>
5048
val key = getKey(input)
5149
activeKeys.add(key)
52-
nextKeysDict.add(key)
50+
nextKeys.add(key)
5351

5452
memoized.get(key) match
5553
case Some((output, inputVar, statusVar)) =>
@@ -73,7 +71,7 @@ class TransitioningSignal[Input, Output, Key](
7371

7472
ordered.addValues(nextOutputs.map(_._1))
7573

76-
val removing = activeKeys.toSet -- nextKeysDict
74+
val removing = activeKeys.toSet -- nextKeys
7775

7876
removing.foreach { key =>
7977
activeKeys.remove(key)
@@ -87,3 +85,5 @@ class TransitioningSignal[Input, Output, Key](
8785
}
8886

8987
ordered.toList.map(memoized(_)._1)
88+
end memoizedProject
89+
end TransitioningSignal

0 commit comments

Comments
 (0)