@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
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 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
};