UNPKG

@progress/kendo-react-dialogs

Version:

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

420 lines (419 loc) 18.5 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 r from "react"; import * as H from "react-dom"; import n from "prop-types"; import { WindowTitleBar as N } from "./WindowTitlebar.mjs"; import { keepFocusInContainer as U, Keys as a, dispatchEvent as g, canUseDOM as S, ZIndexContext as A, classNames as z, Draggable as k, createPropsContext as _, withIdHOC as Z, withPropsContext as O } from "@progress/kendo-react-common"; import { ResizeHandlers as K } from "./WindowResizeHandlers.mjs"; import { MiddleLayerOptimization as P } from "./MiddleLayerOptimization.mjs"; import { windowStage as i } from "./StageEnum.mjs"; import { WindowActionsBar as B } from "./WindowActionsBar.mjs"; import { DEFAULT_DIALOGS_ZINDEX as v, ZINDEX_DIALOGS_STEP as R, DATA_DIALOGS_ID as X } from "./constants.mjs"; import { getMaxZIndex as x } from "./utils.mjs"; const E = 300, C = 300, D = 120, T = 100, l = 5, u = class u extends r.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) => { var p, w, m; if (this.props.modal && U(t, this.element), t.target !== t.currentTarget) return; const e = this.props.minWidth || D, o = this.props.minHeight || T, h = t.metaKey || t.ctrlKey, c = (m = (w = this.height) != null ? w : (p = this.windowElement) == null ? void 0 : p.clientHeight) != null ? m : 0; if (h && this.props.resizable) { switch (t.keyCode) { case a.up: t.preventDefault(), o <= c - l && this.setState({ height: c - l }); break; case a.down: t.preventDefault(), this.setState({ height: c + l }); break; case a.left: e <= this.width - l && this.setState({ width: this.width - l }); break; case a.right: this.setState({ width: this.width + l }); 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), g(this.props.onStageChange, t, this, { state: i.DEFAULT })) : this.windowStage === i.DEFAULT && (this.handleFullscreen(t), g(this.props.onStageChange, t, this, { state: i.FULLSCREEN })); break; case a.down: this.windowStage === i.FULLSCREEN ? (this.handleRestore(t), g(this.props.onStageChange, t, this, { state: i.DEFAULT })) : this.windowStage === i.DEFAULT && (this.handleMinimize(t), g(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((d) => ({ top: d.top - l })); break; case a.down: this.setState((d) => ({ top: d.top + l })); break; case a.left: this.setState((d) => ({ left: d.left - l })); break; case a.right: this.setState((d) => ({ left: d.left + l })); 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: x(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 = () => { var o, h; if (this.props.top !== void 0) return this.props.top; if (this.props.initialTop !== void 0) return this.props.initialTop; let t = C; if (this.props.height !== void 0 ? t = this.props.height : this.props.initialHeight !== void 0 && (t = this.props.initialHeight), this.props.appendTo && t) return this.props.appendTo.offsetHeight / 2 - t / 2; const e = this.getWindow(); return e ? e.innerHeight / 2 - ((h = t != null ? t : (o = this.windowElement) == null ? void 0 : o.clientHeight) != null ? h : 0) / 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 = E; 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 = E; return this.props.width !== void 0 ? t = this.props.width : this.props.initialWidth !== void 0 && (t = this.props.initialWidth), t; }, this.getInitialHeight = () => { let t = C; 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 }), g(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, h = 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 : h, stage: i.FULLSCREEN }), g(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 }), g(this.props.onStageChange, t, this, { state: i.DEFAULT }); }, this.handleCloseWindow = (t) => { t.preventDefault(), g(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) => { var M, F, y; const o = this.props.appendTo ? t.pageX - this.offSetCoordinates.x : t.pageX, h = this.props.appendTo ? t.pageY - this.offSetCoordinates.y : t.pageY, c = this.width, p = (y = (F = this.height) != null ? F : (M = this.windowElement) == null ? void 0 : M.clientHeight) != null ? y : 0, w = this.props.minWidth || D, m = this.props.minHeight || T, d = this.top - h, L = this.left - o, b = o - this.left, W = h - this.top, f = Object.assign({}, this.state, { isDragging: !e.end }); e.direction.indexOf("n") >= 0 && m - (p + d) < 0 && (this.top > 0 && (f.height = p + d), f.top = h), e.direction.indexOf("s") >= 0 && m - W < 0 && (f.height = W), e.direction.indexOf("w") >= 0 && w - (c + L) < 0 && (this.left > 0 && (f.width = c + L), f.left = o), e.direction.indexOf("e") >= 0 && w - b < 0 && (f.width = b), this.setState(f), this.dispatchMoveEvent(this.props.onResize, t, !0, e.end); }, this.dispatchMoveEvent = (t, e, o, h) => { t && t.call(void 0, { nativeEvent: e.nativeEvent ? e.nativeEvent : e.originalEvent, drag: o, end: h, 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 + R : 0) ? this.state.zIndex : this.context + R, this.getDocument = () => { const t = S ? document : null; return this.props.appendTo ? this.props.appendTo.ownerDocument : t; }, 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: E, height: C, focused: !0, zIndex: v }, S && (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: x(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 && S && (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 }), this.props.initialLeft && s.initialLeft !== this.props.initialLeft && this.setState({ left: this.props.initialLeft }), this.props.initialTop && s.initialTop !== this.props.initialTop && this.setState({ top: this.props.initialTop }); 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 = r.Children.toArray(this.props.children), t = this.getContent(s), e = this.getActionBar(s), o = this.getCurrentZIndex(), h = z("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 }), c = /* @__PURE__ */ r.createElement(A.Provider, { value: o }, /* @__PURE__ */ r.createElement(r.Fragment, null, this.props.modal && /* @__PURE__ */ r.createElement( "div", { className: "k-overlay", style: { zIndex: o, ...this.props.overlayStyle } } ), /* @__PURE__ */ r.createElement( "div", { id: this.props.id, [X]: this._id, tabIndex: 0, role: "dialog", "aria-labelledby": this.titleId, onFocus: this.onFocus, onBlur: this.onBlur, onKeyDown: this.onKeyDown, ref: (p) => { this.windowElement = p, this.element = p; }, className: h, style: { top: this.top, left: this.left, width: this.width, height: this.height || "", zIndex: o, ...this.props.style } }, /* @__PURE__ */ r.createElement( P, { shouldUpdateOnDrag: this.props.shouldUpdateOnDrag || !1, isDragging: this.state.isDragging }, /* @__PURE__ */ r.createElement( k, { onPress: this.onPress, onDrag: this.onDrag, onRelease: this.onRelease, autoScroll: !1, ref: (p) => { this.draggable = p; } }, /* @__PURE__ */ r.createElement( N, { 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__ */ r.createElement(r.Fragment, null, /* @__PURE__ */ r.createElement("div", { className: "k-window-content" }, t), e) : null, this.windowStage === i.DEFAULT && this.props.resizable ? /* @__PURE__ */ r.createElement(K, { onResize: this.handleResize }) : null ) ))); return S ? this.props.appendTo !== null ? H.createPortal(c, this.props.appendTo || document.body) : c : 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 === B); } getContent(s) { return s.filter((t) => t && t.type !== B); } generateTitleId() { return "window-title-" + this._id; } }; u.displayName = "Window", u.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 }, u.defaultProps = { minWidth: D, minHeight: T, resizable: !0, draggable: !0, modal: !1, doubleClickStageChange: !0, autoFocus: !0 }, u.contextType = A; let I = u; const G = _(), Y = Z( O( G, I ) ); Y.displayName = "KendoReactWindow"; export { Y as Window, G as WindowPropsContext, I as WindowWithoutContext };