Przeglądaj źródła

Merge pull request #21226 from overleaf/rd-history-filetree

Migrate the history file tree to Bootstrap 5

GitOrigin-RevId: bb5443d91992ff7241ed5c8bb467fa553e55bb0f
Rebeka Dekany 1 rok temu
rodzic
commit
200e7a75f0

+ 16 - 4
services/web/frontend/js/features/history/components/file-tree/history-file-tree-doc.tsx

@@ -4,6 +4,8 @@ import HistoryFileTreeItem from './history-file-tree-item'
 import iconTypeFromName from '../../../file-tree/util/icon-type-from-name'
 import Icon from '../../../../shared/components/icon'
 import type { FileDiff } from '../../services/types/file'
+import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
+import MaterialIcon from '@/shared/components/material-icon'
 
 type HistoryFileTreeDocProps = {
   file: FileDiff
@@ -34,10 +36,20 @@ function HistoryFileTreeDoc({
         name={name}
         operation={'operation' in file ? file.operation : undefined}
         icons={
-          <Icon
-            type={iconTypeFromName(name)}
-            fw
-            className="spaced file-tree-icon"
+          <BootstrapVersionSwitcher
+            bs3={
+              <Icon
+                type={iconTypeFromName(name)}
+                fw
+                className="file-tree-icon"
+              />
+            }
+            bs5={
+              <MaterialIcon
+                type={iconTypeFromName(name)}
+                className="file-tree-icon"
+              />
+            }
           />
         }
       />

+ 48 - 19
services/web/frontend/js/features/history/components/file-tree/history-file-tree-folder.tsx

@@ -6,6 +6,8 @@ import HistoryFileTreeFolderList from './history-file-tree-folder-list'
 
 import Icon from '../../../../shared/components/icon'
 import type { HistoryDoc, HistoryFileTree } from '../../utils/file-tree'
+import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
+import MaterialIcon from '@/shared/components/material-icon'
 
 type HistoryFileTreeFolderProps = {
   name: string
@@ -41,24 +43,46 @@ function HistoryFileTreeFolder({
   })
 
   const icons = (
-    <>
-      <button
-        onClick={() => setExpanded(!expanded)}
-        aria-label={expanded ? t('collapse') : t('expand')}
-        className="history-file-tree-folder-button"
-      >
-        <Icon
-          type={expanded ? 'angle-down' : 'angle-right'}
-          fw
-          className="file-tree-expand-icon"
-        />
-      </button>
-      <Icon
-        type={expanded ? 'folder-open' : 'folder'}
-        fw
-        className="file-tree-folder-icon"
-      />
-    </>
+    <BootstrapVersionSwitcher
+      bs3={
+        <>
+          <button
+            onClick={() => setExpanded(!expanded)}
+            aria-label={expanded ? t('collapse') : t('expand')}
+            className="history-file-tree-folder-button"
+          >
+            <Icon
+              type={expanded ? 'angle-down' : 'angle-right'}
+              fw
+              className="file-tree-expand-icon"
+            />
+          </button>
+          <Icon
+            type={expanded ? 'folder-open' : 'folder'}
+            fw
+            className="file-tree-folder-icon"
+          />
+        </>
+      }
+      bs5={
+        <>
+          <button
+            onClick={() => setExpanded(!expanded)}
+            aria-label={expanded ? t('collapse') : t('expand')}
+            className="history-file-tree-folder-button"
+          >
+            <MaterialIcon
+              type={expanded ? 'expand_more' : 'chevron_right'}
+              className="file-tree-expand-icon"
+            />
+          </button>
+          <MaterialIcon
+            type={expanded ? 'folder_open' : 'folder'}
+            className="file-tree-folder-icon"
+          />
+        </>
+      }
+    />
   )
 
   return (
@@ -71,7 +95,12 @@ function HistoryFileTreeFolder({
         aria-label={name}
         tabIndex={0}
         onClick={() => setExpanded(!expanded)}
-        onKeyDown={() => setExpanded(!expanded)}
+        onKeyDown={event => {
+          if (event.key === 'Enter' || event.key === ' ') {
+            event.preventDefault()
+            setExpanded(!expanded)
+          }
+        }}
       >
         <HistoryFileTreeItem name={name} icons={icons} />
       </li>

+ 2 - 2
services/web/frontend/js/features/history/components/file-tree/history-file-tree-item.tsx

@@ -1,7 +1,7 @@
 import classNames from 'classnames'
 import type { ReactNode } from 'react'
 import type { FileOperation } from '../../services/types/file-operation'
-import Tag from '@/shared/components/tag'
+import OLTag from '@/features/ui/components/ol/ol-tag'
 
 type FileTreeItemProps = {
   name: string
@@ -26,7 +26,7 @@ export default function HistoryFileTreeItem({
           {name}
         </div>
         {operation && (
-          <Tag className="history-file-tree-item-badge">{operation}</Tag>
+          <OLTag className="history-file-tree-item-badge">{operation}</OLTag>
         )}
       </div>
     </div>

+ 0 - 21
services/web/frontend/stylesheets/bootstrap-5/pages/editor/file-tree.scss

@@ -114,16 +114,8 @@
       line-height: var(--file-tree-line-height);
       position: relative;
 
-      &:focus {
-        outline: none;
-      }
-
       .entity {
         user-select: none;
-
-        &:focus {
-          outline: none;
-        }
       }
 
       .entity > .entity-name > button {
@@ -131,10 +123,6 @@
         border: 0;
         padding: 0;
 
-        &:focus {
-          outline: none;
-        }
-
         &.item-name-button {
           color: inherit;
           width: 100%;
@@ -157,10 +145,6 @@
           text-overflow: clip;
         }
 
-        &:focus {
-          outline: none;
-        }
-
         background-color: transparent;
 
         @include fake-full-width-bg(transparent);
@@ -219,11 +203,6 @@
         }
       }
 
-      .material-symbols.folder-open,
-      .material-symbols.fa-folder {
-        color: var(--content-disabled);
-      }
-
       .material-symbols.toggle {
         width: 24px;
         padding: var(--spacing-03);

+ 121 - 2
services/web/frontend/stylesheets/bootstrap-5/pages/editor/history.scss

@@ -359,7 +359,7 @@ history-root {
 }
 
 .history-version-label-tooltip {
-  padding: 6px;
+  padding: var(--spacing-03);
   text-align: initial;
 
   .history-version-label-tooltip-row {
@@ -433,6 +433,125 @@ history-root {
   vertical-align: middle;
 }
 
+.history-file-tree {
+  display: flex !important; // To work around jQuery layout's inline styles
+  flex-direction: column;
+  max-height: 100%;
+
+  ul.history-file-tree-list {
+    margin: 0;
+    overflow: hidden auto;
+
+    .history-file-tree-item > ul,
+    ul[role='tree'] {
+      margin-left: var(--spacing-08);
+    }
+
+    &::after {
+      content: '';
+      display: block;
+      min-height: 25px;
+    }
+
+    li {
+      line-height: var(--file-tree-line-height);
+      position: relative;
+      margin-left: var(--spacing-04);
+
+      .history-file-tree-item {
+        color: var(--file-tree-item-color);
+        cursor: pointer;
+        white-space: nowrap;
+        user-select: none;
+        display: flex;
+        align-items: center;
+
+        @include fake-full-width-bg(transparent);
+
+        &:hover {
+          background-color: var(--file-tree-item-hover-bg);
+
+          // When the entity is a subfolder, the DOM element is "indented" via margin-left. This makes the
+          // element not fill the entire file-tree width (as it's spaced from the left-hand side via margin)
+          // and, in consequence, the background gets clipped. The ::before pseudo-selector is used to fill
+          // the empty space.
+          @include fake-full-width-bg(var(--file-tree-item-hover-bg));
+        }
+
+        .history-file-tree-folder-button {
+          padding: 0;
+          cursor: pointer;
+          background: transparent;
+          border: 0;
+        }
+
+        .history-file-tree-item-name-wrapper {
+          display: flex;
+          align-items: center;
+          width: 100%;
+          overflow: hidden;
+
+          .history-file-tree-item-name {
+            margin-right: var(--spacing-02);
+            overflow: hidden;
+            text-overflow: ellipsis;
+            flex-grow: 1;
+            white-space: pre;
+
+            &.strikethrough {
+              text-decoration: line-through;
+            }
+          }
+
+          .history-file-tree-item-badge {
+            text-transform: capitalize;
+            font-weight: normal;
+            margin-right: var(--spacing-05);
+
+            &:hover {
+              background-color: var(--neutral-20) !important;
+            }
+          }
+        }
+      }
+
+      .material-symbols {
+        color: var(--content-disabled);
+
+        &.file-tree-icon {
+          margin-right: var(--spacing-02);
+          margin-left: var(--spacing-04);
+        }
+
+        &.file-tree-folder-icon {
+          margin-right: var(--spacing-02);
+          vertical-align: sub;
+        }
+
+        &.file-tree-expand-icon {
+          margin-left: var(--spacing-04);
+          vertical-align: sub;
+        }
+      }
+    }
+
+    li.selected > .history-file-tree-item {
+      color: var(--file-tree-item-selected-color);
+      background-color: var(--file-tree-item-selected-bg);
+      font-weight: bold;
+
+      > div > .material-symbols,
+      > button > .material-symbols,
+      > .material-symbols,
+      .material-symbols {
+        color: var(--file-tree-item-selected-color);
+      }
+
+      @include fake-full-width-bg(var(--file-tree-item-selected-bg));
+    }
+  }
+}
+
 .history-error {
-  padding: 16px;
+  padding: var(--spacing-06);
 }