UNPKG

@progress/kendo-react-dialogs

Version:

React Dialogs provide modal and non-modal windows for showing additional information to the user. KendoReact Dialogs package

412 lines (411 loc) 17.9 kB
/** * @license *------------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the package root for more information *------------------------------------------------------------------------------------------- */ import * as h from "react"; import * as A from "react-dom"; import n from "prop-types"; import { WindowTitleBar as B } from "./WindowTitlebar.mjs"; import { keepFocusInContainer as R, Keys as a, dispatchEvent as l, ZIndexContext as W, classNames as x, Draggable as N, canUseDOM as U, createPropsContext as H, withIdHOC as z, withPropsContext as k } from "@progress/kendo-react-common"; import { ResizeHandlers as _ } from "./WindowResizeHandlers.mjs"; import { MiddleLayerOptimization as Z } from "./MiddleLayerOptimization.mjs"; import { windowStage as i } from "./StageEnum.mjs"; import { WindowActionsBar as M } from "./WindowActionsBar.mjs"; import { DEFAULT_DIALOGS_ZINDEX as v, ZINDEX_DIALOGS_STEP as F, DATA_DIALOGS_ID as O } from "./constants.mjs"; import { getMaxZIndex as y } from "./utils.mjs"; const u = 300, w = 300, m = 120, S = 100, d = 5, f = class f extends h.Component { constructor(s) { super(s), this.context = 0, this.draggable = null, this.offSetCoordinates = { x: 0, y: 0 }, this.titleId = this.generateTitleId(), this.mounted = !1, this.activeElement = null, this.onKeyDown = (t) => { if (this.props.modal && R(t, this.element), t.target !== t.currentTarget) return; const e = this.props.minWidth || m, o = this.props.minHeight || S; if ((t.metaKey || t.ctrlKey) && this.props.resizable) { switch (t.keyCode) { case a.up: t.preventDefault(), o <= this.height - d && this.setState({ height: this.height - d }); break; case a.down: t.preventDefault(), this.setState({ height: this.height + d }); break; case a.left: e <= this.width - d && this.setState({ width: this.width - d }); break; case a.right: this.setState({ width: this.width + d }); break; default: return; } this.dispatchMoveEvent(this.props.onResize, t, !1, void 0); return; } if (t.altKey) { switch (t.keyCode) { case a.up: this.windowStage === i.MINIMIZED ? (this.handleRestore(t), l(this.props.onStageChange, t, this, { state: i.DEFAULT })) : this.windowStage === i.DEFAULT && (this.handleFullscreen(t), l(this.props.onStageChange, t, this, { state: i.FULLSCREEN })); break; case a.down: this.windowStage === i.FULLSCREEN ? (this.handleRestore(t), l(this.props.onStageChange, t, this, { state: i.DEFAULT })) : this.windowStage === i.DEFAULT && (this.handleMinimize(t), l(this.props.onStageChange, t, this, { state: i.MINIMIZED })); break; } return; } if (!t.ctrlKey) switch (t.keyCode) { case a.esc: this.props.onClose && this.handleCloseWindow(t); return; case a.up: this.setState((r) => ({ top: r.top - d })); break; case a.down: this.setState((r) => ({ top: r.top + d })); break; case a.left: this.setState((r) => ({ left: r.left - d })); break; case a.right: this.setState((r) => ({ left: r.left + d })); break; default: return; } this.dispatchMoveEvent(this.props.onMove, t, !1, void 0); }, this.onPress = (t) => { const e = t.event; this.windowCoordinatesState.differenceLeft = e.pageX - this.left, this.windowCoordinatesState.differenceTop = e.pageY - this.top; }, this.onDrag = (t) => { const e = t.event; e.originalEvent.preventDefault(), this.windowStage !== i.FULLSCREEN && this.props.draggable && (this.setState({ top: Math.max(e.pageY - this.windowCoordinatesState.differenceTop, 0), left: e.pageX - this.windowCoordinatesState.differenceLeft, isDragging: !0 }), this.props.onMove && this.dispatchMoveEvent(this.props.onMove, e, !0, !1)); }, this.onRelease = (t) => { const e = t.event; this.windowStage !== i.FULLSCREEN && this.props.draggable && this.props.onMove && this.dispatchMoveEvent(this.props.onMove, e, !0, !0), this.setState({ isDragging: !1 }); }, this.onFocus = () => { this._blurTimeout ? (clearTimeout(this._blurTimeout), this._blurTimeout = void 0) : this.setState({ focused: !0, zIndex: y(this.getCurrentZIndex(), this.getDocument(), this._id) }); }, this.onBlur = () => { clearTimeout(this._blurTimeout); const t = this.getWindow(); t && (this._blurTimeout = t.setTimeout(() => { this.mounted && this.setState({ focused: !1 }), this._blurTimeout = void 0; })); }, this.getInitialTop = () => { if (this.props.top !== void 0) return this.props.top; if (this.props.initialTop !== void 0) return this.props.initialTop; let t = w; if (this.props.height !== void 0 ? t = this.props.height : this.props.initialHeight !== void 0 && (t = this.props.initialHeight), this.props.appendTo) return this.props.appendTo.offsetHeight / 2 - t / 2; const e = this.getWindow(); return e ? e.innerHeight / 2 - t / 2 : 0; }, this.getInitialLeft = () => { if (this.props.left !== void 0) return this.props.left; if (this.props.initialLeft !== void 0) return this.props.initialLeft; let t = u; if (this.props.width !== void 0 ? t = this.props.width : this.props.initialWidth !== void 0 && (t = this.props.initialWidth), this.props.appendTo) return this.props.appendTo.offsetWidth / 2 - t / 2; const e = this.getWindow(); return e ? e.innerWidth / 2 - t / 2 : 0; }, this.getInitialWidth = () => { let t = u; return this.props.width !== void 0 ? t = this.props.width : this.props.initialWidth !== void 0 && (t = this.props.initialWidth), t; }, this.getInitialHeight = () => { let t = w; return this.props.height !== void 0 ? t = this.props.height : this.props.initialHeight !== void 0 && (t = this.props.initialHeight), t; }, this.handleMinimize = (t) => { t.preventDefault(), this.windowCoordinatesState.leftBeforeAction = this.left, this.windowCoordinatesState.topBeforeAction = this.top, this.windowCoordinatesState.widthBeforeAction = this.width, this.windowCoordinatesState.heightBeforeAction = this.height, this.setState({ stage: i.MINIMIZED, height: 0 }), l(this.props.onStageChange, t, this, { state: i.MINIMIZED }); }, this.handleFullscreen = (t) => { t.preventDefault(), this.windowCoordinatesState.leftBeforeAction = this.left, this.windowCoordinatesState.topBeforeAction = this.top, this.windowCoordinatesState.widthBeforeAction = this.width, this.windowCoordinatesState.heightBeforeAction = this.height; const e = this.getWindow(), o = e ? e.innerWidth : 0, p = e ? e.innerHeight : 0; this.setState({ left: 0, top: 0, width: this.props.appendTo ? this.props.appendTo.offsetWidth : o, height: this.props.appendTo ? this.props.appendTo.offsetHeight : p, stage: i.FULLSCREEN }), l(this.props.onStageChange, t, this, { state: i.FULLSCREEN }); }, this.handleRestore = (t) => { t.preventDefault(), this.windowStage === i.FULLSCREEN ? this.setState({ stage: i.DEFAULT, left: this.windowCoordinatesState.leftBeforeAction, top: this.windowCoordinatesState.topBeforeAction, width: this.windowCoordinatesState.widthBeforeAction, height: this.windowCoordinatesState.heightBeforeAction }) : this.windowStage === i.MINIMIZED && this.setState({ stage: i.DEFAULT, height: this.windowCoordinatesState.heightBeforeAction }), l(this.props.onStageChange, t, this, { state: i.DEFAULT }); }, this.handleCloseWindow = (t) => { t.preventDefault(), l(this.props.onClose, t, this, { state: void 0 }); }, this.handleDoubleClick = (t) => { this.windowStage === i.FULLSCREEN || this.windowStage === i.MINIMIZED ? this.handleRestore(t) : this.handleFullscreen(t); }, this.handleResize = (t, e) => { const o = this.props.appendTo ? t.pageX - this.offSetCoordinates.x : t.pageX, p = this.props.appendTo ? t.pageY - this.offSetCoordinates.y : t.pageY, r = this.width, c = this.height, C = this.props.minWidth || m, D = this.props.minHeight || S, I = this.top - p, T = this.left - o, L = o - this.left, b = p - this.top, g = Object.assign({}, this.state, { isDragging: !e.end }); e.direction.indexOf("n") >= 0 && D - (c + I) < 0 && (this.top > 0 && (g.height = c + I), g.top = p), e.direction.indexOf("s") >= 0 && D - b < 0 && (g.height = b), e.direction.indexOf("w") >= 0 && C - (r + T) < 0 && (this.left > 0 && (g.width = r + T), g.left = o), e.direction.indexOf("e") >= 0 && C - L < 0 && (g.width = L), this.setState(g), this.dispatchMoveEvent(this.props.onResize, t, !0, e.end); }, this.dispatchMoveEvent = (t, e, o, p) => { t && t.call(void 0, { nativeEvent: e.nativeEvent ? e.nativeEvent : e.originalEvent, drag: o, end: p, target: this, left: this.state.left, top: this.state.top, width: this.state.width, hight: this.state.height, height: this.state.height }); }, this.handleBrowserWindowResize = () => { if (this.windowStage === i.FULLSCREEN) { const t = this.getWindow(), e = t ? t.innerWidth : 0, o = t ? t.innerHeight : 0; this.setState({ width: this.props.appendTo ? this.props.appendTo.offsetWidth : e, height: this.props.appendTo ? this.props.appendTo.offsetHeight : o }); } }, this.getCurrentZIndex = () => !this.state || this.context === void 0 ? this.context ? this.context : v : this.state.zIndex > (this.context ? this.context + F : 0) ? this.state.zIndex : this.context + F, this.getDocument = () => this.props.appendTo ? this.props.appendTo.ownerDocument : document, this.getWindow = () => { const t = this.getDocument(); return t && t.defaultView; }, this.state = { stage: this.props.stage || i.DEFAULT, isDragging: !1, top: 0, left: 0, width: u, height: w, focused: !0, zIndex: v }, this.activeElement = document.activeElement; } get _id() { return this.props.id + "-accessibility-id"; } /** * @hidden */ componentDidMount() { this.element && this.props.autoFocus && this.element.focus({ preventScroll: !0 }); const s = this.getWindow(); s && s.addEventListener("resize", this.handleBrowserWindowResize), this.setState({ stage: this.props.stage || i.DEFAULT, isDragging: !1, top: this.getInitialTop(), left: this.getInitialLeft(), width: this.getInitialWidth(), height: this.getInitialHeight(), focused: !0, zIndex: y(this.getCurrentZIndex(), this.getDocument(), this._id) }), this.windowCoordinatesState = { leftBeforeAction: this.getInitialLeft(), topBeforeAction: this.getInitialTop(), widthBeforeAction: this.getInitialWidth(), heightBeforeAction: this.getInitialHeight() }; const t = this.getDocument(); if (this.props.appendTo && t) { const e = this.props.appendTo.getBoundingClientRect(), o = t.body.getBoundingClientRect(); this.offSetCoordinates.x = e.left - o.left, this.offSetCoordinates.y = e.top - o.top; } this.mounted = !0; } /** * @hidden */ componentWillUnmount() { const s = this.getWindow(); s && s.removeEventListener("resize", this.handleBrowserWindowResize), this.mounted = !1, setTimeout(() => { var t; !this.element && this.activeElement && document && (document.contains(this.activeElement) ? this.activeElement.focus({ preventScroll: !0 }) : this.activeElement.id && ((t = document.getElementById(this.activeElement.id)) == null || t.focus({ preventScroll: !0 }))); }); } /** * @hidden */ componentDidUpdate(s) { this.props.left && s.left !== this.props.left && this.setState({ left: this.props.left }), this.props.top && s.top !== this.props.top && this.setState({ top: this.props.top }); const t = this.getDocument(); if (this.props.appendTo && t) { const e = this.props.appendTo.getBoundingClientRect(), o = t.body.getBoundingClientRect(); this.offSetCoordinates.x = e.left - o.left, this.offSetCoordinates.y = e.top - o.top; } this.mounted = !0; } /** * @hidden */ render() { const s = h.Children.toArray(this.props.children), t = this.getContent(s), e = this.getActionBar(s), o = this.getCurrentZIndex(), p = x("k-window", this.props.className, { [`k-window-${this.props.themeColor}`]: this.props.themeColor, "k-window-minimized": this.state.stage === "MINIMIZED", "k-focus": this.state.focused }), r = /* @__PURE__ */ h.createElement(W.Provider, { value: o }, /* @__PURE__ */ h.createElement(h.Fragment, null, this.props.modal && /* @__PURE__ */ h.createElement( "div", { className: "k-overlay", style: { zIndex: o, ...this.props.overlayStyle } } ), /* @__PURE__ */ h.createElement( "div", { id: this.props.id, [O]: this._id, tabIndex: 0, role: "dialog", "aria-labelledby": this.titleId, onFocus: this.onFocus, onBlur: this.onBlur, onKeyDown: this.onKeyDown, ref: (c) => { this.windowElement = c, this.element = c; }, className: p, style: { top: this.top, left: this.left, width: this.width, height: this.height || "", zIndex: o, ...this.props.style } }, /* @__PURE__ */ h.createElement( Z, { shouldUpdateOnDrag: this.props.shouldUpdateOnDrag || !1, isDragging: this.state.isDragging }, /* @__PURE__ */ h.createElement( N, { onPress: this.onPress, onDrag: this.onDrag, onRelease: this.onRelease, autoScroll: !1, ref: (c) => this.draggable = c }, /* @__PURE__ */ h.createElement( B, { stage: this.windowStage, onDoubleClick: this.props.doubleClickStageChange ? this.handleDoubleClick : void 0, onMinimizeButtonClick: this.handleMinimize, onFullScreenButtonClick: this.handleFullscreen, onRestoreButtonClick: this.handleRestore, onCloseButtonClick: this.handleCloseWindow, closeButton: this.props.closeButton, minimizeButton: this.props.minimizeButton, maximizeButton: this.props.maximizeButton, restoreButton: this.props.restoreButton, id: this.titleId }, this.props.title ) ), this.windowStage !== i.MINIMIZED ? /* @__PURE__ */ h.createElement(h.Fragment, null, /* @__PURE__ */ h.createElement("div", { className: "k-window-content" }, t), e) : null, this.windowStage === i.DEFAULT && this.props.resizable ? /* @__PURE__ */ h.createElement(_, { onResize: this.handleResize }) : null ) ))); return U ? this.props.appendTo !== null ? A.createPortal(r, this.props.appendTo || document.body) : r : null; } // Getters get top() { return this.windowStage !== i.FULLSCREEN ? Math.max(this.props.top || this.state.top, 0) : 0; } get left() { return this.windowStage !== i.FULLSCREEN ? Math.max(this.props.left || this.state.left, 0) : 0; } get width() { let s = this.props.width || this.state.width; if (this.windowStage === i.FULLSCREEN) { if (this.props.appendTo) return s = this.props.appendTo.offsetWidth, s; const t = this.getWindow(); s = t ? t.innerWidth : 0; } return s; } get height() { let s = this.props.height || this.state.height; if (this.windowStage === i.FULLSCREEN) { if (this.props.appendTo) return s = this.props.appendTo.offsetHeight, s; const t = this.getWindow(); s = t ? t.innerHeight : 0; } else this.windowStage === i.MINIMIZED && (s = 0); return s; } get windowStage() { return this.props.stage || this.state.stage; } getActionBar(s) { return s.filter((t) => t && t.type === M); } getContent(s) { return s.filter((t) => t && t.type !== M); } generateTitleId() { return "window-title-" + this._id; } }; f.displayName = "Window", f.propTypes = { width: n.number, height: n.number, left: n.number, top: n.number, initialWidth: n.number, initialHeight: n.number, initialLeft: n.number, initialTop: n.number, minWidth: n.number, minHeight: n.number, resizable: n.bool, draggable: n.bool, title: n.any, shouldUpdateOnDrag: n.bool, stage: n.oneOf(["DEFAULT", "MINIMIZED", "FULLSCREEN"]), className: n.string, id: n.string, style: n.object, overlayStyle: n.object, autoFocus: n.bool }, f.defaultProps = { minWidth: m, minHeight: S, resizable: !0, draggable: !0, modal: !1, doubleClickStageChange: !0, autoFocus: !0 }, f.contextType = W; let E = f; const P = H(), K = z( k( P, E ) ); K.displayName = "KendoReactWindow"; export { K as Window, P as WindowPropsContext, E as WindowWithoutContext };