diff --git a/packages/trees/src/model/publicTypes.ts b/packages/trees/src/model/publicTypes.ts index e43cd6fba..33d6029ad 100644 --- a/packages/trees/src/model/publicTypes.ts +++ b/packages/trees/src/model/publicTypes.ts @@ -142,17 +142,13 @@ export interface FileTreeDirectoryHandle extends FileTreeItemHandleBase { export interface FileTreeFileHandle extends FileTreeItemHandleBase { isDirectory(): false; } - export type FileTreeItemHandle = FileTreeDirectoryHandle | FileTreeFileHandle; - export interface FileTreeRenderOptions { - // Hint how many rows should fit in the first render before the browser can - // measure the real scroll viewport. Fractional values are allowed when the - // desired first-render budget is not an exact multiple of itemHeight. initialVisibleRowCount?: number; itemHeight?: number; overscan?: number; stickyFolders?: boolean; + flattenedSegmentsTruncation?: 'per-segment' | 'end'; } export type FileTreeScrollOffset = 'top' | 'center' | 'nearest'; diff --git a/packages/trees/src/render/FileTree.ts b/packages/trees/src/render/FileTree.ts index 3948e3c77..9080a8b7e 100644 --- a/packages/trees/src/render/FileTree.ts +++ b/packages/trees/src/render/FileTree.ts @@ -190,7 +190,11 @@ export class FileTree readonly #density: FileTreeDensityPreset; readonly #viewOptions: Pick< FileTreeOptions, - 'initialVisibleRowCount' | 'itemHeight' | 'overscan' | 'stickyFolders' + | 'initialVisibleRowCount' + | 'itemHeight' + | 'overscan' + | 'stickyFolders' + | 'flattenedSegmentsTruncation' >; #fileTreeContainer: HTMLElement | undefined; #gitStatusState: FileTreeGitStatusState | null; @@ -230,6 +234,7 @@ export class FileTree stickyFolders, unsafeCSS, initialVisibleRowCount, + flattenedSegmentsTruncation, ...controllerOptions } = options; this.#composition = composition; @@ -248,6 +253,7 @@ export class FileTree itemHeight: this.#density.itemHeight, overscan, stickyFolders, + flattenedSegmentsTruncation, initialVisibleRowCount, }; this.#controller = new FileTreeController({ @@ -561,6 +567,7 @@ export class FileTree itemHeight?: number; overscan?: number; stickyFolders?: boolean; + flattenedSegmentsTruncation?: 'per-segment' | 'end'; } { return { initialViewportHeight: resolveInitialViewportHeight({ @@ -570,6 +577,8 @@ export class FileTree itemHeight: this.#viewOptions.itemHeight, overscan: this.#viewOptions.overscan, stickyFolders: this.#viewOptions.stickyFolders, + flattenedSegmentsTruncation: + this.#viewOptions.flattenedSegmentsTruncation, }; } @@ -877,6 +886,7 @@ export function preloadFileTree(options: FileTreeOptions): FileTreeSsrPayload { searchFakeFocus, stickyFolders, unsafeCSS, + flattenedSegmentsTruncation, initialVisibleRowCount, ...controllerOptions } = options; @@ -925,6 +935,7 @@ export function preloadFileTree(options: FileTreeOptions): FileTreeSsrPayload { searchEnabled: search === true, searchFakeFocus: searchFakeFocus === true, stickyFolders, + flattenedSegmentsTruncation, initialViewportHeight, }) ); diff --git a/packages/trees/src/render/FileTreeView.tsx b/packages/trees/src/render/FileTreeView.tsx index 0d3cbab06..ecf68afc7 100644 --- a/packages/trees/src/render/FileTreeView.tsx +++ b/packages/trees/src/render/FileTreeView.tsx @@ -78,7 +78,8 @@ import { function formatFlattenedSegments( row: FileTreeVisibleRow, renameInput: JSX.Element | null = null, - dragTargetFlattenedSegmentPath: string | null = null + dragTargetFlattenedSegmentPath: string | null = null, + flattenedSegmentsTruncation: 'per-segment' | 'end' = 'per-segment' ): JSX.Element | string { 'use no memo'; const segments = row.flattenedSegments; @@ -86,7 +87,13 @@ function formatFlattenedSegments( return renameInput ?? row.name; } - return ( + return flattenedSegmentsTruncation === 'end' ? ( + (renameInput ?? ( + + {segments.map((segment) => segment.name).join(' / ')} + + )) + ) : ( {segments.map((segment, index) => { const isLast = index === segments.length - 1; @@ -848,6 +855,7 @@ function renderFileTreeRowContent( gitLaneActive = false, renameInput = null, showDecorativeActionAffordance = false, + flattenedSegmentsTruncation = 'per-segment', }: { actionLaneEnabled?: boolean; customDecoration?: FileTreeRowDecoration | null; @@ -857,6 +865,7 @@ function renderFileTreeRowContent( gitLaneActive?: boolean; renameInput?: JSX.Element | null; showDecorativeActionAffordance?: boolean; + flattenedSegmentsTruncation?: 'per-segment' | 'end'; } = {} ): JSX.Element { const targetPath = getFileTreeRowPath(row); @@ -886,7 +895,8 @@ function renderFileTreeRowContent( ? formatFlattenedSegments( row, renameInput, - dragTargetFlattenedSegmentPath + dragTargetFlattenedSegmentPath, + flattenedSegmentsTruncation ) : (renameInput ?? ( @@ -929,6 +939,7 @@ type FileTreeRenderedRowMode = FileTreeRowClickMode; // with a different `registerButton` target. type FileTreeRenderRowFrame = { controller: FileTreeController; + flattenedSegmentsTruncation: 'per-segment' | 'end'; renameView: ReturnType; visualFocusPath: string | null; contextHoverPath: string | null; @@ -999,6 +1010,7 @@ function renderStyledRow( ): JSX.Element { const { controller, + flattenedSegmentsTruncation, renameView, visualFocusPath, contextHoverPath, @@ -1077,6 +1089,7 @@ function renderStyledRow( customDecoration, decorationLaneEnabled, dragTargetFlattenedSegmentPath: dragTarget?.flattenedSegmentPath ?? null, + flattenedSegmentsTruncation, gitDecoration, gitLaneActive, renameInput, @@ -1239,6 +1252,7 @@ export function FileTreeView({ searchFakeFocus = false, slotHost, stickyFolders = false, + flattenedSegmentsTruncation = 'per-segment', initialViewportHeight = FILE_TREE_DEFAULT_VIEWPORT_HEIGHT, }: FileTreeViewProps): JSX.Element { 'use no memo'; @@ -3636,6 +3650,7 @@ export function FileTreeView({ contextMenuRightClickEnabled, contextMenuTriggerMode, controller, + flattenedSegmentsTruncation, directoriesWithGitChanges, dragAndDropEnabled, draggedPathSet,