Simple excel like spreadsheet development kit for angular.js.
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
README.md
capture.png
cellCursor.css
cellCursor.js
example.html
package.json

README.md

angular Cell Cursor

Simple excel like spreadsheet development kit for angularJs.

  • display table cell cursor like Excel.
    • user can move cursor by mouse or keyboad ( ARROW ).
  • range selection like Excel (mouse drag or shift+ARROW , ESC to deselect).
  • user can copy range values to Excel (ctrl+C).
  • user can paste range values from Excel (ctrl+V).
  • user can edit cell value (ENTER,F2, or direct input for example a).
  • user can drag to resize column or row.
  • Easy to extend.

capture

DEMO

http://team-lab.github.io/cell-cursor/example.html

directives

cell-cursor

cell-cursor="expression"

bind cell-cursor. export CellCursor object.

  <div ng-init="items=[{id:1,name:'apple'},{id:2,name:'orange'},{id:3,name:'banana'}]">
    <!-- bind CellCursor to scope.x -->
    <!-- table need tablindex attribute for directive catch keydown events. -->
    <table tabindex="0" cell-cursor="x">
    	<tbody><!-- cursor can move in tbody -->
	    	<tr ng-repeat="i in items">
	    		<td>{{i.id}}</td>
	    		<td>{{i.name}}</td>
	    	</tr>
	    </tbody>
    </table>
    <!-- you can access CellCursor Object -->
    cursor=[{{x.selected.cursor.row}},{{x.selected.cursor.col}}]
  </div>

Events

cell-cursor emit event to scope.

  • $emit("cellCursor", cellCursor, name)
    • on initialized. name is string of cell-cursor attrubtue.
    • cellCursor is controller.

cell-cursor emit event to cellCursor.

  • $emit("cellCursor.select.before", {pos, expanding, size, selected})
    • on before move cursor and area.
    • pos is {row:number, col:number} of next selected.cursor.
    • expanding is {row:number, col:number} of next selected.start.
      • when expanding is true, next selected.start=selected.start
      • when expanding is false, next selected.start=pos
    • can cancel select by call event.preventDefault()
    • can modify pos and expanding to change move.
    • this event is fired if pos and expanding is not moved.
  • $emit("cellCursor.select.after")
    • on after move cursor and area.
    • this event is fired only if pos and expanding is changed.
  • $emit("cellCursor.editor.opening", td, cellCursorOptionsController)
    • on before open editor.
    • td is HTMLCellElement of editor opening.
    • cellCursorOptionsController is CellCursorOptionsController of this cell.
    • can cancel select by call event.preventDefault()
  • $emit('cellCursor.editor.finished', td, cellCursorOptionsController)
    • on after close editor.
    • td is HTMLCellElement of editor opening.
    • cellCursorOptionsController is CellCursorOptionsController of this cell.

cell-cursor-copy

cell-cursor-copy="expression"

When copy called, then set data to clipboard.

  <div ng-app="app" ng-controller="rootCtrl">
    <table tabindex="0" cell-cursor="x" cell-cursor-copy="x.getSelectedCellValues()|cellCursorToTsv">
    	<tbody>
	    	<tr ng-repeat="i in items">
	    		<!-- if you use ng-model on `td`, getSelectedCellValues() get data by ngModelControll.$viewValue -->
	    		<td ng-model="i.id">{{i.id}}</td>
	    		<!-- if you use cell-options on `td`, getSelectedCellValues() get data by getter() -->
	    		<td cell-cursor-options="{getter:getName(i)}">{{i.name}}</td>
	    	</tr>
	    </tbody>
    </table>
  </div>
  <script>
  angular.module('app',['cellCursor'])
  .controller('rootCtrl',['$scope',function($scope){
    $scope.items=[{id:1,name:'apple'},{id:2,name:'orange'},{id:3,name:'banana'}];
		// create getter
		$scope.getName=function(i){
			return function(){
				return "["+i.name+"]";
			}
		}
	}]);
	</script>

cell-cursor-paste

cell-cursor-paste="expression"

When press ctrl+v called, then get data from clipboard.

