mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-11-10 10:17:11 +03:00
Merge pull request #271 from github/super-command-panel
Super command panel
This commit is contained in:
commit
eb179af0d4
@ -18,6 +18,7 @@ require.paths.unshift(fixturePackagesPath)
|
||||
[bindingSetsToRestore, bindingSetsByFirstKeystrokeToRestore] = []
|
||||
|
||||
beforeEach ->
|
||||
jQuery.fx.off = true
|
||||
window.fixturesProject = new Project(require.resolve('fixtures'))
|
||||
window.resetTimeouts()
|
||||
atom.atomPackageStates = {}
|
||||
|
@ -12,3 +12,13 @@
|
||||
'meta-e': 'command-panel:set-selection-as-regex-address'
|
||||
'meta-f': 'command-panel:find-in-file'
|
||||
'meta-F': 'command-panel:find-in-project'
|
||||
|
||||
'.command-panel':
|
||||
'ctrl-{': 'command-panel:collapse-all'
|
||||
'ctrl-}': 'command-panel:expand-all'
|
||||
|
||||
'.command-panel .preview-list':
|
||||
'left': 'command-panel:collapse-result'
|
||||
'ctrl-[': 'command-panel:collapse-result'
|
||||
'right': 'command-panel:expand-result'
|
||||
'ctrl-]': 'command-panel:expand-result'
|
||||
|
@ -11,7 +11,13 @@ module.exports =
|
||||
class CommandPanelView extends View
|
||||
@content: ->
|
||||
@div class: 'command-panel tool-panel', =>
|
||||
@div outlet: 'previewCount', class: 'preview-count'
|
||||
@div class: 'loading', outlet: 'loadingMessage'
|
||||
@div class: 'header', outlet: 'previewHeader', =>
|
||||
@ul outlet: 'expandCollapse', class: 'expand-collapse', =>
|
||||
@li class: 'expand', 'Expand All'
|
||||
@li class: 'collapse', 'Collapse All'
|
||||
@span outlet: 'previewCount', class: 'preview-count'
|
||||
|
||||
@subview 'previewList', new PreviewList(rootView)
|
||||
@ul class: 'error-messages', outlet: 'errorMessages'
|
||||
@div class: 'prompt-and-editor', =>
|
||||
@ -40,8 +46,11 @@ class CommandPanelView extends View
|
||||
rootView.command 'command-panel:repeat-relative-address-in-reverse', => @repeatRelativeAddressInReverse()
|
||||
rootView.command 'command-panel:set-selection-as-regex-address', => @setSelectionAsLastRelativeAddress()
|
||||
|
||||
@on 'click', '.expand', @onExpandAll
|
||||
@on 'click', '.collapse', @onCollapseAll
|
||||
|
||||
@previewList.hide()
|
||||
@previewCount.hide()
|
||||
@previewHeader.hide()
|
||||
@errorMessages.hide()
|
||||
@prompt.iconSize(@miniEditor.getFontSize())
|
||||
|
||||
@ -66,17 +75,35 @@ class CommandPanelView extends View
|
||||
togglePreview: ->
|
||||
if @previewList.is(':focus')
|
||||
@previewList.hide()
|
||||
@previewCount.hide()
|
||||
@previewHeader.hide()
|
||||
@detach()
|
||||
rootView.focus()
|
||||
else
|
||||
@attach() unless @hasParent()
|
||||
if @previewList.hasOperations()
|
||||
@previewList.show().focus()
|
||||
@previewCount.show()
|
||||
@previewHeader.show()
|
||||
else
|
||||
@miniEditor.focus()
|
||||
|
||||
toggleLoading: ->
|
||||
if @loadingMessage.hasClass 'is-loading'
|
||||
@loadingMessage.removeClass 'is-loading'
|
||||
@loadingMessage.html ''
|
||||
@loadingMessage.hide()
|
||||
else
|
||||
@loadingMessage.addClass 'is-loading'
|
||||
@loadingMessage.html 'Searching...'
|
||||
@loadingMessage.show()
|
||||
|
||||
onExpandAll: (event) =>
|
||||
@previewList.expandAllPaths()
|
||||
@previewList.focus()
|
||||
|
||||
onCollapseAll: (event) =>
|
||||
@previewList.collapseAllPaths()
|
||||
@previewList.focus()
|
||||
|
||||
attach: (text='', options={}) ->
|
||||
@errorMessages.hide()
|
||||
|
||||
@ -89,17 +116,19 @@ class CommandPanelView extends View
|
||||
detach: ->
|
||||
rootView.focus()
|
||||
@previewList.hide()
|
||||
@previewCount.hide()
|
||||
@previewHeader.hide()
|
||||
super
|
||||
|
||||
escapedCommand: ->
|
||||
@miniEditor.getText()
|
||||
|
||||
execute: (command=@escapedCommand())->
|
||||
@toggleLoading()
|
||||
@errorMessages.empty()
|
||||
|
||||
try
|
||||
@commandInterpreter.eval(command, rootView.getActiveEditSession()).done ({operationsToPreview, errorMessages}) =>
|
||||
@toggleLoading()
|
||||
@history.push(command)
|
||||
@historyIndex = @history.length
|
||||
|
||||
@ -109,6 +138,7 @@ class CommandPanelView extends View
|
||||
@errorMessages.append $$ ->
|
||||
@li errorMessage for errorMessage in errorMessages
|
||||
else if operationsToPreview?.length
|
||||
@previewHeader.show()
|
||||
@previewList.populate(operationsToPreview)
|
||||
@previewList.focus()
|
||||
@previewCount.text("#{_.pluralize(operationsToPreview.length, 'match', 'matches')} in #{_.pluralize(@previewList.getPathCount(), 'file')}").show()
|
||||
|
28
src/packages/command-panel/lib/operation-view.coffee
Normal file
28
src/packages/command-panel/lib/operation-view.coffee
Normal file
@ -0,0 +1,28 @@
|
||||
{View} = require 'space-pen'
|
||||
|
||||
module.exports =
|
||||
class OperationView extends View
|
||||
@content: ({operation} = {}) ->
|
||||
{prefix, suffix, match, range} = operation.preview()
|
||||
@li 'data-index': operation.index, class: 'operation', =>
|
||||
@span range.start.row + 1, class: 'line-number'
|
||||
@span class: 'preview', =>
|
||||
@span prefix
|
||||
@span match, class: 'match'
|
||||
@span suffix
|
||||
|
||||
initialize: ({@previewList, @operation}) ->
|
||||
@subscribe @previewList, 'core:confirm', =>
|
||||
if @hasClass('selected')
|
||||
@executeOperation()
|
||||
false
|
||||
@on 'mousedown', (e) =>
|
||||
@executeOperation()
|
||||
@previewList.find('.selected').removeClass('selected')
|
||||
@addClass('selected')
|
||||
|
||||
executeOperation: ->
|
||||
editSession = rootView.open(@operation.getPath())
|
||||
bufferRange = @operation.execute(editSession)
|
||||
editSession.setSelectedBufferRange(bufferRange, autoscroll: true) if bufferRange
|
||||
@previewList.focus()
|
58
src/packages/command-panel/lib/path-view.coffee
Normal file
58
src/packages/command-panel/lib/path-view.coffee
Normal file
@ -0,0 +1,58 @@
|
||||
{View} = require 'space-pen'
|
||||
fs = require 'fs'
|
||||
OperationView = require './operation-view'
|
||||
$ = require 'jquery'
|
||||
|
||||
module.exports =
|
||||
class PathView extends View
|
||||
@content: ({path, operations, previewList} = {}) ->
|
||||
classes = ['path']
|
||||
classes.push('readme') if fs.isReadmePath(path)
|
||||
@li class: classes.join(' '), =>
|
||||
@div outlet: 'pathDetails', class: 'path-details', =>
|
||||
@span class: 'path-name', path
|
||||
@span "(#{operations.length})", class: 'path-match-number'
|
||||
@ul outlet: 'matches', class: 'matches', =>
|
||||
for operation in operations
|
||||
@subview "operation#{operation.index}", new OperationView({operation, previewList})
|
||||
|
||||
initialize: ({@previewList}) ->
|
||||
@pathDetails.on 'mousedown', => @toggle(true)
|
||||
@subscribe @previewList, 'command-panel:collapse-result', =>
|
||||
@collapse(true) if @isSelected()
|
||||
@subscribe @previewList, 'command-panel:expand-result', =>
|
||||
@expand(true) if @isSelected()
|
||||
@subscribe @previewList, 'core:confirm', =>
|
||||
if @hasClass('selected')
|
||||
@toggle(true)
|
||||
false
|
||||
|
||||
isSelected: ->
|
||||
@hasClass('selected') or @find('.selected').length
|
||||
|
||||
setSelected: ->
|
||||
@previewList.find('.selected').removeClass('selected')
|
||||
@addClass('selected')
|
||||
|
||||
toggle: (animate) ->
|
||||
if @hasClass('is-collapsed')
|
||||
@expand(animate)
|
||||
else
|
||||
@collapse(animate)
|
||||
|
||||
expand: (animate=false) ->
|
||||
if animate
|
||||
@matches.show 100, => @removeClass 'is-collapsed'
|
||||
else
|
||||
@matches.show()
|
||||
@removeClass 'is-collapsed'
|
||||
|
||||
collapse: (animate=false) ->
|
||||
if animate
|
||||
@matches.hide 100, =>
|
||||
@addClass 'is-collapsed'
|
||||
@setSelected() if @isSelected()
|
||||
else
|
||||
@matches.hide()
|
||||
@addClass 'is-collapsed'
|
||||
@setSelected() if @isSelected()
|
@ -3,24 +3,30 @@ $ = require 'jquery'
|
||||
ScrollView = require 'scroll-view'
|
||||
_ = require 'underscore'
|
||||
fs = require 'fs'
|
||||
PathView = require './path-view'
|
||||
OperationView = require './operation-view'
|
||||
|
||||
module.exports =
|
||||
class PreviewList extends ScrollView
|
||||
@content: ->
|
||||
@ol class: 'preview-list', tabindex: -1, ->
|
||||
@ol class: 'preview-list', tabindex: -1
|
||||
|
||||
selectedOperationIndex: 0
|
||||
operations: null
|
||||
|
||||
initialize: (@rootView) ->
|
||||
initialize: ->
|
||||
super
|
||||
|
||||
@on 'core:move-down', => @selectNextOperation(); false
|
||||
@on 'core:move-up', => @selectPreviousOperation(); false
|
||||
@on 'core:confirm', => @executeSelectedOperation()
|
||||
|
||||
@on 'mousedown', 'li.operation', (e) =>
|
||||
@setSelectedOperationIndex(parseInt($(e.target).closest('li').data('index')))
|
||||
@executeSelectedOperation()
|
||||
@command 'command-panel:collapse-all', => @collapseAllPaths()
|
||||
@command 'command-panel:expand-all', => @expandAllPaths()
|
||||
|
||||
expandAllPaths: ->
|
||||
@children().each (index, element) -> $(element).view().expand()
|
||||
|
||||
collapseAllPaths: ->
|
||||
@children().each (index, element) -> $(element).view().collapse()
|
||||
|
||||
destroy: ->
|
||||
@destroyOperations() if @operations
|
||||
@ -31,26 +37,14 @@ class PreviewList extends ScrollView
|
||||
@destroyOperations() if @operations
|
||||
@operations = operations
|
||||
@empty()
|
||||
@html $$$ ->
|
||||
operation.index = index for operation, index in operations
|
||||
operationsByPath = _.groupBy(operations, (operation) -> operation.getPath())
|
||||
for path, ops of operationsByPath
|
||||
classes = ['path']
|
||||
classes.push('readme') if fs.isReadmePath(path)
|
||||
@li class: classes.join(' '), =>
|
||||
@span path
|
||||
@span "(#{ops.length})", class: 'path-match-number'
|
||||
for operation in ops
|
||||
{prefix, suffix, match, range} = operation.preview()
|
||||
@li 'data-index': operation.index, class: 'operation', =>
|
||||
@span range.start.row + 1, class: 'line-number'
|
||||
@span class: 'preview', =>
|
||||
@span prefix
|
||||
@span match, class: 'match'
|
||||
@span suffix
|
||||
|
||||
@setSelectedOperationIndex(0)
|
||||
operation.index = index for operation, index in operations
|
||||
operationsByPath = _.groupBy(operations, (operation) -> operation.getPath())
|
||||
for path, operations of operationsByPath
|
||||
@append new PathView({path, operations, previewList: this})
|
||||
|
||||
@show()
|
||||
@find('.operation:first').addClass('selected')
|
||||
@setLineNumberWidth()
|
||||
|
||||
setLineNumberWidth: ->
|
||||
@ -61,33 +55,33 @@ class PreviewList extends ScrollView
|
||||
lineNumbers.width(maxWidth)
|
||||
|
||||
selectNextOperation: ->
|
||||
@setSelectedOperationIndex(@selectedOperationIndex + 1)
|
||||
selectedView = @find('.selected').view()
|
||||
|
||||
if selectedView instanceof PathView
|
||||
if selectedView.hasClass('is-collapsed')
|
||||
nextView = selectedView.next().view()
|
||||
else
|
||||
nextView = selectedView.find('.operation:first')
|
||||
else
|
||||
nextView = selectedView.next().view() ? selectedView.closest('.path').next().view()
|
||||
if nextView?
|
||||
selectedView.removeClass('selected')
|
||||
nextView.addClass('selected')
|
||||
@scrollToElement(nextView)
|
||||
|
||||
selectPreviousOperation: ->
|
||||
@setSelectedOperationIndex(@selectedOperationIndex - 1)
|
||||
selectedView = @find('.selected').view()
|
||||
|
||||
setSelectedOperationIndex: (index, scrollToOperation=true) ->
|
||||
index = Math.max(0, index)
|
||||
index = Math.min(@operations.length - 1, index)
|
||||
@children(".selected").removeClass('selected')
|
||||
element = @children("li.operation:eq(#{index})")
|
||||
element.addClass('selected')
|
||||
if selectedView instanceof PathView
|
||||
previousView = selectedView.prev()
|
||||
previousView = previousView.find('.operation:last').view() unless previousView.hasClass('is-collapsed')
|
||||
else
|
||||
previousView = selectedView.prev().view() ? selectedView.closest('.path').view()
|
||||
|
||||
if scrollToOperation
|
||||
if index is 0
|
||||
@scrollToTop()
|
||||
else
|
||||
@scrollToElement(element)
|
||||
|
||||
@selectedOperationIndex = index
|
||||
|
||||
executeSelectedOperation: ->
|
||||
operation = @getSelectedOperation()
|
||||
editSession = @rootView.open(operation.getPath())
|
||||
bufferRange = operation.execute(editSession)
|
||||
editSession.setSelectedBufferRange(bufferRange, autoscroll: true) if bufferRange
|
||||
@focus()
|
||||
false
|
||||
if previousView?
|
||||
selectedView.removeClass('selected')
|
||||
previousView.addClass('selected')
|
||||
@scrollToElement(previousView)
|
||||
|
||||
getPathCount: ->
|
||||
_.keys(_.groupBy(@operations, (operation) -> operation.getPath())).length
|
||||
@ -100,23 +94,27 @@ class PreviewList extends ScrollView
|
||||
@operations = null
|
||||
|
||||
getSelectedOperation: ->
|
||||
@operations[@selectedOperationIndex]
|
||||
@find('.operation.selected').view()?.operation
|
||||
|
||||
scrollToElement: (element) ->
|
||||
top = @scrollTop() + element.position().top
|
||||
top = @scrollTop() + element.offset().top - @offset().top
|
||||
bottom = top + element.outerHeight()
|
||||
|
||||
if bottom > @scrollBottom()
|
||||
@scrollBottom(bottom)
|
||||
if top < @scrollTop()
|
||||
@scrollTop(top)
|
||||
@scrollBottom(bottom) if bottom > @scrollBottom()
|
||||
@scrollTop(top) if top < @scrollTop()
|
||||
|
||||
scrollToBottom: ->
|
||||
super()
|
||||
|
||||
@setSelectedOperationIndex(Infinity, false)
|
||||
@find('.selected').removeClass('selected')
|
||||
lastPath = @find('.path:last')
|
||||
if lastPath.hasClass('is-collapsed')
|
||||
lastPath.addClass('selected')
|
||||
else
|
||||
lastPath.find('.operation:last').addClass('selected')
|
||||
|
||||
scrollToTop: ->
|
||||
super()
|
||||
|
||||
@setSelectedOperationIndex(0, false)
|
||||
@find('.selected').removeClass('selected')
|
||||
@find('.path:first').addClass('selected')
|
||||
|
@ -128,6 +128,9 @@ describe "CommandPanel", ->
|
||||
beforeEach ->
|
||||
expect(commandPanel.previewList).toBeVisible()
|
||||
|
||||
it "shows the expand and collapse all buttons", ->
|
||||
expect(commandPanel.find('.expand-collapse')).toBeVisible()
|
||||
|
||||
describe "when the preview list is focused", ->
|
||||
it "hides the command panel", ->
|
||||
expect(commandPanel.previewList).toMatchSelector(':focus')
|
||||
@ -171,19 +174,19 @@ describe "CommandPanel", ->
|
||||
expect(commandPanel.hasParent()).toBeTruthy()
|
||||
|
||||
describe "when the mini editor is focused", ->
|
||||
it "retains focus on the mini editor and does not show the preview list or preview count", ->
|
||||
it "retains focus on the mini editor and does not show the preview list or preview header", ->
|
||||
expect(commandPanel.miniEditor.isFocused).toBeTruthy()
|
||||
rootView.trigger 'command-panel:toggle-preview'
|
||||
expect(commandPanel.previewList).toBeHidden()
|
||||
expect(commandPanel.previewCount).toBeHidden()
|
||||
expect(commandPanel.previewHeader).toBeHidden()
|
||||
expect(commandPanel.miniEditor.isFocused).toBeTruthy()
|
||||
|
||||
describe "when the mini editor is not focused", ->
|
||||
it "focuses the mini editor and does not show the preview list or preview count", ->
|
||||
it "focuses the mini editor and does not show the preview list or preview header", ->
|
||||
rootView.focus()
|
||||
rootView.trigger 'command-panel:toggle-preview'
|
||||
expect(commandPanel.previewList).toBeHidden()
|
||||
expect(commandPanel.previewCount).toBeHidden()
|
||||
expect(commandPanel.previewHeader).toBeHidden()
|
||||
expect(commandPanel.miniEditor.isFocused).toBeTruthy()
|
||||
|
||||
describe "when the command panel is not visible", ->
|
||||
@ -290,7 +293,7 @@ describe "CommandPanel", ->
|
||||
expect(commandPanel.previewList).toBeVisible()
|
||||
expect(commandPanel.previewList).toMatchSelector ':focus'
|
||||
previewItem = commandPanel.previewList.find("li:contains(sample.js):first")
|
||||
expect(previewItem.text()).toBe "sample.js(1)"
|
||||
expect(previewItem.find('.path-details').text()).toBe "sample.js(1)"
|
||||
expect(previewItem.next().find('.preview').text()).toBe "var quicksort = function () {"
|
||||
expect(previewItem.next().find('.preview > .match').text()).toBe "quicksort"
|
||||
|
||||
@ -392,29 +395,38 @@ describe "CommandPanel", ->
|
||||
expect(previewList.find('li.operation:eq(1)')).toHaveClass 'selected'
|
||||
expect(previewList.getSelectedOperation()).toBe previewList.getOperations()[1]
|
||||
|
||||
_.times previewList.getOperations().length - 2, -> previewList.trigger 'core:move-down'
|
||||
_.times previewList.getOperations().length + previewList.getPathCount(), -> previewList.trigger 'core:move-down'
|
||||
|
||||
expect(previewList.find("li.operation:last")).toHaveClass 'selected'
|
||||
expect(previewList.getSelectedOperation()).toBe _.last(previewList.getOperations())
|
||||
|
||||
expect(previewList.scrollBottom()).toBeCloseTo previewList.prop('scrollHeight'), -1
|
||||
|
||||
_.times previewList.getOperations().length, -> previewList.trigger 'core:move-up'
|
||||
_.times previewList.getOperations().length + previewList.getPathCount(), -> previewList.trigger 'core:move-up'
|
||||
expect(previewList.scrollTop()).toBe 0
|
||||
|
||||
it "doesn't bubble up the event and the command panel text doesn't change", ->
|
||||
rootView.attachToDom()
|
||||
commandPanel.miniEditor.setText "command"
|
||||
previewList.focus()
|
||||
previewList.trigger 'core:move-up'
|
||||
expect(previewList.find('li.operation:eq(0)')).toHaveClass 'selected'
|
||||
expect(commandPanel.miniEditor.getText()).toBe 'command'
|
||||
previewList.trigger 'core:move-down'
|
||||
expect(previewList.find('li.operation:eq(1)')).toHaveClass 'selected'
|
||||
expect(commandPanel.miniEditor.getText()).toBe 'command'
|
||||
previewList.trigger 'core:move-up'
|
||||
expect(previewList.find('li.operation:eq(0)')).toHaveClass 'selected'
|
||||
expect(commandPanel.miniEditor.getText()).toBe 'command'
|
||||
|
||||
it "doesn't select collapsed operations", ->
|
||||
rootView.attachToDom()
|
||||
previewList.trigger 'command-panel:collapse-result'
|
||||
expect(previewList.find('li.path:eq(0)')).toHaveClass 'selected'
|
||||
previewList.trigger 'core:move-down'
|
||||
expect(previewList.find('li.path:eq(1)')).toHaveClass 'selected'
|
||||
previewList.trigger 'core:move-up'
|
||||
expect(previewList.find('li.path:eq(0)')).toHaveClass 'selected'
|
||||
|
||||
describe "when move-to-top and move-to-bottom are triggered on the preview list", ->
|
||||
it "selects the first/last operation", ->
|
||||
it "selects the first path or last operation", ->
|
||||
rootView.attachToDom()
|
||||
expect(previewList.getOperations().length).toBeGreaterThan 0
|
||||
expect(previewList.find('li.operation:eq(0)')).toHaveClass 'selected'
|
||||
@ -425,8 +437,8 @@ describe "CommandPanel", ->
|
||||
expect(previewList.getSelectedOperation()).toBe _.last(previewList.getOperations())
|
||||
|
||||
previewList.trigger 'core:move-to-top'
|
||||
expect(previewList.find('li.operation:eq(0)')).toHaveClass 'selected'
|
||||
expect(previewList.getSelectedOperation()).toBe previewList.getOperations()[0]
|
||||
expect(previewList.find('li.path:eq(0)')).toHaveClass 'selected'
|
||||
expect(previewList.getSelectedOperation()).toBeUndefined()
|
||||
|
||||
describe "when core:confirm is triggered on the preview list", ->
|
||||
it "opens the operation's buffer, selects and scrolls to the search result, and refocuses the preview list", ->
|
||||
@ -453,6 +465,15 @@ describe "CommandPanel", ->
|
||||
|
||||
expect(executeHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "toggles the expansion state when a path is selected", ->
|
||||
rootView.attachToDom()
|
||||
previewList.trigger 'core:move-to-top'
|
||||
expect(previewList.find('li.path:first')).toHaveClass 'selected'
|
||||
expect(previewList.find('li.path:first')).not.toHaveClass 'is-collapsed'
|
||||
previewList.trigger 'core:confirm'
|
||||
expect(previewList.find('li.path:first')).toHaveClass 'selected'
|
||||
expect(previewList.find('li.path:first')).toHaveClass 'is-collapsed'
|
||||
|
||||
describe "when an operation in the preview list is clicked", ->
|
||||
it "opens the operation's buffer, selects the search result, and refocuses the preview list", ->
|
||||
spyOn(previewList, 'focus')
|
||||
@ -465,3 +486,24 @@ describe "CommandPanel", ->
|
||||
expect(editSession.buffer.getPath()).toBe project.resolve(operation.getPath())
|
||||
expect(editSession.getSelectedBufferRange()).toEqual operation.getBufferRange()
|
||||
expect(previewList.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when a path in the preview list is clicked", ->
|
||||
it "shows and hides the matches for that path", ->
|
||||
rootView.attachToDom()
|
||||
expect(previewList.find('li.path:first-child ul.matches')).toBeVisible()
|
||||
previewList.find('li.path:first-child .path-details').mousedown()
|
||||
expect(previewList.find('li.path:first-child ul.matches')).toBeHidden()
|
||||
|
||||
previewList.find('li.path:first-child .path-details').mousedown()
|
||||
expect(previewList.find('li.path:first-child ul.matches')).toBeVisible()
|
||||
|
||||
describe "when command-panel:collapse-result and command-panel:expand-result are triggered", ->
|
||||
it "collapses and selects the path, and then expands the selected path", ->
|
||||
rootView.attachToDom()
|
||||
expect(previewList.find('li.path:first-child ul.matches')).toBeVisible()
|
||||
previewList.trigger 'command-panel:collapse-result'
|
||||
expect(previewList.find('li.path:first-child ul.matches')).toBeHidden()
|
||||
expect(previewList.find('li.path:first-child')).toHaveClass 'selected'
|
||||
previewList.trigger 'command-panel:expand-result'
|
||||
expect(previewList.find('li.path:first-child ul.matches')).toBeVisible()
|
||||
expect(previewList.find('li.path:first-child')).toHaveClass 'selected'
|
||||
|
@ -19,15 +19,10 @@ describe "Gists package", ->
|
||||
[request, originalFxOffValue] = []
|
||||
|
||||
beforeEach ->
|
||||
originalFxOffValue = $.fx.off
|
||||
$.fx.off = true
|
||||
editor.trigger 'gist:create'
|
||||
expect($.ajax).toHaveBeenCalled()
|
||||
request = $.ajax.argsForCall[0][0]
|
||||
|
||||
afterEach ->
|
||||
$.fx.off = originalFxOffValue
|
||||
|
||||
it "creates an Ajax request to api.github.com with the entire buffer contents as the Gist's content", ->
|
||||
expect(request.url).toBe 'https://api.github.com/gists'
|
||||
expect(request.type).toBe 'POST'
|
||||
@ -46,7 +41,7 @@ describe "Gists package", ->
|
||||
expect(rootView.find('.notification')).toExist()
|
||||
expect(rootView.find('.notification .title').text()).toBe 'Gist 1 created'
|
||||
advanceClock(2000)
|
||||
expect(rootView.find('.gist-notification')).not.toExist()
|
||||
expect(rootView.find('.notification')).not.toExist()
|
||||
|
||||
describe "when the editor has a selection", ->
|
||||
beforeEach ->
|
||||
|
@ -60,4 +60,16 @@ html, body {
|
||||
src: url("octicons-regular-webfont.woff") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.is-loading {
|
||||
background-image: url(images/spinner.svg);
|
||||
background-repeat: no-repeat;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
opacity: 0.5;
|
||||
background-size: contain;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
padding-left: 19px;
|
||||
}
|
@ -1,30 +1,77 @@
|
||||
.command-panel {
|
||||
padding: 5px;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.command-panel .is-loading {
|
||||
display: block;
|
||||
margin: 0 auto 10px auto;
|
||||
width: 100px;
|
||||
background-color: #111;
|
||||
background-size: auto;
|
||||
background-position: 5px 5px;
|
||||
padding: 5px 5px 10px 30px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-top: 1px solid rgba(0, 0, 0, 1);
|
||||
border-left: 1px solid rgba(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
.command-panel .preview-list {
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
margin: 10px 0 10px 10px;
|
||||
margin: 0 0 10px 0;
|
||||
position: relative;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.command-panel .preview-count {
|
||||
.command-panel .header:after {
|
||||
content: ".";
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
clear: both;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.command-panel .expand-collapse {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.command-panel .expand-collapse li {
|
||||
display: inline-block;
|
||||
padding: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
text-align: right;
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 24px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.command-panel .preview-count,
|
||||
.command-panel .expand-collapse {
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path {
|
||||
padding-left: 5px;
|
||||
position: relative;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path:before {
|
||||
.command-panel .preview-list .path-details:before {
|
||||
font-family: 'Octicons Regular';
|
||||
font-size: 12px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 5px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
content: "\f05b";
|
||||
position: relative;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .is-collapsed .path-details:before {
|
||||
content: "\f05a";
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path-name:before {
|
||||
font-family: 'Octicons Regular';
|
||||
font-size: 16px;
|
||||
width: 16px;
|
||||
@ -36,13 +83,14 @@
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path.readme:before {
|
||||
.command-panel .preview-list .path.readme .path-name:before {
|
||||
content: "\f007";
|
||||
}
|
||||
|
||||
.command-panel .preview-list .operation {
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .line-number {
|
||||
@ -65,20 +113,8 @@
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.command-panel .prompt-and-editor .prompt:before {
|
||||
color: #969696;
|
||||
content: '\f078';
|
||||
font-family: 'Octicons Regular';
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: -5px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.command-panel .prompt-and-editor .editor {
|
||||
position: relative;
|
||||
left: -4px;
|
||||
margin-right: -4px;
|
||||
}
|
||||
|
||||
.command-panel .prompt-and-editor {
|
||||
|
30
static/images/spinner.svg
Normal file
30
static/images/spinner.svg
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version='1.0' standalone='no'?>
|
||||
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>
|
||||
<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='20px' height='20px'>
|
||||
<g>
|
||||
<circle cx='10' cy='2' r='2' style='opacity:0;fill:#fff'>
|
||||
<animate attributeType='CSS' attributeName='opacity' from='1' to='0' dur='0.8s' repeatCount='indefinite' begin='0s' />
|
||||
</circle>
|
||||
<circle cx='15.6569' cy='4.3431' r='2' style='opacity:0;fill:#fff'>
|
||||
<animate attributeType='CSS' attributeName='opacity' from='1' to='0' dur='0.8s' repeatCount='indefinite' begin='0.1s' />
|
||||
</circle>
|
||||
<circle cx='18' cy='10' r='2' style='opacity:0;fill:#fff'>
|
||||
<animate attributeType='CSS' attributeName='opacity' from='1' to='0' dur='0.8s' repeatCount='indefinite' begin='0.2s' />
|
||||
</circle>
|
||||
<circle cx='15.6569' cy='15.6569' r='2' style='opacity:0;fill:#fff'>
|
||||
<animate attributeType='CSS' attributeName='opacity' from='1' to='0' dur='0.8s' repeatCount='indefinite' begin='0.3s' />
|
||||
</circle>
|
||||
<circle cx='10' cy='18' r='2' style='opacity:0;fill:#fff'>
|
||||
<animate attributeType='CSS' attributeName='opacity' from='1' to='0' dur='0.8s' repeatCount='indefinite' begin='0.4s' />
|
||||
</circle>
|
||||
<circle cx='4.3431' cy='15.6569' r='2' style='opacity:0;fill:#fff'>
|
||||
<animate attributeType='CSS' attributeName='opacity' from='1' to='0' dur='0.8s' repeatCount='indefinite' begin='0.5s' />
|
||||
</circle>
|
||||
<circle cx='2' cy='10' r='2' style='opacity:0;fill:#fff'>
|
||||
<animate attributeType='CSS' attributeName='opacity' from='1' to='0' dur='0.8s' repeatCount='indefinite' begin='0.6s' />
|
||||
</circle>
|
||||
<circle cx='4.3431' cy='4.3431' r='2' style='opacity:0;fill:#fff'>
|
||||
<animate attributeType='CSS' attributeName='opacity' from='1' to='0' dur='0.8s' repeatCount='indefinite' begin='0.7s' />
|
||||
</circle>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
@ -6,39 +6,89 @@
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.command-panel .header {
|
||||
background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05));
|
||||
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||
padding: 5px;
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.05),
|
||||
inset 0 -1px 0 rgba(255, 255, 255, 0.02);
|
||||
margin-bottom: 0;
|
||||
border-top-right-radius: 3px;
|
||||
border-top-left-radius: 3px;
|
||||
}
|
||||
|
||||
.command-panel .preview-list {
|
||||
background-color: #19191b;
|
||||
color: #C5C8C6;
|
||||
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||
color: #6d736f;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.command-panel .preview-count {
|
||||
display: inline-block;
|
||||
margin-top: 5px;
|
||||
color: #969696;
|
||||
background: #161719;
|
||||
padding: 5px;
|
||||
text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.command-panel .expand-collapse {
|
||||
color: #969696;
|
||||
}
|
||||
|
||||
.command-panel .expand-collapse li {
|
||||
background: -webkit-linear-gradient(#19191a, #1a1b1c);
|
||||
margin-left: 5px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 3px;
|
||||
box-shadow:
|
||||
inset -1px -1px 0 rgba(255, 255, 255, 0.1),
|
||||
inset 1px 1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.command-panel .preview-list li.selected, .command-panel .preview-list li.operation:hover {
|
||||
background-color: rgba(255, 255, 255, .13);
|
||||
}
|
||||
|
||||
.command-panel .preview-list:focus li.selected {
|
||||
background-color: rgba(255, 255, 255, .2);
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path {
|
||||
.command-panel .expand-collapse li:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path.selected .path-details,
|
||||
.command-panel .preview-list li.operation.selected {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.command-panel .preview-list:focus .path.selected .path-details,
|
||||
.command-panel .preview-list:focus li.operation.selected {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path {
|
||||
color: #bbb;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path:hover .path-name {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path:before {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path:hover:before {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .line-number {
|
||||
color: rgba(255, 255, 255, .3);
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path-match-number {
|
||||
color: rgba(255, 255, 255, .3);
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.command-panel .preview-list .preview {
|
||||
@ -46,6 +96,18 @@
|
||||
}
|
||||
|
||||
.command-panel .preview-list .preview .match {
|
||||
background-color: rgba(255, 255, 255, .2);
|
||||
color: yellow;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
color: #cca300;
|
||||
}
|
||||
|
||||
.command-panel .preview-list li.operation {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.command-panel .preview-list li.operation:hover,
|
||||
.command-panel li.operation.selected .preview {
|
||||
color: #fff;
|
||||
}
|
||||
{
|
||||
color: #fff;
|
||||
}
|
||||
|
@ -2,12 +2,26 @@
|
||||
background-color: #f4f4f4;
|
||||
border-top: 1px solid #979797;
|
||||
color: #ededed;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.command-panel .header {
|
||||
padding: 5px;
|
||||
background-image: -webkit-linear-gradient(#e7e7e7, #cfcfcf);
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
padding: 5px;
|
||||
border-top-right-radius: 3px;
|
||||
border-top-left-radius: 3px;
|
||||
}
|
||||
|
||||
.command-panel .expand-collapse {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.command-panel .preview-list {
|
||||
background-color: #e7e7e7;
|
||||
color: #222;
|
||||
border: 1px solid #989898;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.command-panel .preview-count {
|
||||
@ -19,11 +33,24 @@
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path {
|
||||
color: #3D5075;
|
||||
color: #999;
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path-name {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path:before {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path:hover:before {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .line-number {
|
||||
color: #3D5075;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path-match-number {
|
||||
@ -31,5 +58,10 @@
|
||||
}
|
||||
|
||||
.command-panel .preview-list .preview .match {
|
||||
background-color: #c8d4d7;
|
||||
}
|
||||
background-color: #bbb;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.command-panel .preview-list li.operation {
|
||||
color: #777;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user