Skip to content

Commit 80561fc

Browse files
authored
Grid ruler marker labels (#6664)
**Problem:** Ruler markers should show a label with the respective CSS value when hovered. **Fix:** 1. Show labels with the printed pin CSS when hovering a ruler marker. For line names, show the line name. For named spans, show `span the-area`. 2. Adjust the markers so they respect the verbatim CSS without special treatment for span-end/start (which we could revisit in the future) 3. Adjust (and make more readable) the invididual marker skew values, which need to be customized in a bunch of combinations (it's subtle, but it's there!). I ended up using a bunch of named conditions, which after some tinkering felt much more readable than a cluster of ifs or switches. Better ideas are more than welcome tho! 🤗 https://github.com/user-attachments/assets/063adeb5-0b09-4a09-8603-77448bfd0cb2 | Config | Before | After | |--------|---------|-----------| | `gridColumn: auto`, `gridRow: auto` | <img width="1120" alt="Screenshot 2024-11-20 at 15 30 23" src="https://github.com/user-attachments/assets/c2d2b355-0312-46f4-a34c-f10f4619ddb8"> | <img width="1120" alt="Screenshot 2024-11-20 at 15 30 20" src="https://github.com/user-attachments/assets/a733ca4c-1d77-4d26-925e-8ce4161a3989"> | | `gridColumn: 3`, `gridRow: 1` | <img width="1120" alt="Screenshot 2024-11-20 at 15 30 36" src="https://github.com/user-attachments/assets/b5365ca3-9d2b-4c26-ab32-1811bbf82cb4"> | <img width="1120" alt="Screenshot 2024-11-20 at 15 30 35" src="https://github.com/user-attachments/assets/ae8a1a1c-67a5-466a-a46c-233f98553705"> | | `gridColumn: span 2 / 4`, `gridRow: span 3` | <img width="1133" alt="Screenshot 2024-11-20 at 15 35 51" src="https://github.com/user-attachments/assets/dbb8d3e3-89ef-4262-90df-f222bb828c83"> | <img width="1133" alt="Screenshot 2024-11-20 at 15 35 50" src="https://github.com/user-attachments/assets/018bd102-7331-4fbb-8ba6-8ab3316d4464"> | <img width="1120" alt="Screenshot 2024-11-20 at 15 31 22" src="https://github.com/user-attachments/assets/83057672-e2e5-4307-91ab-bd1a01e8a520"> <img width="1120" alt="Screenshot 2024-11-20 at 15 31 27" src="https://github.com/user-attachments/assets/4bb71dab-d3d6-46e7-b6ec-6e10a726ae7e"> <img width="1133" alt="Screenshot 2024-11-20 at 15 31 35" src="https://github.com/user-attachments/assets/453040c3-7151-4ff8-bfc8-73ed9925307b"> **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 #6663
1 parent f68eaf0 commit 80561fc

File tree

2 files changed

+150
-39
lines changed

2 files changed

+150
-39
lines changed

editor/src/components/canvas/controls/grid-controls.tsx

+147-39
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ import {
7676
getGlobalFrameOfGridCellFromMetadata,
7777
getGridRelatedIndexes,
7878
getGridElementPinState,
79-
gridPositionToValue,
8079
printPin,
8180
getGridIdentifierContainerOrComponentPath,
8281
gridIdentifierToString,
@@ -120,6 +119,7 @@ import { styleStringInArray } from '../../../utils/common-constants'
120119
import { gridContainerIdentifier, type GridIdentifier } from '../../editor/store/editor-state'
121120
import type { RulerMarkerType } from './grid-controls-ruler-markers'
122121
import { rulerMarkerIcons } from './grid-controls-ruler-markers'
122+
import { isFeatureEnabled } from '../../../utils/feature-switches'
123123

124124
const CELL_ANIMATION_DURATION = 0.15 // seconds
125125

@@ -1213,9 +1213,12 @@ export const GridControlsComponent = ({ targets }: GridControlsProps) => {
12131213
)
12141214
})}
12151215
{/* Ruler markers */}
1216-
{selectedGridItems.map((path) => {
1217-
return <RulerMarkers key={`ruler-markers-${EP.toString(path)}`} path={path} />
1218-
})}
1216+
{when(
1217+
isFeatureEnabled('Grid Ruler Markers'),
1218+
selectedGridItems.map((path) => {
1219+
return <RulerMarkers key={`ruler-markers-${EP.toString(path)}`} path={path} />
1220+
}),
1221+
)}
12191222
<AbsoluteDistanceIndicators targetRootCell={targetRootCell} />
12201223
</CanvasOffsetWrapper>
12211224
</div>
@@ -2186,6 +2189,7 @@ function useSelectedGridItems(): ElementPath[] {
21862189
const rulerMarkerIconSize = 12 // px
21872190

21882191
type RulerMarkerData = {
2192+
parentGrid: GridContainerProperties
21892193
columnStart: RulerMarkerPositionData
21902194
columnEnd: RulerMarkerPositionData
21912195
rowStart: RulerMarkerPositionData
@@ -2196,7 +2200,6 @@ type RulerMarkerPositionData = {
21962200
top: number
21972201
left: number
21982202
position: GridPositionOrSpan | null
2199-
counterpart: GridPositionOrSpan | null
22002203
bound: 'start' | 'end'
22012204
}
22022205

@@ -2217,6 +2220,8 @@ const RulerMarkers = React.memo((props: { path: ElementPath }) => {
22172220
return null
22182221
}
22192222

2223+
const parentGrid = elementMetadata.specialSizeMeasurements.parentContainerGridProperties
2224+
22202225
const originalGrid = findOriginalGrid(store.editor.jsxMetadata, EP.parentPath(props.path))
22212226
if (originalGrid == null) {
22222227
return null
@@ -2269,32 +2274,29 @@ const RulerMarkers = React.memo((props: { path: ElementPath }) => {
22692274
)
22702275

22712276
return {
2277+
parentGrid: parentGrid,
22722278
columnStart: {
22732279
top: gridRect.y,
22742280
left: left,
22752281
position: elementGridProperties.gridColumnStart,
2276-
counterpart: elementGridProperties.gridColumnEnd,
22772282
bound: 'start',
22782283
},
22792284
columnEnd: {
22802285
top: gridRect.y,
22812286
left: left + width,
22822287
position: elementGridProperties.gridColumnEnd,
2283-
counterpart: elementGridProperties.gridColumnStart,
22842288
bound: 'end',
22852289
},
22862290
rowStart: {
22872291
top: top,
22882292
left: gridRect.x,
22892293
position: elementGridProperties.gridRowStart,
2290-
counterpart: elementGridProperties.gridRowEnd,
22912294
bound: 'start',
22922295
},
22932296
rowEnd: {
22942297
top: top + height,
22952298
left: gridRect.x,
22962299
position: elementGridProperties.gridRowEnd,
2297-
counterpart: elementGridProperties.gridRowStart,
22982300
bound: 'end',
22992301
},
23002302
}
@@ -2308,22 +2310,37 @@ const RulerMarkers = React.memo((props: { path: ElementPath }) => {
23082310

23092311
return (
23102312
<React.Fragment>
2311-
<RulerMarkerIndicator marker={markers.columnStart} axis={'column'} />
2312-
<RulerMarkerIndicator marker={markers.columnEnd} axis={'column'} />
2313-
<RulerMarkerIndicator marker={markers.rowStart} axis={'row'} />
2314-
<RulerMarkerIndicator marker={markers.rowEnd} axis={'row'} />
2313+
<RulerMarkerIndicator
2314+
parentGrid={markers.parentGrid}
2315+
marker={markers.columnStart}
2316+
axis={'column'}
2317+
/>
2318+
<RulerMarkerIndicator
2319+
parentGrid={markers.parentGrid}
2320+
marker={markers.columnEnd}
2321+
axis={'column'}
2322+
/>
2323+
<RulerMarkerIndicator
2324+
parentGrid={markers.parentGrid}
2325+
marker={markers.rowStart}
2326+
axis={'row'}
2327+
/>
2328+
<RulerMarkerIndicator parentGrid={markers.parentGrid} marker={markers.rowEnd} axis={'row'} />
23152329
</React.Fragment>
23162330
)
23172331
})
23182332
RulerMarkers.displayName = 'RulerMarkers'
23192333

23202334
const RulerMarkerIndicator = React.memo(
2321-
(props: { marker: RulerMarkerPositionData; axis: 'row' | 'column' }) => {
2335+
(props: {
2336+
parentGrid: GridContainerProperties
2337+
marker: RulerMarkerPositionData
2338+
axis: 'row' | 'column'
2339+
}) => {
23222340
const colorTheme = useColorTheme()
23232341

23242342
const markerType = getRulerMarkerType({
23252343
position: props.marker.position,
2326-
counterpart: props.marker.counterpart,
23272344
bound: props.marker.bound,
23282345
})
23292346
const markerIcon = rulerMarkerIcons[markerType][props.axis]
@@ -2334,21 +2351,24 @@ const RulerMarkerIndicator = React.memo(
23342351
'RulerMarkerIndicator canvasScale',
23352352
)
23362353

2337-
function skewMarkerPosition(axis: 'column' | 'row') {
2338-
if (props.axis === axis) {
2339-
return rulerMarkerIconSize
2340-
} else if (markerType === 'span-end') {
2341-
return rulerMarkerIconSize - 1 // adjust span end position so it just touches the grid line
2342-
} else {
2343-
return rulerMarkerIconSize / 2
2344-
}
2345-
}
2346-
23472354
const scaledTop = props.marker.top * canvasScale
2348-
const top = scaledTop - skewMarkerPosition('column')
2355+
const top =
2356+
scaledTop -
2357+
skewMarkerPosition(props.axis === 'column', props.axis, props.marker.bound, markerType)
23492358

23502359
const scaledLeft = props.marker.left * canvasScale
2351-
const left = scaledLeft - skewMarkerPosition('row')
2360+
const left =
2361+
scaledLeft -
2362+
skewMarkerPosition(props.axis === 'row', props.axis, props.marker.bound, markerType)
2363+
2364+
const labelText = React.useMemo(() => {
2365+
if (props.marker.position == null) {
2366+
return null
2367+
}
2368+
return printPin(props.parentGrid, props.marker.position, props.axis)
2369+
}, [props.marker, props.parentGrid, props.axis])
2370+
2371+
const labelClass = 'ruler-marker-label'
23522372

23532373
return (
23542374
<div
@@ -2357,15 +2377,45 @@ const RulerMarkerIndicator = React.memo(
23572377
top: top,
23582378
left: left,
23592379
color: colorTheme.primary.value,
2380+
maxHeight: rulerMarkerIconSize,
2381+
maxWidth: rulerMarkerIconSize,
23602382
display: 'flex',
2361-
alignItems: 'center',
2362-
justifyContent: 'center',
2363-
height: rulerMarkerIconSize,
2364-
width: rulerMarkerIconSize,
23652383
zoom: 1 / canvasScale,
23662384
}}
2385+
css={{
2386+
[`> .${labelClass}`]: {
2387+
visibility: 'hidden',
2388+
},
2389+
':hover': {
2390+
[`> .${labelClass}`]: {
2391+
visibility: 'visible',
2392+
},
2393+
},
2394+
}}
23672395
>
23682396
{markerIcon}
2397+
{when(
2398+
labelText != null,
2399+
<div
2400+
className={labelClass}
2401+
style={{
2402+
position: 'absolute',
2403+
background: colorTheme.primary.value,
2404+
borderRadius: 2,
2405+
padding: '3px 6px',
2406+
color: colorTheme.white.value,
2407+
height: 20,
2408+
display: 'flex',
2409+
alignItems: 'center',
2410+
justifyContent: 'center',
2411+
top: props.axis === 'column' ? -23 : 0,
2412+
left: props.axis === 'column' ? 0 : undefined,
2413+
right: props.axis === 'row' ? rulerMarkerIconSize + 1 : undefined,
2414+
}}
2415+
>
2416+
{labelText}
2417+
</div>,
2418+
)}
23692419
</div>
23702420
)
23712421
},
@@ -2374,16 +2424,11 @@ RulerMarkerIndicator.displayName = 'RulerMarkerIndicator'
23742424

23752425
function getRulerMarkerType(props: {
23762426
position: GridPositionOrSpan | null
2377-
counterpart: GridPositionOrSpan | null
23782427
bound: 'start' | 'end'
23792428
}): RulerMarkerType {
2380-
const isAuto =
2381-
isAutoGridPin(props.position) ||
2382-
(props.bound === 'start' && isGridSpan(props.position) && isAutoGridPin(props.counterpart))
2383-
const isSpanStart =
2384-
props.bound === 'start' && isGridSpan(props.position) && isGridSpan(props.counterpart)
2385-
const isSpanEnd =
2386-
props.bound === 'end' && (isGridSpan(props.position) || isGridSpan(props.counterpart))
2429+
const isAuto = isAutoGridPin(props.position)
2430+
const isSpanStart = props.bound === 'start' && isGridSpan(props.position)
2431+
const isSpanEnd = props.bound === 'end' && isGridSpan(props.position)
23872432

23882433
if (isSpanStart) {
23892434
return 'span-start'
@@ -2454,3 +2499,66 @@ function getCellCanvasHeightFromBounds(
24542499
return acc + curr.height + padding
24552500
}, currentColumn.height)
24562501
}
2502+
2503+
// This function returns the amount of pixels used to adjust the position of
2504+
// individual ruler markers, which need specific skews based on their shape.
2505+
function skewMarkerPosition(
2506+
isOnTheSameAxis: boolean,
2507+
axis: 'column' | 'row',
2508+
bound: 'start' | 'end',
2509+
markerType: RulerMarkerType,
2510+
): number {
2511+
if (isOnTheSameAxis) {
2512+
return rulerMarkerIconSize
2513+
}
2514+
2515+
// span-end triangle, on the column
2516+
const spanEndColumn = axis === 'column' && markerType === 'span-end'
2517+
if (spanEndColumn) {
2518+
return 10
2519+
}
2520+
const pinnedEndColumn = axis === 'column' && markerType === 'pinned'
2521+
if (pinnedEndColumn) {
2522+
return 5
2523+
}
2524+
// any other ending marker, on the column
2525+
const endColumn = bound === 'end' && axis === 'column'
2526+
if (endColumn) {
2527+
return 2
2528+
}
2529+
2530+
// span-end triangle, on the row
2531+
const spanEndRow = axis === 'row' && markerType === 'span-end'
2532+
if (spanEndRow) {
2533+
return 9
2534+
}
2535+
// any other ending marker, on the row
2536+
const endRow = bound === 'end' && axis === 'row'
2537+
if (endRow) {
2538+
return 6
2539+
}
2540+
2541+
// span-start triangle, on the column
2542+
const spanStartColumn = axis === 'column' && markerType === 'span-start'
2543+
if (spanStartColumn) {
2544+
return 0
2545+
}
2546+
// any starting marker, on the column
2547+
const startColumn = bound === 'start' && axis === 'column'
2548+
if (startColumn) {
2549+
return 5
2550+
}
2551+
2552+
// span-start starting triangle, on the row
2553+
const spanStartRow = axis === 'row' && markerType === 'span-start'
2554+
if (spanStartRow) {
2555+
return 0
2556+
}
2557+
// any other starting marker, on the row
2558+
const startRow = bound === 'start' && axis === 'row'
2559+
if (startRow) {
2560+
return 4
2561+
}
2562+
2563+
return 0
2564+
}

editor/src/utils/feature-switches.ts

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export type FeatureName =
2424
| 'Tailwind'
2525
| 'Import Wizard'
2626
| 'Show Debug Features'
27+
| 'Grid Ruler Markers'
2728

2829
export const AllFeatureNames: FeatureName[] = [
2930
// 'Dragging Reparents By Default', // Removing this option so that we can experiment on this later
@@ -48,6 +49,7 @@ export const AllFeatureNames: FeatureName[] = [
4849
'Canvas Fast Selection Hack',
4950
'Tailwind',
5051
'Import Wizard',
52+
'Grid Ruler Markers',
5153
]
5254

5355
let FeatureSwitches: { [feature in FeatureName]: boolean } = {
@@ -72,6 +74,7 @@ let FeatureSwitches: { [feature in FeatureName]: boolean } = {
7274
'Canvas Fast Selection Hack': true,
7375
'Import Wizard': !IS_TEST_ENVIRONMENT,
7476
'Show Debug Features': false,
77+
'Grid Ruler Markers': false,
7578
}
7679

7780
export const STEGANOGRAPHY_ENABLED = false

0 commit comments

Comments
 (0)