MrPowerGamerBR
09/13/2023, 5:31 AMDavid Herman
09/13/2023, 7:24 AMtransitionend
event for this I believe) only then do you remove the element from the DOMDavid Herman
09/13/2023, 7:34 AMDavid Herman
09/13/2023, 7:36 AMval BoxStyle by ComponentStyle.base {
Modifier
.size(200.px)
.background(Colors.Magenta)
.cursor(Cursor.Pointer)
}
val FadeOut by Keyframes {
0.percent { Modifier.opacity(1.0) }
100.percent { Modifier.opacity(0.0) }
}
enum class BoxState {
NORMAL,
REMOVING,
REMOVED
}
@Page
@Composable
fun HomePage() {
PageLayout {
var boxState by remember { mutableStateOf(BoxState.NORMAL) }
if (boxState != BoxState.REMOVED) {
Box(
BoxStyle.toModifier()
.onClick { boxState = BoxState.REMOVING }
.thenIf(boxState == BoxState.REMOVING) { Modifier.animation(FadeOut.toAnimation(<http://1000.ms|1000.ms>)) }
.onAnimationEnd { boxState = BoxState.REMOVED }
)
}
}
}
David Herman
09/13/2023, 7:38 AMDavid Herman
09/13/2023, 7:47 AMStyleSheet
and then using attrs = { classes(...) }
to add them to say a Div, exercise left to the reader ;P):
val BoxStyle by ComponentStyle.base {
Modifier
.size(200.px)
.background(Colors.Magenta)
.cursor(Cursor.Pointer)
.transition(CSSTransition("opacity", <http://1000.ms|1000.ms>))
}
val AnimatingOutBoxVariant by BoxStyle.addVariantBase {
Modifier.opacity(0f)
}
enum class BoxState {
NORMAL,
REMOVING,
REMOVED
}
@Page
@Composable
fun HomePage() {
PageLayout {
var boxState by remember { mutableStateOf(BoxState.NORMAL) }
if (boxState != BoxState.REMOVED) {
Box(
BoxStyle
.toModifier(AnimatingOutBoxVariant.takeIf { boxState == BoxState.REMOVING })
.onClick { boxState = BoxState.REMOVING }
.onTransitionEnd { boxState = BoxState.REMOVED }
)
}
}
}
MrPowerGamerBR
09/13/2023, 2:48 PMMrPowerGamerBR
09/13/2023, 2:49 PMMrPowerGamerBR
09/13/2023, 2:55 PMDiv(attrs = {
classes("toast-list")
if (globalState.activeSaveBar)
classes("save-bar-active")
}) {
for (toastWithAnimationState in globalState.activeToasts) {
Div(attrs = {
classes(
"toast",
when (toastWithAnimationState.toast.type) {
<http://Toast.Type.INFO|Toast.Type.INFO> -> "info"
Toast.Type.SUCCESS -> "success"
Toast.Type.WARN -> "warn"
}
)
when (toastWithAnimationState.state.value) {
GlobalState.ToastWithAnimationState.State.ADDED -> {
classes("added")
onAnimationEnd {
println("Finished (added) animation")
toastWithAnimationState.state.value = GlobalState.ToastWithAnimationState.State.DEFAULT
}
}
GlobalState.ToastWithAnimationState.State.DEFAULT -> {
// I'm just happy to be here
}
GlobalState.ToastWithAnimationState.State.REMOVED -> {
classes("removed")
onAnimationEnd {
println("Finished (removed) animation!")
globalState.activeToasts.remove(toastWithAnimationState)
}
}
}
}) {
Div(attrs = {
classes("toast-title")
}) {
Text(toastWithAnimationState.toast.title)
}
Div {
toastWithAnimationState.toast.body.invoke()
}
}
}
}
MrPowerGamerBR
09/13/2023, 2:55 PMfun showToast(toastType: Toast.Type, title: String, body: @Composable () -> (Unit) = {}) {
val toast = Toast(
toastType,
title,
body
)
val toastWithAnimationState = ToastWithAnimationState(toast, mutableStateOf(ToastWithAnimationState.State.ADDED))
activeToasts.add(toastWithAnimationState)
launch {
delay(7.seconds)
toastWithAnimationState.state.value = ToastWithAnimationState.State.REMOVED
}
}
class ToastWithAnimationState(
val toast: Toast,
val state: MutableState<State>,
) {
enum class State {
ADDED,
DEFAULT,
REMOVED
}
}
MrPowerGamerBR
09/13/2023, 3:02 PMval toastWithAnimationState = ToastWithAnimationState(toast, Random.nextLong(0, Long.MAX_VALUE), mutableStateOf(ToastWithAnimationState.State.ADDED))
Then, when rendering the toast, we key
it based on the ID!
Div(attrs = {
classes("toast-list")
if (globalState.activeSaveBar)
classes("save-bar-active")
}) {
for (toastWithAnimationState in globalState.activeToasts) {
key(toastWithAnimationState.randomId) {
... the toast code ...
}
}
David Herman
09/13/2023, 4:07 PMDavid Herman
09/13/2023, 4:12 PMdeferRender
which I use for things like popups and it will definitely power toasts when I add them. I suspect it would also solve your problem of interfering with composition. It works by delaying elements that render on top of everything until the end of the DOM.
You can check it out here if curious, but you first have to call renderWithDeferred
(method just below it) as a parent scope (preferably at the root of your site)MrPowerGamerBR
09/13/2023, 4:44 PMGlad you got something working. Your site looks really dynamic!Thanks! It is a configuration dashboard for my Discord bot :3 I will look into the deferRender function later! Maybe it can also be useful for other things I'm working on ๐
David Herman
09/13/2023, 4:45 PMz-index
, then that's the time to look into deferRender
๐