Skip to content

Commit

Permalink
Adjust ruler marker resize for spanning elements (#6703)
Browse files Browse the repository at this point in the history
**Problem:**

Ruler marker resize needs some extra tweaks for spanning elements.

**Fix:**

1. Make it possible to resize correctly a flow item that spans from the
start by dragging its end marker
2. Make it possible to resize correctly a flow item that spans from the
end by dragging its start/end markers and treat them as a more
traditional shrink/expand

Also added specific tests for span combinations.

**Manual Tests:**
I hereby swear that:

- [x] I opened a hydrogen project and it loaded
- [x] I could navigate to various routes in Play mode

Fixes #6701
  • Loading branch information
ruggi authored Dec 5, 2024
1 parent 2a4e2c0 commit 1aa3387
Show file tree
Hide file tree
Showing 2 changed files with 216 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
} from '../../../../utils/utils.test-utils'
import {
RulerMarkerColumnEndTestId,
RulerMarkerColumnStartTestId,
RulerMarkerRowEndTestId,
RulerMarkerRowStartTestId,
} from '../../controls/grid-controls'
Expand Down Expand Up @@ -155,6 +156,127 @@ describe('grid resize element ruler strategy', () => {
expect(element.style.gridColumn).toBe('span 2')
expect(element.style.gridRow).toBe('auto')
})

describe('spans', () => {
it('can resize a flow spanning element (start, from the right)', async () => {
const renderResult = await renderTestEditorWithCode(
ProjectCodeWithSpans,
'await-first-dom-report',
)

await selectComponentsForTest(renderResult, [EP.fromString('sb/grid/flow1')])

const control = await renderResult.renderedDOM.findByTestId(RulerMarkerColumnEndTestId)
const controlRect = control.getBoundingClientRect()
const startPoint = { x: controlRect.x + 5, y: controlRect.y + 5 }
const endPoint = { x: controlRect.x + 200, y: controlRect.y + 5 }
await mouseDownAtPoint(control, startPoint)
await mouseMoveToPoint(control, endPoint)
await mouseUpAtPoint(control, endPoint)

const element = await renderResult.renderedDOM.findByTestId('flow1')
expect(element.style.gridColumn).toBe('span 3')
expect(element.style.gridRow).toBe('auto')
})

it('can resize a flow spanning element (start, from the left)', async () => {
const renderResult = await renderTestEditorWithCode(
ProjectCodeWithSpans,
'await-first-dom-report',
)

await selectComponentsForTest(renderResult, [EP.fromString('sb/grid/flow1')])

const control = await renderResult.renderedDOM.findByTestId(RulerMarkerColumnStartTestId)
const controlRect = control.getBoundingClientRect()
const startPoint = { x: controlRect.x + 5, y: controlRect.y + 5 }
const endPoint = { x: controlRect.x + 200, y: controlRect.y + 5 }
await mouseDownAtPoint(control, startPoint)
await mouseMoveToPoint(control, endPoint)
await mouseUpAtPoint(control, endPoint)

const element = await renderResult.renderedDOM.findByTestId('flow1')
expect(element.style.gridColumn).toBe('')
expect(element.style.gridColumnStart).toBe('')
expect(element.style.gridColumnEnd).toBe('3')
expect(element.style.gridRow).toBe('auto')
})

it('can resize a flow spanning element (end)', async () => {
const renderResult = await renderTestEditorWithCode(
ProjectCodeWithSpans,
'await-first-dom-report',
)

await selectComponentsForTest(renderResult, [EP.fromString('sb/grid/flow2')])

// expand to the right
{
const control = await renderResult.renderedDOM.findByTestId(RulerMarkerColumnEndTestId)
const controlRect = control.getBoundingClientRect()
const startPoint = { x: controlRect.x + 5, y: controlRect.y + 5 }
const endPoint = { x: controlRect.x + 200, y: controlRect.y + 5 }
await mouseDownAtPoint(control, startPoint)
await mouseMoveToPoint(control, endPoint)
await mouseUpAtPoint(control, endPoint)

const element = await renderResult.renderedDOM.findByTestId('flow2')
expect(element.style.gridColumn).toBe('2 / span 3')
expect(element.style.gridRow).toBe('auto')
}

// shrink from the left
{
const control = await renderResult.renderedDOM.findByTestId(RulerMarkerColumnStartTestId)
const controlRect = control.getBoundingClientRect()
const startPoint = { x: controlRect.x + 5, y: controlRect.y + 5 }
const endPoint = { x: controlRect.x + 200, y: controlRect.y + 5 }
await mouseDownAtPoint(control, startPoint)
await mouseMoveToPoint(control, endPoint)
await mouseUpAtPoint(control, endPoint)

const element = await renderResult.renderedDOM.findByTestId('flow2')
expect(element.style.gridColumn).toBe('3 / span 2')
expect(element.style.gridRow).toBe('auto')
}

// expand back from the left
{
const control = await renderResult.renderedDOM.findByTestId(RulerMarkerColumnStartTestId)
const controlRect = control.getBoundingClientRect()
const startPoint = { x: controlRect.x + 5, y: controlRect.y + 5 }
const endPoint = { x: controlRect.x - 400, y: controlRect.y + 5 }
await mouseDownAtPoint(control, startPoint)
await mouseMoveToPoint(control, endPoint)
await mouseUpAtPoint(control, endPoint)

const element = await renderResult.renderedDOM.findByTestId('flow2')
expect(element.style.gridColumn).toBe('1 / span 4')
expect(element.style.gridRow).toBe('auto')
}
})

it('can resize a pinned spanning element', async () => {
const renderResult = await renderTestEditorWithCode(
ProjectCodeWithSpans,
'await-first-dom-report',
)

await selectComponentsForTest(renderResult, [EP.fromString('sb/grid/pinned')])

const control = await renderResult.renderedDOM.findByTestId(RulerMarkerColumnEndTestId)
const controlRect = control.getBoundingClientRect()
const startPoint = { x: controlRect.x + 5, y: controlRect.y + 5 }
const endPoint = { x: controlRect.x + 200, y: controlRect.y + 5 }
await mouseDownAtPoint(control, startPoint)
await mouseMoveToPoint(control, endPoint)
await mouseUpAtPoint(control, endPoint)

const element = await renderResult.renderedDOM.findByTestId('pinned')
expect(element.style.gridColumn).toBe('2 / span 3')
expect(element.style.gridRow).toBe('3')
})
})
})