Expression scope has $data it is clipboard data.

  <div ng-app="app" ng-controller="rootCtrl">
    <table tabindex="0" cell-cursor="x" cell-cursor-paste="x.setSelectedCellValues($data)">
    	<tbody>
	    	<tr ng-repeat="i in items">
	    		<!-- if you use ng-model on `td`, setSelectedCellValues() set data by ngModelControll.$setViewValue -->
	    		<!-- if td has readonly attributes, setSelectedCellValues dose not effect -->
	    		<td ng-model="i.id" ng-readonly="readonly">{{i.id}}</td>
	    		<!-- if you use cell-options on `td`, setSelectedCellValues() set data by setter() -->
	    		<td cell-cursor-options="{setter:setName(i)}">{{i.name}}</td>
	    	</tr>
	    </tbody>
    </table>
    <button ng-click="readonly=!readonly">readonly({{readonly}})</button>
  </div>
  <script>
  angular.module('app',['cellCursor'])
  .controller('rootCtrl',['$scope',function($scope){
    $scope.items=[{id:1,name:'apple'},{id:2,name:'orange'},{id:3,name:'banana'}];
		// create setter
		$scope.setName=function(i){
			return function(v){
				if(v!="badname"){
					return i.name=v;
				}
			}
		}
	}]);
	</script>

cell-cursor-options

cell-cursor-options="expression"

set option object. set to cell( td or th ) element.

{
  "setter":"function|string|boolean: cell value setter",
  "getter":"function|string|boolean: cell value getter",
  "model":"string: expression for bind value in scope",
  "locked":"function|string|boolean: can set cell value?",
  "input":"string: querySelector for input element(that has ngModel)",
  "editor":"string: editor type name. it defined by cellCursorEditorFactory service",
  "on[event]":"function: called on events"
}

Order of getters/setter definitions is getter|setter > bind > ngModel > input.

If locked is true, cell value can't set from all method, and can't open editor.

Event function signeture is function (event, option:(cell-cursor-options), cellCursorOptionsController, cellCursor, td:HTMLCellElement):boolean.

event called now keydown|keypress|keydown|compositionstart|compositionupdate|compositionend|input only.

  <div ng-init="items=[{name:'apple'},{name:'orange'},{name:'banana'}]">
    <table cell-cursor="x">
    	<tr ng-repeat="i in items"><td cell-cursor-options="{model:'i.name',editor:'text'}">{{i.name}}</td></tr>
    </table>
  </div>

You can set function or string or boolean for getter/setter. like belows.

if setter/getter is function, cellCursor call it.

  <div ng-app="app" ng-controller="rootCtrl">
    <table cell-cursor="x">
      <tr ng-repeat="i in items"><td cell-cursor-options="{getter:nameGetter(i),setter:nameSetter(i)}">{{i.name}}</td></tr>
    </table>
  </div>
  <script>
  angular.module('app',['cellCursor'])
  .controller('rootCtrl',['$scope',function($scope){
    $scope.items=[{id:1,name:'apple'},{id:2,name:'orange'},{id:3,name:'banana'}];
    // create getter/setter
    $scope.nameGetter=function(i){
      return function(){ return i; }
    }
    $scope.nameSetter=function(i){
      return function(v){
        if(v!="badname"){ return i.name=v; }
      }
    }
  }]);
  </script>

if setter/getter is string, cellCursor eval it with scope of there.

  <!-- string -->
  <div ng-app="app" ng-controller="rootCtrl">
    <table cell-cursor="x">
      <!-- $data is data for setter -->
      <tr ng-repeat="i in items"><td cell-cursor-options="{getter:'i.getName()',setter:'i.setName($data)}">{{i.name}}</td></tr>
    </table>
  </div>
  <script>
  function Item(data){
    angular.copy(data,this);
  }
  // create getter/setter
  Item.prototype.getName = function(){
    return this.name;
  }
  Item.prototype.setName = function(v){
    if(v!="badname"){ return i.name=v; }
  }
  angular.module('app',['cellCursor'])
  .controller('rootCtrl',['$scope',function($scope){
    $scope.items=[new Item({id:1,name:'apple'}),new Item({id:2,name:'orange'}), new Item({id:3,name:'banana'})];
  }]);
  </script>

if setter/getter is true, cellCursor eval 'model' value for getter/setter.

