diff --git a/services/web/app/views/project/editor/editor-no-symbol-palette.pug b/services/web/app/views/project/editor/editor-no-symbol-palette.pug new file mode 100644 index 0000000000..d858481736 --- /dev/null +++ b/services/web/app/views/project/editor/editor-no-symbol-palette.pug @@ -0,0 +1,40 @@ +.ui-layout-center( + ng-controller="ReviewPanelController", + ng-class="{\ + 'rp-unsupported': editor.showRichText,\ + 'rp-state-current-file': (reviewPanel.subView === SubViews.CUR_FILE),\ + 'rp-state-current-file-expanded': (reviewPanel.subView === SubViews.CUR_FILE && ui.reviewPanelOpen),\ + 'rp-state-current-file-mini': (reviewPanel.subView === SubViews.CUR_FILE && !ui.reviewPanelOpen),\ + 'rp-state-overview': (reviewPanel.subView === SubViews.OVERVIEW),\ + 'rp-size-mini': ui.miniReviewPanelVisible,\ + 'rp-size-expanded': ui.reviewPanelOpen,\ + 'rp-layout-left': reviewPanel.layoutToLeft,\ + 'rp-loading-threads': reviewPanel.loadingThreads,\ + }" + ) + .loading-panel( + ng-show="(!editor.sharejs_doc || editor.opening) && !editor.error_state", + style=showRichText ? "top: 32px" : "", + ) + span(ng-show="editor.open_doc_id") + i.fa.fa-spin.fa-refresh + |   #{translate("loading")}… + span(ng-show="!editor.open_doc_id") + i.fa.fa-arrow-left + |   #{translate("open_a_file_on_the_left")} + + if moduleIncludesAvailable('editor:main') + != moduleIncludes('editor:main', locals) + else + .toolbar.toolbar-editor + + .multi-selection-ongoing( + ng-show="multiSelectedCount > 0" + ) + .multi-selection-message + h4 {{ multiSelectedCount }} #{translate('files_selected')} + + include ./source-editor + + if !isRestrictedTokenMember + include ./review-panel diff --git a/services/web/app/views/project/editor/editor-with-symbol-palette.pug b/services/web/app/views/project/editor/editor-with-symbol-palette.pug new file mode 100644 index 0000000000..32fb59b0ac --- /dev/null +++ b/services/web/app/views/project/editor/editor-with-symbol-palette.pug @@ -0,0 +1,55 @@ +.ui-layout-center( + ng-controller="ReviewPanelController", + ng-class="{\ + 'rp-unsupported': editor.showRichText,\ + 'rp-state-current-file': (reviewPanel.subView === SubViews.CUR_FILE),\ + 'rp-state-current-file-expanded': (reviewPanel.subView === SubViews.CUR_FILE && ui.reviewPanelOpen),\ + 'rp-state-current-file-mini': (reviewPanel.subView === SubViews.CUR_FILE && !ui.reviewPanelOpen),\ + 'rp-state-overview': (reviewPanel.subView === SubViews.OVERVIEW),\ + 'rp-size-mini': ui.miniReviewPanelVisible,\ + 'rp-size-expanded': ui.reviewPanelOpen,\ + 'rp-layout-left': reviewPanel.layoutToLeft,\ + 'rp-loading-threads': reviewPanel.loadingThreads,\ + }" + ) + + .editor-container.full-size( + vertical-resizable-panes="symbol-palette-resizer" + vertical-resizable-panes-hidden-externally-on="symbol-palette-toggled" + vertical-resizable-panes-hidden-initially="true" + vertical-resizable-panes-default-size="196" + vertical-resizable-panes-min-size="144" + vertical-resizable-panes-max-size="336" + vertical-resizable-panes-resize-on="left-pane-resize-all" + ) + .div(vertical-resizable-top) + + .loading-panel( + ng-show="(!editor.sharejs_doc || editor.opening) && !editor.error_state", + style=showRichText ? "top: 32px" : "", + ) + span(ng-show="editor.open_doc_id") + i.fa.fa-spin.fa-refresh + |   #{translate("loading")}… + span(ng-show="!editor.open_doc_id") + i.fa.fa-arrow-left + |   #{translate("open_a_file_on_the_left")} + + if moduleIncludesAvailable('editor:main') + != moduleIncludes('editor:main', locals) + else + .toolbar.toolbar-editor + + .multi-selection-ongoing( + ng-show="multiSelectedCount > 0" + ) + .multi-selection-message + h4 {{ multiSelectedCount }} #{translate('files_selected')} + + include ./source-editor + + if !isRestrictedTokenMember + include ./review-panel + + .div(vertical-resizable-bottom) + include ./symbol-palette diff --git a/services/web/app/views/project/editor/editor.pug b/services/web/app/views/project/editor/editor.pug index 3846b967b7..a6a364fe2e 100644 --- a/services/web/app/views/project/editor/editor.pug +++ b/services/web/app/views/project/editor/editor.pug @@ -12,80 +12,10 @@ div.full-size( custom-toggler-msg-when-open=hasFeature('custom-togglers') ? translate("tooltip_hide_pdf") : false custom-toggler-msg-when-closed=hasFeature('custom-togglers') ? translate("tooltip_show_pdf") : false ) - .ui-layout-center( - ng-controller="ReviewPanelController", - ng-class="{\ - 'rp-unsupported': editor.showRichText,\ - 'rp-state-current-file': (reviewPanel.subView === SubViews.CUR_FILE),\ - 'rp-state-current-file-expanded': (reviewPanel.subView === SubViews.CUR_FILE && ui.reviewPanelOpen),\ - 'rp-state-current-file-mini': (reviewPanel.subView === SubViews.CUR_FILE && !ui.reviewPanelOpen),\ - 'rp-state-overview': (reviewPanel.subView === SubViews.OVERVIEW),\ - 'rp-size-mini': ui.miniReviewPanelVisible,\ - 'rp-size-expanded': ui.reviewPanelOpen,\ - 'rp-layout-left': reviewPanel.layoutToLeft,\ - 'rp-loading-threads': reviewPanel.loadingThreads,\ - }" - ) - .loading-panel( - ng-show="(!editor.sharejs_doc || editor.opening) && !editor.error_state", - style=showRichText ? "top: 32px" : "", - ) - span(ng-show="editor.open_doc_id") - i.fa.fa-spin.fa-refresh - |   #{translate("loading")}… - span(ng-show="!editor.open_doc_id") - i.fa.fa-arrow-left - |   #{translate("open_a_file_on_the_left")} - - if moduleIncludesAvailable('editor:main') - != moduleIncludes('editor:main', locals) - else - .toolbar.toolbar-editor - - .multi-selection-ongoing( - ng-show="multiSelectedCount > 0" - ) - .multi-selection-message - h4 {{ multiSelectedCount }} #{translate('files_selected')} - - #editor( - ace-editor="editor", - ng-if="!editor.showRichText", - ng-show="!!editor.sharejs_doc && !editor.opening && multiSelectedCount === 0 && !editor.error_state", - theme="settings.editorTheme", - keybindings="settings.mode", - font-size="settings.fontSize", - auto-complete="settings.autoComplete", - auto-pair-delimiters="settings.autoPairDelimiters", - spell-check="!anonymous", - spell-check-language="project.spellCheckLanguage" - highlights="onlineUserCursorHighlights[editor.open_doc_id]" - show-print-margin="false", - sharejs-doc="editor.sharejs_doc", - last-updated="editor.last_updated", - cursor-position="editor.cursorPosition", - goto-line="editor.gotoLine", - resize-on="layout:main:resize,layout:pdf:resize,layout:review:resize,review-panel:toggle,layout:flat-screen:toggle", - annotations="pdf.logEntryAnnotations[editor.open_doc_id]", - read-only="!permissions.write", - file-name="editor.open_doc_name", - on-ctrl-enter="recompileViaKey", - on-save="recompileViaKey", - on-ctrl-j="toggleReviewPanel", - on-ctrl-shift-c="addNewCommentFromKbdShortcut", - on-ctrl-shift-a="toggleTrackChangesFromKbdShortcut", - syntax-validation="settings.syntaxValidation", - review-panel="reviewPanel", - events-bridge="reviewPanelEventsBridge" - track-changes= "editor.trackChanges", - doc-id="editor.open_doc_id" - renderer-data="reviewPanel.rendererData" - font-family="settings.fontFamily" - line-height="settings.lineHeight" - ) - - if !isRestrictedTokenMember - include ./review-panel + if showSymbolPalette + include ./editor-with-symbol-palette + else + include ./editor-no-symbol-palette .ui-layout-east div(ng-if="ui.pdfLayout == 'sideBySide'") diff --git a/services/web/app/views/project/editor/source-editor.pug b/services/web/app/views/project/editor/source-editor.pug new file mode 100644 index 0000000000..7fc51ce40a --- /dev/null +++ b/services/web/app/views/project/editor/source-editor.pug @@ -0,0 +1,35 @@ +#editor( + ace-editor="editor", + ng-if="!editor.showRichText", + ng-show="!!editor.sharejs_doc && !editor.opening && multiSelectedCount === 0 && !editor.error_state", + theme="settings.editorTheme", + keybindings="settings.mode", + font-size="settings.fontSize", + auto-complete="settings.autoComplete", + auto-pair-delimiters="settings.autoPairDelimiters", + spell-check="!anonymous", + spell-check-language="project.spellCheckLanguage" + highlights="onlineUserCursorHighlights[editor.open_doc_id]" + show-print-margin="false", + sharejs-doc="editor.sharejs_doc", + last-updated="editor.last_updated", + cursor-position="editor.cursorPosition", + goto-line="editor.gotoLine", + resize-on="layout:main:resize,layout:pdf:resize,layout:review:resize,review-panel:toggle,layout:flat-screen:toggle", + annotations="pdf.logEntryAnnotations[editor.open_doc_id]", + read-only="!permissions.write", + file-name="editor.open_doc_name", + on-ctrl-enter="recompileViaKey", + on-save="recompileViaKey", + on-ctrl-j="toggleReviewPanel", + on-ctrl-shift-c="addNewCommentFromKbdShortcut", + on-ctrl-shift-a="toggleTrackChangesFromKbdShortcut", + syntax-validation="settings.syntaxValidation", + review-panel="reviewPanel", + events-bridge="reviewPanelEventsBridge" + track-changes= "editor.trackChanges", + doc-id="editor.open_doc_id" + renderer-data="reviewPanel.rendererData" + font-family="settings.fontFamily" + line-height="settings.lineHeight" +) diff --git a/services/web/app/views/project/editor/symbol-palette.pug b/services/web/app/views/project/editor/symbol-palette.pug new file mode 100644 index 0000000000..660aa78cc1 --- /dev/null +++ b/services/web/app/views/project/editor/symbol-palette.pug @@ -0,0 +1,2 @@ +if showSymbolPalette + symbol-palette(show="editor.showSymbolPalette" handle-select="editor.insertSymbol") diff --git a/services/web/frontend/js/features/symbol-palette/components/symbol-palette-content.js b/services/web/frontend/js/features/symbol-palette/components/symbol-palette-content.js index 127026e1b8..2e496863d7 100644 --- a/services/web/frontend/js/features/symbol-palette/components/symbol-palette-content.js +++ b/services/web/frontend/js/features/symbol-palette/components/symbol-palette-content.js @@ -55,7 +55,7 @@ export default function SymbolPaletteContent({ handleSelect }) { }, []) return ( - +
({ +export default App.directive('verticalResizablePanes', (localStorage, ide) => ({ restrict: 'A', link(scope, element, attrs) { const name = attrs.verticalResizablePanes const minSize = attrs.verticalResizablePanesMinSize const maxSize = attrs.verticalResizablePanesMaxSize + const defaultSize = attrs.verticalResizablePanesDefaultSize let storedSize = null let manualResizeIncoming = false @@ -41,6 +42,8 @@ export default App.directive('verticalResizablePanes', localStorage => ({ } const toggledExternally = attrs.verticalResizablePanesToggledExternallyOn + const hiddenExternally = attrs.verticalResizablePanesHiddenExternallyOn + const hiddenInitially = attrs.verticalResizablePanesHiddenInitially const resizeOn = attrs.verticalResizablePanesResizeOn const resizerDisabledClass = `${layoutOptions.south.resizerClass}-disabled` @@ -82,6 +85,16 @@ export default App.directive('verticalResizablePanes', localStorage => ({ }) } + if (hiddenExternally) { + ide.$scope.$on(hiddenExternally, (e, open) => { + if (open) { + layoutHandle.show('south') + } else { + layoutHandle.hide('south') + } + }) + } + if (resizeOn) { scope.$on(resizeOn, () => { layoutHandle.resizeAll() @@ -96,6 +109,10 @@ export default App.directive('verticalResizablePanes', localStorage => ({ layoutOptions.south.minSize = minSize } + if (defaultSize) { + layoutOptions.south.size = defaultSize + } + // The `drag` event fires only when the user manually resizes the panes; the `resize` event fires even when // the layout library internally resizes itself. In order to get explicit user-initiated resizes, we need to // listen to `drag` events. However, when the `drag` event fires, the panes aren't yet finished sizing so we @@ -106,5 +123,8 @@ export default App.directive('verticalResizablePanes', localStorage => ({ layoutOptions.south.onresize = handleResize const layoutHandle = element.layout(layoutOptions) + if (hiddenInitially === 'true') { + layoutHandle.hide('south') + } }, })) diff --git a/services/web/frontend/js/ide/editor/EditorManager.js b/services/web/frontend/js/ide/editor/EditorManager.js index 8fd4fd5ed0..10688cbea3 100644 --- a/services/web/frontend/js/ide/editor/EditorManager.js +++ b/services/web/frontend/js/ide/editor/EditorManager.js @@ -19,6 +19,7 @@ import './components/spellMenu' import './directives/aceEditor' import './directives/toggleSwitch' import './controllers/SavingNotificationController' +import '../../features/symbol-palette/controllers/symbol-palette-controller' let EditorManager export default EditorManager = (function () { @@ -27,7 +28,7 @@ export default EditorManager = (function () { this.prototype._syncTimeout = null } - constructor(ide, $scope, localStorage) { + constructor(ide, $scope, localStorage, eventTracking) { this.ide = ide this.editorOpenDocEpoch = 0 // track pending document loads this.$scope = $scope @@ -40,6 +41,19 @@ export default EditorManager = (function () { trackChanges: false, wantTrackChanges: false, showRichText: this.showRichText(), + showSymbolPalette: false, + toggleSymbolPalette: () => { + const newValue = !this.$scope.editor.showSymbolPalette + this.$scope.editor.showSymbolPalette = newValue + ide.$scope.$emit('symbol-palette-toggled', newValue) + eventTracking.sendMB( + newValue ? 'symbol-palette-show' : 'symbol-palette-hide' + ) + }, + insertSymbol: symbol => { + ide.$scope.$emit('editor:replace-selection', symbol.command) + eventTracking.sendMB('symbol-palette-insert') + }, } this.$scope.$on('entity:selected', (event, entity) => { diff --git a/services/web/frontend/js/ide/editor/directives/aceEditor.js b/services/web/frontend/js/ide/editor/directives/aceEditor.js index f32efa0393..0fa47cfaba 100644 --- a/services/web/frontend/js/ide/editor/directives/aceEditor.js +++ b/services/web/frontend/js/ide/editor/directives/aceEditor.js @@ -133,6 +133,11 @@ App.directive( editor.setOption('behavioursEnabled', scope.autoPairDelimiters || false) editor.setOption('wrapBehavioursEnabled', false) + ide.$scope.$on('editor:replace-selection', (event, text) => { + editor.focus() + editor.insert(text) + }) + scope.$watch('autoPairDelimiters', autoPairDelimiters => { if (autoPairDelimiters) { return editor.setOption('behavioursEnabled', true) diff --git a/services/web/frontend/js/ide/review-panel/controllers/ReviewPanelController.js b/services/web/frontend/js/ide/review-panel/controllers/ReviewPanelController.js index 9c09692de4..9158f6a171 100644 --- a/services/web/frontend/js/ide/review-panel/controllers/ReviewPanelController.js +++ b/services/web/frontend/js/ide/review-panel/controllers/ReviewPanelController.js @@ -1224,11 +1224,11 @@ export default App.controller( } } - return ($scope.openTrackChangesUpgradeModal = () => + $scope.openTrackChangesUpgradeModal = () => $modal.open({ templateUrl: 'trackChangesUpgradeModalTemplate', controller: 'TrackChangesUpgradeModalController', scope: $scope.$new(), - })) + }) } ) diff --git a/services/web/frontend/stories/symbol-palette.stories.js b/services/web/frontend/stories/symbol-palette.stories.js index 609e050684..b0a768e246 100644 --- a/services/web/frontend/stories/symbol-palette.stories.js +++ b/services/web/frontend/stories/symbol-palette.stories.js @@ -3,7 +3,11 @@ import React from 'react' import SymbolPalette from '../js/features/symbol-palette/components/symbol-palette' export const Interactive = args => { - return + return ( +
+ +
+ ) } export default { diff --git a/services/web/frontend/stylesheets/app/editor/symbol-palette.less b/services/web/frontend/stylesheets/app/editor/symbol-palette.less index 96c2b80c26..d427badf03 100644 --- a/services/web/frontend/stylesheets/app/editor/symbol-palette.less +++ b/services/web/frontend/stylesheets/app/editor/symbol-palette.less @@ -1,14 +1,20 @@ +.symbol-palette-container { + height: 100%; +} + .symbol-palette { display: flex; flex-direction: column; background: @symbol-palette-bg; color: @symbol-palette-color; - max-width: 600px; + width: 100%; + height: 100%; } .symbol-palette-header { flex-shrink: 0; display: flex; + flex-wrap: wrap; justify-content: space-between; font-family: @font-family-sans-serif; font-size: 16px; @@ -25,6 +31,7 @@ .symbol-palette-body { flex: 1; + overflow-y: auto; } .symbol-palette-items {