Skip to content

Commit

Permalink
data-can-condense initial support (#5757)
Browse files Browse the repository at this point in the history
  • Loading branch information
ruggi authored May 27, 2024
1 parent a0b9cb6 commit 281b595
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 5 deletions.
11 changes: 11 additions & 0 deletions editor/src/components/context-menu-items.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import * as EditorActions from './editor/actions/action-creators'
import {
copySelectionToClipboard,
duplicateSelected,
toggleDataCanCondense,
toggleHidden,
} from './editor/actions/action-creators'
import {
Expand Down Expand Up @@ -329,6 +330,16 @@ export const toggleVisibility: ContextMenuItem<CanvasData> = {
},
}

export const toggleCanCondense: ContextMenuItem<CanvasData> = {
name: 'Toggle Can Condense',
enabled: (data) => {
return data.selectedViews.length > 0
},
action: (data, dispatch?: EditorDispatch) => {
requireDispatch(dispatch)([toggleDataCanCondense(data.selectedViews)], 'everyone')
},
}

export const lineSeparator: ContextMenuItem<unknown> = {
name: RU.create('div', { key: 'separator', className: 'react-contexify__separator' }, ''),
enabled: false,
Expand Down
6 changes: 6 additions & 0 deletions editor/src/components/editor/action-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,11 @@ export type ToggleHidden = {
targets: Array<ElementPath>
}

export type ToggleDataCanCondense = {
action: 'TOGGLE_DATA_CAN_CONDENSE'
targets: Array<ElementPath>
}

export type UnsetProperty = {
action: 'UNSET_PROPERTY'
element: ElementPath
Expand Down Expand Up @@ -1217,6 +1222,7 @@ export type EditorAction =
| Undo
| Redo
| ToggleHidden
| ToggleDataCanCondense
| RenameComponent
| SetPanelVisibility
| ToggleFocusedOmniboxTab
Expand Down
8 changes: 8 additions & 0 deletions editor/src/components/editor/actions/action-creators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ import type {
ReplaceElementInScope,
ElementReplacementPath,
ReplaceJSXElement,
ToggleDataCanCondense,
} from '../action-types'
import type { InsertionSubjectWrapper, Mode } from '../editor-modes'
import { EditorModes, insertionSubject } from '../editor-modes'
Expand Down Expand Up @@ -365,6 +366,13 @@ export function toggleHidden(targets: Array<ElementPath> = []): ToggleHidden {
}
}

export function toggleDataCanCondense(targets: Array<ElementPath>): ToggleDataCanCondense {
return {
action: 'TOGGLE_DATA_CAN_CONDENSE',
targets: targets,
}
}

export function transientActions(
actions: Array<EditorAction>,
elementsToRerender: Array<ElementPath> | null = null,
Expand Down
1 change: 1 addition & 0 deletions editor/src/components/editor/actions/action-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ export function isTransientAction(action: EditorAction): boolean {
case 'ALIGN_SELECTED_VIEWS':
case 'DISTRIBUTE_SELECTED_VIEWS':
case 'TOGGLE_HIDDEN':
case 'TOGGLE_DATA_CAN_CONDENSE':
case 'UPDATE_FILE_PATH':
case 'UPDATE_REMIX_ROUTE':
case 'ADD_FOLDER':
Expand Down
29 changes: 29 additions & 0 deletions editor/src/components/editor/actions/actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ import type {
ReplaceMappedElement,
ReplaceElementInScope,
ReplaceJSXElement,
ToggleDataCanCondense,
} from '../action-types'
import { isLoggedIn } from '../action-types'
import type { Mode } from '../editor-modes'
Expand Down Expand Up @@ -610,6 +611,11 @@ import { fixTopLevelElementsUIDs } from '../../../core/workers/parser-printer/ui
import { nextSelectedTab } from '../../navigator/left-pane/left-pane-utils'
import { getRemixRootDir } from '../store/remix-derived-data'
import { isReplaceKeepChildrenAndStyleTarget } from '../../navigator/navigator-item/component-picker-context-menu'
import {
canCondenseJSXElementChild,
dataCanCondenseProp,
isDataCanCondenseProp,
} from '../../../utils/can-condense'

export const MIN_CODE_PANE_REOPEN_WIDTH = 100

Expand Down Expand Up @@ -2242,6 +2248,29 @@ export const UPDATE_FNS = {
}
}, editor)
},
TOGGLE_DATA_CAN_CONDENSE: (action: ToggleDataCanCondense, editor: EditorModel): EditorModel => {
let working = { ...editor }
for (const path of action.targets) {
working = modifyOpenJsxElementAtPath(
path,
(element) => {
const canCondense = canCondenseJSXElementChild(element)
// remove any data-can-condense props
const props = element.props.filter((prop) => !isDataCanCondenseProp(prop))
// if it needs to switch to true, append the new prop
if (!canCondense) {
props.push(dataCanCondenseProp(true))
}
return {
...element,
props: props,
}
},
working,
)
}
return working
},
RENAME_COMPONENT: (action: RenameComponent, editor: EditorModel): EditorModel => {
const { name } = action
const target = action.target
Expand Down
2 changes: 2 additions & 0 deletions editor/src/components/editor/store/editor-update.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ export function runSimpleLocalEditorAction(
return UPDATE_FNS.SWITCH_EDITOR_MODE(action, state, userState)
case 'TOGGLE_HIDDEN':
return UPDATE_FNS.TOGGLE_HIDDEN(action, state)
case 'TOGGLE_DATA_CAN_CONDENSE':
return UPDATE_FNS.TOGGLE_DATA_CAN_CONDENSE(action, state)
case 'RENAME_COMPONENT':
return UPDATE_FNS.RENAME_COMPONENT(action, state)
case 'INSERT_JSX_ELEMENT':
Expand Down
2 changes: 2 additions & 0 deletions editor/src/components/element-context-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
pasteToReplace,
pasteHere,
replace,
toggleCanCondense,
} from './context-menu-items'
import { ContextMenu } from './context-menu-wrapper'
import { useRefEditorState, useEditorState, Substores } from './editor/store/store-hook'
Expand Down Expand Up @@ -90,6 +91,7 @@ const ElementContextMenuItems: Array<ContextMenuItem<CanvasData>> = [
sendToBack,
lineSeparator,
toggleVisibility,
toggleCanCondense,
lineSeparator,
toggleBackgroundLayersItem,
toggleBorderItem,
Expand Down
23 changes: 18 additions & 5 deletions editor/src/components/navigator/navigator-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import { getUtopiaID } from '../../core/shared/uid-utils'
import { create } from 'tar'
import { emptySet } from '../../core/shared/set-utils'
import { objectMap } from '../../core/shared/object-utils'
import { dataCanCondenseFromMetadata } from '../../utils/can-condense'

export function baseNavigatorDepth(path: ElementPath): number {
// The storyboard means that this starts at -1,
Expand Down Expand Up @@ -581,7 +582,10 @@ function isCondensableLeafEntry(entry: NavigatorTree): boolean {
)
}

function condenseNavigatorTree(navigatorTree: Array<NavigatorTree>): Array<NavigatorTree> {
function condenseNavigatorTree(
metadata: ElementInstanceMetadataMap,
navigatorTree: Array<NavigatorTree>,
): Array<NavigatorTree> {
if (!isFeatureEnabled('Condensed Navigator Entries')) {
return navigatorTree
}
Expand All @@ -601,7 +605,11 @@ function condenseNavigatorTree(navigatorTree: Array<NavigatorTree>): Array<Navig
}

// if the entry only has a single child, we can condense it
if (entry.type === 'regular-entry' && entry.children.length === 1) {
if (
entry.type === 'regular-entry' &&
entry.children.length === 1 &&
dataCanCondenseFromMetadata(metadata, entry.navigatorEntry.elementPath)
) {
return {
type: 'condensed-trunk',
navigatorEntry: entry.navigatorEntry,
Expand Down Expand Up @@ -680,10 +688,11 @@ function flattenCondensedTrunk(entry: CondensedTrunkNavigatorTree): {
}

function getNavigatorRowsForTree(
metadata: ElementInstanceMetadataMap,
navigatorTree: Array<NavigatorTree>,
filterVisible: 'all-navigator-targets' | 'visible-navigator-targets',
): Array<NavigatorRow> {
const condensedTree = condenseNavigatorTree(navigatorTree)
const condensedTree = condenseNavigatorTree(metadata, navigatorTree)

function walkTree(entry: NavigatorTree, indentation: number): Array<NavigatorRow> {
function walkIfSubtreeVisible(e: NavigatorTree, i: number): Array<NavigatorRow> {
Expand Down Expand Up @@ -810,10 +819,14 @@ export function getNavigatorTargets(
projectContents,
)

const navigatorRows = getNavigatorRowsForTree(navigatorTrees, 'all-navigator-targets')
const navigatorRows = getNavigatorRowsForTree(metadata, navigatorTrees, 'all-navigator-targets')
const navigatorTargets = navigatorRows.flatMap(getEntriesForRow)

const visibleNavigatorRows = getNavigatorRowsForTree(navigatorTrees, 'visible-navigator-targets')
const visibleNavigatorRows = getNavigatorRowsForTree(
metadata,
navigatorTrees,
'visible-navigator-targets',
)
const filteredVisibleNavigatorRows = visibleNavigatorRows
const visibleNavigatorTargets = filteredVisibleNavigatorRows.flatMap(getEntriesForRow)

Expand Down
53 changes: 53 additions & 0 deletions editor/src/utils/can-condense.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { emptyComments, type ElementPath } from 'utopia-shared/src/types'
import { MetadataUtils } from '../core/model/element-metadata-utils'
import { isRight } from '../core/shared/either'
import type {
JSXAttributesEntry,
JSXAttributesPart,
JSXElementChild,
} from '../core/shared/element-template'
import {
isJSXAttributeValue,
isJSXAttributesEntry,
isJSXElement,
jsExpressionValue,
jsxAttributesEntry,
type ElementInstanceMetadataMap,
} from '../core/shared/element-template'

export const DataCanCondense = 'data-can-condense'

export function dataCanCondenseFromMetadata(
metadata: ElementInstanceMetadataMap,
path: ElementPath,
): boolean {
const target = MetadataUtils.findElementByElementPath(metadata, path)
return (
target != null &&
isRight(target.element) &&
isJSXElement(target.element.value) &&
canCondenseJSXElementChild(target.element.value)
)
}

export function canCondenseJSXElementChild(element: JSXElementChild) {
return (
isJSXElement(element) &&
element.props.some(
(prop) =>
isDataCanCondenseProp(prop) && isJSXAttributeValue(prop.value) && prop.value.value === true,
)
)
}

interface DataCanCondenseProp extends JSXAttributesEntry {
key: typeof DataCanCondense
}

export function isDataCanCondenseProp(prop: JSXAttributesPart): prop is DataCanCondenseProp {
return isJSXAttributesEntry(prop) && (prop as DataCanCondenseProp).key === 'data-can-condense'
}

export function dataCanCondenseProp(value: boolean): JSXAttributesEntry {
return jsxAttributesEntry(DataCanCondense, jsExpressionValue(value, emptyComments), emptyComments)
}

0 comments on commit 281b595

Please sign in to comment.