Skip to content

Commit

Permalink
fix: revert, and fix control profiles with movable menu button (#255)
Browse files Browse the repository at this point in the history
- reverts commit a2a4944.

- fixes control profiles with standalone layout for menu button
  • Loading branch information
thenick775 authored Jan 28, 2025
1 parent 8116bb7 commit 0fad226
Show file tree
Hide file tree
Showing 13 changed files with 116 additions and 101 deletions.
44 changes: 25 additions & 19 deletions gbajs3/src/components/controls/control-panel.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ describe('<ControlPanel />', () => {

vi.spyOn(contextHooks, 'useLayoutContext').mockImplementation(() => ({
...original(),
initialBounds: {
...original().initialBounds,
screen: { left: 0, bottom: 0 } as DOMRect
layouts: {
...original().layouts,
screen: { initialBounds: { left: 0, bottom: 0 } as DOMRect }
}
}));
});
Expand Down Expand Up @@ -95,28 +95,30 @@ describe('<ControlPanel />', () => {
});

it('sets initial bounds when rendered', async () => {
const setInitialBoundSpy = vi.fn();
const setLayoutSpy = vi.fn();

const { useLayoutContext: originalLayout } = await vi.importActual<
typeof contextHooks
>('../../hooks/context.tsx');

vi.spyOn(contextHooks, 'useLayoutContext').mockImplementation(() => ({
...originalLayout(),
setInitialBound: setInitialBoundSpy
setLayout: setLayoutSpy
}));

renderWithContext(<ControlPanel />);

expect(setInitialBoundSpy).toHaveBeenCalledWith('controlPanel', {
bottom: 0,
height: 0,
left: 0,
right: 0,
top: 0,
width: 0,
x: 0,
y: 0
expect(setLayoutSpy).toHaveBeenCalledWith('controlPanel', {
initialBounds: {
bottom: 0,
height: 0,
left: 0,
right: 0,
top: 0,
width: 0,
x: 0,
y: 0
}
});
});

Expand All @@ -133,11 +135,14 @@ describe('<ControlPanel />', () => {
vi.spyOn(contextHooks, 'useLayoutContext').mockImplementation(() => ({
...originalLayout(),
setLayout: setLayoutSpy,
initialBounds: { screen: new DOMRect() }
hasSetLayout: true,
layouts: { screen: { initialBounds: new DOMRect() } }
}));

renderWithContext(<ControlPanel />);

setLayoutSpy.mockClear(); // clear calls from initial render

// simulate mouse events on wrapper
fireEvent.mouseDown(
screen.getByTestId('control-panel-wrapper'),
Expand Down Expand Up @@ -168,12 +173,11 @@ describe('<ControlPanel />', () => {

// needs to be a consistent object
const testLayout = {
clearLayoutsAndBounds: vi.fn(),
clearLayouts: vi.fn(),
setLayout: setLayoutSpy,
setLayouts: vi.fn(),
setInitialBound: vi.fn(),
layouts: {},
initialBounds: { screen: new DOMRect() }
hasSetLayout: true,
layouts: { screen: { initialBounds: new DOMRect() } }
};

vi.spyOn(contextHooks, 'useLayoutContext').mockImplementation(
Expand All @@ -182,6 +186,8 @@ describe('<ControlPanel />', () => {

renderWithContext(<ControlPanel />);

setLayoutSpy.mockClear(); // clear calls from initial render

fireEvent.resize(screen.getByTestId('control-panel-wrapper'));

// simulate mouse events on a resize handle
Expand Down
16 changes: 7 additions & 9 deletions gbajs3/src/components/controls/control-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,7 @@ export const ControlPanel = () => {
const { isRunning } = useRunningContext();
const { areItemsDraggable, setAreItemsDraggable } = useDragContext();
const { areItemsResizable, setAreItemsResizable } = useResizeContext();
const { layouts, setLayout, initialBounds, setInitialBound } =
useLayoutContext();
const { layouts, setLayout, hasSetLayout } = useLayoutContext();
const theme = useTheme();
const isLargerThanPhone = useMediaQuery(theme.isLargerThanPhone);
const isMobileLandscape = useMediaQuery(theme.isMobileLandscape);
Expand All @@ -105,16 +104,15 @@ export const ControlPanel = () => {

const refSetLayout = useCallback(
(node: Rnd | null) => {
if (!initialBounds?.controlPanel && node)
setInitialBound(
'controlPanel',
node?.resizableElement.current?.getBoundingClientRect()
);
if (!hasSetLayout && node)
setLayout('controlPanel', {
initialBounds: node.resizableElement.current?.getBoundingClientRect()
});
},
[initialBounds?.controlPanel, setInitialBound]
[setLayout, hasSetLayout]
);

const canvasBounds = initialBounds?.screen;
const canvasBounds = layouts?.screen?.initialBounds;

if (!canvasBounds) return null;

Expand Down
6 changes: 3 additions & 3 deletions gbajs3/src/components/controls/virtual-controls.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ describe('<VirtualControls />', () => {

vi.spyOn(contextHooks, 'useLayoutContext').mockImplementation(() => ({
...original(),
initialBounds: {
...original().initialBounds,
controlPanel: { left: 0, bottom: 0 } as DOMRect
layouts: {
...original().layouts,
controlPanel: { initialBounds: { left: 0, bottom: 0 } as DOMRect }
}
}));
});
Expand Down
6 changes: 3 additions & 3 deletions gbajs3/src/components/controls/virtual-controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const VirtualControls = () => {
const { isRunning } = useRunningContext();
const { isAuthenticated } = useAuthContext();
const { setModalContent, setIsModalOpen } = useModalContext();
const { initialBounds } = useLayoutContext();
const { layouts } = useLayoutContext();
const virtualControlToastId = useId();
const quickReload = useQuickReload();
const { syncActionIfEnabled } = useAddCallbacks();
Expand All @@ -73,8 +73,8 @@ export const VirtualControls = () => {
AreVirtualControlsEnabledProps | undefined
>(virtualControlsLocalStorageKey);

const controlPanelBounds = initialBounds?.controlPanel;
const canvasBounds = initialBounds?.screen;
const controlPanelBounds = layouts?.controlPanel?.initialBounds;
const canvasBounds = layouts?.screen?.initialBounds;

if (!controlPanelBounds) return null;

Expand Down
6 changes: 3 additions & 3 deletions gbajs3/src/components/modals/controls.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,14 @@ describe('<ControlsModal />', () => {
});

it('resets movable control layouts', async () => {
const clearLayoutsAndBoundsSpy = vi.fn();
const clearLayoutsSpy = vi.fn();
const { useLayoutContext: original } = await vi.importActual<
typeof contextHooks
>('../../hooks/context.tsx');

vi.spyOn(contextHooks, 'useLayoutContext').mockImplementation(() => ({
...original(),
clearLayoutsAndBounds: clearLayoutsAndBoundsSpy
clearLayouts: clearLayoutsSpy
}));

renderWithContext(<ControlsModal />);
Expand All @@ -115,7 +115,7 @@ describe('<ControlsModal />', () => {

await userEvent.click(resetPositionsButton);

expect(clearLayoutsAndBoundsSpy).toHaveBeenCalledOnce();
expect(clearLayoutsSpy).toHaveBeenCalledOnce();
});

it('closes modal using the close button', async () => {
Expand Down
4 changes: 2 additions & 2 deletions gbajs3/src/components/modals/controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const ControlTabs = ({
resetPositionsButtonId,
setIsSuccessfulSubmit
}: ControlTabsProps) => {
const { clearLayoutsAndBounds } = useLayoutContext();
const { clearLayouts } = useLayoutContext();
const [value, setValue] = useState(0);

const tabIndexToFormId = (tabIndex: number) => {
Expand Down Expand Up @@ -116,7 +116,7 @@ const ControlTabs = ({
<Button
id={resetPositionsButtonId}
sx={{ marginTop: '10px' }}
onClick={clearLayoutsAndBounds}
onClick={clearLayouts}
>
Reset All Positions
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,8 @@ describe('<NavigationMenu />', () => {
position: {
x: movements[1].clientX,
y: movements[1].clientY
}
},
standalone: true
});
});

Expand Down
5 changes: 4 additions & 1 deletion gbajs3/src/components/navigation-menu/navigation-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,10 @@ export const NavigationMenu = () => {
position={layouts?.menuButton?.position ?? { x: 0, y: 0 }}
disabled={!areItemsDraggable}
onStop={(_, data) =>
setLayout('menuButton', { position: { x: 0, y: data.y } })
setLayout('menuButton', {
position: { x: 0, y: data.y },
standalone: true
})
}
>
<HamburgerButton
Expand Down
31 changes: 18 additions & 13 deletions gbajs3/src/components/screen/screen.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,28 +41,30 @@ describe('<Screen />', () => {
});

it('sets initial bounds when rendered', async () => {
const setInitialBoundSpy = vi.fn();
const setLayoutSpy = vi.fn();

const { useLayoutContext: originalLayout } = await vi.importActual<
typeof contextHooks
>('../../hooks/context.tsx');

vi.spyOn(contextHooks, 'useLayoutContext').mockImplementation(() => ({
...originalLayout(),
setInitialBound: setInitialBoundSpy
setLayout: setLayoutSpy
}));

renderWithContext(<Screen />);

expect(setInitialBoundSpy).toHaveBeenCalledWith('screen', {
bottom: 0,
height: 0,
left: 0,
right: 0,
top: 0,
width: 0,
x: 0,
y: 0
expect(setLayoutSpy).toHaveBeenCalledWith('screen', {
initialBounds: {
bottom: 0,
height: 0,
left: 0,
right: 0,
top: 0,
width: 0,
x: 0,
y: 0
}
});
});

Expand Down Expand Up @@ -129,7 +131,9 @@ describe('<Screen />', () => {

vi.spyOn(contextHooks, 'useLayoutContext').mockImplementation(() => ({
...originalLayout(),
setLayout: setLayoutSpy
setLayout: setLayoutSpy,
hasSetLayout: true,
layouts: { screen: { initialBounds: new DOMRect() } }
}));

renderWithContext(<Screen />);
Expand Down Expand Up @@ -163,7 +167,8 @@ describe('<Screen />', () => {
vi.spyOn(contextHooks, 'useLayoutContext').mockImplementation(() => ({
...originalLayout(),
setLayout: setLayoutSpy,
initialBounds: { screen: new DOMRect() }
hasSetLayout: true,
layouts: { screen: { initialBounds: new DOMRect() } }
}));

renderWithContext(<Screen />);
Expand Down
29 changes: 18 additions & 11 deletions gbajs3/src/components/screen/screen.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useMediaQuery } from '@mui/material';
import { useCallback, useRef } from 'react';
import { useOrientation } from '@uidotdev/usehooks';
import { useCallback, useLayoutEffect, useRef } from 'react';
import { Rnd, type Props as RndProps } from 'react-rnd';
import { styled, useTheme } from 'styled-components';

Expand Down Expand Up @@ -83,31 +84,37 @@ export const Screen = () => {
const { setCanvas } = useEmulatorContext();
const { areItemsDraggable } = useDragContext();
const { areItemsResizable } = useResizeContext();
const { layouts, setLayout, initialBounds, setInitialBound } =
useLayoutContext();
const { layouts, setLayout, hasSetLayout } = useLayoutContext();
const screenWrapperXStart = isLargerThanPhone ? NavigationMenuWidth + 10 : 0;
const screenWrapperYStart = isLargerThanPhone && !isMobileLandscape ? 15 : 0;
const rndRef = useRef<Rnd | null>();
const orientation = useOrientation();

const refUpdateDefaultPosition = useCallback(
(node: Rnd | null) => {
if (!layouts?.screen) {
if (!hasSetLayout) {
node?.resizableElement?.current?.style?.removeProperty('width');
node?.resizableElement?.current?.style?.removeProperty('height');
}

if (!initialBounds?.screen && node) {
setInitialBound(
'screen',
node.resizableElement.current?.getBoundingClientRect()
);
}
if (!hasSetLayout && node)
setLayout('screen', {
initialBounds: node.resizableElement.current?.getBoundingClientRect()
});

if (!rndRef.current) rndRef.current = node;
},
[initialBounds?.screen, layouts?.screen, setInitialBound]
[hasSetLayout, setLayout]
);

useLayoutEffect(() => {
if (!hasSetLayout && [0, 90, 270].includes(orientation.angle))
setLayout('screen', {
initialBounds:
rndRef.current?.resizableElement?.current?.getBoundingClientRect()
});
}, [hasSetLayout, isMobileLandscape, setLayout, orientation.angle]);

const refSetCanvas = useCallback(
(node: HTMLCanvasElement | null) => setCanvas(node),
[setCanvas]
Expand Down
12 changes: 5 additions & 7 deletions gbajs3/src/context/layout/layout-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,19 @@ import { createContext } from 'react';
export type Layout = {
position?: { x: number; y: number };
size?: { width: string | number; height: string | number };
initialBounds?: DOMRect;
/** indicates whether or not this layout is standalone, and considered in any relative calculations or counts */
standalone?: boolean;
};

export type Layouts = {
[key: string]: Layout;
};

export type InitialBounds = {
[key: string]: DOMRect | undefined;
};

export type LayoutContextProps = {
layouts: Layouts;
clearLayoutsAndBounds: () => void;
initialBounds?: InitialBounds;
setInitialBound: (key: string, bounds?: DOMRect) => void;
hasSetLayout: boolean;
clearLayouts: () => void;
setLayout: (layoutKey: string, layout: Layout) => void;
setLayouts: (layouts: Layouts) => void;
};
Expand Down
Loading

0 comments on commit 0fad226

Please sign in to comment.