import { HTMLWidget, ISize, Widget } from "@hpcc-js/common"; import "../src/Modal.css"; export class Modal extends HTMLWidget { protected _widget: Widget; protected _relativeTarget: HTMLElement; protected _fade; protected _modal; protected _modalHeader; protected _modalBody; protected _modalHeaderAnnotations; protected _modalHeaderCloseButton; _close: () => void; constructor() { super(); this._tag = "div"; } closeModal() { this.visible(false); } getRelativeTarget() { let relativeTarget; if (this.relativeTargetId()) { relativeTarget = document.getElementById(this.relativeTargetId()); if (relativeTarget) { return relativeTarget; } } if (!relativeTarget) { relativeTarget = this.locateAncestor("layout_Grid"); if (relativeTarget && relativeTarget.element) { return relativeTarget.element().node(); } } return document.body; } setModalSize(): ISize { if (this.fixedHeight() !== null && this.fixedWidth() !== null) { this._modal .style("height", this.fixedHeight()) .style("width", this.fixedWidth()) .style("min-height", null) .style("min-width", null) .style("max-height", null) .style("max-width", null) ; } else if (this.minHeight() || this.minWidth()) { this._modal .style("min-height", this.minHeight()) .style("min-width", this.minWidth()) .style("max-height", this.maxHeight()) .style("max-width", this.maxWidth()) ; } const modalRect = this._modal.node().getBoundingClientRect(); const headerRect = this._modalHeader.node().getBoundingClientRect(); this._modalBody .style("height", (modalRect.height - headerRect.height) + "px") .style("width", modalRect.width); return modalRect; } setFadePosition(rect) { this._fade .style("top", rect.top + "px") .style("left", rect.left + "px") .style("width", rect.width + "px") .style("height", rect.height + "px") ; } setModalPosition(rect) { const modalRect = this.setModalSize(); if (this.fixedTop() !== null && this.fixedLeft() !== null) { this._modal .style("top", `calc(${this.fixedTop()} + ${rect.top}px)`) .style("left", `calc(${this.fixedLeft()} + ${rect.left}px)`) ; } else if (this.fixedHeight() !== null && this.fixedWidth() !== null) { this._modal .style("top", (rect.top + (rect.height / 2) - (modalRect.height / 2)) + "px") .style("left", (rect.left + (rect.width / 2) - (modalRect.width / 2)) + "px") ; } else if (this.minHeight() || this.minWidth()) { const contentRect = this._modal.node().getBoundingClientRect(); this._modal .style("top", (rect.top + (rect.height / 2) - (contentRect.height / 2)) + "px") .style("left", (rect.left + (rect.width / 2) - (contentRect.width / 2)) + "px") ; } } resize(size?: any): this { super.resize(); if (this._modal) this.setModalSize(); return this; } resizeBodySync(width: number, height: number): this { const header = this._modalHeader.node(); const headerRect = header.getBoundingClientRect(); this._modal .style("width", width + "px") .style("height", (height + headerRect.height) + "px") .style("min-width", width + "px") .style("min-height", (height + headerRect.height) + "px") ; this._modalHeader .style("width", width + "px") ; this._modalBody .style("width", width + "px") .style("height", height + "px") ; return this .minWidth(width + "px") .minHeight((height + headerRect.height) + "px") .resize({ height: height + headerRect.height, width }) ; } enter(domNode, element) { super.enter(domNode, element); this._fade = element.append("div") .classed("layout_Modal-fade", true) .classed("layout_Modal-fadeClickable", this.enableClickFadeToClose()) .classed("layout_Modal-fade-hidden", !this.showFade()) ; const header_h = this.titleFontSize() * 2; this._modal = element.append("div") .classed("layout_Modal-content", true) ; this._modalHeader = this._modal.append("div") .classed("layout_Modal-header", true) .style("color", this.titleFontColor()) .style("font-size", this.titleFontSize() + "px") .style("height", header_h + "px") ; this._modalBody = this._modal.append("div") .classed("layout_Modal-body", true) .style("height", `calc( 100% - ${header_h}px )`) .style("overflow-x", this.overflowX()) .style("overflow-y", this.overflowY()) ; this._modalHeader.append("div") .classed("layout_Modal-title", true) .style("line-height", this.titleFontSize() + "px") .style("top", (this.titleFontSize() / 2) + "px") .style("left", (this.titleFontSize() / 2) + "px") .text(this.formattedTitle()) ; this._modalHeaderAnnotations = this._modalHeader.append("div") .classed("layout_Modal-annotations", true) ; this._modalHeaderCloseButton = this._modalHeaderAnnotations.append("div") .classed("layout_Modal-closeButton", true) .html("") ; this._modalHeaderAnnotations .style("line-height", this.titleFontSize() + "px") .style("right", (this.titleFontSize() / 2) + "px") .style("top", (this.titleFontSize() / 2) + "px") ; this._modalHeaderCloseButton.on("click", () => { this.closeModal(); }); this._fade.on("click", n => { if (this.enableClickFadeToClose()) { this.closeModal(); } }); } update(domNode, element) { super.update(domNode, element); element.style("display", this.show() ? null : "none"); this._fade.classed("layout_Modal-fade-hidden", !this.showFade()); this._relativeTarget = this.getRelativeTarget(); this.setModalSize(); const rect = this._relativeTarget.getBoundingClientRect(); this.setFadePosition(rect); this.setModalPosition(rect); if (this.show()) { if (!this._widget.target()) { this._widget.target(this._modalBody.node()); } this._widget.resize().render(); } else { this._widget .target(null) .render() ; } } exit(domNode, element) { if (this._widget) { this._widget.target(null); } super.exit(domNode, element); } formattedTitle() { const title = this.title_exists() ? this.title().trim() : ""; if (title.length > 0 && title.slice(0, 1) === "(" && title.slice(-1) === ")") { return title.slice(1, -1); } return this.title(); } } Modal.prototype._class += " layout_Modal"; export interface Modal { show(): boolean; show(_: boolean): this; showFade(): boolean; showFade(_: boolean): this; enableClickFadeToClose(): boolean; enableClickFadeToClose(_: boolean): this; title(): string; title(_: string): this; title_exists(): boolean; titleFontSize(): number; titleFontSize(_: number): this; titleFontColor(): string; titleFontColor(_: string): this; minWidth(): string; minWidth(_: string): this; minHeight(): string; minHeight(_: string): this; maxWidth(): string; maxWidth(_: string): this; maxHeight(): string; maxHeight(_: string): this; fixedWidth(): string; fixedWidth(_: string): this; fixedHeight(): string; fixedHeight(_: string): this; fixedTop(): string; fixedTop(_: string): this; fixedLeft(): string; fixedLeft(_: string): this; relativeTargetId(): string; relativeTargetId(_: string): this; widget(): Widget; widget(_: Widget): this; overflowX(): "hidden" | "scroll"; overflowX(_: "hidden" | "scroll"): this; overflowY(): "hidden" | "scroll"; overflowY(_: "hidden" | "scroll"): this; } Modal.prototype.publish("title", null, "string", "title"); Modal.prototype.publish("widget", null, "widget", "widget"); Modal.prototype.publish("titleFontSize", 18, "number", "titleFontSize (in pixels)"); Modal.prototype.publish("titleFontColor", "#ffffff", "html-color", "titleFontColor"); Modal.prototype.publish("relativeTargetId", null, "string", "relativeTargetId"); Modal.prototype.publish("show", true, "boolean", "show"); Modal.prototype.publish("showFade", true, "boolean", "showFade"); Modal.prototype.publish("enableClickFadeToClose", true, "boolean", "enableClickFadeToClose"); Modal.prototype.publish("minWidth", "400px", "string", "minWidth"); Modal.prototype.publish("minHeight", "400px", "string", "minHeight"); Modal.prototype.publish("maxWidth", "800px", "string", "maxWidth"); Modal.prototype.publish("maxHeight", "800px", "string", "maxHeight"); Modal.prototype.publish("fixedWidth", null, "string", "fixedWidth"); Modal.prototype.publish("fixedHeight", null, "string", "fixedHeight"); Modal.prototype.publish("fixedTop", null, "string", "fixedTop"); Modal.prototype.publish("fixedLeft", null, "string", "fixedLeft"); Modal.prototype.publish("overflowX", "hidden", "string", "overflowX"); Modal.prototype.publish("overflowY", "scroll", "string", "overflowY");