Просмотр исходного кода

Merge pull request #32313 from overleaf/ae-tab-icon

Add icons to file tabs

GitOrigin-RevId: 7b032c694c4f81e868e5811673b465179e10bae7
Mathias Jakobsen 6 месяцев назад
Родитель
Сommit
3bf3264cfe

+ 24 - 8
services/web/frontend/js/features/ide-react/context/tabs-context.tsx

@@ -9,6 +9,10 @@ import { debugConsole } from '@/utils/debugging'
 import { disambiguatePaths } from '../util/disambiguate-paths'
 import { disambiguatePaths } from '../util/disambiguate-paths'
 import { isSplitTestEnabled } from '@/utils/splitTestUtils'
 import { isSplitTestEnabled } from '@/utils/splitTestUtils'
 import { useUserSettingsContext } from '@/shared/context/user-settings-context'
 import { useUserSettingsContext } from '@/shared/context/user-settings-context'
+import {
+  FileTreeFindResult,
+  isFileRefResult,
+} from '@/features/ide-react/types/file-tree'
 
 
 type PersistedTabInfo = { id: string; lifetime: Lifetime }
 type PersistedTabInfo = { id: string; lifetime: Lifetime }
 
 
@@ -16,7 +20,9 @@ type Lifetime = 'permanent' | 'temporary'
 
 
 export type EditorFileTab = {
 export type EditorFileTab = {
   id: string
   id: string
+  name: string
   displayPath: string
   displayPath: string
+  isLinkedFile: boolean
   lifetime: Lifetime
   lifetime: Lifetime
 }
 }
 
 
@@ -64,19 +70,29 @@ export const TabsProvider: FC<React.PropsWithChildren> = ({ children }) => {
         lifetime: tab.lifetime,
         lifetime: tab.lifetime,
         result: findInTree(fileTreeData, tab.id),
         result: findInTree(fileTreeData, tab.id),
       }))
       }))
-      .filter(x => !!x.result)
+      .filter(x => !!x.result) as {
+      lifetime: Lifetime
+      result: FileTreeFindResult
+    }[]
 
 
     const pathLookup = disambiguatePaths(
     const pathLookup = disambiguatePaths(
-      tabsFileTreeLookup.map(tab => tab.result!),
+      tabsFileTreeLookup.map(tab => tab.result),
       fileTreeData
       fileTreeData
     )
     )
 
 
-    return tabsFileTreeLookup.map(tab => ({
-      id: tab.result!.entity._id,
-      displayPath:
-        pathLookup.get(tab.result!.entity._id) || tab.result!.entity.name,
-      lifetime: tab.lifetime,
-    }))
+    return tabsFileTreeLookup.map(tab => {
+      const entity = tab.result.entity
+
+      return {
+        id: entity._id,
+        name: entity.name,
+        displayPath: pathLookup.get(entity._id) || entity.name,
+        isLinkedFile:
+          isFileRefResult(tab.result) &&
+          !!tab.result.entity.linkedFileData?.provider,
+        lifetime: tab.lifetime,
+      }
+    })
   }, [fileTreeData, openTabs, tabsEnabled])
   }, [fileTreeData, openTabs, tabsEnabled])
 
 
   const openTab = useCallback(
   const openTab = useCallback(

+ 12 - 0
services/web/frontend/js/features/ide-react/types/file-tree.ts

@@ -29,3 +29,15 @@ export type FileTreeFindResult =
   | FileTreeFolderFindResult
   | FileTreeFolderFindResult
   | FileTreeDocumentFindResult
   | FileTreeDocumentFindResult
   | FileTreeFileRefFindResult
   | FileTreeFileRefFindResult
+
+export const isFolderResult = (
+  result: FileTreeFindResult
+): result is FileTreeFolderFindResult => result.type === 'folder'
+
+export const isDocResult = (
+  result: FileTreeFindResult
+): result is FileTreeDocumentFindResult => result.type === 'doc'
+
+export const isFileRefResult = (
+  result: FileTreeFindResult
+): result is FileTreeFileRefFindResult => result.type === 'fileRef'

+ 4 - 0
services/web/frontend/js/features/source-editor/components/tabs/tab.tsx

@@ -2,6 +2,7 @@ import {
   EditorFileTab,
   EditorFileTab,
   TAB_TRANSFER_TYPE,
   TAB_TRANSFER_TYPE,
 } from '@/features/ide-react/context/tabs-context'
 } from '@/features/ide-react/context/tabs-context'
+import FileTreeIcon from '@/features/file-tree/components/file-tree-icon'
 import MaterialIcon from '@/shared/components/material-icon'
 import MaterialIcon from '@/shared/components/material-icon'
 import { debugConsole } from '@/utils/debugging'
 import { debugConsole } from '@/utils/debugging'
 import classNames from 'classnames'
 import classNames from 'classnames'
@@ -196,6 +197,9 @@ export const Tab = memo(function Tab({
         'tab-temporary': tab.lifetime === 'temporary',
         'tab-temporary': tab.lifetime === 'temporary',
       })}
       })}
     >
     >
