@paperbits/core
Version:
Paperbits core components.
159 lines (129 loc) • 6.47 kB
text/typescript
import { Bag } from "@paperbits/common";
import { CollectionModelBinder, IWidgetBinding } from "@paperbits/common/editing";
import { EventManager, Events } from "@paperbits/common/events";
import { IPopupService } from "@paperbits/common/popups";
import { StyleCompiler } from "@paperbits/common/styles";
import { IWidgetService, ViewModelBinder } from "@paperbits/common/widgets";
import { PopupInstanceContract } from "../popupContract";
import { ViewModelBinderSelector } from "../../ko/viewModelBinderSelector";
import { Placeholder } from "../../placeholder/ko/placeholder";
import { PopupHandlers } from "../popupHandlers";
import { PopupInstanceModel } from "../popupModel";
import { PopupViewModel } from "./popup";
import { ComponentFlow } from "@paperbits/common/components";
export class PopupViewModelBinder implements ViewModelBinder<PopupInstanceModel, PopupViewModel> {
constructor(
private readonly containerModelBinder: CollectionModelBinder,
private readonly viewModelBinderSelector: ViewModelBinderSelector,
private readonly popupService: IPopupService,
private readonly eventManager: EventManager,
private readonly styleCompiler: StyleCompiler,
private readonly widgetService: IWidgetService
) { }
public createBinding(model: PopupInstanceModel, viewModel?: PopupViewModel, bindingContext?: Bag<any>): void {
let savingTimeout;
const updateContent = async (): Promise<void> => {
/**
* TODO: Temporary hack. With current model it's impossible to tell which content (page or content)
* is being modified, so we look for active popup.
*/
if (!bindingContext.getHostedDocument) {
return;
}
const activePopup: HTMLElement = bindingContext.getHostedDocument().querySelector(".popup.show");
if (!activePopup) {
return;
}
const popupIdentifier = activePopup.id.replace("popups", "");
const popupKey = `popups/${popupIdentifier}`;
if (model.key !== popupKey) {
return;
}
const popupInstanceContract: PopupInstanceContract = {
type: "popup",
backdrop: model.backdrop,
styles: model.styles,
nodes: []
};
const childNodes = this.containerModelBinder.getChildContracts(model.widgets);
popupInstanceContract.nodes.push(...childNodes);
await this.popupService.updatePopupContent(model.key, popupInstanceContract);
};
const scheduleUpdate = (): void => {
clearTimeout(savingTimeout);
savingTimeout = setTimeout(updateContent, 500);
};
const binding: IWidgetBinding<PopupInstanceModel, PopupViewModel> = {
name: "popup",
displayName: "Popup",
layer: "*",
flow: ComponentFlow.Block,
model: model,
draggable: true,
editor: "popup-editor",
provides: ["popup"],
handler: PopupHandlers,
applyChanges: async () => {
await this.modelToViewModel(model, viewModel, bindingContext);
this.eventManager.dispatchEvent(Events.ContentUpdate, model.key);
},
onCreate: () => {
this.eventManager.addEventListener(Events.ContentUpdate, scheduleUpdate);
},
onDispose: () => {
this.eventManager.removeEventListener(Events.ContentUpdate, scheduleUpdate);
}
};
viewModel["widgetBinding"] = binding;
}
public async modelToViewModel(model: PopupInstanceModel, viewModel?: PopupViewModel, bindingContext?: Bag<any>): Promise<PopupViewModel> {
if (!viewModel) {
viewModel = new PopupViewModel();
}
// let childBindingContext: Bag<any> = {};
// if (bindingContext) {
// childBindingContext = <Bag<any>>Objects.clone(bindingContext);
// childBindingContext.layer = "*";
// }
bindingContext.layer = "*";
const promises = model.widgets.map(widgetModel => {
const definition = this.widgetService.getWidgetDefinitionForModel(widgetModel);
if (definition) {
const bindingPromise = this.widgetService.createWidgetBinding(definition, widgetModel, bindingContext);
return bindingPromise;
}
// legacy binding resolution
const widgetViewModelBinder = this.viewModelBinderSelector.getViewModelBinderByModel(widgetModel);
const bindingPromise = widgetViewModelBinder.modelToViewModel(widgetModel, null, bindingContext);
return bindingPromise;
});
const widgetViewModels = await Promise.all(promises);
// for (const widgetModel of model.widgets) {
// const widgetViewModelBinder = this.viewModelBinderSelector.getViewModelBinderByModel(widgetModel);
// if (widgetViewModelBinder.createWidgetBinding) {
// const binding = await widgetViewModelBinder.createWidgetBinding<any>(widgetModel, bindingContext);
// widgetViewModels.push(binding);
// }
// else {
// const widgetViewModel = await widgetViewModelBinder.modelToViewModel(widgetModel, null, bindingContext);
// widgetViewModels.push(widgetViewModel);
// }
// }
if (widgetViewModels.length === 0) {
widgetViewModels.push(new Placeholder("Popup content"));
}
if (model.styles) {
viewModel.styles(await this.styleCompiler.getStyleModelAsync(model.styles, bindingContext?.styleManager));
}
viewModel.identifier(model.key.replace("popups/", "popups"));
viewModel.widgets(widgetViewModels);
viewModel.backdrop(model.backdrop);
if (!viewModel["widgetBinding"]) {
this.createBinding(model, viewModel, bindingContext);
}
return viewModel;
}
public canHandleModel(model: PopupInstanceModel): boolean {
return model instanceof PopupInstanceModel;
}
}