UNPKG

coffeescript-ui

Version:
336 lines (263 loc) 7.96 kB
### * coffeescript-ui - Coffeescript User Interface System (CUI) * Copyright (c) 2013 - 2016 Programmfabrik GmbH * MIT Licence * https://github.com/programmfabrik/coffeescript-ui, http://www.coffeescript-ui.org ### class CUI.ListViewTree extends CUI.ListView constructor: (opts) -> super(opts) CUI.util.assert(@root instanceof CUI.ListViewTreeNode, "new CUI.ListViewTree", "opts.root must be instance of ListViewTreeNode", opts: @opts) @root.setTree(@) # initOpts: -> super() @addOpts rowMoveWithinNodesOnly: check: Boolean children: check: Array selectable: deprecated: true check: Boolean # dont display a tree hierarchy no_hierarchy: default: false check: Boolean root: check: (v) -> v instanceof CUI.ListViewRow onOpen: check: Function onClose: check: Function readOpts: -> super() if @_selectable != undefined CUI.util.assert(@_selectableRows == undefined, "new CUI.ListViewTree", "opts.selectable cannot be used with opts.selectableRows, use selectableRows only.", opts: @opts) @__selectableRows = @_selectable @ getRowMoveTool: (opts = {}) -> opts.rowMoveWithinNodesOnly = @_rowMoveWithinNodesOnly new CUI.ListViewTreeRowMove(opts) initListView: -> super() if not @_root lv_opts = {} if @_children lv_opts.children = @_children else if @_getChildren lv_opts.getChildren = @_getChildren else lv_opts.children = [] @root = new CUI.ListViewTreeNode(lv_opts) else @root = @_root @ # isSelectable: -> # @_selectable isSelectable: -> !!@__selectableRows # hasSelectableRows: -> # @_selectable isNoHierarchy: -> @_no_hierarchy triggerNodeDeselect: (ev, node) -> info = ev: ev node: node listView: @ @_onDeselect?(ev, info) CUI.Events.trigger node: @ type: "row_deselected" triggerNodeSelect: (ev, node) -> info = ev: ev node: node listView: @ @_onSelect?(ev, info) CUI.Events.trigger node: @ type: "row_selected" render: -> handle_event = (ev) => node = CUI.dom.data(CUI.dom.closest(ev.getCurrentTarget(), ".cui-lv-tree-node"), "listViewRow") if node not instanceof CUI.ListViewTreeNode or node.isLoading() or node.isLeaf() return # This needs to be immediate, "super" listens on the same node ev.stopImmediatePropagation() if ev instanceof CUI.DragoverScrollEvent if ev.getCount() % 50 == 0 @toggleNode(ev, node) else @toggleNode(ev, node) return super() CUI.Events.listen node: @DOM selector: ".cui-tree-node-handle" capture: true type: ["click", "dragover-scroll"] call: (ev) => handle_event(ev) CUI.Events.listen node: @DOM selector: ".cui-lv-tree-node" type: ["click"] call: (ev) => handle_event(ev) if @_no_hierarchy CUI.dom.addClass(@grid, "cui-list-view-tree-no-hierarchy") else CUI.dom.addClass(@grid, "cui-list-view-tree-hierarchy") if @__isFocusable() selectorFocus = "."+@__lvClass+"-quadrant > .cui-lv-tr-outer:focus" CUI.Events.listen type: ["keydown"] node: @DOM selector: selectorFocus call: (ev) => node = CUI.dom.data(ev.getCurrentTarget(), "listViewRow") if not node.isSelectable() return focusNode = (node) => rowIdx = node.getRowIdx() row = @getRow(rowIdx)[0] row.focus() return if ev.getKeyboard() == "Right" and not node.isOpen() node.open().done(() => focusNode(node)) else if ev.getKeyboard() == "Left" and node.isOpen() node.close().done(() => focusNode(node)) return return @DOM toggleNode: (ev, node) -> if node.isOpen() @__runTrigger(ev, "close", node) else @__runTrigger(ev, "open", node) return __runTrigger: (ev, action, node) -> if ev.ctrlKey() @__actionOnNode(ev, action+"Recursively", node) else @__actionOnNode(ev, action, node) return __actionOnNode: (ev, action, node) => hide_spinner = null spinner_timeout = CUI.setTimeout ms: 500 call: => node.showSpinner() spinner_timeout = null hide_spinner = true @stopLayout() ret = node[action]() ret.done => switch action when "open" @_onOpen?(ev, node: node) when "close" @_onClose?(ev, node: node) ret.always => if spinner_timeout CUI.clearTimeout(spinner_timeout) if hide_spinner node.hideSpinner() @startLayout() CUI.Events.trigger type: "content-resize" node: @DOM return ret # console.timeEnd("#{@__uniqueId}: action on node #{action}") deselectRow: (ev, row, newRow) -> if @__selectableRows == "multiple" return super(ev, row, newRow) # we ignore this here, because our "ListViewTreeNode" handles # deselect during "select" return CUI.resolvedPromise() getNodesForMove: (from_i, to_i, after) -> from_node = @getListViewRow(from_i) to_node = @getListViewRow(to_i) CUI.util.assert(from_node, "ListViewTree.moveRow", "from_i node not found", from_i: from_i) CUI.util.assert(to_node, "ListViewTree.moveRow", "to_i node not found", to_i: to_i) if from_node.father == to_node.father and not (to_node.is_open and after) new_father = null else if (to_node.is_open and after) new_father = to_node else new_father = to_node.father if new_father == from_node.father new_father = null [ from_node, to_node, new_father ] moveRow: (from_i, to_i, after) -> # console.error "moveRow", "from_i:", from_i, "to_i:", to_i, "after:", after, "display_from_i:", @getDisplayRowIdx(from_i), "display_to_i:", @getDisplayRowIdx(to_i) [ from_node, to_node, new_father ] = @getNodesForMove(from_i, to_i, after) moveNodePromise = from_node.moveNodeBefore(to_node, new_father, after) CUI.util.assert(CUI.util.isPromise(moveNodePromise), "ListViewTree.moveRow", "moveNodeBefore needs to return a Promise", promise: moveNodePromise) deferred = new CUI.Deferred() moveNodePromise.done( => display_from_i = @getDisplayRowIdx(from_i) display_to_i = @getDisplayRowIdx(to_i) super(from_i, to_i, after, false) # move row in data structure if from_node.father == to_node.father and not (to_node.is_open and after) CUI.util.moveInArray(from_node.getChildIdx(), to_node.getChildIdx(), from_node.father.children, after) else from_node.father.removeChild(from_node) # the node is open and we want to put it after, so # this means that open node is the new father if to_node.is_open and after to_node.children.splice(0,0,from_node) from_node.setFather(to_node) else if not after to_node.father.children.splice(to_node.getChildIdx(),0,from_node) else to_node.father.children.splice(to_node.getChildIdx()+1,0,from_node) from_node.setFather(to_node.father) reloadPromises = [ from_node.reload() ] if new_father reloadPromises.push(new_father.reload()) CUI.whenAll(reloadPromises).done(=> @_onRowMove?(display_from_i, display_to_i, after) CUI.Events.trigger node: @grid type: "row_moved" info: from_i: from_i to_i: to_i after: after deferred.resolve() ).fail(deferred.reject) ).fail(deferred.reject) deferred.promise() getRootChildren: -> @root.children getSelectedNode: (key="selectedNode") -> @root[key] prependNode: (node) -> @addNode(node, false) addNode: (node, append=true) -> CUI.util.assert(node instanceof CUI.ListViewTreeNode, "#{CUI.util.getObjectClass(@)}.addNode", "Node must be instance of ListViewTreeNode", node: node) promise = @root.addNode(node, append) CUI.Events.trigger node: @ type: "row_added" info: node: node promise openTreeNodeByRowDisplayIndex: (index) -> row_index = @getRowIdx(index) row = @getRow(row_index) CUI.dom.data(row[0], "listViewRow").open() CUI.Events.registerEvent bubble: true type: [ "row_removed" "row_added" "row_moved" "row_selected" "row_deselected" ]