mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #3007 from overleaf/pr-file-outline-extras
File outline extras GitOrigin-RevId: c35c4f35dce280c9f44c02b567df9734943d0cb0
This commit is contained in:
parent
79b6f6e473
commit
0e9771ac09
16 changed files with 341 additions and 146 deletions
|
@ -1,4 +1,13 @@
|
||||||
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(
|
||||||
|
ng-controller="FileTreeController"
|
||||||
|
ng-class="{ 'multi-selected': multiSelectedCount > 0 }"
|
||||||
|
ng-show="ui.view != 'history' || !history.isV2"
|
||||||
|
vertical-resizable-panes=user.alphaProgram && "outline-resizer"
|
||||||
|
vertical-resizable-panes-toggled-externally-on=user.alphaProgram && "outline-toggled"
|
||||||
|
)
|
||||||
|
.file-tree(
|
||||||
|
vertical-resizable-top=user.alphaProgram
|
||||||
|
)
|
||||||
.toolbar.toolbar-filetree(ng-if="permissions.write")
|
.toolbar.toolbar-filetree(ng-if="permissions.write")
|
||||||
a(
|
a(
|
||||||
href,
|
href,
|
||||||
|
@ -88,15 +97,17 @@ aside.file-tree(ng-controller="FileTreeController", ng-class="{ 'multi-selected'
|
||||||
|
|
||||||
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"
|
||||||
)
|
)
|
||||||
|
|
|
@ -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,7 +46,6 @@ 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>
|
||||||
</button>
|
|
||||||
<OverlayTrigger placement="top" overlay={tooltip} delayHide={100}>
|
<OverlayTrigger placement="top" overlay={tooltip} delayHide={100}>
|
||||||
<a
|
<a
|
||||||
href="/beta/participate"
|
href="/beta/participate"
|
||||||
|
@ -41,11 +54,12 @@ function OutlinePane({ isTexFile, outline, jumpToLine }) {
|
||||||
className="outline-header-beta-badge"
|
className="outline-header-beta-badge"
|
||||||
>
|
>
|
||||||
<span className="sr-only">
|
<span className="sr-only">
|
||||||
The File outline is a beta feature. Click here to manage your beta
|
The File outline is a beta feature. Click here to manage your
|
||||||
program membership.
|
beta program membership.
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</OverlayTrigger>
|
</OverlayTrigger>
|
||||||
|
</button>
|
||||||
</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
|
localStorage can throw browser exceptions, for example if it is full
|
||||||
We don't use localStorage for anything critical, on in that case just
|
We don't use localStorage for anything critical, on in that case just
|
||||||
fail gracefully.
|
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;
|
||||||
|
|
Loading…
Reference in a new issue