diff --git a/internal_packages/account-sidebar/stylesheets/account-sidebar.less b/internal_packages/account-sidebar/stylesheets/account-sidebar.less index 0c86aeb81..7e2a3b493 100644 --- a/internal_packages/account-sidebar/stylesheets/account-sidebar.less +++ b/internal_packages/account-sidebar/stylesheets/account-sidebar.less @@ -7,7 +7,6 @@ overflow: auto; background-color: @source-list-bg; - box-shadow: inset -1px -2px 5px rgba(0, 0, 0, 0.22); -webkit-user-select: none; .item { @@ -27,6 +26,7 @@ .item-tag { .unread { float: right; + font-weight: @font-weight-medium; color: @source-list-active-bg; background: @source-list-bg; } diff --git a/internal_packages/message-list/lib/main.cjsx b/internal_packages/message-list/lib/main.cjsx index 1380c78a8..faafc87ea 100644 --- a/internal_packages/message-list/lib/main.cjsx +++ b/internal_packages/message-list/lib/main.cjsx @@ -9,17 +9,33 @@ module.exports = activate: (@state={}) -> # Register Message List Actions we provide globally ComponentRegistry.register - name: 'MessageToolbarItems' - role: 'MessageList:Toolbar' + name: 'MessageListSplit' + role: 'Root:Right' + mode: 'split' + view: MessageList + + ComponentRegistry.register + name: 'MessageToolbarItemsSplit' + role: 'Root:Right:Toolbar' + mode: 'split' view: MessageToolbarItems ComponentRegistry.register name: 'MessageList' - role: 'Root:Right' + role: 'Thread:Center' + mode: 'list' view: MessageList + ComponentRegistry.register + name: 'MessageToolbarItems' + role: 'Thread:Center:Toolbar' + mode: 'list' + view: MessageToolbarItems + + deactivate: -> ComponentRegistry.unregister 'MessageToolbarItems' + ComponentRegistry.unregister 'MessageListSplit' ComponentRegistry.unregister 'MessageList' serialize: -> @state diff --git a/internal_packages/message-list/lib/message-toolbar-items.cjsx b/internal_packages/message-list/lib/message-toolbar-items.cjsx index 58ab6e654..5547d8067 100644 --- a/internal_packages/message-list/lib/message-toolbar-items.cjsx +++ b/internal_packages/message-list/lib/message-toolbar-items.cjsx @@ -62,7 +62,7 @@ ArchiveButton = React.createClass module.exports = React.createClass getInitialState: -> - threadIsSelected: false + threadIsSelected: ThreadStore.selectedId()? render: -> classes = React.addons.classSet diff --git a/internal_packages/message-list/stylesheets/message-list.less b/internal_packages/message-list/stylesheets/message-list.less index f5b694608..96fa30371 100644 --- a/internal_packages/message-list/stylesheets/message-list.less +++ b/internal_packages/message-list/stylesheets/message-list.less @@ -12,7 +12,6 @@ text-align: center; position: absolute; pointer-events: none; - transition: opacity .25s ease-in-out; .message-toolbar-items-inner { margin: auto; @@ -22,7 +21,6 @@ } } -// .message-toolbar-items also fades in and out when you select / deselect .message-toolbar-items.hidden { opacity: 0; } diff --git a/internal_packages/mode-switch/lib/main.coffee b/internal_packages/mode-switch/lib/main.coffee new file mode 100644 index 000000000..ebea562ad --- /dev/null +++ b/internal_packages/mode-switch/lib/main.coffee @@ -0,0 +1,9 @@ +{ComponentRegistry} = require 'inbox-exports' +ModeSwitch = require './mode-switch' + +module.exports = + activate: (state) -> + ComponentRegistry.register + name: 'ModeSwitch' + view: ModeSwitch + role: 'Root:Toolbar' diff --git a/internal_packages/mode-switch/lib/mode-switch.cjsx b/internal_packages/mode-switch/lib/mode-switch.cjsx new file mode 100644 index 000000000..b60355e70 --- /dev/null +++ b/internal_packages/mode-switch/lib/mode-switch.cjsx @@ -0,0 +1,62 @@ +{ComponentRegistry, + WorkspaceStore, + Actions} = require "inbox-exports" +{RetinaImg} = require 'ui-components' +React = require "react" +_ = require "underscore-plus" + +module.exports = +ModeSwitch = React.createClass + displayName: 'ModeSwitch' + + getInitialState: -> + mode: WorkspaceStore.selectedLayoutMode() + + componentDidMount: -> + @unsubscribe = WorkspaceStore.listen(@_onStateChanged, @) + + componentWillUnmount: -> + @unsubscribe?() + + render: -> + knobX = if @state.mode is 'list' then 25 else 41 + + # Currently ModeSwitch is an opaque control that is not intended + # to be styled, hence the fixed margins and positions. If we + # turn this into a standard component one day, change! +
+ + + + +
+ + _onStateChanged: -> + @setState + mode: WorkspaceStore.selectedLayoutMode() + + _onToggleMode: -> + if @state.mode is 'list' + Actions.selectLayoutMode('split') + else + Actions.selectLayoutMode('list') + + _onSetMode: (event) -> + Actions.selectLayoutMode(event.target.dataset.mode) + event.stopPropagation() diff --git a/internal_packages/mode-switch/package.json b/internal_packages/mode-switch/package.json new file mode 100644 index 000000000..6f2dc4ca0 --- /dev/null +++ b/internal_packages/mode-switch/package.json @@ -0,0 +1,8 @@ +{ + "name": "mode-switch", + "version": "0.0.1", + "description": "Mode switch", + "main": "./lib/main", + "license": "Proprietary", + "private": true +} diff --git a/internal_packages/mode-switch/stylesheets/mode-switch.less b/internal_packages/mode-switch/stylesheets/mode-switch.less new file mode 100644 index 000000000..8fa915ac2 --- /dev/null +++ b/internal_packages/mode-switch/stylesheets/mode-switch.less @@ -0,0 +1,10 @@ + +.mode-switch { + z-index: 1000; + position: relative; + + .handle { + position:absolute; + transition: left .2s ease-out; + } +} diff --git a/internal_packages/thread-list/stylesheets/thread-list.less b/internal_packages/thread-list/stylesheets/thread-list.less index 958d1c414..e9eacd6b7 100644 --- a/internal_packages/thread-list/stylesheets/thread-list.less +++ b/internal_packages/thread-list/stylesheets/thread-list.less @@ -20,7 +20,7 @@ } .participants { - font-size: @font-size-large; + font-size: @font-size-base; font-weight: @font-weight-semi-bold; text-overflow: ellipsis; overflow: hidden; @@ -43,7 +43,6 @@ height: 99%; width: 5px; top: 0; - left: 1px; background: @unread-color; } diff --git a/keymaps/base.cson b/keymaps/base.cson index 2ebcc24b9..50143d825 100644 --- a/keymaps/base.cson +++ b/keymaps/base.cson @@ -22,11 +22,12 @@ 'a' : 'application:reply-all' # Gmail 'f' : 'application:forward' # Gmail -# Default cross-platform core behaviors + 'escape': 'application:pop-sheet' + + # Default cross-platform core behaviors 'left': 'core:move-left' 'right': 'core:move-right' 'enter': 'core:confirm' - 'escape': 'core:cancel' 'shift-up': 'core:select-up' 'shift-down': 'core:select-down' 'shift-left': 'core:select-left' diff --git a/src/atom.coffee b/src/atom.coffee index 3da2bf343..b58308f10 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -582,6 +582,9 @@ class Atom extends Model 'atom-workspace:logout': => @logout() if @isLoggedIn() + # Make sure we can't be made so small that the interface looks like crap + @getCurrentWindow().setMinimumSize(875, 500) + ipc.on 'onboarding-complete', => maximize = dimensions?.maximized and process.platform isnt 'darwin' @displayWindow({maximize}) diff --git a/src/component-registry.coffee b/src/component-registry.coffee index dcb7f785e..2e4570e2c 100644 --- a/src/component-registry.coffee +++ b/src/component-registry.coffee @@ -22,7 +22,10 @@ Mixin = componentDidMount: -> @_componentUnlistener = ComponentRegistry.listen => - @setState(getViewsByName(@components)) + if @isMounted() is false + console.log('WARNING: ComponentRegistry firing on unmounted component.') + return + @setState getViewsByName(@components) componentWillUnmount: -> @_componentUnlistener() @@ -33,7 +36,7 @@ class Component # Don't shit the bed if the user forgets `new` return new Component(attributes) unless @ instanceof Component - ['name', 'model', 'view', 'role'].map (key) => + ['name', 'model', 'view', 'role', 'mode'].map (key) => @[key] = attributes[key] if attributes[key] unless @name? diff --git a/src/components/retina-img.cjsx b/src/components/retina-img.cjsx index 5be1350cb..0d1511afc 100644 --- a/src/components/retina-img.cjsx +++ b/src/components/retina-img.cjsx @@ -1,13 +1,28 @@ +_ = require 'underscore-plus' React = require 'react' {Utils} = require "inbox-exports" +StylesImpactedByZoom = [ + 'top', + 'left', + 'right', + 'bottom', + 'paddingTop', + 'paddingLeft', + 'paddingRight', + 'paddingBottom', + 'marginTop', + 'marginBottom', + 'marginLeft', + 'marginRight' +] + module.exports = RetinaImg = React.createClass displayName: 'RetinaImg' propTypes: name: React.PropTypes.string style: React.PropTypes.object - className: React.PropTypes.string # Optional additional properties which adjust the provided # name. Makes it easy to write parent components when images @@ -23,7 +38,13 @@ RetinaImg = React.createClass style = @props.style ? {} style.zoom = if pathIsRetina then 0.5 else 1 - + for key, val of style + val = "#{val}" + if key in StylesImpactedByZoom and val.indexOf('%') is -1 + style[key] = val.replace('px','') / style.zoom + + otherProps = _.omit(@props, _.keys(@constructor.propTypes)) + _pathFor: (name) -> [basename, ext] = name.split('.') diff --git a/src/flux/actions.coffee b/src/flux/actions.coffee index 024fc4e34..943e9c748 100644 --- a/src/flux/actions.coffee +++ b/src/flux/actions.coffee @@ -59,6 +59,7 @@ windowActions = [ "selectThreadId", "selectTagId", "selectView", + "selectLayoutMode", # Actions for composer "composeReply", @@ -102,7 +103,8 @@ windowActions = [ "abortDownload", "fileDownloaded", - "popSheet" + "popSheet", + "pushSheet" ] allActions = [].concat(windowActions).concat(globalActions).concat(mainWindowActions) diff --git a/src/flux/stores/workspace-store.coffee b/src/flux/stores/workspace-store.coffee index 987e0c4af..abf2fc532 100644 --- a/src/flux/stores/workspace-store.coffee +++ b/src/flux/stores/workspace-store.coffee @@ -5,10 +5,20 @@ Actions = require '../actions' WorkspaceStore = Reflux.createStore init: -> @_resetInstanceVars() + @listenTo Actions.selectView, @_onSelectView + @listenTo Actions.selectLayoutMode, @_onSelectLayoutMode + + @listenTo Actions.popSheet, @popSheet + @listenTo Actions.searchQueryCommitted, @popToRootSheet + @listenTo Actions.selectThreadId, @pushThreadSheet + atom.commands.add 'body', + 'application:pop-sheet': => @popSheet() _resetInstanceVars: -> + @_sheetStack = ["Root"] @_view = 'threads' + @_layoutMode = 'list' # Inbound Events @@ -16,9 +26,43 @@ WorkspaceStore = Reflux.createStore @_view = view @trigger(@) + _onSelectLayoutMode: (mode) -> + @_layoutMode = mode + @trigger(@) + # Accessing Data selectedView: -> @_view + selectedLayoutMode: -> + @_layoutMode + + sheet: -> + @_sheetStack[@_sheetStack.length - 1] + + sheetStack: -> + @_sheetStack + + # Managing Sheets + + pushSheet: (type) -> + @_sheetStack.push(type) + @trigger() + + pushThreadSheet: (threadId) -> + if @selectedLayoutMode() is 'list' and threadId and @sheet() isnt "Thread" + @pushSheet("Thread") + + popSheet: -> + if @_sheetStack.length > 1 + @_sheetStack.pop() + @trigger() + + popToRootSheet: -> + if @_sheetStack.length > 1 + @_sheetStack = ["Root"] + @trigger() + + module.exports = WorkspaceStore diff --git a/src/sheet-container.cjsx b/src/sheet-container.cjsx index e759769fb..0da610b76 100644 --- a/src/sheet-container.cjsx +++ b/src/sheet-container.cjsx @@ -1,10 +1,11 @@ React = require 'react' -SheetStore = require './sheet-store' Sheet = require './sheet' -{Actions,ComponentRegistry} = require "inbox-exports" Flexbox = require './components/flexbox.cjsx' ReactCSSTransitionGroup = React.addons.CSSTransitionGroup +{Actions, + ComponentRegistry, + WorkspaceStore} = require "inbox-exports" ToolbarSpacer = React.createClass className: 'ToolbarSpacer' @@ -21,15 +22,23 @@ Toolbar = React.createClass type: React.PropTypes.string getInitialState: -> - @_getComponentRegistryState() + @_getStateFromStores() componentDidMount: -> - @unlistener = ComponentRegistry.listen (event) => - @setState(@_getComponentRegistryState()) + @unlisteners = [] + @unlisteners.push WorkspaceStore.listen (event) => + @setState(@_getStateFromStores()) + @unlisteners.push ComponentRegistry.listen (event) => + @setState(@_getStateFromStores()) + window.addEventListener "resize", (event) => + @recomputeLayout() componentWillUnmount: -> @unlistener() if @unlistener + componentWillReceiveProps: (props) -> + @setState(@_getStateFromStores(props)) + componentDidUpdate: -> # Wait for other components that are dirty (the actual columns in the sheet) # to update as well. @@ -43,34 +52,34 @@ Toolbar = React.createClass # Column toolbars contain items with roles attaching them to items # in the sheet. Ex: MessageList:Toolbar items appear in the column # toolbar for the column containing . - columnToolbars = @state.itemsForViews.map ({column, name, items}) => -
+
{@_flexboxForItems(items)}
-
+ {mainToolbar} {columnToolbars} -
+ _flexboxForItems: (items) -> components = items.map ({view, name}) => - + {components} - + recomputeLayout: -> return unless @isMounted() # Find our item containers that are tied to specific columns - columnToolbarEls = this.getDOMNode().querySelectorAll('[data-column]') + columnToolbarEls = @getDOMNode().querySelectorAll('[data-column]') # Find the top sheet in the stack sheet = document.querySelector("[name='Sheet']:last-child") @@ -81,22 +90,33 @@ Toolbar = React.createClass column = columnToolbarEl.dataset.column columnEl = sheet.querySelector("[data-column='#{column}']") continue unless columnEl + + columnToolbarEl.style.display = 'inherit' columnToolbarEl.style.left = "#{columnEl.offsetLeft}px" columnToolbarEl.style.width = "#{columnEl.offsetWidth}px" - _getComponentRegistryState: -> - items = [] - items.push(ComponentRegistry.findAllByRole("Global:Toolbar")...) - items.push(ComponentRegistry.findAllByRole("#{@props.type}:Toolbar")...) + _getStateFromStores: (props) -> + props ?= @props + state = + mode: WorkspaceStore.selectedLayoutMode() + items: [] + itemsForColumns: [] - itemsForViews = [] - for column in ['Left', 'Right', 'Center'] - for {view, name} in ComponentRegistry.findAllByRole("#{@props.type}:#{column}") - itemsForView = ComponentRegistry.findAllByRole("#{name}:Toolbar") - if itemsForView.length > 0 - itemsForViews.push({column, name, items: itemsForView}) + for role in ["Global:Toolbar", "#{props.type}:Toolbar"] + for entry in ComponentRegistry.findAllByRole(role) + continue if entry.mode? and entry.mode != state.mode + state.items.push(entry) + + for column in ["Left", "Center", "Right"] + role = "#{props.type}:#{column}:Toolbar" + items = [] + for entry in ComponentRegistry.findAllByRole(role) + continue if entry.mode? and entry.mode != state.mode + items.push(entry) + if items.length > 0 + state.itemsForColumns.push({column, name, items}) - {items, itemsForViews} + state FlexboxForRoles = React.createClass @@ -128,6 +148,7 @@ FlexboxForRoles = React.createClass items = items.concat(ComponentRegistry.findAllByRole(role)) {items} + module.exports = SheetContainer = React.createClass className: 'SheetContainer' @@ -136,7 +157,7 @@ SheetContainer = React.createClass @_getStateFromStores() componentDidMount: -> - @unsubscribe = SheetStore.listen @_onStoreChange + @unsubscribe = WorkspaceStore.listen @_onStoreChange # It's important that every React class explicitly stops listening to # atom events before it unmounts. Thank you event-kit @@ -146,7 +167,6 @@ SheetContainer = React.createClass render: -> topSheetType = @state.stack[@state.stack.length - 1] -
@@ -180,5 +200,5 @@ SheetContainer = React.createClass @setState @_getStateFromStores() _getStateFromStores: -> - stack: SheetStore.stack() + stack: WorkspaceStore.sheetStack() diff --git a/src/sheet-store.cjsx b/src/sheet-store.cjsx deleted file mode 100644 index f5b01198c..000000000 --- a/src/sheet-store.cjsx +++ /dev/null @@ -1,26 +0,0 @@ -React = require "react" -Reflux = require 'reflux' -Actions = require './flux/actions' - -SheetStore = Reflux.createStore - init: -> - @_stack = ["Root"] - @listenTo Actions.popSheet, @popSheet - - # Exposed Data - - pushSheet: (type) -> - @_stack.push(type) - @trigger() - - popSheet: -> - @_stack.pop() - @trigger() - - topSheetType: -> - @_stack[@_stack.length - 1] - - stack: -> - @_stack - -module.exports = SheetStore diff --git a/src/sheet.cjsx b/src/sheet.cjsx index ed6cff6c3..ab4f84f8a 100644 --- a/src/sheet.cjsx +++ b/src/sheet.cjsx @@ -1,6 +1,7 @@ React = require 'react' _ = require 'underscore-plus' -{Actions,ComponentRegistry} = require "inbox-exports" +{Actions,ComponentRegistry, WorkspaceStore} = require "inbox-exports" +RetinaImg = require './components/retina-img.cjsx' Flexbox = require './components/flexbox.cjsx' ResizableRegion = require './components/resizable-region.cjsx' @@ -18,14 +19,17 @@ Sheet = React.createClass columns: ['Left', 'Center', 'Right'] getInitialState: -> - @_getComponentRegistryState() + @_getStateFromStores() componentDidMount: -> - @unlistener = ComponentRegistry.listen (event) => - @setState(@_getComponentRegistryState()) + @unlisteners ?= [] + @unlisteners.push ComponentRegistry.listen (event) => + @setState(@_getStateFromStores()) + @unlisteners.push WorkspaceStore.listen (event) => + @setState(@_getStateFromStores()) componentWillUnmount: -> - @unlistener() if @unlistener + unlisten() for unlisten in @unlisteners render: -> style = @@ -33,6 +37,13 @@ Sheet = React.createClass backgroundColor:'white' width:'100%' height:'100%' + zIndex: 1 + + # Note - setting the z-index of the sheet is important, even though it's + # always 1. Assigning a z-index creates a "stacking context" in the browser, + # so z-indexes inside the sheet are relative to each other, but something in + # one sheet cannot be on top of something in another sheet. + # http://philipwalton.com/articles/what-no-one-told-you-about-z-index/
return [] if @props.depth is 0 -
- Back +
+
+
_columnFlexboxComponents: -> @@ -83,10 +95,17 @@ Sheet = React.createClass {components} - _getComponentRegistryState: -> + _getStateFromStores: -> state = {} + state.mode = WorkspaceStore.selectedLayoutMode() + for column in @props.columns - state["#{column}"] = ComponentRegistry.findAllByRole("#{@props.type}:#{column}") + views = [] + for entry in ComponentRegistry.findAllByRole("#{@props.type}:#{column}") + continue if entry.mode? and entry.mode != state.mode + views.push(entry) + state["#{column}"] = views + state _pop: -> diff --git a/static/images/sheets/sheet-back@2x.png b/static/images/sheets/sheet-back@2x.png new file mode 100644 index 000000000..5882180b7 Binary files /dev/null and b/static/images/sheets/sheet-back@2x.png differ diff --git a/static/images/splitpane/modeslider-bg@1x.png b/static/images/splitpane/modeslider-bg@1x.png new file mode 100644 index 000000000..a653608ae Binary files /dev/null and b/static/images/splitpane/modeslider-bg@1x.png differ diff --git a/static/images/splitpane/modeslider-bg@2x.png b/static/images/splitpane/modeslider-bg@2x.png new file mode 100644 index 000000000..028e26037 Binary files /dev/null and b/static/images/splitpane/modeslider-bg@2x.png differ diff --git a/static/images/splitpane/modeslider-knob@1x.png b/static/images/splitpane/modeslider-knob@1x.png new file mode 100644 index 000000000..ce2f26258 Binary files /dev/null and b/static/images/splitpane/modeslider-knob@1x.png differ diff --git a/static/images/splitpane/modeslider-knob@2x.png b/static/images/splitpane/modeslider-knob@2x.png new file mode 100644 index 000000000..c1a327b2a Binary files /dev/null and b/static/images/splitpane/modeslider-knob@2x.png differ diff --git a/static/images/splitpane/toolbar-icon-listmode-active@1x.png b/static/images/splitpane/toolbar-icon-listmode-active@1x.png new file mode 100644 index 000000000..c660fceb1 Binary files /dev/null and b/static/images/splitpane/toolbar-icon-listmode-active@1x.png differ diff --git a/static/images/splitpane/toolbar-icon-listmode-active@2x.png b/static/images/splitpane/toolbar-icon-listmode-active@2x.png new file mode 100644 index 000000000..4a7e37121 Binary files /dev/null and b/static/images/splitpane/toolbar-icon-listmode-active@2x.png differ diff --git a/static/images/splitpane/toolbar-icon-listmode@1x.png b/static/images/splitpane/toolbar-icon-listmode@1x.png new file mode 100644 index 000000000..0e60f1a79 Binary files /dev/null and b/static/images/splitpane/toolbar-icon-listmode@1x.png differ diff --git a/static/images/splitpane/toolbar-icon-listmode@2x.png b/static/images/splitpane/toolbar-icon-listmode@2x.png new file mode 100644 index 000000000..dd9e2cd95 Binary files /dev/null and b/static/images/splitpane/toolbar-icon-listmode@2x.png differ diff --git a/static/images/splitpane/toolbar-icon-splitpanes-active@1x.png b/static/images/splitpane/toolbar-icon-splitpanes-active@1x.png new file mode 100644 index 000000000..1d9869d76 Binary files /dev/null and b/static/images/splitpane/toolbar-icon-splitpanes-active@1x.png differ diff --git a/static/images/splitpane/toolbar-icon-splitpanes-active@2x.png b/static/images/splitpane/toolbar-icon-splitpanes-active@2x.png new file mode 100644 index 000000000..8667aa737 Binary files /dev/null and b/static/images/splitpane/toolbar-icon-splitpanes-active@2x.png differ diff --git a/static/images/splitpane/toolbar-icon-splitpanes@1x.png b/static/images/splitpane/toolbar-icon-splitpanes@1x.png new file mode 100644 index 000000000..41ae4cf15 Binary files /dev/null and b/static/images/splitpane/toolbar-icon-splitpanes@1x.png differ diff --git a/static/images/splitpane/toolbar-icon-splitpanes@2x.png b/static/images/splitpane/toolbar-icon-splitpanes@2x.png new file mode 100644 index 000000000..02f4b2633 Binary files /dev/null and b/static/images/splitpane/toolbar-icon-splitpanes@2x.png differ diff --git a/static/workspace.less b/static/workspace.less index 9b3bd227e..db16a6a53 100644 --- a/static/workspace.less +++ b/static/workspace.less @@ -75,6 +75,48 @@ atom-workspace { } } +.sheet-toolbar-enter { + opacity:0; + transition: opacity .20s ease-out; +} + +.sheet-toolbar-enter.sheet-toolbar-enter-active { + opacity:1; +} + +.sheet-toolbar-leave { + opacity:1; + transition: opacity .20s ease-in; +} + +.sheet-toolbar-leave.sheet-toolbar-leave-active { + opacity:0; +} + +.sheet-edge { + height:100%; + z-index: @zindex-popover; + position: absolute; + + .x { + position: absolute; + top: @spacing-standard * 1.5; + left: @spacing-standard * 1.5; + } + .gradient { + width:9px; + height:100%; + background-color: #f4f4f4; + background-image: -webkit-gradient(linear, left center, right center, from(rgb(244, 244, 244)), to(rgb(209, 209, 209))); + background-image: -webkit-linear-gradient(left, rgb(244, 244, 244), rgb(209, 209, 209)); + background-image: -moz-linear-gradient(left, rgb(244, 244, 244), rgb(209, 209, 209)); + background-image: -o-linear-gradient(left, rgb(244, 244, 244), rgb(209, 209, 209)); + background-image: -ms-linear-gradient(left, rgb(244, 244, 244), rgb(209, 209, 209)); + background-image: linear-gradient(left, rgb(244, 244, 244), rgb(209, 209, 209)); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=1,StartColorStr='#f4f4f4', EndColorStr='#d1d1d1'); + } +} + .flexbox-handle-horizontal { width: 6px; top: 0;