Merge pull request #3007 from overleaf/pr-file-outline-extras

File outline extras

GitOrigin-RevId: c35c4f35dce280c9f44c02b567df9734943d0cb0
This commit is contained in:
Paulo Jorge Reis
2020-07-17 02:06:42 +00:00
committed by Copybot
parent 79b6f6e473
commit 0e9771ac09
16 changed files with 341 additions and 146 deletions
@@ -1,102 +1,113 @@
aside.file-tree(ng-controller="FileTreeController", ng-class="{ 'multi-selected': multiSelectedCount > 0 }", ng-show="ui.view != 'history' || !history.isV2").full-size aside.editor-sidebar.full-size(
.toolbar.toolbar-filetree(ng-if="permissions.write") ng-controller="FileTreeController"
a( ng-class="{ 'multi-selected': multiSelectedCount > 0 }"
href, ng-show="ui.view != 'history' || !history.isV2"
ng-click="openNewDocModal()", vertical-resizable-panes=user.alphaProgram && "outline-resizer"
tooltip-html="'"+translate('new_file').replace(' ', '<br>')+"'", vertical-resizable-panes-toggled-externally-on=user.alphaProgram && "outline-toggled"
tooltip-placement="bottom" )
) .file-tree(
i.fa.fa-fw.fa-file vertical-resizable-top=user.alphaProgram
a(
href,
ng-click="openNewFolderModal()",
tooltip-html="'"+translate('new_folder').replace(' ', '<br>')+"'",
tooltip-placement="bottom"
)
i.fa.fa-fw.fa-folder
a(
href,
ng-click="openUploadFileModal()",
tooltip=translate('upload'),
tooltip-placement="bottom"
)
i.fa.fa-fw.fa-upload
.toolbar-right
a(
href,
ng-click="startRenamingSelected()",
tooltip=translate('rename'),
tooltip-placement="bottom",
ng-show="multiSelectedCount == 0"
)
i.fa.fa-fw.fa-pencil
a(
href,
ng-click="openDeleteModalForSelected()",
tooltip=translate('delete'),
tooltip-placement="bottom",
tooltip-append-to-body="true"
)
i.fa.fa-fw.fa-trash-o
.file-tree-inner(
ng-if="rootFolder",
ng-controller="FileTreeRootFolderController",
ng-class="{ 'no-toolbar': !permissions.write }"
) )
ul.list-unstyled.file-tree-list( .toolbar.toolbar-filetree(ng-if="permissions.write")
droppable="permissions.write" a(
accept=".entity-name" href,
on-drop-callback="onDrop" ng-click="openNewDocModal()",
tooltip-html="'"+translate('new_file').replace(' ', '<br>')+"'",
tooltip-placement="bottom"
)
i.fa.fa-fw.fa-file
a(
href,
ng-click="openNewFolderModal()",
tooltip-html="'"+translate('new_folder').replace(' ', '<br>')+"'",
tooltip-placement="bottom"
)
i.fa.fa-fw.fa-folder
a(
href,
ng-click="openUploadFileModal()",
tooltip=translate('upload'),
tooltip-placement="bottom"
)
i.fa.fa-fw.fa-upload
.toolbar-right
a(
href,
ng-click="startRenamingSelected()",
tooltip=translate('rename'),
tooltip-placement="bottom",
ng-show="multiSelectedCount == 0"
)
i.fa.fa-fw.fa-pencil
a(
href,
ng-click="openDeleteModalForSelected()",
tooltip=translate('delete'),
tooltip-placement="bottom",
tooltip-append-to-body="true"
)
i.fa.fa-fw.fa-trash-o
.file-tree-inner(
ng-if="rootFolder",
ng-controller="FileTreeRootFolderController",
ng-class="{ 'no-toolbar': !permissions.write }"
) )
li( ul.list-unstyled.file-tree-list(
ng-show="ui.pdfLayout == 'flat' && (ui.view == 'editor' || ui.view == 'pdf' || ui.view == 'file')" droppable="permissions.write"
ng-class="{ 'selected': ui.view == 'pdf' }" accept=".entity-name"
ng-controller="PdfViewToggleController" on-drop-callback="onDrop"
) )
.entity li(
.entity-name( ng-show="ui.pdfLayout == 'flat' && (ui.view == 'editor' || ui.view == 'pdf' || ui.view == 'file')"
ng-click="togglePdfView()" ng-class="{ 'selected': ui.view == 'pdf' }"
) ng-controller="PdfViewToggleController"
i.fa.fa-fw.toggle )
i.fa.fa-fw.fa-file-pdf-o .entity
| PDF .entity-name(
ng-click="togglePdfView()"
)
i.fa.fa-fw.toggle
i.fa.fa-fw.fa-file-pdf-o
| PDF
file-entity( file-entity(
entity="entity", entity="entity",
permissions="permissions", permissions="permissions",
ng-repeat="entity in rootFolder.children | orderBy:[orderByFoldersFirst, 'name']" ng-repeat="entity in rootFolder.children | orderBy:[orderByFoldersFirst, 'name']"
) )
li(ng-show="deletedDocs.length > 0 && ui.view == 'history'") li(ng-show="deletedDocs.length > 0 && ui.view == 'history'")
h3 #{translate("deleted_files")} h3 #{translate("deleted_files")}
li( li(
ng-class="{ 'selected': entity.selected }", ng-class="{ 'selected': entity.selected }",
ng-repeat="entity in deletedDocs | orderBy:'name'", ng-repeat="entity in deletedDocs | orderBy:'name'",
ng-controller="FileTreeEntityController", ng-controller="FileTreeEntityController",
ng-show="ui.view == 'history'" ng-show="ui.view == 'history'"
) )
.entity .entity
.entity-name( .entity-name(
ng-click="select($event)" ng-click="select($event)"
) )
//- Just a spacer to align with folders //- Just a spacer to align with folders
i.fa.fa-fw.toggle i.fa.fa-fw.toggle
i.fa.fa-fw.fa-file i.fa.fa-fw.fa-file
span {{ entity.name }}
span {{ entity.name }}
if user.alphaProgram if user.alphaProgram
.outline-container( .outline-container(
vertical-resizable-bottom
ng-controller="OutlineController" ng-controller="OutlineController"
) )
outline-pane( outline-pane(
is-tex-file="isTexFile" is-tex-file="isTexFile"
outline="outline" outline="outline"
project-id="project_id"
jump-to-line="jumpToLine" jump-to-line="jumpToLine"
on-toggle="onToggle"
) )
@@ -1,4 +1,4 @@
aside.file-tree.full-size( aside.editor-sidebar.full-size(
ng-controller="HistoryV2FileTreeController" ng-controller="HistoryV2FileTreeController"
ng-if="ui.view == 'history' && history.isV2" ng-if="ui.view == 'history' && history.isV2"
) )
+1
View File
@@ -40,6 +40,7 @@ import './ide/hotkeys/index'
import './ide/wordcount/index' import './ide/wordcount/index'
import './ide/directives/layout' import './ide/directives/layout'
import './ide/directives/validFile' import './ide/directives/validFile'
import './ide/directives/verticalResizablePanes'
import './ide/services/ide' import './ide/services/ide'
import './directives/focus' import './directives/focus'
import './directives/fineUpload' import './directives/fineUpload'
@@ -13,6 +13,7 @@
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/ */
import App from '../../base' import App from '../../base'
import _ from 'lodash'
import 'libs/jquery-layout' import 'libs/jquery-layout'
import 'libs/jquery.ui.touch-punch' import 'libs/jquery.ui.touch-punch'
@@ -233,9 +234,16 @@ ng-click=\"handleClick()\">\
} }
// Save state when exiting // Save state when exiting
$(window).unload(() => $(window).unload(() => {
ide.localStorage(`layout.${name}`, element.layout().readState()) // Save only the state properties for the current layout, ignoring sublayouts inside it.
) // If we save sublayouts state (`children`), the layout library will use it when
// initializing. This raises errors when the sublayout elements aren't available (due to
// being loaded at init or just not existing for the current project/user).
const stateToSave = _.mapValues(element.layout().readState(), pane =>
_.omit(pane, 'children')
)
ide.localStorage(`layout.${name}`, stateToSave)
})
if (attrs.openEast != null) { if (attrs.openEast != null) {
scope.$watch(attrs.openEast, function(value, oldValue) { scope.$watch(attrs.openEast, function(value, oldValue) {
@@ -0,0 +1,93 @@
import App from '../../base'
const layoutOptions = {
center: {
paneSelector: '[vertical-resizable-top]',
paneClass: 'vertical-resizable-top',
size: 'auto'
},
south: {
paneSelector: '[vertical-resizable-bottom]',
paneClass: 'vertical-resizable-bottom',
resizerClass: 'vertical-resizable-resizer',
resizerCursor: 'row-resize',
size: 'auto',
resizable: true,
closable: false,
slidable: false,
spacing_open: 6,
spacing_closed: 6,
maxSize: '75%'
}
}
export default App.directive('verticalResizablePanes', localStorage => ({
restrict: 'A',
link(scope, element, attrs) {
let name = attrs.verticalResizablePanes
let storedSize = null
let manualResizeIncoming = false
if (name) {
const storageKey = `vertical-resizable:${name}:south-size`
storedSize = localStorage(storageKey)
$(window).unload(() => {
if (storedSize) {
localStorage(storageKey, storedSize)
}
})
}
const toggledExternally = attrs.verticalResizablePanesToggledExternallyOn
const resizerDisabledClass = `${layoutOptions.south.resizerClass}-disabled`
function enableResizer() {
if (layoutHandle.resizers && layoutHandle.resizers.south) {
layoutHandle.resizers.south.removeClass(resizerDisabledClass)
}
}
function disableResizer() {
if (layoutHandle.resizers && layoutHandle.resizers.south) {
layoutHandle.resizers.south.addClass(resizerDisabledClass)
}
}
function handleDragEnd() {
manualResizeIncoming = true
}
function handleResize(paneName, paneEl, paneState) {
if (manualResizeIncoming) {
storedSize = paneState.size
}
manualResizeIncoming = false
}
if (toggledExternally) {
scope.$on(toggledExternally, (e, open) => {
let newSize = 'auto'
if (open) {
if (storedSize) {
newSize = storedSize
}
enableResizer()
} else {
disableResizer()
}
layoutHandle.sizePane('south', newSize)
})
}
// 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
// get the pane size *before* the resize happens. We do get the correct size in the next `resize` event.
// The solution to work around this is to set up a flag in `drag` events which tells the next `resize` event
// that it was user-initiated (therefore, storing the value).
layoutOptions.south.ondrag_end = handleDragEnd
layoutOptions.south.onresize = handleResize
const layoutHandle = element.layout(layoutOptions)
}
}))
@@ -132,6 +132,7 @@ export default (EditorManager = (function() {
this.$scope.ui.view = 'editor' this.$scope.ui.view = 'editor'
const done = () => { const done = () => {
this.$scope.$broadcast('doc:after-opened')
if (options.gotoLine != null) { if (options.gotoLine != null) {
// allow Ace to display document before moving, delay until next tick // allow Ace to display document before moving, delay until next tick
// added delay to make this happen later that gotoStoredPosition in // added delay to make this happen later that gotoStoredPosition in
@@ -14,8 +14,8 @@ class OutlineManager {
this.isTexFile = false this.isTexFile = false
this.outline = [] this.outline = []
scope.$watch('editor.sharejs_doc', shareJsDoc => { scope.$on('doc:after-opened', () => {
this.shareJsDoc = shareJsDoc this.shareJsDoc = scope.editor.shareJsDoc
this.isTexFile = isValidTeXFile(scope.editor.open_doc_name) this.isTexFile = isValidTeXFile(scope.editor.open_doc_name)
this.updateOutline() this.updateOutline()
this.broadcastChangeEvent() this.broadcastChangeEvent()
@@ -1,15 +1,28 @@
import React, { useState } from 'react' import React, { useState, useEffect } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { OverlayTrigger, Tooltip } from 'react-bootstrap' import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import classNames from 'classnames' import classNames from 'classnames'
import OutlineRoot from './OutlineRoot' import OutlineRoot from './OutlineRoot'
import localStorage from '../../../modules/localStorage'
function OutlinePane({ isTexFile, outline, jumpToLine }) { function OutlinePane({ isTexFile, outline, projectId, jumpToLine, onToggle }) {
const [expanded, setExpanded] = useState(true) const storageKey = `file_outline.expanded.${projectId}`
const [expanded, setExpanded] = useState(() => {
const storedExpandedState = localStorage(storageKey) !== false
return storedExpandedState
})
const isOpen = isTexFile && expanded
useEffect(
() => {
onToggle(isOpen)
},
[isOpen]
)
const expandCollapseIconClasses = classNames('fa', 'outline-caret-icon', { const expandCollapseIconClasses = classNames('fa', 'outline-caret-icon', {
'fa-angle-down': expanded, 'fa-angle-down': isOpen,
'fa-angle-right': !expanded 'fa-angle-right': !isOpen
}) })
const headerClasses = classNames('outline-pane', { const headerClasses = classNames('outline-pane', {
@@ -18,6 +31,7 @@ function OutlinePane({ isTexFile, outline, jumpToLine }) {
function handleExpandCollapseClick() { function handleExpandCollapseClick() {
if (isTexFile) { if (isTexFile) {
localStorage(storageKey, !expanded)
setExpanded(!expanded) setExpanded(!expanded)
} }
} }
@@ -32,20 +46,20 @@ function OutlinePane({ isTexFile, outline, jumpToLine }) {
> >
<i className={expandCollapseIconClasses} /> <i className={expandCollapseIconClasses} />
<h4 className="outline-header-name">File outline</h4> <h4 className="outline-header-name">File outline</h4>
<OverlayTrigger placement="top" overlay={tooltip} delayHide={100}>
<a
href="/beta/participate"
target="_blank"
rel="noopener noreferrer"
className="outline-header-beta-badge"
>
<span className="sr-only">
The File outline is a beta feature. Click here to manage your
beta program membership.
</span>
</a>
</OverlayTrigger>
</button> </button>
<OverlayTrigger placement="top" overlay={tooltip} delayHide={100}>
<a
href="/beta/participate"
target="_blank"
rel="noopener noreferrer"
className="outline-header-beta-badge"
>
<span className="sr-only">
The File outline is a beta feature. Click here to manage your beta
program membership.
</span>
</a>
</OverlayTrigger>
</header> </header>
{expanded && isTexFile ? ( {expanded && isTexFile ? (
<div className="outline-body"> <div className="outline-body">
@@ -65,7 +79,9 @@ const tooltip = (
OutlinePane.propTypes = { OutlinePane.propTypes = {
isTexFile: PropTypes.bool.isRequired, isTexFile: PropTypes.bool.isRequired,
outline: PropTypes.array.isRequired, outline: PropTypes.array.isRequired,
jumpToLine: PropTypes.func.isRequired projectId: PropTypes.string.isRequired,
jumpToLine: PropTypes.func.isRequired,
onToggle: PropTypes.func
} }
export default OutlinePane export default OutlinePane
@@ -18,10 +18,22 @@ App.controller('OutlineController', function($scope, ide) {
$scope.jumpToLine = lineNo => { $scope.jumpToLine = lineNo => {
ide.outlineManager.jumpToLine(lineNo) ide.outlineManager.jumpToLine(lineNo)
} }
$scope.onToggle = isOpen => {
$scope.$applyAsync(() => {
$scope.$emit('outline-toggled', isOpen)
})
}
}) })
// Wrap React component as Angular component. Only needed for "top-level" component // Wrap React component as Angular component. Only needed for "top-level" component
App.component( App.component(
'outlinePane', 'outlinePane',
react2angular(OutlinePane, ['outline', 'jumpToLine', 'isTexFile']) react2angular(OutlinePane, [
'outline',
'jumpToLine',
'projectId',
'isTexFile',
'onToggle'
])
) )
@@ -1,13 +1,17 @@
angular.module('localStorage', []).value('localStorage', function(...args) { angular.module('localStorage', []).value('localStorage', localStorage)
/*
localStorage can throw browser exceptions, for example if it is full /*
We don't use localStorage for anything critical, on in that case just localStorage can throw browser exceptions, for example if it is full
fail gracefully. We don't use localStorage for anything critical, on in that case just
*/ fail gracefully.
*/
function localStorage(...args) {
try { try {
return $.localStorage(...args) return $.localStorage(...args)
} catch (e) { } catch (e) {
console.error('localStorage exception', e) console.error('localStorage exception', e)
return null return null
} }
}) }
export default localStorage
@@ -61,6 +61,7 @@
@import 'components/popovers.less'; @import 'components/popovers.less';
@import 'components/carousel.less'; @import 'components/carousel.less';
@import 'components/daterange-picker'; @import 'components/daterange-picker';
@import 'components/vertical-resizable-panes.less';
// ngTagsInput // ngTagsInput
@import 'components/tags-input.less'; @import 'components/tags-input.less';
@@ -8,9 +8,16 @@
} }
} }
.file-tree { .editor-sidebar {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
}
.file-tree {
display: flex !important; // To work around jQuery layout's inline styles
flex-direction: column;
flex-grow: 1;
max-height: 100%;
.toolbar.toolbar-filetree { .toolbar.toolbar-filetree {
.toolbar-small-mixin; .toolbar-small-mixin;
@@ -1,10 +1,5 @@
@outline-v-rhythm: 24px;
@outline-h-rhythm: 24px;
@outline-item-h-padding: @outline-h-rhythm * 0.25;
.outline-container { .outline-container {
width: 100%; width: 100%;
max-height: 50%; // While we don't support resizing.
background-color: @file-tree-bg; background-color: @file-tree-bg;
} }
@@ -13,7 +8,7 @@
flex-flow: column; flex-flow: column;
height: 100%; height: 100%;
font-size: @font-size-small; font-size: @font-size-small;
color: #fff; color: @file-tree-item-color;
} }
.outline-pane-disabled { .outline-pane-disabled {
@@ -24,29 +19,33 @@
.toolbar-small-mixin; .toolbar-small-mixin;
.toolbar-alt-mixin; .toolbar-alt-mixin;
display: flex; display: flex;
align-items: center;
padding-right: @outline-h-rhythm * 0.25;
flex-shrink: 0; flex-shrink: 0;
border-bottom: 1px solid @toolbar-border-color;
border-top: 1px solid @toolbar-border-color;
} }
.outline-header-expand-collapse-btn { .outline-header-expand-collapse-btn {
display: inline-block; display: flex;
align-items: center;
background-color: transparent; background-color: transparent;
border: 0; border: 0;
padding: 0; padding: 0 (@outline-h-rhythm * 0.25) 0 0;
font-size: inherit; font-size: inherit;
vertical-align: inherit; vertical-align: inherit;
color: #fff; color: @file-tree-item-color;
flex-grow: 1; flex: 1 0 100%;
text-align: left; text-align: left;
white-space: nowrap; white-space: nowrap;
overflow: hidden; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
border-radius: @border-radius-base;
text-overflow: ellipsis;
&:hover, &:hover,
&:focus { &:focus {
outline: 0; outline: 0;
background-color: @ol-blue-gray-5; }
&:hover {
background-color: @outline-header-hover-bg;
}
&:hover[disabled] {
background-color: transparent;
} }
} }
@@ -54,8 +53,13 @@
display: inline-block; display: inline-block;
font-family: @font-family-sans-serif; font-family: @font-family-sans-serif;
font-size: @font-size-small; font-size: @font-size-small;
color: #fff; color: @file-tree-item-color;
font-weight: 700;
margin: 0; margin: 0;
flex-grow: 1;
flex-shrink: 1;
overflow: hidden;
text-overflow: ellipsis;
} }
.outline-header-beta-badge { .outline-header-beta-badge {
@@ -87,7 +91,7 @@
} }
.outline-body-no-elements { .outline-body-no-elements {
color: @ol-blue-gray-2; color: @outline-no-items-color;
text-align: center; text-align: center;
padding: @outline-v-rhythm @outline-h-rhythm (@outline-v-rhythm * 2); padding: @outline-v-rhythm @outline-h-rhythm (@outline-v-rhythm * 2);
margin-right: -(@outline-h-rhythm * 0.25); margin-right: -(@outline-h-rhythm * 0.25);
@@ -95,11 +99,11 @@
.outline-body-link { .outline-body-link {
display: block; display: block;
color: #fff; color: @file-tree-item-color;
text-decoration: underline; text-decoration: underline;
&:hover, &:hover,
&:focus { &:focus {
color: #fff; color: @file-tree-item-color;
text-decoration: underline; text-decoration: underline;
} }
} }
@@ -111,7 +115,7 @@
&::before { &::before {
content: ''; content: '';
background-color: @ol-blue-gray-3; background-color: @outline-line-guide-color;
top: @outline-h-rhythm / 4; top: @outline-h-rhythm / 4;
bottom: @outline-h-rhythm / 4; bottom: @outline-h-rhythm / 4;
width: 1px; width: 1px;
@@ -127,9 +131,6 @@
} }
} }
.outline-item {
}
.outline-item-no-children { .outline-item-no-children {
padding-left: @outline-h-rhythm - @outline-item-h-padding; padding-left: @outline-h-rhythm - @outline-item-h-padding;
} }
@@ -149,19 +150,19 @@
position: relative; position: relative;
z-index: 1; z-index: 1;
background-color: @file-tree-bg; background-color: @file-tree-bg;
color: @ol-blue-gray-2; color: @outline-expand-collapse-color;
margin-right: -(@outline-item-h-padding); margin-right: -(@outline-item-h-padding);
border-radius: @border-radius-base; border-radius: @border-radius-base;
&:hover, &:hover,
&:focus { &:focus {
outline: 0; outline: 0;
background-color: @ol-blue-gray-5; background-color: @file-tree-item-hover-bg;
} }
} }
.outline-item-link { .outline-item-link {
display: inline; display: inline;
color: #fff; color: @file-tree-item-color;
background-color: transparent; background-color: transparent;
border: 0; border: 0;
position: relative; position: relative;
@@ -174,7 +175,7 @@
&:hover, &:hover,
&:focus { &:focus {
outline: 0; outline: 0;
background-color: @ol-blue-gray-5; background-color: @file-tree-item-hover-bg;
} }
} }
@@ -0,0 +1,21 @@
.vertical-resizable-resizer {
background-color: @vertical-resizable-resizer-bg;
&:hover {
background-color: @vertical-resizable-resizer-hover-bg;
}
&::after {
content: '····';
display: block;
color: @ol-blue-gray-2;
text-align: center;
font-size: 20px;
line-height: 3px;
pointer-events: none;
}
}
.vertical-resizable-resizer-disabled {
pointer-events: none;
}
@@ -57,6 +57,13 @@
@content-alt-bg-color : @ol-blue-gray-0; @content-alt-bg-color : @ol-blue-gray-0;
// File outline
@outline-line-guide-color: @ol-blue-gray-1;
@outline-header-hover-bg: @file-tree-item-hover-bg;
@vertical-resizable-resizer-bg: @ol-blue-gray-1;
@vertical-resizable-resizer-hover-bg: @file-tree-item-hover-bg;
// Editor resizers // Editor resizers
@editor-resizer-bg-color : @ol-blue-gray-1; @editor-resizer-bg-color : @ol-blue-gray-1;
@editor-resizer-bg-color-dragging: @ol-blue-gray-1; @editor-resizer-bg-color-dragging: @ol-blue-gray-1;
@@ -1012,6 +1012,18 @@
@file-tree-multiselect-hover-bg: @ol-dark-blue; @file-tree-multiselect-hover-bg: @ol-dark-blue;
@file-tree-droppable-bg-color: @ol-blue-gray-2; @file-tree-droppable-bg-color: @ol-blue-gray-2;
// File outline
@outline-v-rhythm: 24px;
@outline-h-rhythm: 24px;
@outline-item-h-padding: @outline-h-rhythm * 0.25;
@outline-line-guide-color: @ol-blue-gray-3;
@outline-expand-collapse-color: @ol-blue-gray-2;
@outline-no-items-color: @ol-blue-gray-2;
@outline-header-hover-bg: @ol-blue-gray-6;
@vertical-resizable-resizer-bg: @ol-blue-gray-5;
@vertical-resizable-resizer-hover-bg: @ol-blue-gray-6;
// Editor resizers // Editor resizers
@editor-resizer-bg-color: @ol-blue-gray-5; @editor-resizer-bg-color: @ol-blue-gray-5;
@editor-resizer-bg-color-dragging: @ol-blue-gray-5; @editor-resizer-bg-color-dragging: @ol-blue-gray-5;