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 iconTypeFromName from '../../../file-tree/util/icon-type-from-name'
import Icon from '../../../../shared/components/icon' import Icon from '../../../../shared/components/icon'
import type { FileDiff } from '../../services/types/file' 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 = { type HistoryFileTreeDocProps = {
file: FileDiff file: FileDiff
@ -34,10 +36,20 @@ function HistoryFileTreeDoc({
name={name} name={name}
operation={'operation' in file ? file.operation : undefined} operation={'operation' in file ? file.operation : undefined}
icons={ icons={
<BootstrapVersionSwitcher
bs3={
<Icon <Icon
type={iconTypeFromName(name)} type={iconTypeFromName(name)}
fw fw
className="spaced file-tree-icon" 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 Icon from '../../../../shared/components/icon'
import type { HistoryDoc, HistoryFileTree } from '../../utils/file-tree' 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 = { type HistoryFileTreeFolderProps = {
name: string name: string
@ -41,6 +43,8 @@ function HistoryFileTreeFolder({
}) })
const icons = ( const icons = (
<BootstrapVersionSwitcher
bs3={
<> <>
<button <button
onClick={() => setExpanded(!expanded)} onClick={() => setExpanded(!expanded)}
@ -59,6 +63,26 @@ function HistoryFileTreeFolder({
className="file-tree-folder-icon" 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 ( return (
@ -71,7 +95,12 @@ function HistoryFileTreeFolder({
aria-label={name} aria-label={name}
tabIndex={0} tabIndex={0}
onClick={() => setExpanded(!expanded)} onClick={() => setExpanded(!expanded)}
onKeyDown={() => setExpanded(!expanded)} onKeyDown={event => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault()
setExpanded(!expanded)
}
}}
> >
<HistoryFileTreeItem name={name} icons={icons} /> <HistoryFileTreeItem name={name} icons={icons} />
</li> </li>

View file

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

View file

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

View file

@ -359,7 +359,7 @@ history-root {
} }
.history-version-label-tooltip { .history-version-label-tooltip {
padding: 6px; padding: var(--spacing-03);
text-align: initial; text-align: initial;
.history-version-label-tooltip-row { .history-version-label-tooltip-row {
@ -433,6 +433,125 @@ history-root {
vertical-align: middle; vertical-align: middle;
} }
.history-error { .history-file-tree {
padding: 16px; 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);
} }