mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-01-26 10:01:57 +08:00
fix(composer): Show a scrollbar in the popout composer
Summary: Fixes T1990 Change the ScrollRegion component so that you can optionally provide a getScrollbar prop that resolves to a ScrollRegion.Scrollbar component. This allows you to easily put the Scrollbar outside of the ScrollRegion if necessary. Test Plan: Run tests Reviewers: evan Reviewed By: evan Maniphest Tasks: T1990 Differential Revision: https://phab.nylas.com/D1665
This commit is contained in:
parent
ffe87e4e9c
commit
8c764ca75d
5 changed files with 255 additions and 163 deletions
|
@ -11,6 +11,7 @@ _ = require 'underscore'
|
|||
{ResizableRegion,
|
||||
InjectedComponentSet,
|
||||
InjectedComponent,
|
||||
ScrollRegion,
|
||||
RetinaImg} = require 'nylas-component-kit'
|
||||
|
||||
FileUpload = require './file-upload'
|
||||
|
@ -151,58 +152,46 @@ class ComposerView extends React.Component
|
|||
|
||||
_renderComposer: =>
|
||||
<div className="composer-inner-wrap" onDragOver={@_onDragNoop} onDragLeave={@_onDragNoop} onDragEnd={@_onDragNoop} onDrop={@_onDrop}>
|
||||
|
||||
<div className="composer-cover"
|
||||
style={display: (if @state.isSending then "block" else "none")}>
|
||||
</div>
|
||||
|
||||
<div className="composer-content-wrap">
|
||||
{@_renderBodyScrollbar()}
|
||||
|
||||
<div className="composer-participant-actions">
|
||||
<span className="header-action"
|
||||
style={display: @state.showcc and 'none' or 'inline'}
|
||||
onClick={=> @_showAndFocusCc()}>Cc</span>
|
||||
<div className="composer-centered">
|
||||
<div className="composer-participant-actions">
|
||||
<span className="header-action"
|
||||
style={display: @state.showcc and 'none' or 'inline'}
|
||||
onClick={=> @_showAndFocusCc()}>Cc</span>
|
||||
|
||||
<span className="header-action"
|
||||
style={display: @state.showbcc and 'none' or 'inline'}
|
||||
onClick={=> @_showAndFocusBcc()}>Bcc</span>
|
||||
<span className="header-action"
|
||||
style={display: @state.showbcc and 'none' or 'inline'}
|
||||
onClick={=> @_showAndFocusBcc()}>Bcc</span>
|
||||
|
||||
<span className="header-action"
|
||||
style={display: @state.showsubject and 'none' or 'initial'}
|
||||
onClick={=> @setState {showsubject: true}}>Subject</span>
|
||||
<span className="header-action"
|
||||
style={display: @state.showsubject and 'none' or 'initial'}
|
||||
onClick={=> @setState {showsubject: true}}>Subject</span>
|
||||
|
||||
<span className="header-action"
|
||||
data-tooltip="Popout composer"
|
||||
style={{display: ((@props.mode is "fullwindow") and 'none' or 'initial'), paddingLeft: "1.5em"}}
|
||||
onClick={@_popoutComposer}>
|
||||
<RetinaImg name="composer-popout.png"
|
||||
mode={RetinaImg.Mode.ContentIsMask}
|
||||
style={{position: "relative", top: "-2px"}}/>
|
||||
</span>
|
||||
<span className="header-action"
|
||||
data-tooltip="Popout composer"
|
||||
style={{display: ((@props.mode is "fullwindow") and 'none' or 'initial'), paddingLeft: "1.5em"}}
|
||||
onClick={@_popoutComposer}>
|
||||
<RetinaImg name="composer-popout.png"
|
||||
mode={RetinaImg.Mode.ContentIsMask}
|
||||
style={{position: "relative", top: "-2px"}}/>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{@_renderFields()}
|
||||
|
||||
<div className="compose-body" ref="composeBody" onClick={@_onClickComposeBody}>
|
||||
{@_renderBody()}
|
||||
{@_renderFooterRegions()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{@_renderFields()}
|
||||
|
||||
<div className="compose-body"
|
||||
ref="composeBody"
|
||||
onClick={@_onClickComposeBody}>
|
||||
<ContenteditableComponent ref="contentBody"
|
||||
html={@state.body}
|
||||
onChange={@_onChangeBody}
|
||||
onFilePaste={@_onFilePaste}
|
||||
style={@_precalcComposerCss}
|
||||
initialSelectionSnapshot={@_recoveredSelection}
|
||||
mode={{showQuotedText: @state.showQuotedText}}
|
||||
onChangeMode={@_onChangeEditableMode}
|
||||
onRequestScrollTo={@props.onRequestScrollTo}
|
||||
tabIndex="109" />
|
||||
|
||||
{@_renderFooterRegions()}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="composer-action-bar-wrap">
|
||||
{@_renderActionsRegion()}
|
||||
</div>
|
||||
|
@ -263,6 +252,31 @@ class ComposerView extends React.Component
|
|||
|
||||
fields
|
||||
|
||||
_renderBodyScrollbar: =>
|
||||
if @props.mode is "inline"
|
||||
[]
|
||||
else
|
||||
<ScrollRegion.Scrollbar ref="scrollbar" getScrollRegion={ => @refs.scrollregion } />
|
||||
|
||||
_renderBody: =>
|
||||
if @props.mode is "inline"
|
||||
@_renderBodyContenteditable()
|
||||
else
|
||||
<ScrollRegion className="compose-body-scroll" ref="scrollregion" getScrollbar={ => @refs.scrollbar }>
|
||||
{@_renderBodyContenteditable()}
|
||||
</ScrollRegion>
|
||||
|
||||
_renderBodyContenteditable: =>
|
||||
<ContenteditableComponent ref="contentBody"
|
||||
html={@state.body}
|
||||
onChange={@_onChangeBody}
|
||||
onFilePaste={@_onFilePaste}
|
||||
style={@_precalcComposerCss}
|
||||
initialSelectionSnapshot={@_recoveredSelection}
|
||||
mode={{showQuotedText: @state.showQuotedText}}
|
||||
onChangeMode={@_onChangeEditableMode}
|
||||
onRequestScrollTo={@props.onRequestScrollTo}
|
||||
tabIndex="109" />
|
||||
_renderFooterRegions: =>
|
||||
return <div></div> unless @props.localId
|
||||
|
||||
|
@ -389,10 +403,10 @@ class ComposerView extends React.Component
|
|||
Utils.isForwardedMessage(draft)
|
||||
|
||||
# This lets us click outside of the `contenteditable`'s `contentBody`
|
||||
# and still focus on the contenteditable
|
||||
# and simulate what happens when you click beneath the text *in* the
|
||||
# contentEditable.
|
||||
_onClickComposeBody: (event) =>
|
||||
if event.target is React.findDOMNode(@refs.composeBody)
|
||||
@focus("contentBody")
|
||||
@refs.contentBody.selectEnd()
|
||||
|
||||
_onDraftChanged: =>
|
||||
return unless @_proxy
|
||||
|
|
|
@ -99,6 +99,7 @@ class ContenteditableComponent extends React.Component
|
|||
tabIndex={@props.tabIndex}
|
||||
style={@props.style ? {}}
|
||||
onBlur={@_onBlur}
|
||||
onClick={@_onClick}
|
||||
onPaste={@_onPaste}
|
||||
onInput={@_onInput}
|
||||
dangerouslySetInnerHTML={@_dangerouslySetInnerHTML()}></div>
|
||||
|
@ -108,6 +109,21 @@ class ContenteditableComponent extends React.Component
|
|||
focus: =>
|
||||
@_editableNode().focus()
|
||||
|
||||
selectEnd: =>
|
||||
range = document.createRange()
|
||||
range.selectNodeContents(@_editableNode())
|
||||
range.collapse(false)
|
||||
@_editableNode().focus()
|
||||
selection = window.getSelection()
|
||||
selection.removeAllRanges()
|
||||
selection.addRange(range)
|
||||
|
||||
_onClick: (event) ->
|
||||
# We handle mouseDown, mouseMove, mouseUp, but we want to stop propagation
|
||||
# of `click` to make it clear that we've handled the event.
|
||||
# Note: Related to composer-view#_onClickComposeBody
|
||||
event.stopPropagation()
|
||||
|
||||
_onInput: (event) =>
|
||||
@_dragging = false
|
||||
|
||||
|
|
|
@ -44,20 +44,24 @@
|
|||
}
|
||||
|
||||
.composer-content-wrap {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
width: 100%;
|
||||
max-width: @compose-width;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
padding-top: 5px;
|
||||
|
||||
flex: 1;
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex-flow: column;
|
||||
}
|
||||
|
||||
.composer-centered {
|
||||
display:flex;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
max-width: @compose-width;
|
||||
margin: 0 auto;
|
||||
margin-top:@spacing-standard;
|
||||
}
|
||||
.text-actions {
|
||||
text-align: right;
|
||||
line-height: 1.4;
|
||||
|
@ -133,6 +137,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.compose-body-scroll {
|
||||
position:initial;
|
||||
}
|
||||
|
||||
.compose-body {
|
||||
flex: 1;
|
||||
z-index: 1;
|
||||
|
@ -200,10 +208,6 @@ body.is-blurred .composer-inner-wrap .tokenizing-field .token {
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
.composer-content-wrap {
|
||||
margin-top: @spacing-standard;
|
||||
}
|
||||
|
||||
.composer-inner-wrap {
|
||||
|
||||
.composer-action-bar-wrap {
|
||||
|
|
|
@ -3,10 +3,109 @@ React = require 'react/addons'
|
|||
{Utils} = require 'nylas-exports'
|
||||
classNames = require 'classnames'
|
||||
|
||||
class Scrollbar extends React.Component
|
||||
@displayName: 'Scrollbar'
|
||||
@propTypes:
|
||||
scrollTooltipComponent: React.PropTypes.func
|
||||
getScrollRegion: React.PropTypes.func
|
||||
|
||||
constructor: (@props) ->
|
||||
@state =
|
||||
totalHeight: 0
|
||||
trackHeight: 0
|
||||
viewportHeight: 0
|
||||
viewportScrollTop: 0
|
||||
dragging: false
|
||||
scrolling: false
|
||||
|
||||
componentWillUnmount: =>
|
||||
@_onHandleUp({preventDefault: -> })
|
||||
|
||||
setStateFromScrollRegion: (state) ->
|
||||
@setState(state)
|
||||
|
||||
render: ->
|
||||
containerClasses = classNames
|
||||
'scrollbar-track': true
|
||||
'dragging': @state.dragging
|
||||
'scrolling': @state.scrolling
|
||||
|
||||
tooltip = []
|
||||
if @props.scrollTooltipComponent
|
||||
tooltip = <@props.scrollTooltipComponent viewportCenter={@state.viewportScrollTop + @state.viewportHeight / 2} totalHeight={@state.totalHeight} />
|
||||
|
||||
<div className={containerClasses} style={@_scrollbarWrapStyles()} onMouseEnter={@recomputeDimensions}>
|
||||
<div className="scrollbar-track-inner" ref="track" onClick={@_onScrollJump}>
|
||||
<div className="scrollbar-handle" onMouseDown={@_onHandleDown} style={@_scrollbarHandleStyles()} ref="handle" onClick={@_onHandleClick} >
|
||||
<div className="tooltip">{tooltip}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
recomputeDimensions: (options = {}) =>
|
||||
if @props.getScrollRegion?
|
||||
@props.getScrollRegion()._recomputeDimensions(options)
|
||||
@_recomputeDimensions(options)
|
||||
|
||||
_recomputeDimensions: ({avoidForcingLayout}) =>
|
||||
if not avoidForcingLayout
|
||||
trackNode = React.findDOMNode(@refs.track)
|
||||
trackHeight = trackNode.clientHeight
|
||||
if trackHeight isnt @state.trackHeight
|
||||
@setState({trackHeight})
|
||||
|
||||
_scrollbarHandleStyles: =>
|
||||
handleHeight = @_getHandleHeight()
|
||||
handleTop = (@state.viewportScrollTop / (@state.totalHeight - @state.viewportHeight)) * (@state.trackHeight - handleHeight)
|
||||
|
||||
position:'relative'
|
||||
height: handleHeight
|
||||
top: handleTop
|
||||
|
||||
_scrollbarWrapStyles: =>
|
||||
position:'absolute'
|
||||
top: 0
|
||||
bottom: 0
|
||||
right: 0
|
||||
zIndex: 2
|
||||
|
||||
_onHandleDown: (event) =>
|
||||
handleNode = React.findDOMNode(@refs.handle)
|
||||
@_trackOffset = React.findDOMNode(@refs.track).getBoundingClientRect().top
|
||||
@_mouseOffsetWithinHandle = event.pageY - handleNode.getBoundingClientRect().top
|
||||
window.addEventListener("mousemove", @_onHandleMove)
|
||||
window.addEventListener("mouseup", @_onHandleUp)
|
||||
@setState(dragging: true)
|
||||
event.preventDefault()
|
||||
|
||||
_onHandleMove: (event) =>
|
||||
trackY = event.pageY - @_trackOffset - @_mouseOffsetWithinHandle
|
||||
trackPxToViewportPx = (@state.totalHeight - @state.viewportHeight) / (@state.trackHeight - @_getHandleHeight())
|
||||
@props.getScrollRegion().scrollTop = trackY * trackPxToViewportPx
|
||||
event.preventDefault()
|
||||
|
||||
_onHandleUp: (event) =>
|
||||
window.removeEventListener("mousemove", @_onHandleMove)
|
||||
window.removeEventListener("mouseup", @_onHandleUp)
|
||||
@setState(dragging: false)
|
||||
event.preventDefault()
|
||||
|
||||
_onHandleClick: (event) =>
|
||||
# Avoid event propogating up to track
|
||||
event.stopPropagation()
|
||||
|
||||
_onScrollJump: (event) =>
|
||||
@_trackOffset = React.findDOMNode(@refs.track).getBoundingClientRect().top
|
||||
@_mouseOffsetWithinHandle = @_getHandleHeight() / 2
|
||||
@_onHandleMove(event)
|
||||
|
||||
_getHandleHeight: =>
|
||||
Math.min(@state.totalHeight, Math.max(40, (@state.trackHeight / @state.totalHeight) * @state.trackHeight))
|
||||
|
||||
|
||||
###
|
||||
The ScrollRegion component attaches a custom scrollbar.
|
||||
###
|
||||
|
||||
class ScrollRegion extends React.Component
|
||||
@displayName: "ScrollRegion"
|
||||
|
||||
|
@ -16,13 +115,13 @@ class ScrollRegion extends React.Component
|
|||
className: React.PropTypes.string
|
||||
scrollTooltipComponent: React.PropTypes.func
|
||||
children: React.PropTypes.oneOfType([React.PropTypes.element, React.PropTypes.array])
|
||||
getScrollbar: React.PropTypes.func
|
||||
|
||||
constructor: (@props) ->
|
||||
@state =
|
||||
totalHeight:0
|
||||
viewportHeight: 0
|
||||
viewportScrollTop: 0
|
||||
dragging: false
|
||||
scrolling: false
|
||||
|
||||
Object.defineProperty(@, 'scrollTop', {
|
||||
|
@ -31,10 +130,7 @@ class ScrollRegion extends React.Component
|
|||
})
|
||||
|
||||
componentDidMount: =>
|
||||
@_recomputeDimensions()
|
||||
|
||||
componentWillUnmount: =>
|
||||
@_onHandleUp()
|
||||
@recomputeDimensions()
|
||||
|
||||
shouldComponentUpdate: (newProps, newState) =>
|
||||
# Because this component renders @props.children, it needs to update
|
||||
|
@ -48,20 +144,17 @@ class ScrollRegion extends React.Component
|
|||
'dragging': @state.dragging
|
||||
'scrolling': @state.scrolling
|
||||
|
||||
scrollbar = []
|
||||
if not @props.getScrollbar
|
||||
scrollbar = <Scrollbar
|
||||
ref="scrollbar"
|
||||
scrollTooltipComponent={@props.scrollTooltipComponent}
|
||||
getScrollRegion={@_getSelf} />
|
||||
|
||||
otherProps = _.omit(@props, _.keys(@constructor.propTypes))
|
||||
|
||||
tooltip = []
|
||||
if @props.scrollTooltipComponent
|
||||
tooltip = <@props.scrollTooltipComponent viewportCenter={@state.viewportScrollTop + @state.viewportHeight / 2} totalHeight={@state.totalHeight} />
|
||||
|
||||
<div className={containerClasses} {...otherProps}>
|
||||
<div className="scrollbar-track" style={@_scrollbarWrapStyles()} onMouseEnter={@_recomputeDimensions}>
|
||||
<div className="scrollbar-track-inner" ref="track" onClick={@_onScrollJump}>
|
||||
<div className="scrollbar-handle" onMouseDown={@_onHandleDown} style={@_scrollbarHandleStyles()} ref="handle" onClick={@_onHandleClick} >
|
||||
<div className="tooltip">{tooltip}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{scrollbar}
|
||||
<div className="scroll-region-content" onScroll={@_onScroll} ref="content">
|
||||
<div className="scroll-region-content-inner">
|
||||
{@props.children}
|
||||
|
@ -76,29 +169,15 @@ class ScrollRegion extends React.Component
|
|||
adjustment = Utils.scrollAdjustmentToMakeNodeVisibleInContainer(node, container)
|
||||
@scrollTop += adjustment if adjustment isnt 0
|
||||
|
||||
_scrollbarWrapStyles: =>
|
||||
position:'absolute'
|
||||
top: 0
|
||||
bottom: 0
|
||||
right: 0
|
||||
zIndex: 2
|
||||
recomputeDimensions: (options = {}) =>
|
||||
scrollbar = @props.getScrollbar?() ? @refs.scrollbar
|
||||
scrollbar._recomputeDimensions(options)
|
||||
@_recomputeDimensions(options)
|
||||
|
||||
_scrollbarHandleStyles: =>
|
||||
handleHeight = @_getHandleHeight()
|
||||
handleTop = (@state.viewportScrollTop / (@state.totalHeight - @state.viewportHeight)) * (@state.trackHeight - handleHeight)
|
||||
|
||||
position:'relative'
|
||||
height: handleHeight
|
||||
top: handleTop
|
||||
|
||||
_getHandleHeight: =>
|
||||
Math.min(@state.totalHeight, Math.max(40, (@state.trackHeight / @state.totalHeight) * @state.trackHeight))
|
||||
|
||||
_recomputeDimensions: ({avoidForcingLayout} = {}) =>
|
||||
_recomputeDimensions: ({avoidForcingLayout}) =>
|
||||
return unless @refs.content
|
||||
|
||||
contentNode = React.findDOMNode(@refs.content)
|
||||
trackNode = React.findDOMNode(@refs.track)
|
||||
viewportScrollTop = contentNode.scrollTop
|
||||
|
||||
# While we're scrolling, calls to contentNode.scrollHeight / clientHeight
|
||||
|
@ -111,58 +190,37 @@ class ScrollRegion extends React.Component
|
|||
viewportHeight = @state.viewportHeight ? contentNode.clientHeight
|
||||
else
|
||||
totalHeight = contentNode.scrollHeight
|
||||
trackHeight = trackNode.clientHeight
|
||||
viewportHeight = contentNode.clientHeight
|
||||
|
||||
if @state.totalHeight != totalHeight or
|
||||
@state.trackHeight != trackHeight or
|
||||
@state.viewportHeight != viewportHeight or
|
||||
@state.viewportScrollTop != viewportScrollTop
|
||||
@setState({totalHeight, trackHeight, viewportScrollTop, viewportHeight})
|
||||
@_setSharedState({totalHeight, viewportScrollTop, viewportHeight})
|
||||
|
||||
_onHandleDown: (event) =>
|
||||
handleNode = React.findDOMNode(@refs.handle)
|
||||
@_trackOffset = React.findDOMNode(@refs.track).getBoundingClientRect().top
|
||||
@_mouseOffsetWithinHandle = event.pageY - handleNode.getBoundingClientRect().top
|
||||
window.addEventListener("mousemove", @_onHandleMove)
|
||||
window.addEventListener("mouseup", @_onHandleUp)
|
||||
@setState(dragging: true)
|
||||
|
||||
_onHandleMove: (event) =>
|
||||
trackY = event.pageY - @_trackOffset - @_mouseOffsetWithinHandle
|
||||
trackPxToViewportPx = (@state.totalHeight - @state.viewportHeight) / (@state.trackHeight - @_getHandleHeight())
|
||||
|
||||
contentNode = React.findDOMNode(@refs.content)
|
||||
contentNode.scrollTop = trackY * trackPxToViewportPx
|
||||
|
||||
_onHandleUp: (event) =>
|
||||
window.removeEventListener("mousemove", @_onHandleMove)
|
||||
window.removeEventListener("mouseup", @_onHandleUp)
|
||||
@setState(dragging: false)
|
||||
|
||||
_onHandleClick: (event) =>
|
||||
# Avoid event propogating up to track
|
||||
event.stopPropagation()
|
||||
|
||||
_onScrollJump: (event) =>
|
||||
@_trackOffset = React.findDOMNode(@refs.track).getBoundingClientRect().top
|
||||
@_mouseOffsetWithinHandle = @_getHandleHeight() / 2
|
||||
@_onHandleMove(event)
|
||||
_setSharedState: (state) ->
|
||||
scrollbar = @props.getScrollbar?() ? @refs.scrollbar
|
||||
scrollbar.setStateFromScrollRegion(state)
|
||||
@setState(state)
|
||||
|
||||
_onScroll: (event) =>
|
||||
if not @state.scrolling
|
||||
@_recomputeDimensions()
|
||||
@setState(scrolling: true)
|
||||
@recomputeDimensions()
|
||||
@_setSharedState(scrolling: true)
|
||||
else
|
||||
@_recomputeDimensions({avoidForcingLayout: true})
|
||||
@recomputeDimensions({avoidForcingLayout: true})
|
||||
|
||||
@props.onScroll?(event)
|
||||
|
||||
@_onScrollEnd ?= _.debounce =>
|
||||
@setState(scrolling: false)
|
||||
@_setSharedState(scrolling: false)
|
||||
@props.onScrollEnd?(event)
|
||||
, 250
|
||||
@_onScrollEnd()
|
||||
|
||||
_getSelf: =>
|
||||
@
|
||||
|
||||
|
||||
ScrollRegion.Scrollbar = Scrollbar
|
||||
|
||||
module.exports = ScrollRegion
|
||||
|
|
|
@ -54,54 +54,34 @@
|
|||
.scroll-region-content-inner {
|
||||
transform:translate3d(0,0,0);
|
||||
}
|
||||
|
||||
.scrollbar-track {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
transition-delay: 0.5s;
|
||||
padding:3px;
|
||||
width:17px;
|
||||
background: @list-bg;
|
||||
border-left: 1px solid @border-color-divider;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
transition-delay: 0s;
|
||||
}
|
||||
|
||||
/* Used to read the track height with padding applied. */
|
||||
.scrollbar-track-inner {
|
||||
height:100%;
|
||||
}
|
||||
|
||||
.scrollbar-handle {
|
||||
background-color: lighten(@gray, 40%);
|
||||
border:1px solid lighten(@gray, 30%);
|
||||
border-radius:8px;
|
||||
.tooltip {
|
||||
opacity: 0;
|
||||
display:none;
|
||||
transition: opacity 0.3s;
|
||||
top: 50%;
|
||||
transform: translate(-100%, -50%);
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.scroll-region.scrolling {
|
||||
|
||||
.scroll-region.scrolling {
|
||||
.scroll-region-content-inner {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.scrollbar-track {
|
||||
.scrollbar-track {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
transition-delay: 0.5s;
|
||||
padding:3px;
|
||||
width:17px;
|
||||
background: @list-bg;
|
||||
border-left: 1px solid @border-color-divider;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
transition-delay: 0s;
|
||||
}
|
||||
}
|
||||
.scroll-region.dragging {
|
||||
.scrollbar-track {
|
||||
|
||||
&.scrolling {
|
||||
opacity: 1;
|
||||
transition-delay: 0s;
|
||||
}
|
||||
|
||||
&.dragging {
|
||||
opacity: 1;
|
||||
.scrollbar-handle {
|
||||
cursor: default;
|
||||
|
@ -113,4 +93,24 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Used to read the track height with padding applied. */
|
||||
.scrollbar-track-inner {
|
||||
height:100%;
|
||||
}
|
||||
|
||||
.scrollbar-handle {
|
||||
background-color: lighten(@gray, 40%);
|
||||
border:1px solid lighten(@gray, 30%);
|
||||
border-radius:8px;
|
||||
.tooltip {
|
||||
opacity: 0;
|
||||
display:none;
|
||||
transition: opacity 0.3s;
|
||||
top: 50%;
|
||||
transform: translate(-100%, -50%);
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue