switchers now fade between components, are documented and public

This commit is contained in:
Jakob Pinterits
2024-06-02 21:57:42 +02:00
parent 6b70b90fca
commit 0d3bb2b3ae
7 changed files with 86 additions and 14 deletions

View File

@@ -3,12 +3,12 @@ import { ComponentBase, ComponentState } from './componentBase';
import { componentsById } from '../componentManagement';
import { LayoutContext, updateLayout } from '../layouting';
import { easeInOut } from '../easeFunctions';
const TRANSITION_TIME: number = 0.35;
import { commitCss } from '../utils';
export type SwitcherState = ComponentState & {
_type_: 'Switcher-builtin';
content?: ComponentId | null;
transition_time?: number;
};
export class SwitcherComponent extends ComponentBase {
@@ -39,6 +39,7 @@ export class SwitcherComponent extends ComponentBase {
createElement(): HTMLElement {
let element = document.createElement('div');
element.classList.add('rio-switcher');
return element;
}
@@ -46,6 +47,15 @@ export class SwitcherComponent extends ComponentBase {
deltaState: SwitcherState,
latentComponents: Set<ComponentBase>
): void {
// Update the transition time first, in case the code below is about
// to start an animation.
if (deltaState.transition_time !== undefined) {
this.element.style.setProperty(
'--rio-switcher-transition-time',
`${deltaState.transition_time}s`
);
}
// Update the child
if (
!this.isInitialized ||
@@ -56,13 +66,41 @@ export class SwitcherComponent extends ComponentBase {
// Out with the old
if (this.activeChildContainer !== null) {
// The old component may be used somewhere else in the UI, so
// the switcher can't rely on it still being available. To get
// around this, create a copy of the element's HTML tree and use
// that for the animation.
//
// Moreover, teh component may have already been removed from
// the switcher. This can happen when it was moved into another
// component. Thus, fetch the component by its id, rather than
// using the contained HTML node.
let oldComponent = componentsById[this.state.content!]!;
let oldElementClone = oldComponent.element.cloneNode(
true
) as HTMLElement;
// Discard the old component
this.replaceOnlyChild(
latentComponents,
null,
this.activeChildContainer
);
this.activeChildContainer.remove();
// Animate out the old component
this.activeChildContainer.appendChild(oldElementClone);
this.activeChildContainer.classList.remove(
'rio-switcher-active'
);
// Make sure to remove the child after the animation finishes
let oldChildContainer = this.activeChildContainer;
setTimeout(() => {
oldChildContainer.remove();
}, this.state.transition_time * 1000);
// No more children :(
this.activeChildContainer = null;
this.activeChildInstance = null;
}
@@ -86,6 +124,10 @@ export class SwitcherComponent extends ComponentBase {
// Remember the child, as it is needed frequently
this.activeChildInstance = componentsById[deltaState.content!]!;
// Animate the child in
commitCss(this.activeChildContainer);
this.activeChildContainer.classList.add('rio-switcher-active');
}
// Start the layouting process
@@ -142,7 +184,7 @@ export class SwitcherComponent extends ComponentBase {
let now = Date.now();
let linearT = Math.min(
1,
(now - this.animationStartedAt) / 1000 / TRANSITION_TIME
(now - this.animationStartedAt) / 1000 / this.state.transition_time
);
let easedT = easeInOut(linearT);

View File

@@ -2372,6 +2372,16 @@ textarea:not(:placeholder-shown) ~ .rio-input-box-label,
overflow: hidden;
}
.rio-switcher > * {
opacity: 0;
transition: opacity var(--rio-switcher-transition-time) ease-in-out;
}
.rio-switcher-active {
opacity: 1;
}
// Tooltip
.rio-tooltip {
pointer-events: none;

View File

@@ -1,5 +1,6 @@
from __future__ import annotations
from dataclasses import KW_ONLY
from typing import final
import rio
@@ -14,22 +15,41 @@ __all__ = [
@final
class Switcher(FundamentalComponent):
"""
TODO
Smoothly transitions between components.
The `Switcher` component is a container which can display one component at a
time. What makes it useful, is that when you change the `content` attribute,
rather than instantly swapping the displayed component, it will smoothly
transition between the two.
Moreover, whenever the content's size changes, the `Switcher` will
smoothly resize to match the new size. This means you can use switchers
to smoothly transition between components of different sizes.
`content` may also be `None`, in which case the `Switcher` won't display
anything. This in turn allows you to animate the appearance or disappearance
of a component, e.g. for a sidebar.
## Attributes
`content`: The currently displayed component.
`content`: The component to display inside the switcher. If `None`, the
switcher will be empty.
`transition_time`: How many seconds it should take for the switcher to
transition between components and sizes.
## Metadata
`public`: False
`experimental`: True
"""
content: rio.Component | None
_: KW_ONLY
transition_time: float = 0.35
Switcher._unique_id = "Switcher-builtin"

View File

@@ -10,7 +10,7 @@ class AssetError(Exception):
access a nonexistent asset.
"""
def __init__(self, message: str):
def __init__(self, message: str) -> None:
super().__init__(message)
@property
@@ -40,7 +40,7 @@ class ClipboardError(Exception):
Exception raised for errors related to clipboard operations.
"""
def __init__(self, message: str):
def __init__(self, message: str) -> None:
super().__init__(message)
@property

View File

@@ -103,7 +103,7 @@ class LinearGradientFill(Fill):
self,
*stops: tuple[Color, float],
angle_degrees: float = 0.0,
):
) -> None:
# Make sure there's at least one stop
if not stops:
raise ValueError("Gradients must have at least 1 stop")
@@ -158,7 +158,7 @@ class ImageFill(Fill):
image: ImageLike,
*,
fill_mode: Literal["fit", "stretch", "zoom"] = "fit",
):
) -> None:
"""
## Parameters

View File

@@ -22,7 +22,7 @@ __all__ = [
# input class has been created, some forward references may not be evaluatable
# yet.
class get_local_annotations(Mapping[str, introspection.types.TypeAnnotation]):
def __init__(self, cls: type, *, strict: bool = False):
def __init__(self, cls: type, *, strict: bool = False) -> None:
# Note: Don't use `typing.get_type_hints` because it has a stupid bug in
# python 3.10 where it dies if something is annotated as
# `dataclasses.KW_ONLY`.

View File

@@ -12,7 +12,7 @@ T = TypeVar("T")
class SessionAttachments:
def __init__(self, sess: session.Session):
def __init__(self, sess: session.Session) -> None:
self._session = sess
self._attachments: dict[type, object] = {}