const ProjectCode = `
Expand Down Expand Up @@ -232,3 +354,61 @@ export var storyboard = (
</Storyboard>
)
`

const ProjectCodeWithSpans = `
import * as React from 'react'
import { Storyboard } from 'utopia-api'
export var storyboard = (
<Storyboard data-uid='sb'>
<div
style={{
backgroundColor: '#aaaaaa33',
position: 'absolute',
left: 210,
top: 80,
width: 690,
height: 459,
display: 'grid',
gap: 10,
gridTemplateColumns: '1fr 1fr 1fr 1fr',
gridTemplateRows: '1fr 1fr 1fr 1fr',
}}
data-uid='grid'
data-testid='grid'
>
<div
style={{
backgroundColor: '#09f',
alignSelf: 'stretch',
justifySelf: 'stretch',
gridColumn: 'span 2',
}}
data-uid='flow1'
data-testid='flow1'
/>
<div
style={{
backgroundColor: '#f90',
alignSelf: 'stretch',
justifySelf: 'stretch',
gridColumn: '2 / span 2',
}}
data-uid='flow2'
data-testid='flow2'
/>
<div
style={{
backgroundColor: '#f09',
gridColumn: '2 / span 2',
gridRow: 3,
alignSelf: 'stretch',
justifySelf: 'stretch',
}}
data-uid='pinned'
data-testid='pinned'
/>
</div>
</Storyboard>
)
`
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import type {
GridElementProperties,
GridPositionOrSpan,
} from '../../../../core/shared/element-template'
import { gridPositionValue, isGridPositionValue } from '../../../../core/shared/element-template'
import {
gridPositionValue,
isGridPositionValue,
isGridSpan,
} from '../../../../core/shared/element-template'
import type { CanvasPoint, CanvasRectangle, CanvasVector } from '../../../../core/shared/math-utils'
import { canvasRectangle, isInfinityRectangle } from '../../../../core/shared/math-utils'
import { assertNever } from '../../../../core/shared/utils'
Expand Down Expand Up @@ -160,8 +164,18 @@ export const gridResizeElementRulerStrategy: CanvasStrategyFactory = (
closestVertical,
)

const columnCount = getCellsCount(resizedProps.gridColumnStart, resizedProps.gridColumnEnd)
const rowCount = getCellsCount(resizedProps.gridRowStart, resizedProps.gridRowEnd)
const columnCount = getCellsCount(
resizedProps.gridColumnStart,
resizedProps.gridColumnEnd,
bounds.column,
bounds.width,
)
const rowCount = getCellsCount(
resizedProps.gridRowStart,
resizedProps.gridRowEnd,
bounds.row,
bounds.height,
)

const normalizedGridProps: GridElementProperties = {
gridColumnStart: normalizeGridElementPositionAfterResize(
Expand Down Expand Up @@ -295,12 +309,30 @@ function getResizedElementProperties(
function getCellsCount(
start: GridPositionOrSpan | null,
end: GridPositionOrSpan | null,
originalStart: number,
originalSize: number,
): number | null {
// start is a number
if (isGridPositionValue(start) && start.numericalPosition != null) {
// end is also a number, return the difference
if (isGridPositionValue(end) && end.numericalPosition != null) {
return end.numericalPosition - start.numericalPosition
}
return start.numericalPosition

// end is a discrete span, recalculate the size as a shrink or an expansion of the original size
if (isGridSpan(end) && end.type === 'SPAN_NUMERIC') {
return originalSize + (originalStart - start.numericalPosition)
}
}

// start is a discrete span
if (isGridSpan(start) && start.type === 'SPAN_NUMERIC') {
// end is a number, return the max between the span size and the start position
if (isGridPositionValue(end) && end.numericalPosition != null) {
return Math.max(end.numericalPosition, start.value) - 1
}
}

// nothing specific found, will be handled with a separate fallback
return null
}

0 comments on commit 1aa3387

Please sign in to comment.