diff --git a/services/web/frontend/fonts/material-symbols/MaterialSymbolsRoundedUnfilledPartialSlice.woff2 b/services/web/frontend/fonts/material-symbols/MaterialSymbolsRoundedUnfilledPartialSlice.woff2 index 68ddbc999d..0bfc703101 100644 Binary files a/services/web/frontend/fonts/material-symbols/MaterialSymbolsRoundedUnfilledPartialSlice.woff2 and b/services/web/frontend/fonts/material-symbols/MaterialSymbolsRoundedUnfilledPartialSlice.woff2 differ diff --git a/services/web/frontend/fonts/material-symbols/unfilled-symbols.mjs b/services/web/frontend/fonts/material-symbols/unfilled-symbols.mjs index 54052d8773..193da5d796 100644 --- a/services/web/frontend/fonts/material-symbols/unfilled-symbols.mjs +++ b/services/web/frontend/fonts/material-symbols/unfilled-symbols.mjs @@ -3,14 +3,17 @@ // to update the font file with the latest icons. export default /** @type {const} */ ([ + 'book_5', 'code', 'description', 'forum', 'help', + 'image', 'integration_instructions', 'picture_as_pdf', 'rate_review', 'report', 'settings', + 'table_chart', 'web_asset', ]) diff --git a/services/web/frontend/js/features/file-tree/components/file-tree-doc.tsx b/services/web/frontend/js/features/file-tree/components/file-tree-doc.tsx index 8d9d6163fe..3b67073316 100644 --- a/services/web/frontend/js/features/file-tree/components/file-tree-doc.tsx +++ b/services/web/frontend/js/features/file-tree/components/file-tree-doc.tsx @@ -1,12 +1,6 @@ import { useSelectableEntity } from '../contexts/file-tree-selectable' - +import FileTreeIcon from './file-tree-icon' import FileTreeItemInner from './file-tree-item/file-tree-item-inner' -import { useTranslation } from 'react-i18next' -import Icon from '../../../shared/components/icon' -import iconTypeFromName from '../util/icon-type-from-name' -import classnames from 'classnames' -import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' -import MaterialIcon from '@/shared/components/material-icon' function FileTreeDoc({ name, @@ -46,52 +40,4 @@ function FileTreeDoc({ ) } -export const FileTreeIcon = ({ - isLinkedFile, - name, -}: { - name: string - isLinkedFile?: boolean -}) => { - const { t } = useTranslation() - - const className = classnames('file-tree-icon', { - 'linked-file-icon': isLinkedFile, - }) - - return ( - <> -   - - - {isLinkedFile && ( - - )} - - } - bs5={ - <> - - {isLinkedFile && ( - - )} - - } - /> - - ) -} - export default FileTreeDoc diff --git a/services/web/frontend/js/features/file-tree/components/file-tree-folder-icons.tsx b/services/web/frontend/js/features/file-tree/components/file-tree-folder-icons.tsx new file mode 100644 index 0000000000..9ee8f7229f --- /dev/null +++ b/services/web/frontend/js/features/file-tree/components/file-tree-folder-icons.tsx @@ -0,0 +1,77 @@ +import { useTranslation } from 'react-i18next' +import Icon from '../../../shared/components/icon' +import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' +import MaterialIcon from '@/shared/components/material-icon' +import { useFeatureFlag } from '@/shared/context/split-test-context' + +function FileTreeFolderIcons({ + expanded, + onExpandCollapseClick, +}: { + expanded: boolean + onExpandCollapseClick: () => void +}) { + const { t } = useTranslation() + + const newEditor = useFeatureFlag('editor-redesign') + + if (newEditor) { + return ( + <> + + + ) + } + + return ( + + + + + } + bs5={ + <> + + + + } + /> + ) +} + +export default FileTreeFolderIcons diff --git a/services/web/frontend/js/features/file-tree/components/file-tree-folder-list.tsx b/services/web/frontend/js/features/file-tree/components/file-tree-folder-list.tsx index 7641ad906c..1706554960 100644 --- a/services/web/frontend/js/features/file-tree/components/file-tree-folder-list.tsx +++ b/services/web/frontend/js/features/file-tree/components/file-tree-folder-list.tsx @@ -32,39 +32,47 @@ function FileTreeFolderList({ return (
    - {folders.sort(compareFunction).map(folder => { - return ( - - ) - })} - {docsAndFiles.sort(compareFunction).map(doc => { - if ('isFile' in doc) { +
    + {folders.sort(compareFunction).map(folder => { return ( - ) - } + })} + {docsAndFiles.sort(compareFunction).map(doc => { + if ('isFile' in doc) { + return ( + + ) + } - return - })} - {children} + return + })} + {children} +
) } diff --git a/services/web/frontend/js/features/file-tree/components/file-tree-folder.tsx b/services/web/frontend/js/features/file-tree/components/file-tree-folder.tsx index abcb6474cd..5ad8481349 100644 --- a/services/web/frontend/js/features/file-tree/components/file-tree-folder.tsx +++ b/services/web/frontend/js/features/file-tree/components/file-tree-folder.tsx @@ -1,8 +1,6 @@ import { useEffect } from 'react' -import { useTranslation } from 'react-i18next' import classNames from 'classnames' -import Icon from '../../../shared/components/icon' import { useFileTreeSelectable, useSelectableEntity, @@ -12,11 +10,10 @@ import { useDroppable } from '../contexts/file-tree-draggable' import FileTreeItemInner from './file-tree-item/file-tree-item-inner' import FileTreeFolderList from './file-tree-folder-list' import usePersistedState from '../../../shared/hooks/use-persisted-state' -import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' -import MaterialIcon from '@/shared/components/material-icon' import { Folder } from '../../../../../types/folder' import { Doc } from '../../../../../types/doc' import { FileRef } from '../../../../../types/file-ref' +import FileTreeFolderIcons from './file-tree-folder-icons' function FileTreeFolder({ name, @@ -31,8 +28,6 @@ function FileTreeFolder({ docs: Doc[] files: FileRef[] }) { - const { t } = useTranslation() - const { isSelected, props: selectableEntityProps } = useSelectableEntity( id, 'folder' @@ -58,47 +53,6 @@ function FileTreeFolder({ const { isOver: isOverRoot, dropRef: dropRefRoot } = useDroppable(id) const { isOver: isOverList, dropRef: dropRefList } = useDroppable(id) - const icons = ( - - - - - } - bs5={ - <> - - - - } - /> - ) - return ( <>
  • + } />
  • {expanded ? ( diff --git a/services/web/frontend/js/features/file-tree/components/file-tree-icon.tsx b/services/web/frontend/js/features/file-tree/components/file-tree-icon.tsx new file mode 100644 index 0000000000..d9e78e6675 --- /dev/null +++ b/services/web/frontend/js/features/file-tree/components/file-tree-icon.tsx @@ -0,0 +1,81 @@ +import { useTranslation } from 'react-i18next' +import Icon from '../../../shared/components/icon' +import iconTypeFromName, { + newEditorIconTypeFromName, +} from '../util/icon-type-from-name' +import classnames from 'classnames' +import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' +import MaterialIcon from '@/shared/components/material-icon' +import { useFeatureFlag } from '@/shared/context/split-test-context' + +function FileTreeIcon({ + isLinkedFile, + name, +}: { + name: string + isLinkedFile?: boolean +}) { + const { t } = useTranslation() + + const className = classnames('file-tree-icon', { + 'linked-file-icon': isLinkedFile, + }) + + const newEditor = useFeatureFlag('editor-redesign') + + if (newEditor) { + return ( + <> + + {isLinkedFile && ( + + )} + + ) + } + + return ( + <> +   + + + {isLinkedFile && ( + + )} + + } + bs5={ + <> + + {isLinkedFile && ( + + )} + + } + /> + + ) +} + +export default FileTreeIcon diff --git a/services/web/frontend/js/features/file-tree/util/icon-type-from-name.ts b/services/web/frontend/js/features/file-tree/util/icon-type-from-name.ts index 68eb393fec..1df52b8f70 100644 --- a/services/web/frontend/js/features/file-tree/util/icon-type-from-name.ts +++ b/services/web/frontend/js/features/file-tree/util/icon-type-from-name.ts @@ -1,4 +1,24 @@ import { isBootstrap5 } from '@/features/utils/bootstrap-5' +import { AvailableUnfilledIcon } from '@/shared/components/material-icon' + +// TODO ide-redesign-cleanup: Make this the default export and remove the legacy version +export const newEditorIconTypeFromName = ( + name: string +): AvailableUnfilledIcon => { + let ext = name.split('.').pop() + ext = ext ? ext.toLowerCase() : ext + + if (ext && ['png', 'pdf', 'jpg', 'jpeg', 'gif'].includes(ext)) { + return 'image' + } else if (ext && ['csv', 'xls', 'xlsx'].includes(ext)) { + return 'table_chart' + } else if (ext && ['py', 'r'].includes(ext)) { + return 'code' + } else if (ext && ['bib'].includes(ext)) { + return 'book_5' + } + return 'description' +} export default function iconTypeFromName(name: string): string { let ext = name.split('.').pop() diff --git a/services/web/frontend/js/features/ide-redesign/components/rail.tsx b/services/web/frontend/js/features/ide-redesign/components/rail.tsx index e9d21af972..a5ffa47089 100644 --- a/services/web/frontend/js/features/ide-redesign/components/rail.tsx +++ b/services/web/frontend/js/features/ide-redesign/components/rail.tsx @@ -5,6 +5,7 @@ import MaterialIcon, { } from '@/shared/components/material-icon' import { Panel } from 'react-resizable-panels' import { useLayoutContext } from '@/shared/context/layout-context' +import { FileTree } from '@/features/ide-react/components/file-tree' type RailElement = { icon: AvailableUnfilledIcon @@ -26,7 +27,12 @@ const RAIL_TABS: RailElement[] = [ { key: 'file-tree', icon: 'description', - component: <>File tree, + component: ( + <> + {/* TODO: add panel for file outline */} + + + ), }, { key: 'integrations', @@ -127,11 +133,11 @@ const RailTab = ({ }) => { return ( - + {active ? ( + + ) : ( + + )} ) } diff --git a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/file-tree.scss b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/file-tree.scss index efff19c941..5368b69dc8 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/file-tree.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/file-tree.scss @@ -5,8 +5,11 @@ --file-tree-bg: var(--bg-dark-tertiary); --file-tree-item-selected-color: var(--content-primary-dark); --file-tree-item-dragging-bg: #{rgb($bg-dark-secondary, 0.9)}; + --file-tree-item-dragging-color: var(--content-primary-dark); --file-tree-item-dragging-preview-bg: #{rgb($bg-accent-01, 0.6)}; + --file-tree-item-dragging-preview-colour: var(--content-primary-dark); --file-tree-line-height: 2.05; + --file-tree-icon-colour: var(--content-disabled); } @include theme('light') { @@ -15,6 +18,96 @@ --file-tree-bg: var(--bg-light-primary); --file-tree-item-selected-color: var(--bg-light-primary); --file-tree-item-dragging-bg: #{rgb($bg-light-tertiary, 0.9)}; + --file-tree-item-dragging-color: var(--content-secondary); + --file-tree-item-dragging-preview-colour: var(--bg-light-primary); +} + +// TODO ide-redesign-cleanup: Replace the existing styling with these overrides. +.ide-redesign-main { + --file-tree-item-hover-bg: var(--bg-light-secondary); + --file-tree-item-selected-bg: var(--bg-dark-primary); + --file-tree-item-selected-color: var(--white); + --file-tree-item-color: var(--content-primary); + --file-tree-bg: var(--white); + --file-tree-icon-colour: var(--content-primary); + --file-tree-item-dragging-bg: #{rgb($bg-dark-primary, 0.9)}; + --file-tree-item-dragging-color: var(--white); + --file-tree-item-dragging-preview-bg: #{rgb($bg-light-secondary, 0.6)}; + --file-tree-item-dragging-preview-colour: #{rgb($content-primary, 0.6)}; + + .file-tree { + background-color: var(--file-tree-bg); + } + + .file-tree ul.file-tree-list { + margin: var(--spacing-02); + } + + .file-tree-folder-list { + border-left: 1px solid + color-mix(in srgb, var(--border-primary) 24%, transparent); + margin-left: 14px !important; + + &.file-tree-list { + border-left: none; + margin-left: var(--spacing-02) !important; + + > .file-tree-folder-list-inner { + margin-left: 0; + } + } + } + + .file-tree-folder-list-inner { + margin-left: 10px; + display: flex; + flex-direction: column; + gap: var(--spacing-02); + } + + .item-name-button, + .folder-expand-collapse-button { + display: flex; + align-items: center; + height: 20px !important; + } + + .file-tree ul.file-tree-list li .material-symbols.file-tree-expand-icon { + margin-left: 0; + } + + .file-tree ul.file-tree-list li .material-symbols.file-tree-icon { + margin-left: 0; + margin-right: 0; + } + + // TODO ide-redesign-cleanup: Remove the !important overrides once + // we have replaced the default styling + .linked-file-highlight { + background-color: var(--file-tree-bg) !important; + color: var(--file-tree-icon-colour) !important; + left: 14px !important; + } + + .entity-name { + color: var(--file-tree-item-color); + border-radius: var(--border-radius-base); + padding: var(--spacing-02); + + // TODO ide-redesign-cleanup: This is here to override the fake-full-width-bg + // mixin. We can just remove that mixin when we clean this up. + &::before { + content: none !important; + } + } + + .item-name-button { + margin-left: var(--spacing-02); + } + + .dnd-draggable-preview-item { + border-radius: var(--border-radius-base); + } } .ide-react-file-tree-panel { @@ -214,7 +307,7 @@ } .material-symbols { - color: var(--content-disabled); + color: var(--file-tree-icon-colour); &.file-tree-icon { margin-right: var(--spacing-02); @@ -252,7 +345,7 @@ width: 24px; padding: var(--spacing-03); font-size: var(--font-size-03); - color: var(--content-disabled); + color: var(--file-tree-icon-colour); } .file-tree-dropdown-toggle { @@ -410,7 +503,7 @@ @include fake-full-width-bg(var(--file-tree-item-dragging-bg)); - color: var(--file-tree-item-color); + color: var(--file-tree-item-dragging-color); .material-symbols { color: var(--content-disabled) !important; @@ -447,7 +540,7 @@ } .dnd-draggable-preview-item { - color: var(--file-tree-item-selected-color); + color: var(--file-tree-item-dragging-preview-colour); background-color: var(--file-tree-item-dragging-preview-bg); width: 75%; padding-left: var(--spacing-08); diff --git a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/toolbar.scss b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/toolbar.scss index 464ad52df0..e7f0c5baf9 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/toolbar.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/toolbar.scss @@ -51,6 +51,7 @@ --toolbar-btn-hover-bg-color: var(--neutral-80); --toolbar-btn-hover-color: var(--white); --editor-toolbar-bg: var(--white); + --toolbar-filetree-bg-color: var(--white); .toolbar { border-bottom: none; diff --git a/services/web/test/frontend/features/file-tree/components/file-tree-root.spec.tsx b/services/web/test/frontend/features/file-tree/components/file-tree-root.spec.tsx index b26dca9a49..4c2680a072 100644 --- a/services/web/test/frontend/features/file-tree/components/file-tree-root.spec.tsx +++ b/services/web/test/frontend/features/file-tree/components/file-tree-root.spec.tsx @@ -372,7 +372,7 @@ describe('', function () { cy.findByRole('treeitem', { name: 'abcdef.tex' }).then($itemEl => { cy.findByTestId('file-tree-list-root').then($rootEl => { - expect($itemEl.get(0).parentNode).to.equal($rootEl.get(0)) + expect($itemEl.get(0).parentNode?.parentNode).to.equal($rootEl.get(0)) }) }) })