From cabbf97e070a2a3824bc0182d85c29e6df165c39 Mon Sep 17 00:00:00 2001 From: David <33458145+davidmcpowell@users.noreply.github.com> Date: Thu, 6 Feb 2025 15:41:25 +0000 Subject: [PATCH] Merge pull request #23344 from overleaf/dp-file-tree-toolbar Update file tree toolbar in new editor GitOrigin-RevId: 11ca678b50e0c525ae60b806dfbc08773b661b7b --- .../web/frontend/extracted-translations.json | 3 + ...alSymbolsRoundedUnfilledPartialSlice.woff2 | Bin 3636 -> 3860 bytes .../material-symbols/unfilled-symbols.mjs | 4 + .../file-tree/components/file-tree-root.tsx | 5 +- .../context/file-tree-open-context.tsx | 13 ++ .../components/file-tree-outline-panel.tsx | 15 ++- .../components/file-tree-toolbar.tsx | 112 ++++++++++++++++++ .../hooks/use-collapsible-file-tree.tsx | 12 ++ .../bootstrap-5/pages/editor/file-tree.scss | 58 +++++++++ .../bootstrap-5/pages/editor/outline.scss | 4 + services/web/locales/en.json | 3 + 11 files changed, 224 insertions(+), 5 deletions(-) create mode 100644 services/web/frontend/js/features/ide-redesign/components/file-tree-toolbar.tsx create mode 100644 services/web/frontend/js/features/ide-redesign/hooks/use-collapsible-file-tree.tsx diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index e22484be68..c7bbef0fd4 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -525,6 +525,7 @@ "file_or_folder_name_already_exists": "", "file_outline": "", "file_size": "", + "file_tree": "", "files_cannot_include_invalid_characters": "", "files_selected": "", "fill_in_our_quick_survey": "", @@ -668,6 +669,7 @@ "hide_configuration": "", "hide_deleted_user": "", "hide_document_preamble": "", + "hide_file_tree": "", "hide_local_file_contents": "", "hide_outline": "", "history": "", @@ -1462,6 +1464,7 @@ "show_all": "", "show_all_projects": "", "show_document_preamble": "", + "show_file_tree": "", "show_hotkeys": "", "show_in_code": "", "show_in_pdf": "", diff --git a/services/web/frontend/fonts/material-symbols/MaterialSymbolsRoundedUnfilledPartialSlice.woff2 b/services/web/frontend/fonts/material-symbols/MaterialSymbolsRoundedUnfilledPartialSlice.woff2 index b0b89fa3322db8d10eb9e86f1205151047c67370..fb158e5ea1ae7ea123f89aa8e94b14e21a53828d 100644 GIT binary patch literal 3860 zcmV+v59{!EPew8T0RR9101p%Z4gdfE03KKX01mnU0RR9100000000000000000000 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 Wg8vA+TxZ#1Js&0002y7H8Z5 literal 3636 zcmV-44$JX(Pew8T0RR9101h+&4gdfE02@3201ev!0RR9100000000000000000000 z0000Sgj5D#KT}jeRAK;ubP)&&q-dT_3s3+7HUcCAPy`?ag$@TG3W^Q4{`v0*5C3|7_q|}C3^NMvB(iY^yhqft3W?E(2dp zfUNjbOvvvoX&o7<$w*CvVUByO=&iR7)@3QQMha`TDdjJmy>mU=Bk;0@fA9Avf>&%y zTsLA#YcC1sf&PO16+DpL=bPCL;3M_Lle>`TycpM>c)!q#Ph|@?% zWWNE!#*tn&d)*4ssMPpfq1l>cix(1|v+nU!B7hY0h_78AL=mD7I`6s8GzQR;9QS&n z#3+!Z2?j1M1w585NMLlWQlJSr%vd^nl_n~3c8@Z3O1LPrr9>2j&XkQMMlpHv*jx?; z8YoksF&bMC5KW+_hCx}6a&UMcN_kB}!^AQ<@;PW_!6w+`DN;|16XVF9b&6Od(8xrI z)g(w#&qY0kjR`d#7P{qNSzrllbU}?pohCftqH^$cVoSLQabW}GjA;z&b#feyJuF}d zJu#3N=qN4*RxO3(ylE3Gd^`ktDoCq1nzNQq?WpZcP{0LpG;!TX7?@HPd8a59CX7Lh zN}vp?n3zOwYEhvHO|eEJODKT`f@>qFnxU^y}peP=@fC6+z4j4Ra*gjV|a7^!hLLgSITDwI^@I)(It5 zKBa&iPeF=^*ah-FX^lXNgktzFUusNaQXwY+O67wda)bUpT)Hrm#jIlEhs7TreVp}a z^r!ispMU=QrTBGLurgR1Y!7w>mx9~D{~zOij``Os3@rzV)iRr#nuGKCo`qepb(z2hDxvHglah)tq4J#?{Q#_^VKjY_Fw2gdv$YxLM$c zfW;B|=Ge&Z;HXz?wv3^pA&&(BTq6)T2pGxGVxKRiY{ep?Ce~C zv5M!ZYKPor>(SdVI!ULzqQl0vyqL5bZDaj?7brbFS|9YQm+AaHP}uv#5bq*6c;`w3 z7y}&)4@M7<-Y*_e6X>>lT)C!`3-67zE0!c6%PuFBm(Ngmpl!b;|04e6u2{5mZ0SW?uF*iRzk<<&J z2D9iMa=OIWc|;@*O)9|3_O|#b%%8B_(0eygE5J5zfi7I=(uJ@MJk6Btu3LTUgtZmK z%SW)_{yW!A!5fQwmA?m|iT42lGD%|W%Iyc?@E%@2LX%WC3~2#-0D6fydvqf3;%2Ie z$}u5#bWLHYz<&aTeZ0Q#`nO05o|!WehQ5qw86JB$HA3lUb9-`g!VpPG;`E&ev-NvX zV1?ZF;}{@lbBcTvSfhT-oJ7Ey2+kc^ZupcPGpE4ow*S;y^H!Vtam6_um(PClR9wCS zTyXBE-=0sC1|g!B_nv%nE=>;WuA+4!DqR%j$*6Q;bhG^@-k9Tt)eGdWeZ95VTkKaJ z2KLY-y2!0(>wN6Kk}mKUc*O@$@ZdyuA+0sn(hFFptQ?^3p83`6tLM0gT!1e;pHjmk ziYD#_<{I?r#D~W;vPu=&6O7(Bc6s=rEYqf^cp@TNt=4psrfEu2B+s&zMCn30GKNLV zJxG_3h0R1(=rE|DN!LguBS&PM(JTESvXQX&A8=9}LxOt|jDlV-S5gB1ysQ=F&7By^x&a5qcgu-y0%>Gs*1X z+KC46&@4P-z=JLngTB&ln^@lZS;5gmXXBM6~^>IVZK zpz-O(9;0gPF(&2u`UM+Ylm6dvvHAS-H;dyZHuLk(7n_ft;OklZ#PQ7{fFHU4%&V`S zY3|0>zaZ3)|6{#vH4a|)-384pH6|UOXy2<9R_gi+_ybG#zWDz+pH5$WY}dMZyJl>^ z>7q}w*ZH&mqVdJXkH5WS#~Pfjm+Iw8sjho?Fk41RV#dR>tWjc;aUVTYDj%*X96Qva+h=Z~^ zJtGkZp`6k=pr2#WY;I|^Q$TSSgd0DT{1_B_)m_r1Jfh4303sbdW+cSW{pOM}0N_bD z&tZh#J-Jhdb*}|(3gkVGBtcxQb#9}09@bO$Q(Cl`@MpFIqU{| zMpWsNeYp7bn=W%jSN6d>F|+oOlm?9w_sAF}E_Pku7jK@dXv3_vNaazpxP5n8Tg@{SQ%nOWG)#&c z6<6inrU7u+H1TbHO>ynp+Jvppj@(1GD|YN5NIQT-1N6+pd?f$Jl>mT4XY|&4B7krP zV#?7AHe?jqniR)tE{h#^i12DqD|d+tKb5p0^b{E-$(uJ_=;|E5Pj+B!s#9}zNQ**{ z`%No=L(JOMS_DNDR|=pYRXSA&f>h~LA<6%?g@F04TfYwC$MOV@TjQYcNOHkt6r{oO zZ>pFi@=diI#{6eU7QXox{`Oyzq|1MR!$dchBwZpJCOOk3O}4-@<@KM-2h|S#TwY)M zxw>C%|IgLcpyp#_pIYCsZOyGm$$qu|W3>Q>Qtla=VJ@B_6pBYkxJ!nnJ5-2*++A>|3#HbW73Ru%I5NR zFAu9M_f}IE?V9SV33RQvSfZK#JI(j7e__^^CGAn!Rf*~4 zwZoCS761T%0OZ@xKmRU|YMzzwf3j2_L|cBGn|*wG9^V_|f5*=#ua&RB#3~0Sft4S< zln3K~$Nwv@!QSgCln1Xg`O5u2Aef;dr@Bbg~Oaun+Lm7|i!@tg@PW$l8AWRRwd zY6TewIfAK#xWHP>Vt{HcIUUAK(xZziIH;<)-&Q(sputWx1}vB`(?v0DbYVfqO?09B z>;o9crUMOn3}j)63fd_|Lzn8nB6fY{^y5HFD|R|5qm^!)!Zmur2%$6rDFeeqH>=Um zL6xlN#NXYD6Boh=B8Ctmh$nKviCoa@f(vu8(f#P-$)=cMvWd6cVQrMGZJAChZS+gw z5kP*^Fd}gw8+W#1!XEc@p@0I?391~X)HYCZ8PuQ*blN~YZ3?QEbGX9%d(Cf%@io`g GEdT(4`{-Bz diff --git a/services/web/frontend/fonts/material-symbols/unfilled-symbols.mjs b/services/web/frontend/fonts/material-symbols/unfilled-symbols.mjs index 7ed8321b44..1c551ebdbd 100644 --- a/services/web/frontend/fonts/material-symbols/unfilled-symbols.mjs +++ b/services/web/frontend/fonts/material-symbols/unfilled-symbols.mjs @@ -1,20 +1,24 @@ // @ts-check // Make sure to run the build-unfilled.mjs script after updating this list // to update the font file with the latest icons. +// You may need to hard reload your browser window to see the changes. export default /** @type {const} */ ([ 'book_5', 'code', + 'create_new_folder', 'description', 'forum', 'help', 'image', 'info', 'integration_instructions', + 'note_add', 'picture_as_pdf', 'rate_review', 'report', 'settings', 'table_chart', + 'upload_file', 'web_asset', ]) diff --git a/services/web/frontend/js/features/file-tree/components/file-tree-root.tsx b/services/web/frontend/js/features/file-tree/components/file-tree-root.tsx index 9377faad7f..24db6255f9 100644 --- a/services/web/frontend/js/features/file-tree/components/file-tree-root.tsx +++ b/services/web/frontend/js/features/file-tree/components/file-tree-root.tsx @@ -6,6 +6,7 @@ import FileTreeContext from './file-tree-context' import FileTreeDraggablePreviewLayer from './file-tree-draggable-preview-layer' import FileTreeFolderList from './file-tree-folder-list' import FileTreeToolbar from './file-tree-toolbar' +import FileTreeToolbarNew from '@/features/ide-redesign/components/file-tree-toolbar' import FileTreeModalDelete from './modals/file-tree-modal-delete' import FileTreeModalCreateFolder from './modals/file-tree-modal-create-folder' import FileTreeModalError from './modals/file-tree-modal-error' @@ -18,6 +19,7 @@ import FileTreeInner from './file-tree-inner' import { useDragLayer } from 'react-dnd' import classnames from 'classnames' import { pathInFolder } from '@/features/file-tree/util/path' +import { useFeatureFlag } from '@/shared/context/split-test-context' const FileTreeRoot = React.memo<{ onSelect: () => void @@ -41,6 +43,7 @@ const FileTreeRoot = React.memo<{ const { _id: projectId } = useProjectContext() const { fileTreeData } = useFileTreeData() const isReady = Boolean(projectId && fileTreeData) + const newEditor = useFeatureFlag('editor-redesign') useEffect(() => { if (fileTreeContainer) { @@ -97,7 +100,7 @@ const FileTreeRoot = React.memo<{ fileTreeContainer={fileTreeContainer} > {isConnected ? null :
} - + {newEditor ? : } diff --git a/services/web/frontend/js/features/ide-react/context/file-tree-open-context.tsx b/services/web/frontend/js/features/ide-react/context/file-tree-open-context.tsx index b72a061b67..eec94f5d99 100644 --- a/services/web/frontend/js/features/ide-react/context/file-tree-open-context.tsx +++ b/services/web/frontend/js/features/ide-react/context/file-tree-open-context.tsx @@ -30,6 +30,8 @@ const FileTreeOpenContext = createContext< handleFileTreeInit: () => void handleFileTreeSelect: (selectedEntities: FileTreeFindResult[]) => void handleFileTreeDelete: (entity: FileTreeFindResult) => void + fileTreeExpanded: boolean + toggleFileTreeExpanded: () => void } | undefined >(undefined) @@ -46,6 +48,13 @@ export const FileTreeOpenProvider: FC = ({ children }) => { const [selectedEntityCount, setSelectedEntityCount] = useState(0) const [fileTreeReady, setFileTreeReady] = useState(false) + // NOTE: Only used in editor redesign + const [fileTreeExpanded, setFileTreeExpanded] = useState(true) + + const toggleFileTreeExpanded = useCallback(() => { + setFileTreeExpanded(prev => !prev) + }, []) + const handleFileTreeInit = useCallback(() => { setFileTreeReady(true) }, []) @@ -129,6 +138,8 @@ export const FileTreeOpenProvider: FC = ({ children }) => { handleFileTreeInit, handleFileTreeSelect, handleFileTreeDelete, + fileTreeExpanded, + toggleFileTreeExpanded, } }, [ handleFileTreeDelete, @@ -136,6 +147,8 @@ export const FileTreeOpenProvider: FC = ({ children }) => { handleFileTreeSelect, openEntity, selectedEntityCount, + fileTreeExpanded, + toggleFileTreeExpanded, ]) return ( diff --git a/services/web/frontend/js/features/ide-redesign/components/file-tree-outline-panel.tsx b/services/web/frontend/js/features/ide-redesign/components/file-tree-outline-panel.tsx index f67619bc3c..bf69d40e59 100644 --- a/services/web/frontend/js/features/ide-redesign/components/file-tree-outline-panel.tsx +++ b/services/web/frontend/js/features/ide-redesign/components/file-tree-outline-panel.tsx @@ -3,35 +3,42 @@ import { FileTree } from '@/features/ide-react/components/file-tree' import { OutlineContainer } from '@/features/outline/components/outline-container' import { VerticalResizeHandle } from '@/features/ide-react/components/resize/vertical-resize-handle' import { useOutlinePane } from '@/features/ide-react/hooks/use-outline-pane' +import useCollapsibleFileTree from '../hooks/use-collapsible-file-tree' +import classNames from 'classnames' function FileTreeOutlinePanel() { const { outlineEnabled, outlinePanelRef } = useOutlinePane() + const { fileTreeExpanded, fileTreePanelRef } = useCollapsibleFileTree() return ( diff --git a/services/web/frontend/js/features/ide-redesign/components/file-tree-toolbar.tsx b/services/web/frontend/js/features/ide-redesign/components/file-tree-toolbar.tsx new file mode 100644 index 0000000000..cdc28ef4b7 --- /dev/null +++ b/services/web/frontend/js/features/ide-redesign/components/file-tree-toolbar.tsx @@ -0,0 +1,112 @@ +import { useTranslation } from 'react-i18next' +import * as eventTracking from '../../../infrastructure/event-tracking' +import { useFileTreeActionable } from '@/features/file-tree/contexts/file-tree-actionable' +import { useFileTreeData } from '@/shared/context/file-tree-data-context' +import OLTooltip from '@/features/ui/components/ol/ol-tooltip' +import MaterialIcon, { + AvailableUnfilledIcon, +} from '@/shared/components/material-icon' +import React from 'react' +import useCollapsibleFileTree from '../hooks/use-collapsible-file-tree' + +function FileTreeToolbar() { + const { t } = useTranslation() + const { fileTreeExpanded, toggleFileTreeExpanded } = useCollapsibleFileTree() + + return ( +
+ + +
+ ) +} + +function FileTreeActionButtons() { + const { t } = useTranslation() + const { fileTreeReadOnly } = useFileTreeData() + + const { + canCreate, + startCreatingFolder, + startCreatingDocOrFile, + startUploadingDocOrFile, + } = useFileTreeActionable() + + if (!canCreate || fileTreeReadOnly) return null + + const createWithAnalytics = () => { + eventTracking.sendMB('new-file-click', { location: 'toolbar' }) + startCreatingDocOrFile() + } + + const uploadWithAnalytics = () => { + eventTracking.sendMB('upload-click', { location: 'toolbar' }) + startUploadingDocOrFile() + } + + return ( +
+ + + +
+ ) +} + +function FileTreeActionButton({ + id, + description, + onClick, + iconType, +}: { + id: string + description: string + onClick: () => void + iconType: AvailableUnfilledIcon +}) { + return ( + + + + ) +} + +export default FileTreeToolbar diff --git a/services/web/frontend/js/features/ide-redesign/hooks/use-collapsible-file-tree.tsx b/services/web/frontend/js/features/ide-redesign/hooks/use-collapsible-file-tree.tsx new file mode 100644 index 0000000000..89b4d252a1 --- /dev/null +++ b/services/web/frontend/js/features/ide-redesign/hooks/use-collapsible-file-tree.tsx @@ -0,0 +1,12 @@ +import { ImperativePanelHandle } from 'react-resizable-panels' +import { useRef } from 'react' +import useCollapsiblePanel from '@/features/ide-react/hooks/use-collapsible-panel' +import { useFileTreeOpenContext } from '@/features/ide-react/context/file-tree-open-context' + +export default function useCollapsibleFileTree() { + const { fileTreeExpanded, toggleFileTreeExpanded } = useFileTreeOpenContext() + const fileTreePanelRef = useRef(null) + useCollapsiblePanel(fileTreeExpanded, fileTreePanelRef) + + return { fileTreeExpanded, fileTreePanelRef, toggleFileTreeExpanded } +} 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 5368b69dc8..a0d0ab8c83 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 @@ -35,6 +35,63 @@ --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-outline-panel-group { + background-color: var(--file-tree-bg); + } + + .file-tree-toolbar { + display: flex; + justify-content: space-between; + height: 28px; + margin: var(--spacing-02); + } + + .file-tree-panel { + min-height: 36px; + } + + .file-tree-panel-collapsed { + max-height: 36px; + } + + .file-tree-expand-collapse-button { + border-radius: var(--border-radius-base); + color: var(--outline-content-color); + display: flex; + align-items: center; + background-color: transparent; + border: 0; + padding: 0 var(--spacing-02); + flex-grow: 1; + + &:hover { + background-color: var(--file-tree-item-hover-bg); + } + + h4 { + font-size: var(--font-size-02); + margin: 0; + font-weight: normal; + } + } + + .file-tree-toolbar-action-buttons { + display: flex; + } + + .file-tree-toolbar-action-button { + padding: var(--spacing-02); + border-radius: var(--border-radius-full); + + &:hover { + background-color: var(--file-tree-item-hover-bg); + } + + .material-symbols { + font-size: 16px; + } + } + .file-tree { background-color: var(--file-tree-bg); } @@ -47,6 +104,7 @@ border-left: 1px solid color-mix(in srgb, var(--border-primary) 24%, transparent); margin-left: 14px !important; + margin-top: 0; &.file-tree-list { border-left: none; diff --git a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/outline.scss b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/outline.scss index c716cd426e..5e240cb19c 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/outline.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/outline.scss @@ -37,6 +37,10 @@ ); --outline-container-color-bg: var(--bg-light-primary); + .file-outline-panel { + min-height: 36px; + } + .outline-pane { padding: 4px; } diff --git a/services/web/locales/en.json b/services/web/locales/en.json index 9fc78cdb23..ed609bc70b 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -701,6 +701,7 @@ "file_outline": "File outline", "file_size": "File size", "file_too_large": "File too large", + "file_tree": "File tree", "files_cannot_include_invalid_characters": "File name is empty or contains invalid characters", "files_selected": "files selected.", "fill_in_our_quick_survey": "Fill in our quick survey.", @@ -892,6 +893,7 @@ "hide_configuration": "Hide configuration", "hide_deleted_user": "Hide deleted users", "hide_document_preamble": "Hide document preamble", + "hide_file_tree": "Hide file tree", "hide_local_file_contents": "Hide Local File Contents", "hide_outline": "Hide File outline", "history": "History", @@ -1933,6 +1935,7 @@ "show_all": "show all", "show_all_projects": "Show all projects", "show_document_preamble": "Show document preamble", + "show_file_tree": "Show file tree", "show_hotkeys": "Show Hotkeys", "show_in_code": "Show in code", "show_in_pdf": "Show in PDF",