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

Migrate the history file tree to Bootstrap 5

GitOrigin-RevId: bb5443d91992ff7241ed5c8bb467fa553e55bb0f
This commit is contained in:
Rebeka Dekany 2024-10-22 14:57:16 +02:00 committed by Copybot
parent d0b5c2f307
commit 200e7a75f0
5 changed files with 188 additions and 49 deletions

View file

@ -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"
/>
}
/>
}
/>

View file

@ -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>

View file

@ -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>

View file

@ -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);

View file

@ -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-error {
padding: 16px;
.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: var(--spacing-06);
}