From 80561fcf8cfbf02922db32a74223eb6304338fbc Mon Sep 17 00:00:00 2001 From: Federico Ruggi <1081051+ruggi@users.noreply.github.com> Date: Thu, 21 Nov 2024 12:46:08 +0100 Subject: [PATCH] Grid ruler marker labels (#6664) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **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` | Screenshot 2024-11-20 at 15 30 23 | Screenshot 2024-11-20 at 15 30 20 | | `gridColumn: 3`, `gridRow: 1` | Screenshot
2024-11-20 at 15 30 36 | Screenshot 2024-11-20 at 15 30 35 | | `gridColumn: span 2 / 4`, `gridRow: span 3` | Screenshot 2024-11-20 at 15 35 51 | Screenshot 2024-11-20 at 15 35 50 | Screenshot 2024-11-20 at 15 31 22 Screenshot 2024-11-20 at 15 31 27 Screenshot 2024-11-20 at 15 31 35 **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 --- .../canvas/controls/grid-controls.tsx | 186 ++++++++++++++---- editor/src/utils/feature-switches.ts | 3 + 2 files changed, 150 insertions(+), 39 deletions(-) diff --git a/editor/src/components/canvas/controls/grid-controls.tsx b/editor/src/components/canvas/controls/grid-controls.tsx index aa6503487aee..c7d500b72dfc 100644 --- a/editor/src/components/canvas/controls/grid-controls.tsx +++ b/editor/src/components/canvas/controls/grid-controls.tsx @@ -76,7 +76,6 @@ import { getGlobalFrameOfGridCellFromMetadata, getGridRelatedIndexes, getGridElementPinState, - gridPositionToValue, printPin, getGridIdentifierContainerOrComponentPath, gridIdentifierToString, @@ -120,6 +119,7 @@ import { styleStringInArray } from '../../../utils/common-constants' import { gridContainerIdentifier, type GridIdentifier } from '../../editor/store/editor-state' import type { RulerMarkerType } from './grid-controls-ruler-markers' import { rulerMarkerIcons } from './grid-controls-ruler-markers' +import { isFeatureEnabled } from '../../../utils/feature-switches' const CELL_ANIMATION_DURATION = 0.15 // seconds @@ -1213,9 +1213,12 @@ export const GridControlsComponent = ({ targets }: GridControlsProps) => { ) })} {/* Ruler markers */} - {selectedGridItems.map((path) => { - return - })} + {when( + isFeatureEnabled('Grid Ruler Markers'), + selectedGridItems.map((path) => { + return + }), + )} @@ -2186,6 +2189,7 @@ function useSelectedGridItems(): ElementPath[] { const rulerMarkerIconSize = 12 // px type RulerMarkerData = { + parentGrid: GridContainerProperties columnStart: RulerMarkerPositionData columnEnd: RulerMarkerPositionData rowStart: RulerMarkerPositionData @@ -2196,7 +2200,6 @@ type RulerMarkerPositionData = { top: number left: number position: GridPositionOrSpan | null - counterpart: GridPositionOrSpan | null bound: 'start' | 'end' } @@ -2217,6 +2220,8 @@ const RulerMarkers = React.memo((props: { path: ElementPath }) => { return null } + const parentGrid = elementMetadata.specialSizeMeasurements.parentContainerGridProperties + const originalGrid = findOriginalGrid(store.editor.jsxMetadata, EP.parentPath(props.path)) if (originalGrid == null) { return null @@ -2269,32 +2274,29 @@ const RulerMarkers = React.memo((props: { path: ElementPath }) => { ) return { + parentGrid: parentGrid, columnStart: { top: gridRect.y, left: left, position: elementGridProperties.gridColumnStart, - counterpart: elementGridProperties.gridColumnEnd, bound: 'start', }, columnEnd: { top: gridRect.y, left: left + width, position: elementGridProperties.gridColumnEnd, - counterpart: elementGridProperties.gridColumnStart, bound: 'end', }, rowStart: { top: top, left: gridRect.x, position: elementGridProperties.gridRowStart, - counterpart: elementGridProperties.gridRowEnd, bound: 'start', }, rowEnd: { top: top + height, left: gridRect.x, position: elementGridProperties.gridRowEnd, - counterpart: elementGridProperties.gridRowStart, bound: 'end', }, } @@ -2308,22 +2310,37 @@ const RulerMarkers = React.memo((props: { path: ElementPath }) => { return ( - - - - + + + + ) }) RulerMarkers.displayName = 'RulerMarkers' const RulerMarkerIndicator = React.memo( - (props: { marker: RulerMarkerPositionData; axis: 'row' | 'column' }) => { + (props: { + parentGrid: GridContainerProperties + marker: RulerMarkerPositionData + axis: 'row' | 'column' + }) => { const colorTheme = useColorTheme() const markerType = getRulerMarkerType({ position: props.marker.position, - counterpart: props.marker.counterpart, bound: props.marker.bound, }) const markerIcon = rulerMarkerIcons[markerType][props.axis] @@ -2334,21 +2351,24 @@ const RulerMarkerIndicator = React.memo( 'RulerMarkerIndicator canvasScale', ) - function skewMarkerPosition(axis: 'column' | 'row') { - if (props.axis === axis) { - return rulerMarkerIconSize - } else if (markerType === 'span-end') { - return rulerMarkerIconSize - 1 // adjust span end position so it just touches the grid line - } else { - return rulerMarkerIconSize / 2 - } - } - const scaledTop = props.marker.top * canvasScale - const top = scaledTop - skewMarkerPosition('column') + const top = + scaledTop - + skewMarkerPosition(props.axis === 'column', props.axis, props.marker.bound, markerType) const scaledLeft = props.marker.left * canvasScale - const left = scaledLeft - skewMarkerPosition('row') + const left = + scaledLeft - + skewMarkerPosition(props.axis === 'row', props.axis, props.marker.bound, markerType) + + const labelText = React.useMemo(() => { + if (props.marker.position == null) { + return null + } + return printPin(props.parentGrid, props.marker.position, props.axis) + }, [props.marker, props.parentGrid, props.axis]) + + const labelClass = 'ruler-marker-label' return (
.${labelClass}`]: { + visibility: 'hidden', + }, + ':hover': { + [`> .${labelClass}`]: { + visibility: 'visible', + }, + }, + }} > {markerIcon} + {when( + labelText != null, +
+ {labelText} +
, + )}
) }, @@ -2374,16 +2424,11 @@ RulerMarkerIndicator.displayName = 'RulerMarkerIndicator' function getRulerMarkerType(props: { position: GridPositionOrSpan | null - counterpart: GridPositionOrSpan | null bound: 'start' | 'end' }): RulerMarkerType { - const isAuto = - isAutoGridPin(props.position) || - (props.bound === 'start' && isGridSpan(props.position) && isAutoGridPin(props.counterpart)) - const isSpanStart = - props.bound === 'start' && isGridSpan(props.position) && isGridSpan(props.counterpart) - const isSpanEnd = - props.bound === 'end' && (isGridSpan(props.position) || isGridSpan(props.counterpart)) + const isAuto = isAutoGridPin(props.position) + const isSpanStart = props.bound === 'start' && isGridSpan(props.position) + const isSpanEnd = props.bound === 'end' && isGridSpan(props.position) if (isSpanStart) { return 'span-start' @@ -2454,3 +2499,66 @@ function getCellCanvasHeightFromBounds( return acc + curr.height + padding }, currentColumn.height) } + +// This function returns the amount of pixels used to adjust the position of +// individual ruler markers, which need specific skews based on their shape. +function skewMarkerPosition( + isOnTheSameAxis: boolean, + axis: 'column' | 'row', + bound: 'start' | 'end', + markerType: RulerMarkerType, +): number { + if (isOnTheSameAxis) { + return rulerMarkerIconSize + } + + // span-end triangle, on the column + const spanEndColumn = axis === 'column' && markerType === 'span-end' + if (spanEndColumn) { + return 10 + } + const pinnedEndColumn = axis === 'column' && markerType === 'pinned' + if (pinnedEndColumn) { + return 5 + } + // any other ending marker, on the column + const endColumn = bound === 'end' && axis === 'column' + if (endColumn) { + return 2 + } + + // span-end triangle, on the row + const spanEndRow = axis === 'row' && markerType === 'span-end' + if (spanEndRow) { + return 9 + } + // any other ending marker, on the row + const endRow = bound === 'end' && axis === 'row' + if (endRow) { + return 6 + } + + // span-start triangle, on the column + const spanStartColumn = axis === 'column' && markerType === 'span-start' + if (spanStartColumn) { + return 0 + } + // any starting marker, on the column + const startColumn = bound === 'start' && axis === 'column' + if (startColumn) { + return 5 + } + + // span-start starting triangle, on the row + const spanStartRow = axis === 'row' && markerType === 'span-start' + if (spanStartRow) { + return 0 + } + // any other starting marker, on the row + const startRow = bound === 'start' && axis === 'row' + if (startRow) { + return 4 + } + + return 0 +} diff --git a/editor/src/utils/feature-switches.ts b/editor/src/utils/feature-switches.ts index fab803319515..4b3344b20189 100644 --- a/editor/src/utils/feature-switches.ts +++ b/editor/src/utils/feature-switches.ts @@ -24,6 +24,7 @@ export type FeatureName = | 'Tailwind' | 'Import Wizard' | 'Show Debug Features' + | 'Grid Ruler Markers' export const AllFeatureNames: FeatureName[] = [ // 'Dragging Reparents By Default', // Removing this option so that we can experiment on this later @@ -48,6 +49,7 @@ export const AllFeatureNames: FeatureName[] = [ 'Canvas Fast Selection Hack', 'Tailwind', 'Import Wizard', + 'Grid Ruler Markers', ] let FeatureSwitches: { [feature in FeatureName]: boolean } = { @@ -72,6 +74,7 @@ let FeatureSwitches: { [feature in FeatureName]: boolean } = { 'Canvas Fast Selection Hack': true, 'Import Wizard': !IS_TEST_ENVIRONMENT, 'Show Debug Features': false, + 'Grid Ruler Markers': false, } export const STEGANOGRAPHY_ENABLED = false