Skip to content

Commit

Permalink
feature(grids) Check Element Against Containing Block (#6702)
Browse files Browse the repository at this point in the history
- Added `rectangleContainsRectangleInclusive` utility function.
- Added `getNewGridElementPropsCheckingOriginalGrid` as a wrapper around
`getNewGridElementProps` which checks if the element is outside of the
containing block anywhere.
  • Loading branch information
seanparsons authored Dec 5, 2024
1 parent 1aa3387 commit 24bf538
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,16 @@ import {
isAutoGridPin,
getCommandsForGridItemPlacement,
sortElementsByGridPosition,
getGridRelativeContainingBlock,
} from './grid-helpers'
import type { GridCellCoordinates } from './grid-cell-bounds'
import {
canvasRectangle,
offsetPoint,
offsetRect,
rectContainsPoint,
zeroCanvasRect,
} from '../../../../core/shared/math-utils'

export const gridChangeElementLocationStrategy: CanvasStrategyFactory = (
canvasState: InteractionCanvasState,
Expand Down Expand Up @@ -172,7 +180,7 @@ function getCommandsAndPatchForGridChangeElementLocation(
}
}

interface NewGridElementProps {
export interface NewGridElementProps {
gridElementProperties: GridElementProperties
targetCellCoords: GridCellCoordinates
targetRootCell: GridCellCoordinates
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -698,7 +698,7 @@ export var storyboard = (
}
})

it('can move absolute element among grid cells', async () => {
it('can move absolute element among grid cells (within containing block)', async () => {
const editor = await renderTestEditorWithCode(ProjectCode, 'await-first-dom-report')

const child = editor.renderedDOM.getByTestId('child')
Expand Down Expand Up @@ -730,13 +730,56 @@ export var storyboard = (
await mouseMoveToPoint(dragTarget, endPoint)
await mouseUpAtPoint(dragTarget, endPoint)

{
const { top, left, gridColumn, gridRow } = child.style
expect({ top, left, gridColumn, gridRow }).toEqual({
gridColumn: '1',
gridRow: '1',
left: '252px',
top: '256px',
})
}
})

it('can move absolute element among grid cells', async () => {
const editor = await renderTestEditorWithCode(ProjectCode, 'await-first-dom-report')

const child = editor.renderedDOM.getByTestId('child')

{
const { top, left, gridColumn, gridRow } = child.style
expect({ top, left, gridColumn, gridRow }).toEqual({
gridColumn: '1',
gridRow: '1',
left: '12px',
top: '16px',
})
}

await selectComponentsForTest(editor, [EP.fromString('sb/scene/grid/child')])

const childBounds = child.getBoundingClientRect()
const childCenter = windowPoint({
x: Math.floor(childBounds.left + childBounds.width / 2),
y: Math.floor(childBounds.top + childBounds.height / 2),
})

const endPoint = offsetPoint(childCenter, windowPoint({ x: 300, y: 300 }))
const dragTarget = editor.renderedDOM.getByTestId(
GridCellTestId(EP.fromString('sb/scene/grid/child')),
)

await mouseDownAtPoint(dragTarget, childCenter)
await mouseMoveToPoint(dragTarget, endPoint)
await mouseUpAtPoint(dragTarget, endPoint)

{
const { top, left, gridColumn, gridRow } = child.style
expect({ top, left, gridColumn, gridRow }).toEqual({
gridColumn: '2',
gridRow: '2',
left: '21.5px',
top: '32px',
left: '81.5px',
top: '92px',
})
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,14 @@ export var storyboard = (
>
<div
style={{
gridColumn: 1,
gridRow: 1,
backgroundColor: '#f0f',
position: 'absolute',
left: 300,
top: 300,
width: 79,
height: 86,
gridColumn: 1,
gridRow: 1,
}}
data-uid='dragme'
data-testid='dragme'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ import {
type ElementInstanceMetadataMap,
type GridContainerProperties,
} from '../../../../core/shared/element-template'
import type { CanvasVector } from '../../../../core/shared/math-utils'
import type { CanvasRectangle, CanvasVector } from '../../../../core/shared/math-utils'
import {
canvasRectangle,
canvasRectangleToLocalRectangle,
isNotNullFiniteRectangle,
nullIfInfinity,
offsetPoint,
offsetRect,
rectangleContainsRectangleInclusive,
rectContainsPoint,
windowPoint,
zeroCanvasRect,
zeroRectIfNullOrInfinity,
Expand Down Expand Up @@ -42,6 +46,7 @@ import {
getParentGridTemplatesFromChildMeasurements,
gridMoveStrategiesExtraCommands,
} from './grid-helpers'
import type { NewGridElementProps } from './grid-change-element-location-strategy'
import {
getNewGridElementProps,
runGridChangeElementLocation,
Expand Down Expand Up @@ -177,6 +182,57 @@ function getCommandsAndPatchForGridAbsoluteMove(
}
}

export function getNewGridElementPropsCheckingOriginalGrid(
originalGridMetadata: ElementInstanceMetadata,
interactionData: DragInteractionData,
selectedElementMetadata: ElementInstanceMetadata,
gridCellGlobalFrames: GridCellGlobalFrames,
coordinateSystemBounds: CanvasRectangle,
newPathAfterReparent: ElementPath | null,
): NewGridElementProps | null {
if (interactionData.drag == null) {
return null
}

// Identify the containing block position and size.
const originalContainingBlockRectangle = getGridRelativeContainingBlock(
originalGridMetadata,
selectedElementMetadata,
selectedElementMetadata.specialSizeMeasurements.elementGridProperties,
)

// Capture the original position of the grid child.
const originalCanvasFrame = selectedElementMetadata.globalFrame
if (!isNotNullFiniteRectangle(originalCanvasFrame)) {
return null
}

// Identify if the new position of the grid child is wholly inside the containing block's
// global frame.
const containingBlockGlobalFrame = offsetRect(
canvasRectangle(originalContainingBlockRectangle),
coordinateSystemBounds,
)
const gridChildNewGlobalFrame = offsetRect(originalCanvasFrame, interactionData.drag)
const insideOriginalContainingBlock = rectangleContainsRectangleInclusive(
containingBlockGlobalFrame,
gridChildNewGlobalFrame,
)

// If the element is inside the containing block,
// then don't attempt to move it.
if (insideOriginalContainingBlock) {
return null
}

return getNewGridElementProps(
interactionData,
selectedElementMetadata,
gridCellGlobalFrames,
newPathAfterReparent,
)
}

function runGridMoveAbsolute(
jsxMetadata: ElementInstanceMetadataMap,
interactionData: DragInteractionData,
Expand Down Expand Up @@ -260,18 +316,6 @@ function runGridMoveAbsolute(
]
}

// The element may be moving to a different grid position, which is then used
// to calculate the potentially new containing block.
const newGridElementProps = getNewGridElementProps(
interactionData,
selectedElementMetadata,
gridCellGlobalFrames,
null,
)

const coordinateSystemBounds =
selectedElementMetadata.specialSizeMeasurements.immediateParentBounds ?? zeroCanvasRect

// Get the metadata of the original grid.
const originalGridPath = findOriginalGrid(
jsxMetadata,
Expand All @@ -285,6 +329,23 @@ function runGridMoveAbsolute(
return []
}

const coordinateSystemBounds =
selectedElementMetadata.specialSizeMeasurements.immediateParentBounds
if (coordinateSystemBounds == null) {
return []
}

// The element may be moving to a different grid position, which is then used
// to calculate the potentially new containing block.
const newGridElementProps = getNewGridElementPropsCheckingOriginalGrid(
originalGrid,
interactionData,
selectedElementMetadata,
gridCellGlobalFrames,
coordinateSystemBounds,
null,
)

// Get the containing block of the grid child.
const containingBlockRectangle = getGridRelativeContainingBlock(
originalGrid,
Expand All @@ -302,14 +363,16 @@ function runGridMoveAbsolute(

// otherwise, return a change location + absolute adjustment
return [
...runGridChangeElementLocation(
jsxMetadata,
interactionData,
selectedElementMetadata,
gridCellGlobalFrames,
gridTemplate,
null,
),
...(newGridElementProps == null
? []
: runGridChangeElementLocation(
jsxMetadata,
interactionData,
selectedElementMetadata,
gridCellGlobalFrames,
gridTemplate,
null,
)),
...getMoveCommandsForDrag(
containingRect,
element,
Expand Down
12 changes: 12 additions & 0 deletions editor/src/core/shared/math-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,18 @@ export function rectangleContainsRectangle(
)
}

export function rectangleContainsRectangleInclusive(
outer: CanvasRectangle,
inner: CanvasRectangle,
): boolean {
return (
outer.x <= inner.x &&
inner.x + inner.width <= outer.x + outer.width &&
outer.y <= inner.y &&
inner.y + inner.height <= outer.y + outer.height
)
}

export function rectangleFromTLBR(
topLeft: CanvasPoint,
bottomRight: CanvasPoint,
Expand Down

0 comments on commit 24bf538

Please sign in to comment.