Skip to content

Commit

Permalink
NavigatorTree (#5716)
Browse files Browse the repository at this point in the history
* introducing the NavigatorRow

* progress

* Update navigator-row.tsx

* navigatorRows boilerplate, not working

* stash progress

* WIP stash

* fixing jest tests

* fixing for data elements that don't show up in the dom metadata

* removing slot-entry as it seems like it's the same as a leaf-entry

* fixing hidden subtrees

* oopsie fix Fragments!

* fixing the missing conditional True False labels

* Update navigator-utils.ts

* fix render prop labels

* fixing the render prop children entry

* introducing condensed trees

* re-unifying code

* the most primitive way to display these poor condensed rows

* disable condensing leafs as they look scary bad

* indentation!

* Update navigator-utils.ts

* fixing the horizontal paddings

* hopefully fixing a few navigator snapshots

* typo

* fixing up render prop value navigator entries

* flip around the navigator tree building, walkRegularNavigatorEntry becomes the "catch all" case

* oh I realize there's a separate category of element hidden vs subtree hidden

* Update conditional-section.tsx

* bye bye walkAndAddKeys

* Update navigator-utils.ts

* Update navigator-utils.ts

* switchifying

---------

Co-authored-by: Federico Ruggi <[email protected]>
  • Loading branch information
balazsbajorics and ruggi authored May 23, 2024
1 parent 403e73d commit 92c4be1
Show file tree
Hide file tree
Showing 12 changed files with 882 additions and 309 deletions.
1 change: 1 addition & 0 deletions editor/src/components/editor/actions/actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,7 @@ export function restoreDerivedState(history: StateHistory): DerivedState {
const poppedDerived = history.current.derived

return {
navigatorRows: poppedDerived.navigatorRows,
navigatorTargets: poppedDerived.navigatorTargets,
visibleNavigatorTargets: poppedDerived.visibleNavigatorTargets,
autoFocusedPaths: poppedDerived.autoFocusedPaths,
Expand Down
20 changes: 19 additions & 1 deletion editor/src/components/editor/store/editor-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ import { emptyImports } from '../../../core/workers/common/project-file-utils'
import type { CommentFilterMode } from '../../inspector/sections/comment-section'
import type { Collaborator } from '../../../core/shared/multiplayer'
import type { OnlineState } from '../online-status'
import type { NavigatorRow } from '../../navigator/navigator-row'

const ObjectPathImmutable: any = OPI

Expand Down Expand Up @@ -2364,6 +2365,17 @@ export function navigatorEntriesEqual(
}
}

export function navigatorRowToKey(row: NavigatorRow): string {
switch (row.type) {
case 'regular-row':
return navigatorEntryToKey(row.entry)
case 'condensed-row':
return `condensed-${row.entries.map(navigatorEntryToKey).join('-')}`
default:
assertNever(row)
}
}

export function navigatorEntryToKey(entry: NavigatorEntry): string {
switch (entry.type) {
case 'REGULAR':
Expand Down Expand Up @@ -2466,6 +2478,7 @@ export function isSlotNavigatorEntry(entry: NavigatorEntry): entry is SlotNaviga
}

export interface DerivedState {
navigatorRows: Array<NavigatorRow>
navigatorTargets: Array<NavigatorEntry>
visibleNavigatorTargets: Array<NavigatorEntry>
autoFocusedPaths: Array<ElementPath>
Expand All @@ -2479,6 +2492,7 @@ export interface DerivedState {

function emptyDerivedState(editor: EditorState): DerivedState {
return {
navigatorRows: [],
navigatorTargets: [],
visibleNavigatorTargets: [],
autoFocusedPaths: [],
Expand Down Expand Up @@ -2840,6 +2854,7 @@ function getElementWarningsInner(
const getElementWarnings = memoize(getElementWarningsInner, { maxSize: 1 })

type CacheableDerivedState = {
navigatorRows: Array<NavigatorRow>
navigatorTargets: Array<NavigatorEntry>
visibleNavigatorTargets: Array<NavigatorEntry>
elementWarnings: { [key: string]: ElementWarnings }
Expand All @@ -2855,7 +2870,7 @@ function deriveCacheableStateInner(
hiddenInNavigator: ElementPath[],
propertyControlsInfo: PropertyControlsInfo,
): CacheableDerivedState {
const { navigatorTargets, visibleNavigatorTargets } = getNavigatorTargets(
const { navigatorRows, navigatorTargets, visibleNavigatorTargets } = getNavigatorTargets(
jsxMetadata,
elementPathTree,
collapsedViews,
Expand Down Expand Up @@ -2884,6 +2899,7 @@ function deriveCacheableStateInner(
)

return {
navigatorRows: navigatorRows,
navigatorTargets: navigatorTargets,
visibleNavigatorTargets: visibleNavigatorTargets,
elementWarnings: warnings,
Expand All @@ -2906,6 +2922,7 @@ export function deriveState(
cacheKey === 'patched' ? patchedDeriveCacheableState : unpatchedDeriveCacheableState

const {
navigatorRows,
navigatorTargets,
visibleNavigatorTargets,
elementWarnings: warnings,
Expand All @@ -2929,6 +2946,7 @@ export function deriveState(
const filePathMappings = getFilePathMappings(editor.projectContents)

const derived: DerivedState = {
navigatorRows: navigatorRows,
navigatorTargets: navigatorTargets,
visibleNavigatorTargets: visibleNavigatorTargets,
autoFocusedPaths: autoFocusedPaths,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,13 @@ import {
textFileContents,
unparsed,
} from '../../../core/shared/project-file-types'
import { regularNavigatorRow } from '../../navigator/navigator-row'

describe('DerivedStateKeepDeepEquality', () => {
const oldValue: DerivedState = {
navigatorRows: [
regularNavigatorRow(regularNavigatorEntry(EP.elementPath([['scene'], ['aaa', 'bbb']])), 1),
],
navigatorTargets: [regularNavigatorEntry(EP.elementPath([['scene'], ['aaa', 'bbb']]))],
visibleNavigatorTargets: [regularNavigatorEntry(EP.elementPath([['scene'], ['aaa', 'bbb']]))],
autoFocusedPaths: [EP.elementPath([['scene'], ['aaa']])],
Expand Down Expand Up @@ -98,6 +102,9 @@ describe('DerivedStateKeepDeepEquality', () => {
filePathMappings: [],
}
const newSameValue: DerivedState = {
navigatorRows: [
regularNavigatorRow(regularNavigatorEntry(EP.elementPath([['scene'], ['aaa', 'bbb']])), 1),
],
navigatorTargets: [regularNavigatorEntry(EP.elementPath([['scene'], ['aaa', 'bbb']]))],
visibleNavigatorTargets: [regularNavigatorEntry(EP.elementPath([['scene'], ['aaa', 'bbb']]))],
autoFocusedPaths: [EP.elementPath([['scene'], ['aaa']])],
Expand Down Expand Up @@ -131,6 +138,9 @@ describe('DerivedStateKeepDeepEquality', () => {
filePathMappings: [],
}
const newDifferentValue: DerivedState = {
navigatorRows: [
regularNavigatorRow(regularNavigatorEntry(EP.elementPath([['scene'], ['aaa', 'ddd']])), 2),
],
navigatorTargets: [regularNavigatorEntry(EP.elementPath([['scene'], ['aaa', 'ddd']]))],
visibleNavigatorTargets: [regularNavigatorEntry(EP.elementPath([['scene'], ['aaa', 'bbb']]))],
autoFocusedPaths: [EP.elementPath([['scene'], ['aaa']])],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,8 @@ import type {
} from '../../custom-code/internal-property-controls'
import type { OnlineState } from '../online-status'
import { onlineState } from '../online-status'
import type { NavigatorRow } from '../../navigator/navigator-row'
import { condensedNavigatorRow, regularNavigatorRow } from '../../navigator/navigator-row'

export function ElementPropertyPathKeepDeepEquality(): KeepDeepEqualityCall<ElementPropertyPath> {
return combine2EqualityCalls(
Expand Down Expand Up @@ -785,6 +787,41 @@ export const SlotNavigatorEntryKeepDeepEquality: KeepDeepEqualityCall<SlotNaviga
(elementPath, prop) => ({ type: 'SLOT', elementPath: elementPath, prop: prop }),
)

export const NavigatorRowKeepDeepEquality: KeepDeepEqualityCall<NavigatorRow> = (
oldValue,
newValue,
) => {
switch (oldValue.type) {
case 'regular-row':
if (oldValue.type === newValue.type) {
return combine2EqualityCalls(
(row) => row.entry,
NavigatorEntryKeepDeepEquality,
(row) => row.indentation,
createCallWithTripleEquals<number>(),
regularNavigatorRow,
)(oldValue, newValue)
}
break
case 'condensed-row':
if (oldValue.type === newValue.type) {
return combine3EqualityCalls(
(row) => row.entries,
arrayDeepEquality(NavigatorEntryKeepDeepEquality),
(row) => row.variant,
createCallWithTripleEquals<'trunk' | 'leaf'>(),
(row) => row.indentation,
createCallWithTripleEquals<number>(),
condensedNavigatorRow,
)(oldValue, newValue)
}
break
default:
assertNever(oldValue)
}
return keepDeepEqualityResult(newValue, false)
}

export const NavigatorEntryKeepDeepEquality: KeepDeepEqualityCall<NavigatorEntry> = (
oldValue,
newValue,
Expand Down Expand Up @@ -890,7 +927,9 @@ export const NavigatorStateKeepDeepEquality: KeepDeepEqualityCall<NavigatorState
)

export function DerivedStateKeepDeepEquality(): KeepDeepEqualityCall<DerivedState> {
return combine9EqualityCalls(
return combine10EqualityCalls(
(state) => state.navigatorRows,
arrayDeepEquality(NavigatorRowKeepDeepEquality),
(state) => state.navigatorTargets,
arrayDeepEquality(NavigatorEntryKeepDeepEquality),
(state) => state.visibleNavigatorTargets,
Expand All @@ -910,6 +949,7 @@ export function DerivedStateKeepDeepEquality(): KeepDeepEqualityCall<DerivedStat
(state) => state.filePathMappings,
createCallWithShallowEquals(),
(
navigatorRows,
navigatorTargets,
visibleNavigatorTargets,
autoFocusedPaths,
Expand All @@ -921,6 +961,7 @@ export function DerivedStateKeepDeepEquality(): KeepDeepEqualityCall<DerivedStat
filePathMappings,
) => {
return {
navigatorRows: navigatorRows,
navigatorTargets: navigatorTargets,
visibleNavigatorTargets: visibleNavigatorTargets,
autoFocusedPaths: autoFocusedPaths,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ import {
import { useDispatch } from '../../../editor/store/dispatch-context'
import type { NavigatorEntry } from '../../../editor/store/editor-state'
import { Substores, useEditorState } from '../../../editor/store/store-hook'
import type { MetadataSubstate } from '../../../editor/store/store-hook-substore-types'
import type {
MetadataSubstate,
ProjectContentAndMetadataSubstate,
} from '../../../editor/store/store-hook-substore-types'
import { LayoutIcon } from '../../../navigator/navigator-item/layout-icon'
import {
getNavigatorEntryLabel,
Expand Down Expand Up @@ -74,10 +77,11 @@ type BranchNavigatorEntries = {
}

const branchNavigatorEntriesSelector = createCachedSelector(
(store: MetadataSubstate) => store.editor.jsxMetadata,
(store: MetadataSubstate) => store.editor.elementPathTree,
(_store: MetadataSubstate, paths: ElementPath[]) => paths,
(jsxMetadata, elementPathTree, paths): BranchNavigatorEntries | null => {
(store: ProjectContentAndMetadataSubstate) => store.editor.jsxMetadata,
(store: ProjectContentAndMetadataSubstate) => store.editor.elementPathTree,
(store: ProjectContentAndMetadataSubstate) => store.editor.projectContents,
(_store: ProjectContentAndMetadataSubstate, paths: ElementPath[]) => paths,
(jsxMetadata, elementPathTree, projectContents, paths): BranchNavigatorEntries | null => {
if (paths.length !== 1) {
return null
}
Expand All @@ -98,7 +102,7 @@ const branchNavigatorEntriesSelector = createCachedSelector(
[],
[],
{},
{},
projectContents,
).navigatorTargets

function getNavigatorEntry(clause: JSXElementChild): NavigatorEntry | null {
Expand Down Expand Up @@ -290,7 +294,7 @@ export const ConditionalSection = React.memo(({ paths }: { paths: ElementPath[]
}, [paths, conditionExpression, setConditionExpression, originalConditionExpression, dispatch])

const branchNavigatorEntries = useEditorState(
Substores.metadata,
Substores.projectContentsAndMetadata,
(store) => branchNavigatorEntriesSelector(store, paths),
'ConditionalSection branches',
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export const NavigatorItemDragType = 'navigator-item-drag-item' as const
export interface NavigatorItemDragAndDropWrapperPropsBase {
type: typeof NavigatorItemDragType
index: number
indentation: number
entryDepth: number
appropriateDropTargetHint: DropTargetHint | null
editorDispatch: EditorDispatch
Expand Down Expand Up @@ -871,6 +872,7 @@ export const NavigatorItemContainer = React.memo((props: NavigatorItemDragAndDro
<NavigatorItem
navigatorEntry={props.navigatorEntry}
index={props.index}
indentation={props.indentation}
getSelectedViewsInRange={props.getSelectedViewsInRange}
noOfChildren={props.noOfChildren}
label={props.label}
Expand Down Expand Up @@ -1024,6 +1026,7 @@ export const SyntheticNavigatorItemContainer = React.memo(
<NavigatorItem
navigatorEntry={navigatorEntry}
index={props.index}
indentation={props.indentation}
getSelectedViewsInRange={props.getSelectedViewsInRange}
noOfChildren={props.noOfChildren}
label={props.label}
Expand Down Expand Up @@ -1066,6 +1069,7 @@ export const RenderPropNavigatorItemContainer = React.memo(
<NavigatorItem
navigatorEntry={navigatorEntry}
index={props.index}
indentation={props.indentation}
getSelectedViewsInRange={props.getSelectedViewsInRange}
noOfChildren={props.noOfChildren}
label={props.label}
Expand Down Expand Up @@ -1107,6 +1111,7 @@ export const SlotNavigatorItemContainer = React.memo((props: SlotNavigatorItemCo
<NavigatorItem
navigatorEntry={navigatorEntry}
index={props.index}
indentation={props.indentation}
getSelectedViewsInRange={props.getSelectedViewsInRange}
noOfChildren={props.noOfChildren}
label={props.label}
Expand Down Expand Up @@ -1153,6 +1158,7 @@ export const ConditionalClauseNavigatorItemContainer = React.memo(
<NavigatorItem
navigatorEntry={props.navigatorEntry}
index={props.index}
indentation={props.indentation}
getSelectedViewsInRange={props.getSelectedViewsInRange}
noOfChildren={props.noOfChildren}
label={props.label}
Expand Down Expand Up @@ -1198,6 +1204,7 @@ export const ErrorNavigatorItemContainer = React.memo((props: ErrorNavigatorItem
<NavigatorItem
navigatorEntry={props.navigatorEntry}
index={props.index}
indentation={props.indentation}
getSelectedViewsInRange={props.getSelectedViewsInRange}
noOfChildren={props.noOfChildren}
label={props.label}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,13 @@ import {
import { navigatorDepth } from '../navigator-utils'
import { maybeConditionalExpression } from '../../../core/model/conditionals'
import { getRouteComponentNameForOutlet } from '../../canvas/remix/remix-utils'
import { CondensedNavigatorRow, isRegulaNavigatorRow, type NavigatorRow } from '../navigator-row'
import { BasePaddingUnit } from './navigator-item'

interface NavigatorItemWrapperProps {
index: number
targetComponentKey: string
navigatorEntry: NavigatorEntry
navigatorRow: NavigatorRow
getCurrentlySelectedEntries: () => Array<NavigatorEntry>
getSelectedViewsInRange: (index: number) => Array<ElementPath>
windowStyle: React.CSSProperties
Expand Down Expand Up @@ -218,8 +220,57 @@ export function getNavigatorEntryLabel(
}
}

export const NavigatorItemWrapper: React.FunctionComponent<
React.PropsWithChildren<NavigatorItemWrapperProps>
export const NavigatorItemWrapper: React.FunctionComponent<NavigatorItemWrapperProps> = React.memo(
(props) => {
if (isRegulaNavigatorRow(props.navigatorRow)) {
const navigatorEntry = props.navigatorRow.entry
return (
<SingleEntryNavigatorItemWrapper
index={props.index}
indentation={props.navigatorRow.indentation}
targetComponentKey={props.targetComponentKey}
navigatorRow={props.navigatorRow}
getCurrentlySelectedEntries={props.getCurrentlySelectedEntries}
getSelectedViewsInRange={props.getSelectedViewsInRange}
windowStyle={props.windowStyle}
navigatorEntry={navigatorEntry}
/>
)
}
return (
<div
style={{
...props.windowStyle,
left: 5 + 12 + 6 + BasePaddingUnit * props.navigatorRow.indentation,
}}
>
{props.navigatorRow.variant === 'trunk' ? (
<React.Fragment>
{props.navigatorRow.entries.map((entry) => (
<span key={EP.toString(entry.elementPath)}>{EP.toUid(entry.elementPath)} / </span>
))}
</React.Fragment>
) : (
<React.Fragment>
[
{props.navigatorRow.entries.map((entry) => (
<span key={EP.toString(entry.elementPath)}>{EP.toUid(entry.elementPath)}, </span>
))}
]
</React.Fragment>
)}
</div>
)
},
)

type SingleEntryNavigatorItemWrapperProps = NavigatorItemWrapperProps & {
indentation: number
navigatorEntry: NavigatorEntry
}

const SingleEntryNavigatorItemWrapper: React.FunctionComponent<
React.PropsWithChildren<SingleEntryNavigatorItemWrapperProps>
> = React.memo((props) => {
const isSelected = useEditorState(
Substores.selectedViews,
Expand Down Expand Up @@ -345,6 +396,7 @@ export const NavigatorItemWrapper: React.FunctionComponent<
const navigatorItemProps: NavigatorItemDragAndDropWrapperPropsBase = {
type: NavigatorItemDragType,
index: props.index,
indentation: props.indentation,
editorDispatch: dispatch,
entryDepth: entryDepth,
selected: isSelected,
Expand Down
Loading

0 comments on commit 92c4be1

Please sign in to comment.