mirror of
https://github.com/rio-labs/rio.git
synced 2026-05-01 00:20:53 -05:00
161 lines
5.2 KiB
TypeScript
161 lines
5.2 KiB
TypeScript
import { applySwitcheroo } from "../designApplication";
|
|
import { ColorSet, ComponentId } from "../dataModels";
|
|
import { ComponentBase, ComponentState } from "./componentBase";
|
|
import { getPositionerByName, PopupManager } from "../popupManager";
|
|
import { stopPropagation } from "../eventHandling";
|
|
|
|
export type PopupState = ComponentState & {
|
|
_type_: "Popup-builtin";
|
|
anchor?: ComponentId;
|
|
content?: ComponentId;
|
|
is_open?: boolean;
|
|
modal: boolean;
|
|
user_closable: boolean;
|
|
color?: ColorSet | "none";
|
|
corner_radius?: number | [number, number, number, number];
|
|
position?:
|
|
| "auto"
|
|
| "left"
|
|
| "top"
|
|
| "right"
|
|
| "bottom"
|
|
| "center"
|
|
| "fullscreen"
|
|
| "dropdown";
|
|
alignment?: number;
|
|
gap?: number;
|
|
};
|
|
|
|
export class PopupComponent extends ComponentBase {
|
|
declare state: Required<PopupState>;
|
|
|
|
private contentContainer: HTMLElement;
|
|
|
|
private popupManager: PopupManager;
|
|
|
|
createElement(): HTMLElement {
|
|
let element = document.createElement("div");
|
|
element.classList.add("rio-popup-anchor");
|
|
|
|
this.contentContainer = document.createElement("div");
|
|
this.contentContainer.classList.add(
|
|
"rio-popup-manager-animation-scale",
|
|
"rio-popup-content"
|
|
);
|
|
|
|
// Initialize the popup manager. Many of these values will be
|
|
// overwritten by the updateElement method.
|
|
this.popupManager = new PopupManager({
|
|
anchor: element,
|
|
content: this.contentContainer,
|
|
positioner: getPositionerByName("center", 0, 0.5),
|
|
modal: false,
|
|
userClosable: false,
|
|
onUserClose: this.onUserClose.bind(this),
|
|
});
|
|
|
|
// Prevent clicking through the popup
|
|
["click", "pointerdown", "pointerup", "pointermove"].forEach(
|
|
(eventType) => {
|
|
this.contentContainer.addEventListener(
|
|
eventType,
|
|
stopPropagation
|
|
);
|
|
}
|
|
);
|
|
|
|
return element;
|
|
}
|
|
|
|
updateElement(
|
|
deltaState: PopupState,
|
|
latentComponents: Set<ComponentBase>
|
|
): void {
|
|
super.updateElement(deltaState, latentComponents);
|
|
|
|
// Update the children
|
|
if (deltaState.anchor !== undefined) {
|
|
this.replaceOnlyChild(
|
|
latentComponents,
|
|
deltaState.anchor,
|
|
this.element
|
|
);
|
|
}
|
|
|
|
if (deltaState.content !== undefined) {
|
|
this.replaceOnlyChild(
|
|
latentComponents,
|
|
deltaState.content,
|
|
this.contentContainer
|
|
);
|
|
}
|
|
|
|
// Update the popup manager
|
|
if (
|
|
deltaState.position !== undefined ||
|
|
deltaState.alignment !== undefined ||
|
|
deltaState.gap !== undefined
|
|
) {
|
|
this.popupManager.positioner = getPositionerByName(
|
|
deltaState.position ?? this.state.position,
|
|
deltaState.gap ?? this.state.gap,
|
|
deltaState.alignment ?? this.state.alignment
|
|
);
|
|
}
|
|
|
|
// Open / Close
|
|
if (deltaState.is_open !== undefined) {
|
|
// If the Popup was *created* with `is_open=True`, then our element
|
|
// isn't attached to the DOM yet and trying to open the popup will
|
|
// fail. Lazy workaround: Delay it.
|
|
requestAnimationFrame(() => {
|
|
this.popupManager.isOpen = deltaState.is_open!;
|
|
});
|
|
}
|
|
|
|
// Colorize
|
|
if (deltaState.color === "none") {
|
|
applySwitcheroo(this.contentContainer, "keep");
|
|
this.contentContainer.style.removeProperty("background-color");
|
|
this.contentContainer.style.removeProperty("box-shadow");
|
|
} else if (deltaState.color !== undefined) {
|
|
applySwitcheroo(this.contentContainer, deltaState.color);
|
|
this.contentContainer.style.backgroundColor = `var(--rio-local-bg)`;
|
|
this.contentContainer.style.boxShadow = `0 0 1rem var(--rio-global-shadow-color)`;
|
|
}
|
|
|
|
// Update the corner radius
|
|
if (deltaState.corner_radius !== undefined) {
|
|
if (typeof deltaState.corner_radius === "number") {
|
|
this.contentContainer.style.borderRadius = `${deltaState.corner_radius}rem`;
|
|
} else {
|
|
this.contentContainer.style.borderRadius = `${deltaState.corner_radius[0]}rem ${deltaState.corner_radius[1]}rem ${deltaState.corner_radius[2]}rem ${deltaState.corner_radius[3]}rem`;
|
|
}
|
|
}
|
|
|
|
// Modal
|
|
if (deltaState.modal !== undefined) {
|
|
this.popupManager.modal = deltaState.modal;
|
|
}
|
|
|
|
// User closable
|
|
if (deltaState.user_closable !== undefined) {
|
|
this.popupManager.userClosable = deltaState.user_closable;
|
|
}
|
|
}
|
|
|
|
onDestruction(): void {
|
|
super.onDestruction();
|
|
this.popupManager.destroy();
|
|
}
|
|
|
|
onUserClose(): void {
|
|
// The popup manager was closed by external user action. This doesn't
|
|
// need any updates to the popup manager, but Python should be notified
|
|
// of the state change.
|
|
this.setStateAndNotifyBackend({
|
|
is_open: false,
|
|
});
|
|
}
|
|
}
|