{model:'i.name',getter:true,setter:true" is the same as {getter:'i.getName()',setter:'i.setName($data)}".

if setter/getter is false, value readonly/writeonly.

{model:'i.name',getter:false,setter:false" is the same as {getter:'noop()',setter:'noop()".

cell-cursor-cell

cell-cursor-cell="expression"

set option object. set to cell( td or th ) element.

and, it directive wrap inner content <div style="overflow:hidden;white-space:nowrap"></div> (this tag hide overflow content).

cell-cursor-cell is extended cell-cursor-options directive.

{
  "setter","getter",..."on[event]":"same as `cell-cursor-options`",
  "hide":"boolean: td visible or hide (it works like ng-if)",
  "width":"number: set td style 'width' and 'max-width' and 'min-width'"
}

cell-cursor-drag

cell-cursor-drag="expression"

expression indicate CellCursor object.

  <div ng-app="app" ng-controller="rootCtrl">
    <table tabindex="0" cell-cursor="x">
      <tbody>
        <tr ng-repeat="i in items">
          <td ng-model="i.id" ng-readonly="readonly" cell-cursor-drag="x">{{i.name}}</td>
        </tr>
      </tbody>
    </table>
  </div>
  <script>
  angular.module('app',['cellCursor'])
  .controller('rootCtrl',['$scope',function($scope){
    $scope.items=[{id:1,name:'apple'},{id:2,name:'orange'},{id:3,name:'banana'}];
    // after initialized handler
    $scope.$on("cellCursor",function(e, cellCursor, name){
      if(name=='x'){
        // set drag handler
        cellCursor.$on("cellCursor.drag",function(e, fromPos, toPos){
          if(fromPos.row!=toPos.row){
            // cut & paste
            var s = $scope.items.splice(fromPos.row,1);
            $scope.items.splice.apply($scope.items,[toPos.row,0].concat(s));
            fromPos.row = toPos.row;
          }else{
            e.preventDefault();
          }
        });
      }
    });
  }]);
  </script>

Events

cell-cursor-drag emit event to cellCursor.

  • $emit("cellCursor.drag.start", pos)
    • on mousedown
  • $emit("cellCursor.drag", fromPos, toPos)
    • on mousemove to othre cell
  • $emit("cellCursor.drag.end", pos)
    • on mouseup

argument pos is target cell position object {row, col}.

cell-cursor-col-resize

cell-cursor-col-resize="expression"

expression indicate event data object.

drag resize handler. User can drag and resize to column width. ( in html, set style 'max-width' and 'min-width' to all cell elements. ) and reset by dblclick.

<table>
  <tr>
    <!-- set handler as class name -->
    <!-- wrap content div that has 'overflow:hidden' if you need -->
    <td><div cell-cursor-col-resize="'A'"></div><div style="overflow:hidden">This name is 'A'.</div></td>
    <td><div cell-cursor-col-resize="'B'"></div><div style="overflow:hidden">This name is 'B'.</div></td>
    <td><div cell-cursor-col-resize="'C'"></div><div style="overflow:hidden">This name is 'C'.</div></td>
  </tr>
</table>

Events

cell-cursor-row-resize emit event to cellCursor.

  • $emit("cellCursor.colResize.start", pos, size, data)
    • on mousedown
    • can cancel by call event.preventDefault().
  • $emit("cellCursor.colResize.resizing", pos, newSize, oldSize, data)
    • on mousemove before resize
    • can cancel by call event.preventDefault().
    • can modify size by modify argument newSize.width.
  • $emit("cellCursor.colResize.resized", pos, size, data)
    • on mousemove after resize
  • $emit("cellCursor.colResize.end, pos, size, data)`
    • on mouseup
  • $emit("cellCursor.colResize.reset, pos, size, data)`
    • on dblclick
    • can cancel by call event.preventDefault().

argument pos is target cell position object {row, col}. argument size is target cell size object {width, height}. argument data is value of attribute cell-cursor-col-resize.

cell-cursor-row-resize

cell-cursor-col-resize="expression"

expression indicate event data object.

drag resize handler. User can drag and resize to row height. ( in html, set style 'max-height' and 'min-height' to tr elements. ) and reset by dblclick.

<table>
  <tr>
    <!-- set handler as class name -->
    <td><div cell-cursor-row-resize="'A'"></div>A</td>
  </tr>
  <tr>
    <td><div cell-cursor-row-resize="'B'"></div>B</td>
  </tr>
  <tr>
    <td><div cell-cursor-row-resize="'C'"></div>C</td>
  </tr>
</table>

Events

cell-cursor-row-resize emit event to cellCursor.

  • $emit("cellCursor.rowResize.start", pos, size, data)
    • on mousedown
    • can cancel by call event.preventDefault().
  • $emit("cellCursor.rowResize.resizing", pos, newSize, oldSize, data)
    • on mousemove before resize
    • can cancel by call event.preventDefault().
    • can modify size by modify argument newSize.height.
  • $emit("cellCursor.rowResize.resized", pos, size, data)
    • on mousemove after resize
  • $emit("cellCursor.rowResize.end, pos, size, data)`
    • on mouseup
  • $emit("cellCursor.rowResize.reset, pos, size, data)`
    • on dblclick
    • can cancel by call event.preventDefault().

argument pos is target cell position object {row, col}. argument size is target cell size object {width, height}. argument data is value of attribute cell-cursor-row-resize.