UNPKG

@paperbits/core

Version:
410 lines (339 loc) 14.8 kB
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"; @Component({ selector: "popup-editor", template: template }) 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); } @Param() public model: PopupInstanceModel; @Event() public onChange: (model: PopupInstanceModel) => void; @OnMounted() 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); } }