diff --git a/.changeset/orange-walls-join.md b/.changeset/orange-walls-join.md new file mode 100644 index 000000000..327be81ca --- /dev/null +++ b/.changeset/orange-walls-join.md @@ -0,0 +1,6 @@ +--- +"@ensembleui/react-framework": patch +"@ensembleui/react-kitchen-sink": patch +--- + +Added the draggable property to modals diff --git a/apps/kitchen-sink/src/ensemble/screens/forms.yaml b/apps/kitchen-sink/src/ensemble/screens/forms.yaml index a0e80ac1c..37cc74f73 100644 --- a/apps/kitchen-sink/src/ensemble/screens/forms.yaml +++ b/apps/kitchen-sink/src/ensemble/screens/forms.yaml @@ -34,6 +34,7 @@ View: name: widgets height: 200px width: 900px + draggable: true - Button: label: show dialog onTap: diff --git a/packages/runtime/package.json b/packages/runtime/package.json index be8f8f69d..7eb6114e1 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -56,5 +56,8 @@ "runes2": "^1.1.4", "tsconfig": "workspace:*", "tsup": "^7.2.0" + }, + "dependencies": { + "react-draggable": "^4.4.6" } } diff --git a/packages/runtime/src/runtime/modal/index.tsx b/packages/runtime/src/runtime/modal/index.tsx index b0fc9c3f5..00547f943 100644 --- a/packages/runtime/src/runtime/modal/index.tsx +++ b/packages/runtime/src/runtime/modal/index.tsx @@ -1,5 +1,5 @@ import { Modal } from "antd"; -import type { PropsWithChildren } from "react"; +import type { PropsWithChildren, ReactNode } from "react"; import { createContext, useCallback, @@ -17,11 +17,16 @@ import { CloseOutlined } from "@ant-design/icons"; import { generateRandomString, useEvaluate } from "@ensembleui/react-framework"; import { isString, omit, pick } from "lodash-es"; import { useNavigate } from "react-router-dom"; +import Draggable, { + type DraggableData, + type DraggableEvent, +} from "react-draggable"; import { getComponentStyles } from "../../shared/styles"; import { getCustomStyles, getFullScreenStyles } from "./utils"; export interface ModalProps { title?: string | React.ReactNode; + draggable?: boolean; maskClosable?: boolean; mask?: boolean; hideCloseIcon?: boolean; @@ -284,6 +289,14 @@ export const ModalWrapper: React.FC = ({ children }) => { ); + const draggleRef = useRef(null); + const [bounds, setBounds] = useState({ + left: 0, + top: 0, + bottom: 0, + right: 0, + }); + return ( {children} @@ -292,6 +305,33 @@ export const ModalWrapper: React.FC = ({ children }) => { if (!modal.visible) { return null; } + const modalRender = (modalBody: ReactNode) => { + return modal.options.draggable ? ( + { + const { clientWidth, clientHeight } = + window.document.documentElement; + const targetRect = draggleRef.current?.getBoundingClientRect(); + if (!targetRect) { + return; + } + setBounds({ + left: -targetRect.left + uiData.x, + right: clientWidth - (targetRect.right - uiData.x), + top: -targetRect.top + uiData.y, + bottom: clientHeight - (targetRect.bottom - uiData.y), + }); + }} + > +
{modalBody}
+
+ ) : ( +
{modalBody}
+ ); + }; const { options, key } = modal; const modalContent = ( <> @@ -307,6 +347,7 @@ export const ModalWrapper: React.FC = ({ children }) => { key={modal.key} mask={options.mask} maskClosable={options.maskClosable} + modalRender={modalRender} onCancel={(): void => closeModal(index)} open={modal.visible} style={{ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ffe714cdc..8bcd2f53b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -289,6 +289,7 @@ importers: jwt-decode: ^4.0.0 lodash-es: ^4.17.21 react-chartjs-2: ^5.2.0 + react-draggable: ^4.4.6 react-easy-crop: ^5.0.5 react-markdown: ^8.0.7 react-resizable: ^3.0.5 @@ -298,6 +299,8 @@ importers: runes2: ^1.1.4 tsconfig: workspace:* tsup: ^7.2.0 + dependencies: + react-draggable: 4.4.6 devDependencies: '@ant-design/icons': 5.3.1 '@emotion/react': 11.11.4_@types+react@18.2.62 @@ -6546,7 +6549,6 @@ packages: /clsx/1.2.1: resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} engines: {node: '>=6'} - dev: true /clsx/2.1.0: resolution: {integrity: sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==} @@ -11781,7 +11783,6 @@ packages: /object-assign/4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - dev: true /object-hash/3.0.0: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} @@ -13177,7 +13178,6 @@ packages: loose-envify: 1.4.0 object-assign: 4.1.1 react-is: 16.13.1 - dev: true /property-information/6.4.1: resolution: {integrity: sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w==} @@ -14443,7 +14443,6 @@ packages: dependencies: clsx: 1.2.1 prop-types: 15.8.1 - dev: true /react-easy-crop/5.0.5: resolution: {integrity: sha512-GH7Jw3898ytSaN4i4Oxi7j3BKzapZ2pVgnKIl+gFIUjA+NsDgdBSIpiBQUrPFIvHzSnPmz0kGCpX95X6NgsDzA==} @@ -14483,7 +14482,6 @@ packages: /react-is/16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - dev: true /react-is/17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}