@paperbits/core
Version:
Paperbits core components.
410 lines (339 loc) • 14.8 kB
text/typescript
import * as ko from "knockout";
import template from "./popupEditor.html";
import { ViewManager } from "@paperbits/common/ui";
import { WidgetEditor } from "@paperbits/common/widgets";
import { StyleHelper } from "@paperbits/styles";
import { Component, OnMounted, Param, Event } from "@paperbits/common/ko/decorators";
import { PopupInstanceModel } from "../popupModel";
import { BackgroundStylePluginConfig, TypographyStylePluginConfig, ContainerStylePluginConfig, SizeStylePluginConfig, BoxStylePluginConfig, BorderStylePluginConfig, PaddingStylePluginConfig, BorderRadiusStylePluginConfig, ShadowStylePluginConfig } from "@paperbits/styles/plugins";
import { EventManager, Events } from "@paperbits/common/events";
import { PositionStylePluginConfig } from "@paperbits/styles/plugins/position";
import { TransformStylePluginConfig } from "@paperbits/styles/plugins/transform";
import { SizeUnits, Size, CalcExpression } from "@paperbits/styles/size";
export class PopupEditor implements WidgetEditor<PopupInstanceModel> {
public readonly containerBackground: ko.Observable<BackgroundStylePluginConfig>;
public readonly backdropBackground: ko.Observable<BackgroundStylePluginConfig>;
public readonly typography: ko.Observable<TypographyStylePluginConfig>;
public readonly containerConfig: ko.Observable<ContainerStylePluginConfig>;
public readonly containerSizeStyles: ko.Observable<SizeStylePluginConfig>;
public readonly containerSizeStylesResponsive: ko.Observable<boolean>;
public readonly containerShadow: ko.Observable<ShadowStylePluginConfig>;
public readonly backdrop: ko.Observable<boolean>;
public readonly position: ko.Observable<string>;
public readonly containerBox: ko.Observable<BoxStylePluginConfig>;
public readonly offsetX: ko.Observable<number>;
public readonly offsetY: ko.Observable<number>;
constructor(
private readonly viewManager: ViewManager,
private readonly eventManager: EventManager,
) {
this.containerConfig = ko.observable<ContainerStylePluginConfig>();
this.containerBackground = ko.observable<BackgroundStylePluginConfig>();
this.backdropBackground = ko.observable<BackgroundStylePluginConfig>();
this.containerBox = ko.observable<BoxStylePluginConfig>();
this.containerSizeStyles = ko.observable<SizeStylePluginConfig>();
this.containerShadow = ko.observable<ShadowStylePluginConfig>();
this.containerSizeStylesResponsive = ko.observable<boolean>();
this.position = ko.observable();
this.backdrop = ko.observable(true);
this.offsetX = ko.observable(0);
this.offsetY = ko.observable(0);
}
public model: PopupInstanceModel;
public onChange: (model: PopupInstanceModel) => void;
public async initialize(): Promise<void> {
this.backdrop(this.model.backdrop);
this.updateObservables();
this.eventManager.addEventListener(Events.ViewportChange, this.updateObservables);
this.position.subscribe(this.onPositionChange);
this.offsetX.subscribe(this.onOffsetChange);
this.offsetY.subscribe(this.onOffsetChange);
this.backdrop.subscribe(this.onBackdropChange);
}
private updateObservables(): void {
this.determinePosition();
this.determineOffset();
const popupContainerStyles = StyleHelper
.style(this.model.styles)
.component("popupContainer")
.variation("default");
const containerStyleConfig = popupContainerStyles.plugin("container").getConfig<ContainerStylePluginConfig>();
this.containerConfig(containerStyleConfig);
const containerBackgroundStyleConfig = popupContainerStyles.plugin("background").getConfig<BackgroundStylePluginConfig>();
this.containerBackground(containerBackgroundStyleConfig);
const shadowConfig = popupContainerStyles.plugin("shadow").getConfig<ShadowStylePluginConfig>();
this.containerShadow(shadowConfig);
const containerSizeStyles = popupContainerStyles.plugin("size").getConfig<SizeStylePluginConfig>();
this.containerSizeStyles(containerSizeStyles);
const backdropBackgroundStyleConfig = StyleHelper
.style(this.model.styles)
.component("popupBackdrop")
.variation("default")
.plugin("background")
.getConfig<BackgroundStylePluginConfig>();
this.backdropBackground(backdropBackgroundStyleConfig);
const containerPaddingStyles = popupContainerStyles.plugin("padding").getConfig<PaddingStylePluginConfig>();
const containerBorderStyles = popupContainerStyles.plugin("border").getConfig<BorderStylePluginConfig>();
const containerBorderRadiusStyles = popupContainerStyles.plugin("borderRadius").getConfig<BorderRadiusStylePluginConfig>();
const containerBox: BoxStylePluginConfig = {
padding: containerPaddingStyles,
border: containerBorderStyles,
borderRadius: containerBorderRadiusStyles
};
this.containerBox(containerBox);
}
public onContainerContainerChange(pluginConfig: ContainerStylePluginConfig): void {
StyleHelper
.style(this.model.styles)
.component("popupContainer")
.variation("default")
.plugin("container")
.setConfig(pluginConfig);
this.onChange(this.model);
}
public onContainerBackgroundChange(pluginConfig: BackgroundStylePluginConfig): void {
StyleHelper
.style(this.model.styles)
.component("popupContainer")
.variation("default")
.plugin("background")
.setConfig(pluginConfig);
this.onChange(this.model);
}
public onShadowUpdate(pluginConfig: ShadowStylePluginConfig): void {
StyleHelper
.style(this.model.styles)
.component("popupContainer")
.variation("default")
.plugin("shadow")
.setConfig(pluginConfig);
this.onChange(this.model);
}
public onBackdropBackgroundChange(pluginConfig: BackgroundStylePluginConfig): void {
StyleHelper
.style(this.model.styles)
.component("popupBackdrop")
.variation("default")
.plugin("background")
.setConfig(pluginConfig);
this.onChange(this.model);
}
public onContainerSizeUpdate(containerSizeStyles: SizeStylePluginConfig): void {
this.containerSizeStyles(containerSizeStyles);
StyleHelper
.style(this.model.styles)
.component("popupContainer")
.variation("default")
.plugin("size")
.setConfig(containerSizeStyles);
this.onChange(this.model);
}
private onBackdropChange(backdrop: boolean): void {
this.model.backdrop = backdrop;
this.onChange(this.model);
}
private determinePosition(): void {
const positionStyles = StyleHelper
.style(this.model.styles)
.component("popupContainer")
.variation("default")
.plugin("position")
.getConfig<PositionStylePluginConfig>();
if (positionStyles.position === "absolute") {
this.position("attached");
return;
}
if (positionStyles.left === 0) {
this.position("left");
return;
}
if (positionStyles.right === 0) {
this.position("right");
return;
}
if (positionStyles.top === 0) {
this.position("top");
return;
}
if (positionStyles.bottom === 0) {
this.position("bottom");
return;
}
this.position("center");
}
private determineOffset(): void {
const transformPluginConfig = StyleHelper
.style(this.model.styles)
.component("popupContainer")
.variation("default")
.plugin("transform")
.getConfig<TransformStylePluginConfig>();
let offsetX = new Size(0);
let offsetY = new Size(0);
if (CalcExpression.isExpr(transformPluginConfig.translate.x)) {
const parsed = CalcExpression.parse(transformPluginConfig.translate.x);
if (parsed && parsed.members.length > 1) {
offsetX = parsed.members[1]; // second expression member is offsetX
}
}
if (CalcExpression.isExpr(transformPluginConfig.translate.y)) {
const result = CalcExpression.parse(transformPluginConfig.translate.y);
if (result && result.members.length > 1) {
offsetY = result.members[1]; // second expression member is offsetY
}
}
if (this.position() === "right") {
offsetX.value = -offsetX.value;
}
if (this.position() === "bottom") {
offsetY.value = -offsetY.value;
}
this.offsetX(offsetX.value);
this.offsetY(offsetY.value);
this.recalculateStyles();
}
private onOffsetChange(): void {
this.recalculateStyles();
}
private recalculateStyles(): void {
const position = this.position();
const offsetXString = this.offsetX();
const offsetYString = this.offsetY();
let positionPluginConfig: PositionStylePluginConfig;
let transform: TransformStylePluginConfig;
const offsetX = new Size(offsetXString || 0);
const offsetY = new Size(offsetYString || 0);
switch (position) {
case "attached":
positionPluginConfig = {
position: "absolute"
};
transform = {
translate: {
// x: offsetX.toString(),
// y: offsetY.toString()
x: StyleHelper.calculate(new Size(0, SizeUnits.pixels), offsetX),
y: StyleHelper.calculate(new Size(0, SizeUnits.pixels), offsetY),
}
};
break;
case "center":
positionPluginConfig = {
position: "fixed",
top: "50%",
left: "50%"
};
transform = {
translate: {
x: StyleHelper.calculate(new Size(-50, SizeUnits.percents), offsetX),
y: StyleHelper.calculate(new Size(-50, SizeUnits.percents), offsetY),
}
};
break;
case "top":
positionPluginConfig = {
position: "fixed",
top: 0,
left: "50%"
};
transform = {
translate: {
x: StyleHelper.calculate(new Size(-50, SizeUnits.percents), offsetX),
y: offsetY.toString(),
}
};
break;
case "left":
positionPluginConfig = {
position: "fixed",
top: "50%",
left: 0
};
transform = {
translate: {
x: offsetX.toString(),
y: StyleHelper.calculate(new Size(-50, SizeUnits.percents), offsetY),
}
};
break;
case "right":
offsetX.value = -offsetX.value;
positionPluginConfig = {
position: "fixed",
top: "50%",
right: 0
};
transform = {
translate: {
x: offsetX.toString(),
y: StyleHelper.calculate(new Size(-50, SizeUnits.percents), offsetY),
}
};
break;
case "bottom":
offsetY.value = -offsetY.value;
positionPluginConfig = {
position: "fixed",
left: "50%",
bottom: 0
};
transform = {
translate: {
x: StyleHelper.calculate(new Size(-50, SizeUnits.percents), offsetX),
y: offsetY.toString()
}
};
break;
}
StyleHelper
.style(this.model.styles)
.component("popupContainer")
.variation("default")
.plugin("position")
.setConfig(positionPluginConfig);
StyleHelper
.style(this.model.styles)
.component("popupContainer")
.variation("default")
.plugin("transform")
.setConfig(transform);
this.onChange(this.model);
setTimeout(() => { // Temporary hack to reposition popup:
const hostDocument = this.viewManager.getHostDocument();
hostDocument.dispatchEvent(new CustomEvent("onPopupRepositionRequested"));
}, 10);
}
public onPositionChange(position: string): void {
this.position(position);
this.recalculateStyles();
}
public onBoxUpdate(pluginConfig: BoxStylePluginConfig): void {
this.containerBox(pluginConfig);
StyleHelper
.style(this.model.styles)
.component("popupContainer")
.variation("default")
.plugin("padding")
.setConfig(pluginConfig.padding);
StyleHelper
.style(this.model.styles)
.component("popupContainer")
.variation("default")
.plugin("border")
.setConfig(pluginConfig.border);
StyleHelper
.style(this.model.styles)
.component("popupContainer")
.variation("default")
.plugin("borderRadius")
.setConfig(pluginConfig.borderRadius);
this.onChange(this.model);
}
}