From 97b027545832ed560b99d97df92d796705df1431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20S=C5=82abek?= Date: Thu, 14 Nov 2024 11:41:02 +0100 Subject: [PATCH 01/13] chore: bump github actions plugin --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b69f1b99..01939a0c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,7 +85,7 @@ jobs: - uses: codecov/codecov-action@v3 - name: Store test results if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: test-results path: | From 10a67845358b270e70785ccaf4ea19f9185ea26f Mon Sep 17 00:00:00 2001 From: Dawid Poliszak Date: Tue, 19 Nov 2024 11:45:34 +0100 Subject: [PATCH 02/13] chore: change animation library to react-transition-state --- package-lock.json | 29 ++---- package.json | 2 +- src/components/ModalMask.tsx | 12 +-- src/components/TransitionProvider.tsx | 77 ++++++++++++++++ src/components/WindowManager.tsx | 19 ++-- src/components/WindowsContainer.tsx | 18 +--- src/components/getFadeInAnimation.ts | 14 ++- src/components/window/SnapMask.tsx | 40 ++++----- src/components/window/Window.tsx | 8 +- src/components/window/WindowFrame.tsx | 120 +++++++++++-------------- src/components/window/useSnapAreas.tsx | 2 +- src/hooks/manager.ts | 15 ++-- 12 files changed, 194 insertions(+), 162 deletions(-) create mode 100644 src/components/TransitionProvider.tsx diff --git a/package-lock.json b/package-lock.json index f978cfa7..6ee3a88a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "react-hotkeys-hook": "4.5.0", "react-inspector": "6.0.2", "react-rnd": "10.4.10", - "react-transition-group": "4.4.5", + "react-transition-state": "2.2.0", "reselect": "5.1.0", "rooks": "7.14.1" }, @@ -15123,15 +15123,6 @@ "utila": "~0.4" } }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, "node_modules/dom-serializer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", @@ -28282,19 +28273,13 @@ } } }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, + "node_modules/react-transition-state": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/react-transition-state/-/react-transition-state-2.2.0.tgz", + "integrity": "sha512-D3EyLku1Sdxrxq26Fo4Jh0q1BLEFQfDOxKKiSuyqWH84+hM6y0Guc0hcW2IXMXY5l5gQCgkOQ9y90xx6mNoj5w==", "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" + "react": ">=16.8.0", + "react-dom": ">=16.8.0" } }, "node_modules/read-only-stream": { diff --git a/package.json b/package.json index 2fb2a775..bd6a6b17 100644 --- a/package.json +++ b/package.json @@ -128,7 +128,7 @@ "react-hotkeys-hook": "4.5.0", "react-inspector": "6.0.2", "react-rnd": "10.4.10", - "react-transition-group": "4.4.5", + "react-transition-state": "2.2.0", "reselect": "5.1.0", "rooks": "7.14.1" }, diff --git a/src/components/ModalMask.tsx b/src/components/ModalMask.tsx index 214a6915..9b279322 100644 --- a/src/components/ModalMask.tsx +++ b/src/components/ModalMask.tsx @@ -1,9 +1,11 @@ import { css, cx } from "@emotion/css"; -import React, { forwardRef, RefObject } from "react"; +import React from "react"; import { rgba } from "../rgba"; import { useModalMaskTheme } from "../themeHooks"; +import { WindowId } from "../types"; +import { useTransition } from "./TransitionProvider"; -export const ModalMask = forwardRef(({ zIndex }: { zIndex?: number }, ref: RefObject): JSX.Element => { +export const ModalMask = ({ zIndex, id }: { zIndex?: number; id: WindowId }): JSX.Element => { const modalMaskTheme = useModalMaskTheme(); const modalMaskClass = css({ top: 0, @@ -13,7 +15,7 @@ export const ModalMask = forwardRef(({ zIndex }: { zIndex?: number }, ref: RefOb position: "fixed", background: rgba("black", 0.6), }); - return
; -}); + const { getTransitionStyle } = useTransition(); -ModalMask.displayName = "ModalMask"; + return
; +}; diff --git a/src/components/TransitionProvider.tsx b/src/components/TransitionProvider.tsx new file mode 100644 index 00000000..d0b3cb8e --- /dev/null +++ b/src/components/TransitionProvider.tsx @@ -0,0 +1,77 @@ +import React, { createContext, PropsWithChildren, useCallback, useContext } from "react"; +import { useTransitionMap } from "react-transition-state"; +import { defaultFadeAnimation, FadeInAnimation } from "./getFadeInAnimation"; +import { WindowId } from "../types"; + +const TRANSITION_TIMEOUT = 100; +const transitionsAnimationTime = new Map(); +const TransitionContext = createContext<{ + startTransition: (id: string) => Promise; + finishTransition: (id: string) => Promise; + getTransitionStyle: (id: string, fadeInAnimation?: FadeInAnimation) => string[]; +}>(null); + +export const TransitionProvider = ({ children }: PropsWithChildren) => { + const transition = useTransitionMap({ + timeout: TRANSITION_TIMEOUT, + exit: true, + preExit: false, + allowMultiple: true, + }); + + const getTransitionStyle = useCallback( + (id: WindowId, fadeInAnimation: FadeInAnimation = defaultFadeAnimation) => { + transitionsAnimationTime.set(id, fadeInAnimation.animationTime * 1000); + const transitionItem = transition.stateMap.get(id); + const entered = transitionItem.status === "entered" && fadeInAnimation.entered; + const exited = transitionItem.status === "exited" && fadeInAnimation.exited; + + return [fadeInAnimation.mounted, entered, exited]; + }, + [transition.stateMap], + ); + + const startTransition = useCallback( + (id: WindowId) => { + return new Promise((resolve) => { + transition.setItem(id); + transition.toggle(id, true); + const transitionAnimationTime = transitionsAnimationTime.get(id); + + setTimeout(() => { + resolve(true); + }, TRANSITION_TIMEOUT + transitionAnimationTime); + }); + }, + [transition], + ); + + const finishTransition = useCallback( + (id: WindowId) => { + return new Promise((resolve) => { + transition.toggle(id, false); + const transitionAnimationTime = transitionsAnimationTime.get(id); + setTimeout(() => { + transition.deleteItem(id); + transitionsAnimationTime.delete(id); + resolve(true); + }, TRANSITION_TIMEOUT + transitionAnimationTime); + }); + }, + [transition], + ); + + return ( + {children} + ); +}; + +export const useTransition = () => { + const context = useContext(TransitionContext); + + if (!context) { + throw new Error(); + } + + return context; +}; diff --git a/src/components/WindowManager.tsx b/src/components/WindowManager.tsx index 73d044d3..7ac79df3 100644 --- a/src/components/WindowManager.tsx +++ b/src/components/WindowManager.tsx @@ -5,6 +5,7 @@ import { AppTheme } from "../AppTheme"; import { WindowManagerContextProvider } from "../context"; import { ContentGetter } from "./window/WindowContent"; import { WindowsContainer } from "./WindowsContainer"; +import { TransitionProvider } from "./TransitionProvider"; const defaultTheme = { backgroundOpacity: 0.9, @@ -34,13 +35,15 @@ export function WindowManager({ ...props }: WindowManagerProps): JSX.Element { return ( - -
- {children} - defaultsDeep(theme, outerTheme, defaultTheme)}> - - -
-
+ + +
+ {children} + defaultsDeep(theme, outerTheme, defaultTheme)}> + + +
+
+
); } diff --git a/src/components/WindowsContainer.tsx b/src/components/WindowsContainer.tsx index 25735b87..8e28fcda 100644 --- a/src/components/WindowsContainer.tsx +++ b/src/components/WindowsContainer.tsx @@ -1,9 +1,7 @@ import { flatMap } from "lodash"; import React, { useRef } from "react"; import { createPortal } from "react-dom"; -import { CSSTransition, TransitionGroup } from "react-transition-group"; import { useWindowManager } from "../hooks"; -import { defaultFadeAnimation } from "./getFadeInAnimation"; import { ModalMask } from "./ModalMask"; import { Window } from "./window/Window"; import { ContentGetter } from "./window/WindowContent"; @@ -16,22 +14,14 @@ interface WindowsContainerProps { export function WindowsContainer({ container = document.body, contentGetter }: WindowsContainerProps): JSX.Element { const { windows } = useWindowManager(); - const modalMaskRef = useRef(); - const windowRef = useRef(); return createPortal( - + {flatMap(windows, (d, index) => [ - d.isModal && ( - - - - ), - - - , + d.isModal && , + , ]).filter(Boolean)} - , + , container, ); } diff --git a/src/components/getFadeInAnimation.ts b/src/components/getFadeInAnimation.ts index eef0ed91..6c8df492 100644 --- a/src/components/getFadeInAnimation.ts +++ b/src/components/getFadeInAnimation.ts @@ -1,23 +1,19 @@ import { css } from "@emotion/css"; export const getFadeInAnimation = (t = 0.25) => ({ - enter: css({ + animationTime: t, + mounted: css({ opacity: 0, - }), - enterActive: css({ - opacity: 1, transition: `opacity ${t}s ease-in-out`, - pointerEvents: "none", }), - exit: css({ + entered: css({ opacity: 1, }), - exitActive: css({ + exited: css({ opacity: 0, - transition: `opacity ${t}s ease-in-out`, - pointerEvents: "none", }), }); +export type FadeInAnimation = ReturnType; export const defaultFadeAnimation = getFadeInAnimation(); export const fastFadeAnimation = getFadeInAnimation(0.15); diff --git a/src/components/window/SnapMask.tsx b/src/components/window/SnapMask.tsx index 314e42ce..3df3b85e 100644 --- a/src/components/window/SnapMask.tsx +++ b/src/components/window/SnapMask.tsx @@ -1,38 +1,32 @@ import { useTheme } from "@emotion/react"; -import React, { useRef } from "react"; -import { CSSTransition, TransitionGroup } from "react-transition-group"; +import React from "react"; import { rgba } from "../../rgba"; -import { fastFadeAnimation } from "../getFadeInAnimation"; import { Box } from "./useSnapAreas"; export const SnapMask = ({ previewBox }: { previewBox: Box }) => { - const nodeRef = useRef(null); const { colors, spacing: { baseUnit }, } = useTheme(); return ( - + <> {previewBox && ( - -
- +
)} - + ); }; diff --git a/src/components/window/Window.tsx b/src/components/window/Window.tsx index a986b1d4..4c0b6958 100644 --- a/src/components/window/Window.tsx +++ b/src/components/window/Window.tsx @@ -1,4 +1,4 @@ -import React, { forwardRef, RefObject, useCallback } from "react"; +import React, { useCallback } from "react"; import { useWindowManager, useWindowZoom } from "../../hooks"; import { WindowWithOrder } from "../../types"; import { ContentGetter, WindowContent } from "./WindowContent"; @@ -9,7 +9,7 @@ export interface WindowProps { contentGetter: ContentGetter; } -export const Window = forwardRef(({ data, contentGetter }: WindowProps, ref: RefObject): JSX.Element => { +export const Window = ({ data, contentGetter }: WindowProps): JSX.Element => { const { isResizable, isStatic, focusParent, id, order, shouldCloseOnEsc } = data; const { focus: onFocus, close: onClose } = useWindowManager(id); @@ -31,7 +31,7 @@ export const Window = forwardRef(({ data, contentGetter }: WindowProps, ref: Ref height={data.height} minWidth={data.minWidth} minHeight={data.minHeight} - ref={ref} + id={data.id} layoutData={{ width: data.width, height: data.height, @@ -43,6 +43,6 @@ export const Window = forwardRef(({ data, contentGetter }: WindowProps, ref: Ref ); -}); +}; Window.displayName = "Window"; diff --git a/src/components/window/WindowFrame.tsx b/src/components/window/WindowFrame.tsx index e6c0d4a1..501edfd7 100644 --- a/src/components/window/WindowFrame.tsx +++ b/src/components/window/WindowFrame.tsx @@ -1,24 +1,24 @@ import { css, cx } from "@emotion/css"; import { isEqual } from "lodash"; import { mapValues } from "lodash/fp"; -import React, { forwardRef, PropsWithChildren, RefObject, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; +import React, { PropsWithChildren, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import FocusLock from "react-focus-lock"; import { Position, Rnd } from "react-rnd"; -import { CSSTransition } from "react-transition-group"; -import { useMutationObserver, usePreviousDifferent, usePreviousImmediate } from "rooks"; +import { useMutationObserver, usePreviousDifferent } from "rooks"; import { DRAG_HANDLE_CLASS_NAME, DRAG_PREVENT_CLASS_NAME } from "../../consts"; import { useViewportSize } from "../../hooks"; import { useScrollFix } from "../../hooks/useScrollFix"; import { useFrameTheme } from "../../themeHooks"; import { LayoutData } from "../../types"; import { random } from "../../utils"; -import { defaultFadeAnimation } from "../getFadeInAnimation"; import { SnapMask } from "./SnapMask"; import { Coords, Side, Size } from "./types"; import { Box, useSnapAreas } from "./useSnapAreas"; import { useSnapSide } from "./useSnapSide"; +import { useTransition } from "../TransitionProvider"; interface WindowFrameProps { + id: string; focusGroup?: string; zIndex?: number; randomizePosition?: number; @@ -47,11 +47,6 @@ interface WindowFrameProps { layoutData?: LayoutData; } -const zoomAnimation = { - enter: css({ transition: "all 150ms" }), - exit: css({ transition: "all 150ms" }), -}; - function calcCoord(start: number, end: number, size: number, viewportSize: number, padding: number) { return Math.max(padding, end >= viewportSize - padding / 2 ? viewportSize - size - padding : start); } @@ -89,7 +84,8 @@ const windowClass = css({ willChange: "transform, top, left, minWidth, minHeight, width, height", }); -export const WindowFrame = forwardRef((props: PropsWithChildren, windowRef: RefObject): JSX.Element => { +export const WindowFrame = (props: PropsWithChildren): JSX.Element => { + const { getTransitionStyle } = useTransition(); const { focusGroup, zIndex, @@ -101,7 +97,9 @@ export const WindowFrame = forwardRef((props: PropsWithChildren(); const viewport = useViewportSize(); @@ -113,7 +111,6 @@ export const WindowFrame = forwardRef((props: PropsWithChildren= 0 && right >= 0 ? viewport.width - left - right : width ?? props.width, }; }); - const prevSize = usePreviousImmediate(size); const { focusWrapperTheme, windowTheme, windowMargin } = useFrameTheme(); const dragging = useRef(false); @@ -231,15 +228,6 @@ export const WindowFrame = forwardRef((props: PropsWithChildren { - const { height, width } = ref.current.getBoundingClientRect(); - setSize({ width, height }); - }, []); - - const onExited = useCallback(() => { - setSize(prevSize || null); - }, [prevSize]); - const onResizeStop = useCallback( (e, dir, el, delta, position) => { const { width, height } = el.getBoundingClientRect(); @@ -287,57 +275,49 @@ export const WindowFrame = forwardRef((props: PropsWithChildren normalizeMinSize(minHeight, viewport.height), [normalizeMinSize, viewport.height, minHeight]); useScrollFix(ref.current); - return ( -
- {/*fallback animation for lazy loaded content*/} - - - + + {/* trap keyboard focus within group (windows opened since last modal) */} + +
{ + event.key === "Escape" && onEscape?.(); + touch(); + }} + tabIndex={-1} + className={cx(focusWrapperClass, focusWrapperTheme)} + data-testid="window" > - {/* trap keyboard focus within group (windows opened since last modal) */} - -
{ - event.key === "Escape" && onEscape?.(); - touch(); - }} - tabIndex={-1} - className={cx(focusWrapperClass, focusWrapperTheme)} - data-testid="window" - > - {props.children} -
-
- - - + {props.children} +
+
+
-
+ ); -}); - -WindowFrame.displayName = "WindowFrame"; +}; diff --git a/src/components/window/useSnapAreas.tsx b/src/components/window/useSnapAreas.tsx index d32a8be0..68439677 100644 --- a/src/components/window/useSnapAreas.tsx +++ b/src/components/window/useSnapAreas.tsx @@ -50,7 +50,7 @@ export function useSnapAreas(margin = 0, onSnapCallback?: (box: Box, side: Side) return null; } }, - [height, width], + [height, margin, width], ); const onSideEdgeSnap = useCallback( diff --git a/src/hooks/manager.ts b/src/hooks/manager.ts index 4e0c9273..4daa2e80 100644 --- a/src/hooks/manager.ts +++ b/src/hooks/manager.ts @@ -3,6 +3,8 @@ import { WindowManagerContext } from "../context"; import { closeWindow, getWindowsWithOrder, openWindow } from "../store"; import { WindowId, WindowManagerState, WindowType } from "../types"; import { ViewportContext, ViewportContextType } from "../ViewportContext"; +import { useTransition } from "../components/TransitionProvider"; +import { v4 as uuid } from "uuid"; export function useViewportSize(): ViewportContextType { const context = useContext(ViewportContext); @@ -19,12 +21,14 @@ export function useRawState(): WindowManagerSta export function useWindowManager(parent?: WindowId) { const [state, dispatch] = useContext(WindowManagerContext); - + const transition = useTransition(); const open = useCallback( - (windowData: Partial> = {}) => { - return dispatch(openWindow({ parent, ...windowData })); + async (windowData: Partial> = {}) => { + const windowId: WindowId = windowData.id || uuid(); + await transition.startTransition(windowId); + return dispatch(openWindow({ parent, ...windowData, id: windowId })); }, - [dispatch, parent], + [dispatch, parent, transition], ); const focus = useCallback( @@ -36,10 +40,11 @@ export function useWindowManager(parent?: Windo const close = useCallback( async (id: WindowId = parent) => { + await transition.finishTransition(id); await dispatch(closeWindow(id)); return id; }, - [dispatch, parent], + [dispatch, parent, transition], ); const windows = useMemo(() => getWindowsWithOrder(state), [state]); From 9af367b8a92fdfb47b7b267f26f57fb724e21f36 Mon Sep 17 00:00:00 2001 From: Dawid Poliszak Date: Tue, 19 Nov 2024 11:46:33 +0100 Subject: [PATCH 03/13] fix: issue with manually change window size --- package-lock.json | 16 ++++++++-------- package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6ee3a88a..4315562f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "react-focus-lock": "2.12.1", "react-hotkeys-hook": "4.5.0", "react-inspector": "6.0.2", - "react-rnd": "10.4.10", + "react-rnd": "10.4.13", "react-transition-state": "2.2.0", "reselect": "5.1.0", "rooks": "7.14.1" @@ -28000,9 +28000,9 @@ } }, "node_modules/re-resizable": { - "version": "6.9.14", - "resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.9.14.tgz", - "integrity": "sha512-2UbPrpezMr6gkHKNCRA/N6QGGU237SKOZ78yMHId204A/oXWSAREAIuGZNQ9qlrJosewzcsv2CphZH3u7hC6ng==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.10.0.tgz", + "integrity": "sha512-hysSK0xmA5nz24HBVztlk4yCqCLCvS32E6ZpWxVKop9x3tqCa4yAj1++facrmkOf62JsJHjmjABdKxXofYioCw==", "peerDependencies": { "react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0" @@ -28237,11 +28237,11 @@ } }, "node_modules/react-rnd": { - "version": "10.4.10", - "resolved": "https://registry.npmjs.org/react-rnd/-/react-rnd-10.4.10.tgz", - "integrity": "sha512-YjQAgEeSbNUoOXSD9ZBvIiLVizFb+bNhpDk8DbIRHA557NW02CXbwsAeOTpJQnsdhEL+NP2I+Ssrwejqcodtjg==", + "version": "10.4.13", + "resolved": "https://registry.npmjs.org/react-rnd/-/react-rnd-10.4.13.tgz", + "integrity": "sha512-Vgbf0iihspcQ6nkaFhpOGWfmnuVbhkhoB0hBbYl8aRDA4horsQHESc4E1z7O/P27kFFjK2aqM0u5CGzfr9gEZA==", "dependencies": { - "re-resizable": "6.9.14", + "re-resizable": "6.10.0", "react-draggable": "4.4.6", "tslib": "2.6.2" }, diff --git a/package.json b/package.json index bd6a6b17..131c8490 100644 --- a/package.json +++ b/package.json @@ -127,7 +127,7 @@ "react-focus-lock": "2.12.1", "react-hotkeys-hook": "4.5.0", "react-inspector": "6.0.2", - "react-rnd": "10.4.10", + "react-rnd": "10.4.13", "react-transition-state": "2.2.0", "reselect": "5.1.0", "rooks": "7.14.1" From 59c85064cf16ee7b06e1f4f133f9f85b1376fa4e Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 25 Nov 2024 08:49:18 +0000 Subject: [PATCH 04/13] chore(release): 1.9.1-beta.1 [skip ci] ## [1.9.1-beta.1](https://github.com/touk/nk-windows/compare/v1.9.0...v1.9.1-beta.1) (2024-11-25) ### Bug Fixes * issue with manually change window size ([9af367b](https://github.com/touk/nk-windows/commit/9af367b8a92fdfb47b7b267f26f57fb724e21f36)) --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 778b4dbb..070f28e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.9.1-beta.1](https://github.com/touk/nk-windows/compare/v1.9.0...v1.9.1-beta.1) (2024-11-25) + + +### Bug Fixes + +* issue with manually change window size ([9af367b](https://github.com/touk/nk-windows/commit/9af367b8a92fdfb47b7b267f26f57fb724e21f36)) + # [1.9.0](https://github.com/touk/nk-windows/compare/v1.8.0...v1.9.0) (2024-09-16) diff --git a/package-lock.json b/package-lock.json index 4315562f..fc20d741 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@touk/window-manager", - "version": "1.9.0", + "version": "1.9.1-beta.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@touk/window-manager", - "version": "1.9.0", + "version": "1.9.1-beta.1", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 131c8490..849cff1c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@touk/window-manager", - "version": "1.9.0", + "version": "1.9.1-beta.1", "types": "./cjs/index.d.ts", "main": "./cjs/index.js", "module": "./esm/index.js", From 8926d57e612850fcf9cd697c4454276e6e152ebf Mon Sep 17 00:00:00 2001 From: Dawid Poliszak Date: Wed, 27 Nov 2024 09:33:44 +0100 Subject: [PATCH 05/13] chore: revert change animation library --- package-lock.json | 29 ++++-- package.json | 2 +- src/components/ModalMask.tsx | 12 ++- src/components/TransitionProvider.tsx | 77 ---------------- src/components/WindowManager.tsx | 19 ++-- src/components/WindowsContainer.tsx | 18 +++- src/components/getFadeInAnimation.ts | 14 +-- src/components/window/SnapMask.tsx | 40 +++++---- src/components/window/Window.tsx | 8 +- src/components/window/WindowFrame.tsx | 120 ++++++++++++++----------- src/components/window/useSnapAreas.tsx | 2 +- src/hooks/manager.ts | 15 ++-- 12 files changed, 162 insertions(+), 194 deletions(-) delete mode 100644 src/components/TransitionProvider.tsx diff --git a/package-lock.json b/package-lock.json index fc20d741..905696b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "react-hotkeys-hook": "4.5.0", "react-inspector": "6.0.2", "react-rnd": "10.4.13", - "react-transition-state": "2.2.0", + "react-transition-group": "4.4.5", "reselect": "5.1.0", "rooks": "7.14.1" }, @@ -15123,6 +15123,15 @@ "utila": "~0.4" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/dom-serializer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", @@ -28273,13 +28282,19 @@ } } }, - "node_modules/react-transition-state": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/react-transition-state/-/react-transition-state-2.2.0.tgz", - "integrity": "sha512-D3EyLku1Sdxrxq26Fo4Jh0q1BLEFQfDOxKKiSuyqWH84+hM6y0Guc0hcW2IXMXY5l5gQCgkOQ9y90xx6mNoj5w==", + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "react": ">=16.6.0", + "react-dom": ">=16.6.0" } }, "node_modules/read-only-stream": { diff --git a/package.json b/package.json index 849cff1c..27ecaa2c 100644 --- a/package.json +++ b/package.json @@ -128,7 +128,7 @@ "react-hotkeys-hook": "4.5.0", "react-inspector": "6.0.2", "react-rnd": "10.4.13", - "react-transition-state": "2.2.0", + "react-transition-group": "4.4.5", "reselect": "5.1.0", "rooks": "7.14.1" }, diff --git a/src/components/ModalMask.tsx b/src/components/ModalMask.tsx index 9b279322..214a6915 100644 --- a/src/components/ModalMask.tsx +++ b/src/components/ModalMask.tsx @@ -1,11 +1,9 @@ import { css, cx } from "@emotion/css"; -import React from "react"; +import React, { forwardRef, RefObject } from "react"; import { rgba } from "../rgba"; import { useModalMaskTheme } from "../themeHooks"; -import { WindowId } from "../types"; -import { useTransition } from "./TransitionProvider"; -export const ModalMask = ({ zIndex, id }: { zIndex?: number; id: WindowId }): JSX.Element => { +export const ModalMask = forwardRef(({ zIndex }: { zIndex?: number }, ref: RefObject): JSX.Element => { const modalMaskTheme = useModalMaskTheme(); const modalMaskClass = css({ top: 0, @@ -15,7 +13,7 @@ export const ModalMask = ({ zIndex, id }: { zIndex?: number; id: WindowId }): JS position: "fixed", background: rgba("black", 0.6), }); - const { getTransitionStyle } = useTransition(); + return
; +}); - return
; -}; +ModalMask.displayName = "ModalMask"; diff --git a/src/components/TransitionProvider.tsx b/src/components/TransitionProvider.tsx deleted file mode 100644 index d0b3cb8e..00000000 --- a/src/components/TransitionProvider.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import React, { createContext, PropsWithChildren, useCallback, useContext } from "react"; -import { useTransitionMap } from "react-transition-state"; -import { defaultFadeAnimation, FadeInAnimation } from "./getFadeInAnimation"; -import { WindowId } from "../types"; - -const TRANSITION_TIMEOUT = 100; -const transitionsAnimationTime = new Map(); -const TransitionContext = createContext<{ - startTransition: (id: string) => Promise; - finishTransition: (id: string) => Promise; - getTransitionStyle: (id: string, fadeInAnimation?: FadeInAnimation) => string[]; -}>(null); - -export const TransitionProvider = ({ children }: PropsWithChildren) => { - const transition = useTransitionMap({ - timeout: TRANSITION_TIMEOUT, - exit: true, - preExit: false, - allowMultiple: true, - }); - - const getTransitionStyle = useCallback( - (id: WindowId, fadeInAnimation: FadeInAnimation = defaultFadeAnimation) => { - transitionsAnimationTime.set(id, fadeInAnimation.animationTime * 1000); - const transitionItem = transition.stateMap.get(id); - const entered = transitionItem.status === "entered" && fadeInAnimation.entered; - const exited = transitionItem.status === "exited" && fadeInAnimation.exited; - - return [fadeInAnimation.mounted, entered, exited]; - }, - [transition.stateMap], - ); - - const startTransition = useCallback( - (id: WindowId) => { - return new Promise((resolve) => { - transition.setItem(id); - transition.toggle(id, true); - const transitionAnimationTime = transitionsAnimationTime.get(id); - - setTimeout(() => { - resolve(true); - }, TRANSITION_TIMEOUT + transitionAnimationTime); - }); - }, - [transition], - ); - - const finishTransition = useCallback( - (id: WindowId) => { - return new Promise((resolve) => { - transition.toggle(id, false); - const transitionAnimationTime = transitionsAnimationTime.get(id); - setTimeout(() => { - transition.deleteItem(id); - transitionsAnimationTime.delete(id); - resolve(true); - }, TRANSITION_TIMEOUT + transitionAnimationTime); - }); - }, - [transition], - ); - - return ( - {children} - ); -}; - -export const useTransition = () => { - const context = useContext(TransitionContext); - - if (!context) { - throw new Error(); - } - - return context; -}; diff --git a/src/components/WindowManager.tsx b/src/components/WindowManager.tsx index 7ac79df3..73d044d3 100644 --- a/src/components/WindowManager.tsx +++ b/src/components/WindowManager.tsx @@ -5,7 +5,6 @@ import { AppTheme } from "../AppTheme"; import { WindowManagerContextProvider } from "../context"; import { ContentGetter } from "./window/WindowContent"; import { WindowsContainer } from "./WindowsContainer"; -import { TransitionProvider } from "./TransitionProvider"; const defaultTheme = { backgroundOpacity: 0.9, @@ -35,15 +34,13 @@ export function WindowManager({ ...props }: WindowManagerProps): JSX.Element { return ( - - -
- {children} - defaultsDeep(theme, outerTheme, defaultTheme)}> - - -
-
-
+ +
+ {children} + defaultsDeep(theme, outerTheme, defaultTheme)}> + + +
+
); } diff --git a/src/components/WindowsContainer.tsx b/src/components/WindowsContainer.tsx index 8e28fcda..25735b87 100644 --- a/src/components/WindowsContainer.tsx +++ b/src/components/WindowsContainer.tsx @@ -1,7 +1,9 @@ import { flatMap } from "lodash"; import React, { useRef } from "react"; import { createPortal } from "react-dom"; +import { CSSTransition, TransitionGroup } from "react-transition-group"; import { useWindowManager } from "../hooks"; +import { defaultFadeAnimation } from "./getFadeInAnimation"; import { ModalMask } from "./ModalMask"; import { Window } from "./window/Window"; import { ContentGetter } from "./window/WindowContent"; @@ -14,14 +16,22 @@ interface WindowsContainerProps { export function WindowsContainer({ container = document.body, contentGetter }: WindowsContainerProps): JSX.Element { const { windows } = useWindowManager(); + const modalMaskRef = useRef(); + const windowRef = useRef(); return createPortal( - + {flatMap(windows, (d, index) => [ - d.isModal && , - , + d.isModal && ( + + + + ), + + + , ]).filter(Boolean)} - , + , container, ); } diff --git a/src/components/getFadeInAnimation.ts b/src/components/getFadeInAnimation.ts index 6c8df492..eef0ed91 100644 --- a/src/components/getFadeInAnimation.ts +++ b/src/components/getFadeInAnimation.ts @@ -1,19 +1,23 @@ import { css } from "@emotion/css"; export const getFadeInAnimation = (t = 0.25) => ({ - animationTime: t, - mounted: css({ + enter: css({ opacity: 0, + }), + enterActive: css({ + opacity: 1, transition: `opacity ${t}s ease-in-out`, + pointerEvents: "none", }), - entered: css({ + exit: css({ opacity: 1, }), - exited: css({ + exitActive: css({ opacity: 0, + transition: `opacity ${t}s ease-in-out`, + pointerEvents: "none", }), }); -export type FadeInAnimation = ReturnType; export const defaultFadeAnimation = getFadeInAnimation(); export const fastFadeAnimation = getFadeInAnimation(0.15); diff --git a/src/components/window/SnapMask.tsx b/src/components/window/SnapMask.tsx index 3df3b85e..314e42ce 100644 --- a/src/components/window/SnapMask.tsx +++ b/src/components/window/SnapMask.tsx @@ -1,32 +1,38 @@ import { useTheme } from "@emotion/react"; -import React from "react"; +import React, { useRef } from "react"; +import { CSSTransition, TransitionGroup } from "react-transition-group"; import { rgba } from "../../rgba"; +import { fastFadeAnimation } from "../getFadeInAnimation"; import { Box } from "./useSnapAreas"; export const SnapMask = ({ previewBox }: { previewBox: Box }) => { + const nodeRef = useRef(null); const { colors, spacing: { baseUnit }, } = useTheme(); return ( - <> + {previewBox && ( -
+ +
+ )} - + ); }; diff --git a/src/components/window/Window.tsx b/src/components/window/Window.tsx index 4c0b6958..a986b1d4 100644 --- a/src/components/window/Window.tsx +++ b/src/components/window/Window.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from "react"; +import React, { forwardRef, RefObject, useCallback } from "react"; import { useWindowManager, useWindowZoom } from "../../hooks"; import { WindowWithOrder } from "../../types"; import { ContentGetter, WindowContent } from "./WindowContent"; @@ -9,7 +9,7 @@ export interface WindowProps { contentGetter: ContentGetter; } -export const Window = ({ data, contentGetter }: WindowProps): JSX.Element => { +export const Window = forwardRef(({ data, contentGetter }: WindowProps, ref: RefObject): JSX.Element => { const { isResizable, isStatic, focusParent, id, order, shouldCloseOnEsc } = data; const { focus: onFocus, close: onClose } = useWindowManager(id); @@ -31,7 +31,7 @@ export const Window = ({ data, contentGetter }: WindowProps): JSX.Element => { height={data.height} minWidth={data.minWidth} minHeight={data.minHeight} - id={data.id} + ref={ref} layoutData={{ width: data.width, height: data.height, @@ -43,6 +43,6 @@ export const Window = ({ data, contentGetter }: WindowProps): JSX.Element => { ); -}; +}); Window.displayName = "Window"; diff --git a/src/components/window/WindowFrame.tsx b/src/components/window/WindowFrame.tsx index 501edfd7..e6c0d4a1 100644 --- a/src/components/window/WindowFrame.tsx +++ b/src/components/window/WindowFrame.tsx @@ -1,24 +1,24 @@ import { css, cx } from "@emotion/css"; import { isEqual } from "lodash"; import { mapValues } from "lodash/fp"; -import React, { PropsWithChildren, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; +import React, { forwardRef, PropsWithChildren, RefObject, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import FocusLock from "react-focus-lock"; import { Position, Rnd } from "react-rnd"; -import { useMutationObserver, usePreviousDifferent } from "rooks"; +import { CSSTransition } from "react-transition-group"; +import { useMutationObserver, usePreviousDifferent, usePreviousImmediate } from "rooks"; import { DRAG_HANDLE_CLASS_NAME, DRAG_PREVENT_CLASS_NAME } from "../../consts"; import { useViewportSize } from "../../hooks"; import { useScrollFix } from "../../hooks/useScrollFix"; import { useFrameTheme } from "../../themeHooks"; import { LayoutData } from "../../types"; import { random } from "../../utils"; +import { defaultFadeAnimation } from "../getFadeInAnimation"; import { SnapMask } from "./SnapMask"; import { Coords, Side, Size } from "./types"; import { Box, useSnapAreas } from "./useSnapAreas"; import { useSnapSide } from "./useSnapSide"; -import { useTransition } from "../TransitionProvider"; interface WindowFrameProps { - id: string; focusGroup?: string; zIndex?: number; randomizePosition?: number; @@ -47,6 +47,11 @@ interface WindowFrameProps { layoutData?: LayoutData; } +const zoomAnimation = { + enter: css({ transition: "all 150ms" }), + exit: css({ transition: "all 150ms" }), +}; + function calcCoord(start: number, end: number, size: number, viewportSize: number, padding: number) { return Math.max(padding, end >= viewportSize - padding / 2 ? viewportSize - size - padding : start); } @@ -84,8 +89,7 @@ const windowClass = css({ willChange: "transform, top, left, minWidth, minHeight, width, height", }); -export const WindowFrame = (props: PropsWithChildren): JSX.Element => { - const { getTransitionStyle } = useTransition(); +export const WindowFrame = forwardRef((props: PropsWithChildren, windowRef: RefObject): JSX.Element => { const { focusGroup, zIndex, @@ -97,9 +101,7 @@ export const WindowFrame = (props: PropsWithChildren): JSX.Ele resizable = false, moveable = false, layoutData = {}, - id, } = props; - const { minWidth = props.minWidth ?? 400, minHeight = props.minHeight ?? 140 } = layoutData; const ref = useRef(); const viewport = useViewportSize(); @@ -111,6 +113,7 @@ export const WindowFrame = (props: PropsWithChildren): JSX.Ele width: left >= 0 && right >= 0 ? viewport.width - left - right : width ?? props.width, }; }); + const prevSize = usePreviousImmediate(size); const { focusWrapperTheme, windowTheme, windowMargin } = useFrameTheme(); const dragging = useRef(false); @@ -228,6 +231,15 @@ export const WindowFrame = (props: PropsWithChildren): JSX.Ele [onSideSnap, onSnapCallback, savePosition, side], ); + const onEnter = useCallback(() => { + const { height, width } = ref.current.getBoundingClientRect(); + setSize({ width, height }); + }, []); + + const onExited = useCallback(() => { + setSize(prevSize || null); + }, [prevSize]); + const onResizeStop = useCallback( (e, dir, el, delta, position) => { const { width, height } = el.getBoundingClientRect(); @@ -275,49 +287,57 @@ export const WindowFrame = (props: PropsWithChildren): JSX.Ele const currentMinHeight = useMemo(() => normalizeMinSize(minHeight, viewport.height), [normalizeMinSize, viewport.height, minHeight]); useScrollFix(ref.current); + return ( - <> - - {/* trap keyboard focus within group (windows opened since last modal) */} - -
{ - event.key === "Escape" && onEscape?.(); - touch(); - }} - tabIndex={-1} - className={cx(focusWrapperClass, focusWrapperTheme)} - data-testid="window" +
+ {/*fallback animation for lazy loaded content*/} + + + - {props.children} -
- - + {/* trap keyboard focus within group (windows opened since last modal) */} + +
{ + event.key === "Escape" && onEscape?.(); + touch(); + }} + tabIndex={-1} + className={cx(focusWrapperClass, focusWrapperTheme)} + data-testid="window" + > + {props.children} +
+
+ + + - +
); -}; +}); + +WindowFrame.displayName = "WindowFrame"; diff --git a/src/components/window/useSnapAreas.tsx b/src/components/window/useSnapAreas.tsx index 68439677..d32a8be0 100644 --- a/src/components/window/useSnapAreas.tsx +++ b/src/components/window/useSnapAreas.tsx @@ -50,7 +50,7 @@ export function useSnapAreas(margin = 0, onSnapCallback?: (box: Box, side: Side) return null; } }, - [height, margin, width], + [height, width], ); const onSideEdgeSnap = useCallback( diff --git a/src/hooks/manager.ts b/src/hooks/manager.ts index 4daa2e80..4e0c9273 100644 --- a/src/hooks/manager.ts +++ b/src/hooks/manager.ts @@ -3,8 +3,6 @@ import { WindowManagerContext } from "../context"; import { closeWindow, getWindowsWithOrder, openWindow } from "../store"; import { WindowId, WindowManagerState, WindowType } from "../types"; import { ViewportContext, ViewportContextType } from "../ViewportContext"; -import { useTransition } from "../components/TransitionProvider"; -import { v4 as uuid } from "uuid"; export function useViewportSize(): ViewportContextType { const context = useContext(ViewportContext); @@ -21,14 +19,12 @@ export function useRawState(): WindowManagerSta export function useWindowManager(parent?: WindowId) { const [state, dispatch] = useContext(WindowManagerContext); - const transition = useTransition(); + const open = useCallback( - async (windowData: Partial> = {}) => { - const windowId: WindowId = windowData.id || uuid(); - await transition.startTransition(windowId); - return dispatch(openWindow({ parent, ...windowData, id: windowId })); + (windowData: Partial> = {}) => { + return dispatch(openWindow({ parent, ...windowData })); }, - [dispatch, parent, transition], + [dispatch, parent], ); const focus = useCallback( @@ -40,11 +36,10 @@ export function useWindowManager(parent?: Windo const close = useCallback( async (id: WindowId = parent) => { - await transition.finishTransition(id); await dispatch(closeWindow(id)); return id; }, - [dispatch, parent, transition], + [dispatch, parent], ); const windows = useMemo(() => getWindowsWithOrder(state), [state]); From b41192ac6a42c4623cd26354ff8691a302239ed6 Mon Sep 17 00:00:00 2001 From: Dawid Poliszak Date: Wed, 27 Nov 2024 11:03:58 +0100 Subject: [PATCH 06/13] Revert "revert change animation library" --- package-lock.json | 29 ++---- package.json | 2 +- src/components/ModalMask.tsx | 12 +-- src/components/TransitionProvider.tsx | 77 ++++++++++++++++ src/components/WindowManager.tsx | 19 ++-- src/components/WindowsContainer.tsx | 18 +--- src/components/getFadeInAnimation.ts | 14 ++- src/components/window/SnapMask.tsx | 40 ++++----- src/components/window/Window.tsx | 8 +- src/components/window/WindowFrame.tsx | 120 +++++++++++-------------- src/components/window/useSnapAreas.tsx | 2 +- src/hooks/manager.ts | 15 ++-- 12 files changed, 194 insertions(+), 162 deletions(-) create mode 100644 src/components/TransitionProvider.tsx diff --git a/package-lock.json b/package-lock.json index 905696b9..fc20d741 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "react-hotkeys-hook": "4.5.0", "react-inspector": "6.0.2", "react-rnd": "10.4.13", - "react-transition-group": "4.4.5", + "react-transition-state": "2.2.0", "reselect": "5.1.0", "rooks": "7.14.1" }, @@ -15123,15 +15123,6 @@ "utila": "~0.4" } }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, "node_modules/dom-serializer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", @@ -28282,19 +28273,13 @@ } } }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, + "node_modules/react-transition-state": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/react-transition-state/-/react-transition-state-2.2.0.tgz", + "integrity": "sha512-D3EyLku1Sdxrxq26Fo4Jh0q1BLEFQfDOxKKiSuyqWH84+hM6y0Guc0hcW2IXMXY5l5gQCgkOQ9y90xx6mNoj5w==", "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" + "react": ">=16.8.0", + "react-dom": ">=16.8.0" } }, "node_modules/read-only-stream": { diff --git a/package.json b/package.json index 27ecaa2c..849cff1c 100644 --- a/package.json +++ b/package.json @@ -128,7 +128,7 @@ "react-hotkeys-hook": "4.5.0", "react-inspector": "6.0.2", "react-rnd": "10.4.13", - "react-transition-group": "4.4.5", + "react-transition-state": "2.2.0", "reselect": "5.1.0", "rooks": "7.14.1" }, diff --git a/src/components/ModalMask.tsx b/src/components/ModalMask.tsx index 214a6915..9b279322 100644 --- a/src/components/ModalMask.tsx +++ b/src/components/ModalMask.tsx @@ -1,9 +1,11 @@ import { css, cx } from "@emotion/css"; -import React, { forwardRef, RefObject } from "react"; +import React from "react"; import { rgba } from "../rgba"; import { useModalMaskTheme } from "../themeHooks"; +import { WindowId } from "../types"; +import { useTransition } from "./TransitionProvider"; -export const ModalMask = forwardRef(({ zIndex }: { zIndex?: number }, ref: RefObject): JSX.Element => { +export const ModalMask = ({ zIndex, id }: { zIndex?: number; id: WindowId }): JSX.Element => { const modalMaskTheme = useModalMaskTheme(); const modalMaskClass = css({ top: 0, @@ -13,7 +15,7 @@ export const ModalMask = forwardRef(({ zIndex }: { zIndex?: number }, ref: RefOb position: "fixed", background: rgba("black", 0.6), }); - return
; -}); + const { getTransitionStyle } = useTransition(); -ModalMask.displayName = "ModalMask"; + return
; +}; diff --git a/src/components/TransitionProvider.tsx b/src/components/TransitionProvider.tsx new file mode 100644 index 00000000..d0b3cb8e --- /dev/null +++ b/src/components/TransitionProvider.tsx @@ -0,0 +1,77 @@ +import React, { createContext, PropsWithChildren, useCallback, useContext } from "react"; +import { useTransitionMap } from "react-transition-state"; +import { defaultFadeAnimation, FadeInAnimation } from "./getFadeInAnimation"; +import { WindowId } from "../types"; + +const TRANSITION_TIMEOUT = 100; +const transitionsAnimationTime = new Map(); +const TransitionContext = createContext<{ + startTransition: (id: string) => Promise; + finishTransition: (id: string) => Promise; + getTransitionStyle: (id: string, fadeInAnimation?: FadeInAnimation) => string[]; +}>(null); + +export const TransitionProvider = ({ children }: PropsWithChildren) => { + const transition = useTransitionMap({ + timeout: TRANSITION_TIMEOUT, + exit: true, + preExit: false, + allowMultiple: true, + }); + + const getTransitionStyle = useCallback( + (id: WindowId, fadeInAnimation: FadeInAnimation = defaultFadeAnimation) => { + transitionsAnimationTime.set(id, fadeInAnimation.animationTime * 1000); + const transitionItem = transition.stateMap.get(id); + const entered = transitionItem.status === "entered" && fadeInAnimation.entered; + const exited = transitionItem.status === "exited" && fadeInAnimation.exited; + + return [fadeInAnimation.mounted, entered, exited]; + }, + [transition.stateMap], + ); + + const startTransition = useCallback( + (id: WindowId) => { + return new Promise((resolve) => { + transition.setItem(id); + transition.toggle(id, true); + const transitionAnimationTime = transitionsAnimationTime.get(id); + + setTimeout(() => { + resolve(true); + }, TRANSITION_TIMEOUT + transitionAnimationTime); + }); + }, + [transition], + ); + + const finishTransition = useCallback( + (id: WindowId) => { + return new Promise((resolve) => { + transition.toggle(id, false); + const transitionAnimationTime = transitionsAnimationTime.get(id); + setTimeout(() => { + transition.deleteItem(id); + transitionsAnimationTime.delete(id); + resolve(true); + }, TRANSITION_TIMEOUT + transitionAnimationTime); + }); + }, + [transition], + ); + + return ( + {children} + ); +}; + +export const useTransition = () => { + const context = useContext(TransitionContext); + + if (!context) { + throw new Error(); + } + + return context; +}; diff --git a/src/components/WindowManager.tsx b/src/components/WindowManager.tsx index 73d044d3..7ac79df3 100644 --- a/src/components/WindowManager.tsx +++ b/src/components/WindowManager.tsx @@ -5,6 +5,7 @@ import { AppTheme } from "../AppTheme"; import { WindowManagerContextProvider } from "../context"; import { ContentGetter } from "./window/WindowContent"; import { WindowsContainer } from "./WindowsContainer"; +import { TransitionProvider } from "./TransitionProvider"; const defaultTheme = { backgroundOpacity: 0.9, @@ -34,13 +35,15 @@ export function WindowManager({ ...props }: WindowManagerProps): JSX.Element { return ( - -
- {children} - defaultsDeep(theme, outerTheme, defaultTheme)}> - - -
-
+ + +
+ {children} + defaultsDeep(theme, outerTheme, defaultTheme)}> + + +
+
+
); } diff --git a/src/components/WindowsContainer.tsx b/src/components/WindowsContainer.tsx index 25735b87..8e28fcda 100644 --- a/src/components/WindowsContainer.tsx +++ b/src/components/WindowsContainer.tsx @@ -1,9 +1,7 @@ import { flatMap } from "lodash"; import React, { useRef } from "react"; import { createPortal } from "react-dom"; -import { CSSTransition, TransitionGroup } from "react-transition-group"; import { useWindowManager } from "../hooks"; -import { defaultFadeAnimation } from "./getFadeInAnimation"; import { ModalMask } from "./ModalMask"; import { Window } from "./window/Window"; import { ContentGetter } from "./window/WindowContent"; @@ -16,22 +14,14 @@ interface WindowsContainerProps { export function WindowsContainer({ container = document.body, contentGetter }: WindowsContainerProps): JSX.Element { const { windows } = useWindowManager(); - const modalMaskRef = useRef(); - const windowRef = useRef(); return createPortal( - + {flatMap(windows, (d, index) => [ - d.isModal && ( - - - - ), - - - , + d.isModal && , + , ]).filter(Boolean)} - , + , container, ); } diff --git a/src/components/getFadeInAnimation.ts b/src/components/getFadeInAnimation.ts index eef0ed91..6c8df492 100644 --- a/src/components/getFadeInAnimation.ts +++ b/src/components/getFadeInAnimation.ts @@ -1,23 +1,19 @@ import { css } from "@emotion/css"; export const getFadeInAnimation = (t = 0.25) => ({ - enter: css({ + animationTime: t, + mounted: css({ opacity: 0, - }), - enterActive: css({ - opacity: 1, transition: `opacity ${t}s ease-in-out`, - pointerEvents: "none", }), - exit: css({ + entered: css({ opacity: 1, }), - exitActive: css({ + exited: css({ opacity: 0, - transition: `opacity ${t}s ease-in-out`, - pointerEvents: "none", }), }); +export type FadeInAnimation = ReturnType; export const defaultFadeAnimation = getFadeInAnimation(); export const fastFadeAnimation = getFadeInAnimation(0.15); diff --git a/src/components/window/SnapMask.tsx b/src/components/window/SnapMask.tsx index 314e42ce..3df3b85e 100644 --- a/src/components/window/SnapMask.tsx +++ b/src/components/window/SnapMask.tsx @@ -1,38 +1,32 @@ import { useTheme } from "@emotion/react"; -import React, { useRef } from "react"; -import { CSSTransition, TransitionGroup } from "react-transition-group"; +import React from "react"; import { rgba } from "../../rgba"; -import { fastFadeAnimation } from "../getFadeInAnimation"; import { Box } from "./useSnapAreas"; export const SnapMask = ({ previewBox }: { previewBox: Box }) => { - const nodeRef = useRef(null); const { colors, spacing: { baseUnit }, } = useTheme(); return ( - + <> {previewBox && ( - -
- +
)} - + ); }; diff --git a/src/components/window/Window.tsx b/src/components/window/Window.tsx index a986b1d4..4c0b6958 100644 --- a/src/components/window/Window.tsx +++ b/src/components/window/Window.tsx @@ -1,4 +1,4 @@ -import React, { forwardRef, RefObject, useCallback } from "react"; +import React, { useCallback } from "react"; import { useWindowManager, useWindowZoom } from "../../hooks"; import { WindowWithOrder } from "../../types"; import { ContentGetter, WindowContent } from "./WindowContent"; @@ -9,7 +9,7 @@ export interface WindowProps { contentGetter: ContentGetter; } -export const Window = forwardRef(({ data, contentGetter }: WindowProps, ref: RefObject): JSX.Element => { +export const Window = ({ data, contentGetter }: WindowProps): JSX.Element => { const { isResizable, isStatic, focusParent, id, order, shouldCloseOnEsc } = data; const { focus: onFocus, close: onClose } = useWindowManager(id); @@ -31,7 +31,7 @@ export const Window = forwardRef(({ data, contentGetter }: WindowProps, ref: Ref height={data.height} minWidth={data.minWidth} minHeight={data.minHeight} - ref={ref} + id={data.id} layoutData={{ width: data.width, height: data.height, @@ -43,6 +43,6 @@ export const Window = forwardRef(({ data, contentGetter }: WindowProps, ref: Ref ); -}); +}; Window.displayName = "Window"; diff --git a/src/components/window/WindowFrame.tsx b/src/components/window/WindowFrame.tsx index e6c0d4a1..501edfd7 100644 --- a/src/components/window/WindowFrame.tsx +++ b/src/components/window/WindowFrame.tsx @@ -1,24 +1,24 @@ import { css, cx } from "@emotion/css"; import { isEqual } from "lodash"; import { mapValues } from "lodash/fp"; -import React, { forwardRef, PropsWithChildren, RefObject, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; +import React, { PropsWithChildren, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import FocusLock from "react-focus-lock"; import { Position, Rnd } from "react-rnd"; -import { CSSTransition } from "react-transition-group"; -import { useMutationObserver, usePreviousDifferent, usePreviousImmediate } from "rooks"; +import { useMutationObserver, usePreviousDifferent } from "rooks"; import { DRAG_HANDLE_CLASS_NAME, DRAG_PREVENT_CLASS_NAME } from "../../consts"; import { useViewportSize } from "../../hooks"; import { useScrollFix } from "../../hooks/useScrollFix"; import { useFrameTheme } from "../../themeHooks"; import { LayoutData } from "../../types"; import { random } from "../../utils"; -import { defaultFadeAnimation } from "../getFadeInAnimation"; import { SnapMask } from "./SnapMask"; import { Coords, Side, Size } from "./types"; import { Box, useSnapAreas } from "./useSnapAreas"; import { useSnapSide } from "./useSnapSide"; +import { useTransition } from "../TransitionProvider"; interface WindowFrameProps { + id: string; focusGroup?: string; zIndex?: number; randomizePosition?: number; @@ -47,11 +47,6 @@ interface WindowFrameProps { layoutData?: LayoutData; } -const zoomAnimation = { - enter: css({ transition: "all 150ms" }), - exit: css({ transition: "all 150ms" }), -}; - function calcCoord(start: number, end: number, size: number, viewportSize: number, padding: number) { return Math.max(padding, end >= viewportSize - padding / 2 ? viewportSize - size - padding : start); } @@ -89,7 +84,8 @@ const windowClass = css({ willChange: "transform, top, left, minWidth, minHeight, width, height", }); -export const WindowFrame = forwardRef((props: PropsWithChildren, windowRef: RefObject): JSX.Element => { +export const WindowFrame = (props: PropsWithChildren): JSX.Element => { + const { getTransitionStyle } = useTransition(); const { focusGroup, zIndex, @@ -101,7 +97,9 @@ export const WindowFrame = forwardRef((props: PropsWithChildren(); const viewport = useViewportSize(); @@ -113,7 +111,6 @@ export const WindowFrame = forwardRef((props: PropsWithChildren= 0 && right >= 0 ? viewport.width - left - right : width ?? props.width, }; }); - const prevSize = usePreviousImmediate(size); const { focusWrapperTheme, windowTheme, windowMargin } = useFrameTheme(); const dragging = useRef(false); @@ -231,15 +228,6 @@ export const WindowFrame = forwardRef((props: PropsWithChildren { - const { height, width } = ref.current.getBoundingClientRect(); - setSize({ width, height }); - }, []); - - const onExited = useCallback(() => { - setSize(prevSize || null); - }, [prevSize]); - const onResizeStop = useCallback( (e, dir, el, delta, position) => { const { width, height } = el.getBoundingClientRect(); @@ -287,57 +275,49 @@ export const WindowFrame = forwardRef((props: PropsWithChildren normalizeMinSize(minHeight, viewport.height), [normalizeMinSize, viewport.height, minHeight]); useScrollFix(ref.current); - return ( -
- {/*fallback animation for lazy loaded content*/} - - - + + {/* trap keyboard focus within group (windows opened since last modal) */} + +
{ + event.key === "Escape" && onEscape?.(); + touch(); + }} + tabIndex={-1} + className={cx(focusWrapperClass, focusWrapperTheme)} + data-testid="window" > - {/* trap keyboard focus within group (windows opened since last modal) */} - -
{ - event.key === "Escape" && onEscape?.(); - touch(); - }} - tabIndex={-1} - className={cx(focusWrapperClass, focusWrapperTheme)} - data-testid="window" - > - {props.children} -
-
- - - + {props.children} +
+
+
-
+ ); -}); - -WindowFrame.displayName = "WindowFrame"; +}; diff --git a/src/components/window/useSnapAreas.tsx b/src/components/window/useSnapAreas.tsx index d32a8be0..68439677 100644 --- a/src/components/window/useSnapAreas.tsx +++ b/src/components/window/useSnapAreas.tsx @@ -50,7 +50,7 @@ export function useSnapAreas(margin = 0, onSnapCallback?: (box: Box, side: Side) return null; } }, - [height, width], + [height, margin, width], ); const onSideEdgeSnap = useCallback( diff --git a/src/hooks/manager.ts b/src/hooks/manager.ts index 4e0c9273..4daa2e80 100644 --- a/src/hooks/manager.ts +++ b/src/hooks/manager.ts @@ -3,6 +3,8 @@ import { WindowManagerContext } from "../context"; import { closeWindow, getWindowsWithOrder, openWindow } from "../store"; import { WindowId, WindowManagerState, WindowType } from "../types"; import { ViewportContext, ViewportContextType } from "../ViewportContext"; +import { useTransition } from "../components/TransitionProvider"; +import { v4 as uuid } from "uuid"; export function useViewportSize(): ViewportContextType { const context = useContext(ViewportContext); @@ -19,12 +21,14 @@ export function useRawState(): WindowManagerSta export function useWindowManager(parent?: WindowId) { const [state, dispatch] = useContext(WindowManagerContext); - + const transition = useTransition(); const open = useCallback( - (windowData: Partial> = {}) => { - return dispatch(openWindow({ parent, ...windowData })); + async (windowData: Partial> = {}) => { + const windowId: WindowId = windowData.id || uuid(); + await transition.startTransition(windowId); + return dispatch(openWindow({ parent, ...windowData, id: windowId })); }, - [dispatch, parent], + [dispatch, parent, transition], ); const focus = useCallback( @@ -36,10 +40,11 @@ export function useWindowManager(parent?: Windo const close = useCallback( async (id: WindowId = parent) => { + await transition.finishTransition(id); await dispatch(closeWindow(id)); return id; }, - [dispatch, parent], + [dispatch, parent, transition], ); const windows = useMemo(() => getWindowsWithOrder(state), [state]); From 13736c3594bbfc340d0687b66a2fe0e58dbf4dc1 Mon Sep 17 00:00:00 2001 From: Dawid Poliszak Date: Wed, 27 Nov 2024 09:33:44 +0100 Subject: [PATCH 07/13] fix: revert change animation library --- package-lock.json | 29 ++++-- package.json | 2 +- src/components/ModalMask.tsx | 12 ++- src/components/TransitionProvider.tsx | 77 ---------------- src/components/WindowManager.tsx | 19 ++-- src/components/WindowsContainer.tsx | 18 +++- src/components/getFadeInAnimation.ts | 14 +-- src/components/window/SnapMask.tsx | 40 +++++---- src/components/window/Window.tsx | 8 +- src/components/window/WindowFrame.tsx | 120 ++++++++++++++----------- src/components/window/useSnapAreas.tsx | 2 +- src/hooks/manager.ts | 15 ++-- 12 files changed, 162 insertions(+), 194 deletions(-) delete mode 100644 src/components/TransitionProvider.tsx diff --git a/package-lock.json b/package-lock.json index fc20d741..905696b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "react-hotkeys-hook": "4.5.0", "react-inspector": "6.0.2", "react-rnd": "10.4.13", - "react-transition-state": "2.2.0", + "react-transition-group": "4.4.5", "reselect": "5.1.0", "rooks": "7.14.1" }, @@ -15123,6 +15123,15 @@ "utila": "~0.4" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/dom-serializer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", @@ -28273,13 +28282,19 @@ } } }, - "node_modules/react-transition-state": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/react-transition-state/-/react-transition-state-2.2.0.tgz", - "integrity": "sha512-D3EyLku1Sdxrxq26Fo4Jh0q1BLEFQfDOxKKiSuyqWH84+hM6y0Guc0hcW2IXMXY5l5gQCgkOQ9y90xx6mNoj5w==", + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "react": ">=16.6.0", + "react-dom": ">=16.6.0" } }, "node_modules/read-only-stream": { diff --git a/package.json b/package.json index 849cff1c..27ecaa2c 100644 --- a/package.json +++ b/package.json @@ -128,7 +128,7 @@ "react-hotkeys-hook": "4.5.0", "react-inspector": "6.0.2", "react-rnd": "10.4.13", - "react-transition-state": "2.2.0", + "react-transition-group": "4.4.5", "reselect": "5.1.0", "rooks": "7.14.1" }, diff --git a/src/components/ModalMask.tsx b/src/components/ModalMask.tsx index 9b279322..214a6915 100644 --- a/src/components/ModalMask.tsx +++ b/src/components/ModalMask.tsx @@ -1,11 +1,9 @@ import { css, cx } from "@emotion/css"; -import React from "react"; +import React, { forwardRef, RefObject } from "react"; import { rgba } from "../rgba"; import { useModalMaskTheme } from "../themeHooks"; -import { WindowId } from "../types"; -import { useTransition } from "./TransitionProvider"; -export const ModalMask = ({ zIndex, id }: { zIndex?: number; id: WindowId }): JSX.Element => { +export const ModalMask = forwardRef(({ zIndex }: { zIndex?: number }, ref: RefObject): JSX.Element => { const modalMaskTheme = useModalMaskTheme(); const modalMaskClass = css({ top: 0, @@ -15,7 +13,7 @@ export const ModalMask = ({ zIndex, id }: { zIndex?: number; id: WindowId }): JS position: "fixed", background: rgba("black", 0.6), }); - const { getTransitionStyle } = useTransition(); + return
; +}); - return
; -}; +ModalMask.displayName = "ModalMask"; diff --git a/src/components/TransitionProvider.tsx b/src/components/TransitionProvider.tsx deleted file mode 100644 index d0b3cb8e..00000000 --- a/src/components/TransitionProvider.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import React, { createContext, PropsWithChildren, useCallback, useContext } from "react"; -import { useTransitionMap } from "react-transition-state"; -import { defaultFadeAnimation, FadeInAnimation } from "./getFadeInAnimation"; -import { WindowId } from "../types"; - -const TRANSITION_TIMEOUT = 100; -const transitionsAnimationTime = new Map(); -const TransitionContext = createContext<{ - startTransition: (id: string) => Promise; - finishTransition: (id: string) => Promise; - getTransitionStyle: (id: string, fadeInAnimation?: FadeInAnimation) => string[]; -}>(null); - -export const TransitionProvider = ({ children }: PropsWithChildren) => { - const transition = useTransitionMap({ - timeout: TRANSITION_TIMEOUT, - exit: true, - preExit: false, - allowMultiple: true, - }); - - const getTransitionStyle = useCallback( - (id: WindowId, fadeInAnimation: FadeInAnimation = defaultFadeAnimation) => { - transitionsAnimationTime.set(id, fadeInAnimation.animationTime * 1000); - const transitionItem = transition.stateMap.get(id); - const entered = transitionItem.status === "entered" && fadeInAnimation.entered; - const exited = transitionItem.status === "exited" && fadeInAnimation.exited; - - return [fadeInAnimation.mounted, entered, exited]; - }, - [transition.stateMap], - ); - - const startTransition = useCallback( - (id: WindowId) => { - return new Promise((resolve) => { - transition.setItem(id); - transition.toggle(id, true); - const transitionAnimationTime = transitionsAnimationTime.get(id); - - setTimeout(() => { - resolve(true); - }, TRANSITION_TIMEOUT + transitionAnimationTime); - }); - }, - [transition], - ); - - const finishTransition = useCallback( - (id: WindowId) => { - return new Promise((resolve) => { - transition.toggle(id, false); - const transitionAnimationTime = transitionsAnimationTime.get(id); - setTimeout(() => { - transition.deleteItem(id); - transitionsAnimationTime.delete(id); - resolve(true); - }, TRANSITION_TIMEOUT + transitionAnimationTime); - }); - }, - [transition], - ); - - return ( - {children} - ); -}; - -export const useTransition = () => { - const context = useContext(TransitionContext); - - if (!context) { - throw new Error(); - } - - return context; -}; diff --git a/src/components/WindowManager.tsx b/src/components/WindowManager.tsx index 7ac79df3..73d044d3 100644 --- a/src/components/WindowManager.tsx +++ b/src/components/WindowManager.tsx @@ -5,7 +5,6 @@ import { AppTheme } from "../AppTheme"; import { WindowManagerContextProvider } from "../context"; import { ContentGetter } from "./window/WindowContent"; import { WindowsContainer } from "./WindowsContainer"; -import { TransitionProvider } from "./TransitionProvider"; const defaultTheme = { backgroundOpacity: 0.9, @@ -35,15 +34,13 @@ export function WindowManager({ ...props }: WindowManagerProps): JSX.Element { return ( - - -
- {children} - defaultsDeep(theme, outerTheme, defaultTheme)}> - - -
-
-
+ +
+ {children} + defaultsDeep(theme, outerTheme, defaultTheme)}> + + +
+
); } diff --git a/src/components/WindowsContainer.tsx b/src/components/WindowsContainer.tsx index 8e28fcda..25735b87 100644 --- a/src/components/WindowsContainer.tsx +++ b/src/components/WindowsContainer.tsx @@ -1,7 +1,9 @@ import { flatMap } from "lodash"; import React, { useRef } from "react"; import { createPortal } from "react-dom"; +import { CSSTransition, TransitionGroup } from "react-transition-group"; import { useWindowManager } from "../hooks"; +import { defaultFadeAnimation } from "./getFadeInAnimation"; import { ModalMask } from "./ModalMask"; import { Window } from "./window/Window"; import { ContentGetter } from "./window/WindowContent"; @@ -14,14 +16,22 @@ interface WindowsContainerProps { export function WindowsContainer({ container = document.body, contentGetter }: WindowsContainerProps): JSX.Element { const { windows } = useWindowManager(); + const modalMaskRef = useRef(); + const windowRef = useRef(); return createPortal( - + {flatMap(windows, (d, index) => [ - d.isModal && , - , + d.isModal && ( + + + + ), + + + , ]).filter(Boolean)} - , + , container, ); } diff --git a/src/components/getFadeInAnimation.ts b/src/components/getFadeInAnimation.ts index 6c8df492..eef0ed91 100644 --- a/src/components/getFadeInAnimation.ts +++ b/src/components/getFadeInAnimation.ts @@ -1,19 +1,23 @@ import { css } from "@emotion/css"; export const getFadeInAnimation = (t = 0.25) => ({ - animationTime: t, - mounted: css({ + enter: css({ opacity: 0, + }), + enterActive: css({ + opacity: 1, transition: `opacity ${t}s ease-in-out`, + pointerEvents: "none", }), - entered: css({ + exit: css({ opacity: 1, }), - exited: css({ + exitActive: css({ opacity: 0, + transition: `opacity ${t}s ease-in-out`, + pointerEvents: "none", }), }); -export type FadeInAnimation = ReturnType; export const defaultFadeAnimation = getFadeInAnimation(); export const fastFadeAnimation = getFadeInAnimation(0.15); diff --git a/src/components/window/SnapMask.tsx b/src/components/window/SnapMask.tsx index 3df3b85e..314e42ce 100644 --- a/src/components/window/SnapMask.tsx +++ b/src/components/window/SnapMask.tsx @@ -1,32 +1,38 @@ import { useTheme } from "@emotion/react"; -import React from "react"; +import React, { useRef } from "react"; +import { CSSTransition, TransitionGroup } from "react-transition-group"; import { rgba } from "../../rgba"; +import { fastFadeAnimation } from "../getFadeInAnimation"; import { Box } from "./useSnapAreas"; export const SnapMask = ({ previewBox }: { previewBox: Box }) => { + const nodeRef = useRef(null); const { colors, spacing: { baseUnit }, } = useTheme(); return ( - <> + {previewBox && ( -
+ +
+ )} - + ); }; diff --git a/src/components/window/Window.tsx b/src/components/window/Window.tsx index 4c0b6958..a986b1d4 100644 --- a/src/components/window/Window.tsx +++ b/src/components/window/Window.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from "react"; +import React, { forwardRef, RefObject, useCallback } from "react"; import { useWindowManager, useWindowZoom } from "../../hooks"; import { WindowWithOrder } from "../../types"; import { ContentGetter, WindowContent } from "./WindowContent"; @@ -9,7 +9,7 @@ export interface WindowProps { contentGetter: ContentGetter; } -export const Window = ({ data, contentGetter }: WindowProps): JSX.Element => { +export const Window = forwardRef(({ data, contentGetter }: WindowProps, ref: RefObject): JSX.Element => { const { isResizable, isStatic, focusParent, id, order, shouldCloseOnEsc } = data; const { focus: onFocus, close: onClose } = useWindowManager(id); @@ -31,7 +31,7 @@ export const Window = ({ data, contentGetter }: WindowProps): JSX.Element => { height={data.height} minWidth={data.minWidth} minHeight={data.minHeight} - id={data.id} + ref={ref} layoutData={{ width: data.width, height: data.height, @@ -43,6 +43,6 @@ export const Window = ({ data, contentGetter }: WindowProps): JSX.Element => { ); -}; +}); Window.displayName = "Window"; diff --git a/src/components/window/WindowFrame.tsx b/src/components/window/WindowFrame.tsx index 501edfd7..e6c0d4a1 100644 --- a/src/components/window/WindowFrame.tsx +++ b/src/components/window/WindowFrame.tsx @@ -1,24 +1,24 @@ import { css, cx } from "@emotion/css"; import { isEqual } from "lodash"; import { mapValues } from "lodash/fp"; -import React, { PropsWithChildren, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; +import React, { forwardRef, PropsWithChildren, RefObject, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import FocusLock from "react-focus-lock"; import { Position, Rnd } from "react-rnd"; -import { useMutationObserver, usePreviousDifferent } from "rooks"; +import { CSSTransition } from "react-transition-group"; +import { useMutationObserver, usePreviousDifferent, usePreviousImmediate } from "rooks"; import { DRAG_HANDLE_CLASS_NAME, DRAG_PREVENT_CLASS_NAME } from "../../consts"; import { useViewportSize } from "../../hooks"; import { useScrollFix } from "../../hooks/useScrollFix"; import { useFrameTheme } from "../../themeHooks"; import { LayoutData } from "../../types"; import { random } from "../../utils"; +import { defaultFadeAnimation } from "../getFadeInAnimation"; import { SnapMask } from "./SnapMask"; import { Coords, Side, Size } from "./types"; import { Box, useSnapAreas } from "./useSnapAreas"; import { useSnapSide } from "./useSnapSide"; -import { useTransition } from "../TransitionProvider"; interface WindowFrameProps { - id: string; focusGroup?: string; zIndex?: number; randomizePosition?: number; @@ -47,6 +47,11 @@ interface WindowFrameProps { layoutData?: LayoutData; } +const zoomAnimation = { + enter: css({ transition: "all 150ms" }), + exit: css({ transition: "all 150ms" }), +}; + function calcCoord(start: number, end: number, size: number, viewportSize: number, padding: number) { return Math.max(padding, end >= viewportSize - padding / 2 ? viewportSize - size - padding : start); } @@ -84,8 +89,7 @@ const windowClass = css({ willChange: "transform, top, left, minWidth, minHeight, width, height", }); -export const WindowFrame = (props: PropsWithChildren): JSX.Element => { - const { getTransitionStyle } = useTransition(); +export const WindowFrame = forwardRef((props: PropsWithChildren, windowRef: RefObject): JSX.Element => { const { focusGroup, zIndex, @@ -97,9 +101,7 @@ export const WindowFrame = (props: PropsWithChildren): JSX.Ele resizable = false, moveable = false, layoutData = {}, - id, } = props; - const { minWidth = props.minWidth ?? 400, minHeight = props.minHeight ?? 140 } = layoutData; const ref = useRef(); const viewport = useViewportSize(); @@ -111,6 +113,7 @@ export const WindowFrame = (props: PropsWithChildren): JSX.Ele width: left >= 0 && right >= 0 ? viewport.width - left - right : width ?? props.width, }; }); + const prevSize = usePreviousImmediate(size); const { focusWrapperTheme, windowTheme, windowMargin } = useFrameTheme(); const dragging = useRef(false); @@ -228,6 +231,15 @@ export const WindowFrame = (props: PropsWithChildren): JSX.Ele [onSideSnap, onSnapCallback, savePosition, side], ); + const onEnter = useCallback(() => { + const { height, width } = ref.current.getBoundingClientRect(); + setSize({ width, height }); + }, []); + + const onExited = useCallback(() => { + setSize(prevSize || null); + }, [prevSize]); + const onResizeStop = useCallback( (e, dir, el, delta, position) => { const { width, height } = el.getBoundingClientRect(); @@ -275,49 +287,57 @@ export const WindowFrame = (props: PropsWithChildren): JSX.Ele const currentMinHeight = useMemo(() => normalizeMinSize(minHeight, viewport.height), [normalizeMinSize, viewport.height, minHeight]); useScrollFix(ref.current); + return ( - <> - - {/* trap keyboard focus within group (windows opened since last modal) */} - -
{ - event.key === "Escape" && onEscape?.(); - touch(); - }} - tabIndex={-1} - className={cx(focusWrapperClass, focusWrapperTheme)} - data-testid="window" +
+ {/*fallback animation for lazy loaded content*/} + + + - {props.children} -
- - + {/* trap keyboard focus within group (windows opened since last modal) */} + +
{ + event.key === "Escape" && onEscape?.(); + touch(); + }} + tabIndex={-1} + className={cx(focusWrapperClass, focusWrapperTheme)} + data-testid="window" + > + {props.children} +
+
+ + + - +
); -}; +}); + +WindowFrame.displayName = "WindowFrame"; diff --git a/src/components/window/useSnapAreas.tsx b/src/components/window/useSnapAreas.tsx index 68439677..d32a8be0 100644 --- a/src/components/window/useSnapAreas.tsx +++ b/src/components/window/useSnapAreas.tsx @@ -50,7 +50,7 @@ export function useSnapAreas(margin = 0, onSnapCallback?: (box: Box, side: Side) return null; } }, - [height, margin, width], + [height, width], ); const onSideEdgeSnap = useCallback( diff --git a/src/hooks/manager.ts b/src/hooks/manager.ts index 4daa2e80..4e0c9273 100644 --- a/src/hooks/manager.ts +++ b/src/hooks/manager.ts @@ -3,8 +3,6 @@ import { WindowManagerContext } from "../context"; import { closeWindow, getWindowsWithOrder, openWindow } from "../store"; import { WindowId, WindowManagerState, WindowType } from "../types"; import { ViewportContext, ViewportContextType } from "../ViewportContext"; -import { useTransition } from "../components/TransitionProvider"; -import { v4 as uuid } from "uuid"; export function useViewportSize(): ViewportContextType { const context = useContext(ViewportContext); @@ -21,14 +19,12 @@ export function useRawState(): WindowManagerSta export function useWindowManager(parent?: WindowId) { const [state, dispatch] = useContext(WindowManagerContext); - const transition = useTransition(); + const open = useCallback( - async (windowData: Partial> = {}) => { - const windowId: WindowId = windowData.id || uuid(); - await transition.startTransition(windowId); - return dispatch(openWindow({ parent, ...windowData, id: windowId })); + (windowData: Partial> = {}) => { + return dispatch(openWindow({ parent, ...windowData })); }, - [dispatch, parent, transition], + [dispatch, parent], ); const focus = useCallback( @@ -40,11 +36,10 @@ export function useWindowManager(parent?: Windo const close = useCallback( async (id: WindowId = parent) => { - await transition.finishTransition(id); await dispatch(closeWindow(id)); return id; }, - [dispatch, parent, transition], + [dispatch, parent], ); const windows = useMemo(() => getWindowsWithOrder(state), [state]); From c429109384194d0230dfe3d6bd7827b4e13d51cc Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 27 Nov 2024 10:18:55 +0000 Subject: [PATCH 08/13] chore(release): 1.9.1-beta.2 [skip ci] ## [1.9.1-beta.2](https://github.com/touk/nk-windows/compare/v1.9.1-beta.1...v1.9.1-beta.2) (2024-11-27) ### Bug Fixes * revert change animation library ([13736c3](https://github.com/touk/nk-windows/commit/13736c3594bbfc340d0687b66a2fe0e58dbf4dc1)) --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 070f28e1..c7d65cf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.9.1-beta.2](https://github.com/touk/nk-windows/compare/v1.9.1-beta.1...v1.9.1-beta.2) (2024-11-27) + + +### Bug Fixes + +* revert change animation library ([13736c3](https://github.com/touk/nk-windows/commit/13736c3594bbfc340d0687b66a2fe0e58dbf4dc1)) + ## [1.9.1-beta.1](https://github.com/touk/nk-windows/compare/v1.9.0...v1.9.1-beta.1) (2024-11-25) diff --git a/package-lock.json b/package-lock.json index 905696b9..eda93045 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@touk/window-manager", - "version": "1.9.1-beta.1", + "version": "1.9.1-beta.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@touk/window-manager", - "version": "1.9.1-beta.1", + "version": "1.9.1-beta.2", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 27ecaa2c..1f907bb5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@touk/window-manager", - "version": "1.9.1-beta.1", + "version": "1.9.1-beta.2", "types": "./cjs/index.d.ts", "main": "./cjs/index.js", "module": "./esm/index.js", From e15935acb84771237fee072b58109153741ae61c Mon Sep 17 00:00:00 2001 From: Dawid Poliszak Date: Wed, 27 Nov 2024 14:31:23 +0100 Subject: [PATCH 09/13] fix: Maximum update depth exceeded error --- src/components/window/WindowFrame.tsx | 36 ++++++--------------------- src/themeHooks.tsx | 2 +- 2 files changed, 8 insertions(+), 30 deletions(-) diff --git a/src/components/window/WindowFrame.tsx b/src/components/window/WindowFrame.tsx index e6c0d4a1..3b23b867 100644 --- a/src/components/window/WindowFrame.tsx +++ b/src/components/window/WindowFrame.tsx @@ -1,5 +1,4 @@ import { css, cx } from "@emotion/css"; -import { isEqual } from "lodash"; import { mapValues } from "lodash/fp"; import React, { forwardRef, PropsWithChildren, RefObject, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import FocusLock from "react-focus-lock"; @@ -114,6 +113,7 @@ export const WindowFrame = forwardRef((props: PropsWithChildren { - if (ref.current) { + if (ref.current && !wasMaximized) { const randomize = mapValues((v: number) => Math.max(0, v + random(randomizePosition))); const { height, width } = ref.current.getBoundingClientRect(); const x = (viewport.width - width) / 2; - const y = (viewport.height * 0.75 - height) / 2; + const y = (viewport.height * 0.65 - height) / 2; const center = randomize({ x, y }); setPosition( roundCoords({ - x: initialPosition.x ?? center.x, - y: initialPosition.y ?? center.y, + x: initialPosition.x ?? calcCoord(center.x, center.x + width, width, viewport.width, windowMargin), + y: initialPosition.y ?? calcCoord(center.y, center.y + height, height, viewport.height, windowMargin), }), ); } }, - [randomizePosition, viewport.height, viewport.width], + [randomizePosition, viewport.height, viewport.width, wasMaximized, windowMargin], ); const onContentChanged = useCallback(() => { @@ -154,28 +154,6 @@ export const WindowFrame = forwardRef((props: PropsWithChildren { - const width = size?.width || box?.width || 0; - const height = size?.height || box?.height || 0; - return roundCoords({ - x: calcCoord(box.x, box.x + box.width, width, viewport.width, windowMargin), - y: calcCoord(box.y, box.y + box.height, height, viewport.height, windowMargin), - }); - }, - [size, windowMargin], - ); - - useLayoutEffect(() => { - if (contentAvailable && position && !(maximized || wasMaximized)) { - const newValue = calcEdgePosition(viewport); - setPosition((current) => (isEqual(newValue, current) ? current : newValue)); - } - }, [contentAvailable, wasMaximized, calcEdgePosition, maximized, position, viewport]); - const savePosition = useCallback((position: Position) => !maximized && setPosition(roundCoords(position)), [maximized]); const [side, setSide] = useState(Side.none); @@ -260,7 +238,7 @@ export const WindowFrame = forwardRef((props: PropsWithChildren ({ width: `calc(100% - ${position?.x <= windowMargin ? windowMargin * 2 : position?.x || 0}px)`, - height: viewport.height - (position?.y <= windowMargin ? windowMargin * 2 : position?.y || 0), + height: viewport.height - (position?.y <= windowMargin ? windowMargin * 2 : position?.y + windowMargin || 0), }), [windowMargin, position, viewport.height], ); diff --git a/src/themeHooks.tsx b/src/themeHooks.tsx index 5fa9f7cb..432c8b14 100644 --- a/src/themeHooks.tsx +++ b/src/themeHooks.tsx @@ -1,6 +1,6 @@ import { css, cx } from "@emotion/css"; import { useTheme } from "@emotion/react"; -import React, { useMemo } from "react"; +import { useMemo } from "react"; import { rgba } from "./rgba"; export function useFrameTheme() { From b6a2ad7a1e07a181165312152fe512492bd2934f Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 27 Nov 2024 13:43:40 +0000 Subject: [PATCH 10/13] chore(release): 1.9.1-beta.3 [skip ci] ## [1.9.1-beta.3](https://github.com/touk/nk-windows/compare/v1.9.1-beta.2...v1.9.1-beta.3) (2024-11-27) ### Bug Fixes * Maximum update depth exceeded error ([e15935a](https://github.com/touk/nk-windows/commit/e15935acb84771237fee072b58109153741ae61c)) --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7d65cf6..e7c5d7e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.9.1-beta.3](https://github.com/touk/nk-windows/compare/v1.9.1-beta.2...v1.9.1-beta.3) (2024-11-27) + + +### Bug Fixes + +* Maximum update depth exceeded error ([e15935a](https://github.com/touk/nk-windows/commit/e15935acb84771237fee072b58109153741ae61c)) + ## [1.9.1-beta.2](https://github.com/touk/nk-windows/compare/v1.9.1-beta.1...v1.9.1-beta.2) (2024-11-27) diff --git a/package-lock.json b/package-lock.json index eda93045..f72c4210 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@touk/window-manager", - "version": "1.9.1-beta.2", + "version": "1.9.1-beta.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@touk/window-manager", - "version": "1.9.1-beta.2", + "version": "1.9.1-beta.3", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 1f907bb5..eab679e9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@touk/window-manager", - "version": "1.9.1-beta.2", + "version": "1.9.1-beta.3", "types": "./cjs/index.d.ts", "main": "./cjs/index.js", "module": "./esm/index.js", From 23a895b07a5f37d897f5b796463b5bc2bd0afcf1 Mon Sep 17 00:00:00 2001 From: Dawid Poliszak Date: Wed, 4 Dec 2024 11:51:19 +0100 Subject: [PATCH 11/13] fix: window position --- src/components/window/WindowFrame.tsx | 41 +++++++++++++++++++++------ 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/src/components/window/WindowFrame.tsx b/src/components/window/WindowFrame.tsx index 3b23b867..8026846b 100644 --- a/src/components/window/WindowFrame.tsx +++ b/src/components/window/WindowFrame.tsx @@ -1,10 +1,11 @@ import { css, cx } from "@emotion/css"; +import { isEqual } from "lodash"; import { mapValues } from "lodash/fp"; import React, { forwardRef, PropsWithChildren, RefObject, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import FocusLock from "react-focus-lock"; import { Position, Rnd } from "react-rnd"; import { CSSTransition } from "react-transition-group"; -import { useMutationObserver, usePreviousDifferent, usePreviousImmediate } from "rooks"; +import { useDebounce, useMutationObserver, usePreviousDifferent, usePreviousImmediate } from "rooks"; import { DRAG_HANDLE_CLASS_NAME, DRAG_PREVENT_CLASS_NAME } from "../../consts"; import { useViewportSize } from "../../hooks"; import { useScrollFix } from "../../hooks/useScrollFix"; @@ -113,7 +114,6 @@ export const WindowFrame = forwardRef((props: PropsWithChildren { - if (ref.current && !wasMaximized) { + if (ref.current) { const randomize = mapValues((v: number) => Math.max(0, v + random(randomizePosition))); const { height, width } = ref.current.getBoundingClientRect(); const x = (viewport.width - width) / 2; - const y = (viewport.height * 0.65 - height) / 2; + const y = (viewport.height * 0.75 - height) / 2; const center = randomize({ x, y }); setPosition( roundCoords({ - x: initialPosition.x ?? calcCoord(center.x, center.x + width, width, viewport.width, windowMargin), - y: initialPosition.y ?? calcCoord(center.y, center.y + height, height, viewport.height, windowMargin), + x: initialPosition.x ?? center.x, + y: initialPosition.y ?? center.y, }), ); } }, - [randomizePosition, viewport.height, viewport.width, wasMaximized, windowMargin], + [randomizePosition, viewport.height, viewport.width], ); const onContentChanged = useCallback(() => { @@ -154,6 +154,31 @@ export const WindowFrame = forwardRef((props: PropsWithChildren { + const width = size?.width || box?.width || 0; + const height = size?.height || box?.height || 0; + return roundCoords({ + x: calcCoord(box.x, box.x + box.width, width, viewport.width, windowMargin), + y: calcCoord(box.y, box.y + box.height, height, viewport.height, windowMargin), + }); + }, + [size, windowMargin], + ); + + // it fixes an issue with Maximum update depth exceeded error + const debounceSetPosition = useDebounce(setPosition, 0, { leading: true }); + + useLayoutEffect(() => { + if (contentAvailable && position && !(maximized || wasMaximized)) { + const newValue = calcEdgePosition(viewport); + debounceSetPosition((current) => (isEqual(newValue, current) ? current : newValue)); + } + }, [contentAvailable, wasMaximized, calcEdgePosition, maximized, position, viewport, debounceSetPosition]); + const savePosition = useCallback((position: Position) => !maximized && setPosition(roundCoords(position)), [maximized]); const [side, setSide] = useState(Side.none); @@ -238,7 +263,7 @@ export const WindowFrame = forwardRef((props: PropsWithChildren ({ width: `calc(100% - ${position?.x <= windowMargin ? windowMargin * 2 : position?.x || 0}px)`, - height: viewport.height - (position?.y <= windowMargin ? windowMargin * 2 : position?.y + windowMargin || 0), + height: viewport.height - (position?.y <= windowMargin ? windowMargin * 2 : position?.y || 0), }), [windowMargin, position, viewport.height], ); From 437697d07d664882ba67e1c480f31cdddc0eff51 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 27 Nov 2024 13:43:40 +0000 Subject: [PATCH 12/13] chore(release): 1.9.1-beta.3 [skip ci] ## [1.9.1-beta.3](https://github.com/touk/nk-windows/compare/v1.9.1-beta.2...v1.9.1-beta.3) (2024-11-27) ### Bug Fixes * Maximum update depth exceeded error ([e15935a](https://github.com/touk/nk-windows/commit/e15935acb84771237fee072b58109153741ae61c)) --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7d65cf6..e7c5d7e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.9.1-beta.3](https://github.com/touk/nk-windows/compare/v1.9.1-beta.2...v1.9.1-beta.3) (2024-11-27) + + +### Bug Fixes + +* Maximum update depth exceeded error ([e15935a](https://github.com/touk/nk-windows/commit/e15935acb84771237fee072b58109153741ae61c)) + ## [1.9.1-beta.2](https://github.com/touk/nk-windows/compare/v1.9.1-beta.1...v1.9.1-beta.2) (2024-11-27) diff --git a/package-lock.json b/package-lock.json index eda93045..f72c4210 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@touk/window-manager", - "version": "1.9.1-beta.2", + "version": "1.9.1-beta.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@touk/window-manager", - "version": "1.9.1-beta.2", + "version": "1.9.1-beta.3", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 1f907bb5..eab679e9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@touk/window-manager", - "version": "1.9.1-beta.2", + "version": "1.9.1-beta.3", "types": "./cjs/index.d.ts", "main": "./cjs/index.js", "module": "./esm/index.js", From 0611a304e888a1e1b6bbd1b81ccdf86be0611fed Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 5 Dec 2024 14:25:09 +0000 Subject: [PATCH 13/13] chore(release): 1.9.1-beta.4 [skip ci] ## [1.9.1-beta.4](https://github.com/touk/nk-windows/compare/v1.9.1-beta.3...v1.9.1-beta.4) (2024-12-05) ### Bug Fixes * window position ([23a895b](https://github.com/touk/nk-windows/commit/23a895b07a5f37d897f5b796463b5bc2bd0afcf1)) --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7c5d7e5..07215dce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.9.1-beta.4](https://github.com/touk/nk-windows/compare/v1.9.1-beta.3...v1.9.1-beta.4) (2024-12-05) + + +### Bug Fixes + +* window position ([23a895b](https://github.com/touk/nk-windows/commit/23a895b07a5f37d897f5b796463b5bc2bd0afcf1)) + ## [1.9.1-beta.3](https://github.com/touk/nk-windows/compare/v1.9.1-beta.2...v1.9.1-beta.3) (2024-11-27) diff --git a/package-lock.json b/package-lock.json index f72c4210..020a0443 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@touk/window-manager", - "version": "1.9.1-beta.3", + "version": "1.9.1-beta.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@touk/window-manager", - "version": "1.9.1-beta.3", + "version": "1.9.1-beta.4", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index eab679e9..9384c4a9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@touk/window-manager", - "version": "1.9.1-beta.3", + "version": "1.9.1-beta.4", "types": "./cjs/index.d.ts", "main": "./cjs/index.js", "module": "./esm/index.js",