+      <span className="editor-file-tab-icon">
+        <FileTreeIcon isLinkedFile={tab.isLinkedFile} name={tab.name} />
+      </span>
       <div className="editor-file-tab-path">&lrm;{tab.displayPath}</div>
       <div className="editor-file-tab-path">&lrm;{tab.displayPath}</div>
       <div className="editor-file-tab-action">
       <div className="editor-file-tab-action">
         <button
         <button

+ 37 - 0
services/web/frontend/stylesheets/pages/editor/tabs.scss

@@ -41,6 +41,7 @@
 
 
 .editor-file-tab {
 .editor-file-tab {
   --tab-bg-color: transparent;
   --tab-bg-color: transparent;
+  --tab-icon-colour: var(--content-secondary-themed);
 
 
   display: inline-flex;
   display: inline-flex;
   align-items: center;
   align-items: center;
@@ -61,6 +62,7 @@
 
 
   &.tab-selected {
   &.tab-selected {
     --tab-bg-color: var(--bg-secondary-themed);
     --tab-bg-color: var(--bg-secondary-themed);
+    --tab-icon-colour: var(--content-primary-themed);
   }
   }
 
 
   .material-symbols {
   .material-symbols {
@@ -68,6 +70,34 @@
     font-size: var(--font-size-03);
     font-size: var(--font-size-03);
   }
   }
 
 
+  .file-tree-icon,
+  .linked-file-highlight {
+    color: var(--tab-icon-colour);
+  }
+
+  .editor-file-tab-icon {
+    position: relative;
+    display: inline-flex;
+    align-items: center;
+    flex: 0 0 auto;
+  }
+
+  .file-tree-icon {
+    margin-right: var(--spacing-01);
+  }
+
+  .linked-file-icon {
+    position: relative;
+    left: -2px;
+  }
+
+  .linked-file-highlight {
+    position: absolute;
+    top: 6px;
+    left: 6px !important;
+    font-size: var(--font-size-01);
+  }
+
   &.tab-drop-right {
   &.tab-drop-right {
     border-right-color: var(--border-primary-themed);
     border-right-color: var(--border-primary-themed);
   }
   }
@@ -80,6 +110,11 @@
     font-style: italic;
     font-style: italic;
   }
   }
 
 
+  .editor-file-tab-action {
+    display: flex;
+    align-items: center;
+  }
+
   .editor-file-tab-close-action {
   .editor-file-tab-close-action {
     background: transparent;
     background: transparent;
     border: none;
     border: none;
@@ -87,6 +122,8 @@
     cursor: pointer;
     cursor: pointer;
     margin: 0;
     margin: 0;
     padding: 0;
     padding: 0;
+    display: flex;
+    align-items: center;
 
 
     &:hover {
     &:hover {
       color: var(--content-secondary-themed);
       color: var(--content-secondary-themed);