diff --git a/services/web/app/views/templates.jade b/services/web/app/views/templates.jade index 04b152dbf5..586719a62d 100644 --- a/services/web/app/views/templates.jade +++ b/services/web/app/views/templates.jade @@ -66,6 +66,8 @@ i(class="sprite-{{ type }}") span.name {{ name }} input.rename.js-rename + .dropdown-caret + i.icon-chevron-down script(type="text/template")#folderTemplate .entity-list-item(class="entity-{{ type }}", entity-type="{{ type }}", id="{{ id }}") @@ -76,6 +78,8 @@ i(class="sprite-{{ type }}") span.name {{ name }} input.rename.js-rename + .dropdown-caret + i.icon-chevron-down script(type="text/template")#entityListTemplate .contents @@ -110,6 +114,9 @@ script(type="text/template")#contextMenuEntryTemplate li a(href="#") {{text}} + + script(type="text/template")#contextMenuDividerTemplate + li.divider script(type="text/template")#genericModalTemplate .modal diff --git a/services/web/public/coffee/file-tree/EntityView.coffee b/services/web/public/coffee/file-tree/EntityView.coffee index 3b792f7004..eab47883bb 100644 --- a/services/web/public/coffee/file-tree/EntityView.coffee +++ b/services/web/public/coffee/file-tree/EntityView.coffee @@ -1,7 +1,8 @@ define [ + "utils/ContextMenu" "libs/backbone" "libs/mustache" -], () -> +], (ContextMenu) -> EntityView = Backbone.View.extend entityTemplate: $("#entityTemplate").html() @@ -14,6 +15,8 @@ define [ events: () -> events = {} events["click ##{@model.id} > .js-clickable"] = "parentOnClick" + events["click .dropdown-caret"] = "showContextMenuFromCaret" + events["contextmenu"] = "showContextMenuFromRightClick" return events render: () -> @@ -71,6 +74,47 @@ define [ if @ide.isAllowedToDoIt "readAndWrite" @startRename() + showContextMenuFromCaret: (e) -> + e.stopPropagation() + caret = @$(".dropdown-caret") + offset = caret.offset() + position = + top: offset.top + caret.outerHeight() + right: $(document.body).width() - (offset.left + caret.outerWidth()) + @toggleContextMenu(position) + + showContextMenuFromRightClick: (e) -> + e.preventDefault() + e.stopPropagation() + position = + left: e.pageX + top: e.pageY + @showContextMenu(position) + + toggleContextMenu: (position) -> + if @contextMenu? + @contextMenu.destroy() + else + @showContextMenu(position) + + showContextMenu: (position) -> + entries = @getContextMenuEntries() + + @contextMenu = new ContextMenu(position, entries) + @contextMenu.on "destroy", () => + delete @contextMenu + + getContextMenuEntries: () -> + return [{ + text: "Rename" + onClick: () => + @startRename() + }, { + text: "Delete" + onClick: () => + @manager.confirmDelete(@model) + }] + _initializeDrag: () -> @$entityListItemEl.draggable delay: 250 diff --git a/services/web/public/coffee/file-tree/FileTreeManager.coffee b/services/web/public/coffee/file-tree/FileTreeManager.coffee index 6526d91dad..4d3a03894f 100644 --- a/services/web/public/coffee/file-tree/FileTreeManager.coffee +++ b/services/web/public/coffee/file-tree/FileTreeManager.coffee @@ -218,52 +218,53 @@ define [ # value is "name.tex" input[0].setSelectionRange(0, defaultName.indexOf("|")) - showNewDocModal: () -> + showNewDocModal: (parentFolder = @getCurrentFolder()) -> + return if !parentFolder? @showNewEntityModal "Document", "name|.tex", (name) => - @addDocToCurrentFolder name + @addDocToFolder parentFolder, name - showNewFolderModal: () -> + showNewFolderModal: (parentFolder = @getCurrentFolder()) -> + return if !parentFolder? @showNewEntityModal "Folder", "name|", (name) => - @addFolderToCurrentFolder name + @addFolderToFolder parentFolder, name - showUploadFileModal: () -> - folder = @getCurrentFolder() - return if !folder? - @ide.fileUploadManager.showUploadDialog folder.id + showUploadFileModal: (parentFolder = @getCurrentFolder()) -> + return if !parentFolder? + @ide.fileUploadManager.showUploadDialog parentFolder.id addDoc: (folder_id, name) -> @ide.socket.emit 'addDoc', folder_id, name - addDocToCurrentFolder: (name) -> - folder = @getCurrentFolder() - return if !folder? - @addDoc folder.id, name + addDocToFolder: (parentFolder, name) -> + @addDoc parentFolder.id, name addFolder: (parent_folder_id, name) -> @ide.socket.emit 'addFolder', parent_folder_id, name - addFolderToCurrentFolder: (name) -> - parentFolder = @getCurrentFolder() + addFolderToFolder: (parentFolder, name) -> return if !parentFolder? @addFolder parentFolder.id, name # DELETING - confirmDelete: () -> + confirmDelete: (entity) -> Modal.createModal title: "Confirm Deletion" - message: "Are you sure you want to delete the selected files?" + message: "Are you sure you want to delete #{entity.get("name")}?" buttons: [{ text: "Cancel" class: "btn" },{ text: "Delete" class: "btn btn-danger" - callback: () => @_doDelete() + callback: () => @_doDelete(entity) }] - _doDelete: () -> + confirmDeleteOfSelectedEntity: () -> entity = @getSelectedEntity() return if !entity? + @confirmDelete(entity) + + _doDelete: (entity) -> @ide.socket.emit 'deleteEntity', entity.id, entity.get("type") @onDeleteEntity entity.id diff --git a/services/web/public/coffee/file-tree/FolderView.coffee b/services/web/public/coffee/file-tree/FolderView.coffee index e7e5ed6901..304059ef28 100644 --- a/services/web/public/coffee/file-tree/FolderView.coffee +++ b/services/web/public/coffee/file-tree/FolderView.coffee @@ -119,3 +119,27 @@ define [ @hideEntries() else @showEntries() + + getContextMenuEntries: (args...) -> + entries = EntityView::getContextMenuEntries.apply(this, args) + entries.push { + divider: true + } + entries.push @getFolderContextMenuEntries()... + return entries + + getFolderContextMenuEntries: () -> + return [{ + text: "New file" + onClick: () => + @manager.showNewDocModal(@model) + }, { + text: "New folder" + onClick: () => + @manager.showNewFolderModal(@model) + }, { + text: "Upload file" + onClick: () => + @manager.showUploadFileModal(@model) + }] + diff --git a/services/web/public/coffee/file-tree/RootFolderView.coffee b/services/web/public/coffee/file-tree/RootFolderView.coffee index 470975447c..8d6a638c88 100644 --- a/services/web/public/coffee/file-tree/RootFolderView.coffee +++ b/services/web/public/coffee/file-tree/RootFolderView.coffee @@ -19,7 +19,7 @@ define [ @manager.showUploadFileModal() "click .js-delete-btn" : (e) -> e.preventDefault() - @manager.confirmDelete() + @manager.confirmDeleteOfSelectedEntity() "click .js-rename-btn" : (e) -> e.preventDefault() @manager.renameSelected() @@ -50,6 +50,9 @@ define [ onToggle: () -> e.preventDefault() + getContextMenuEntries: () -> + @getFolderContextMenuEntries() + hideToggle: () -> @$(".js-toggle").hide() diff --git a/services/web/public/coffee/utils/ContextMenu.coffee b/services/web/public/coffee/utils/ContextMenu.coffee index e541379c3c..10a62aabe2 100644 --- a/services/web/public/coffee/utils/ContextMenu.coffee +++ b/services/web/public/coffee/utils/ContextMenu.coffee @@ -13,44 +13,50 @@ define [ return @ onClick: (e) -> + e.preventDefault() if @options.onClick @options.onClick() ContextMenu = Backbone.View.extend - template: $("#contextMenuTemplate").html() + templates: + menu: $("#contextMenuTemplate").html() + divider: $("#contextMenuDividerTemplate").html() - initialize: () -> - @entries = [] + initialize: (position, entries) -> + if ContextMenu.currentMenu? + ContextMenu.currentMenu.destroy() + ContextMenu.currentMenu = @ @render() - @hide() + for entry in entries + @addEntry(entry) + @show(position) render: () -> - @setElement($(@template)) + @setElement($(@templates.menu)) $(document.body).append(@$el) return @ - hide: () -> @$el.hide() + destroy: () -> + @$el.remove() + @trigger "destroy" - show: (left, top) -> + show: (position) -> page = $(document.body) page.on "click.hideContextMenu", (e) => page.off "click.hideContextMenu" - @hide() + @destroy() @$el.css position: "absolute" "z-index": 10000 - top: (top || 0) + "px" - left: (left || 0) + "px" - @$el.show() + @$el.css position addEntry: (options) -> - entry = new ContextMenuEntry(options) - @$el.append entry.render().el - @entries.push entry + if options.divider + @$el.append $(@templates.divider) + else + entry = new ContextMenuEntry(options) + @$el.append entry.render().el - clearEntries: () -> - while @entries.length > 0 - @entries.pop().remove() diff --git a/services/web/public/stylesheets/less/editor.less b/services/web/public/stylesheets/less/editor.less index c1b93552aa..4844db82ed 100644 --- a/services/web/public/stylesheets/less/editor.less +++ b/services/web/public/stylesheets/less/editor.less @@ -443,7 +443,7 @@ body.editor { overflow: hidden; cursor: default; border-right: 1px solid #aaa; - background: url('/img/noise.png') #eee; + background-color: rgb(238,238,238); padding: 6px 0 0 6px; ul { @@ -459,10 +459,14 @@ body.editor { list-style: none; white-space: nowrap; cursor: pointer; + position: relative; .clickable { height: 24px; margin-left: 20px; } + .dropdown-caret { + display: none; + } } .entity-folder { @@ -495,9 +499,33 @@ body.editor { } li.selected, .entity-list-item.selected { - background-color: rgba(55,120,194,0.3); + background-color: rgb(185,201,227); .border-radius(5px 0 0 5px); - color: #3d5979 + color: #3d5979; + .dropdown-caret { + display: block; + padding: 4px 6px 4px 4px; + position: absolute; + top: 0; + right: 0; + bottom: 0; + background-color: rgb(185,201,227); + &:before { + content: " "; + #gradient > .horizontal(rgba(185,201,227,0), rgb(185,201,227)); + background-color: transparent; + width: 12px; + height: 100%; + position: absolute; + top: 0; + bottom: 0; + right: 20px + } + i { + vertical-align: top; + margin: 0; + } + } } li img, .entity-list-item i { @@ -600,6 +628,12 @@ table { } } +.context-menu { + display: block; + left: auto; + .border-radius(0); +} + .clear-modal-backdrop { background: none; }