@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
JavaScript
/**
* @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
};