<!DOCTYPE html>
<html>
<head>
<link data-require="bootstrap-css@*" data-semver="3.0.3" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" />
<link rel="stylesheet" href="style.css" />
</head>
<body ng-app="gridApp">
<div class="container">
<h1>Excel Sheet in AngularJS!</h1>
<p class="alert alert-info">Right click on cells to avail actions</p>
<div ng-controller="GridCtrl" class="spreadsheet-container">
<table class="spreadsheet table table-condensed table-striped table-bordered">
<thead>
<tr class="row-header">
<th class="col-sno"></th>
<td class="col text-center" ng-repeat="col in records[0]">
<span>{{$index}}</span>
</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="record in records">
<td class="col-sno text-center">{{$index}}</td>
<td class="col" ng-repeat="col in record" ng-right-click="openContextMenu($event, $parent.$index, $index)" ng-click="openContextMenu($event, $parent.$index, $index)">
<input class="editable-cell" ng-model="col.value" />
</td>
</tr>
</tbody>
</table>
<div class="context-menu" ng-show="isContextMenuVisible" ng-style="contextMenuStyle">
<ul class="dropdown-menu">
<li>
<a tabindex="-1" ng-click="addRow()">Add Row</a>
</li>
<li>
<a tabindex="-1" ng-click="removeRow()">Remove Row</a>
</li>
<li>
<a tabindex="-1" ng-click="addColumn()">Add Column</a>
</li>
<li>
<a tabindex="-1" ng-click="removeColumn()">Remove Column</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<script data-require="jquery@*" data-semver="2.0.3" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script data-require="angular.js@*" data-semver="1.2.11" src="http://code.angularjs.org/1.2.11/angular.js"></script>
<script src="script.js"></script>
</body>
</html>
// Code goes here
'use strict';
var gridApp = angular.module('gridApp', []);
gridApp.directive('ngRightClick', function($parse) {
return function(scope, element, attrs) {
var fn = $parse(attrs.ngRightClick);
element.bind('contextmenu', function(event) {
scope.$apply(function() {
event.preventDefault();
fn(scope, {
$event: event
});
});
});
};
});
gridApp.controller('GridCtrl', function($scope, $document, $rootScope) {
var hideContextMenu = function() {
$scope.isContextMenuVisible = false;
if (!$rootScope.$$phase) {
$rootScope.$apply();
}
};
$scope.numRows = 0;
$scope.numColumns = 0;
$scope.isContextMenuVisible = false;
$scope.contextmenuRowIndex = -1;
$scope.contextmenuColumnIndex = -1
$scope.openContextMenu = function($event, rowIndex, columnIndex) {
$event.preventDefault();
if ($event.button === 0) {
$scope.isContextMenuVisible = false;
return;
}
$scope.contextmenuRowIndex = rowIndex;
$scope.contextmenuColumnIndex = columnIndex;
$scope.contextMenuStyle = {
top: $event.clientY + 'px',
left: $event.clientX + 'px'
};
$scope.isContextMenuVisible = true;
};
$scope.addRow = function() {
var i,
record,
cell,
index = $scope.contextmenuRowIndex;
record = [];
for (i = 0; i < $scope.numColumns; i++) {
cell = {
value: 'New Cell'
}
record.push(cell);
}
$scope.records.splice(index, 0, record);
$scope.isContextMenuVisible = false;
$scope.numRows = $scope.records.length;
};
$scope.removeRow = function() {
var index = $scope.contextmenuRowIndex;
$scope.records.splice(index, 1);
$scope.isContextMenuVisible = false;
$scope.numRows = $scope.records.length;
};
$scope.addColumn = function() {
var i, record;
for(i = 0; i < $scope.records.length; i++) {
record = $scope.records[i];
record.splice($scope.contextmenuColumnIndex, 0, {value: 'New Col'});
}
$scope.numColumns = record.length;
};
$scope.removeColumn = function() {
var i, record;
for(i = 0; i < $scope.records.length; i++) {
record = $scope.records[i];
record.splice($scope.contextmenuColumnIndex, 1);
}
$scope.numColumns = record.length;
};
$document.bind('click', function($evt) {
var target = angular.element($evt.target).closest('table');
if (target.length === 0) {
hideContextMenu();
}
});
$scope.init = function() {
var i, j, column, cell;
var records = [],
record;
$scope.numRows = 10;
$scope.numColumns = 20;
for (i = 0; i < $scope.numRows; i++) {
record = [];
for (j = 0; j < $scope.numColumns; j++) {
cell = {
value: ''
}
record.push(cell);
}
records.push(record);
}
$scope.records = records;
}
$scope.init();
});
/* Styles go here */
.editable-cell {
border: none;
background-color: inherit;
}
.container {
position:relative;
}
.context-menu {
position: absolute;
}
.context-menu .dropdown-menu {
display:block;
position:static;
}
input.editable-cell{
padding-left: 5px;
width: 100%;
height: 100%;
}
input.editable-cell:focus {
outline: 1px solid #777;
}
.spreadsheet-container {
width: 100%;
height: 100%;
overflow: scroll;
}
.spreadsheet.table{
width: auto;
height: auto;
}
.spreadsheet th.col, .spreadsheet td.col{
padding: 0;
min-width: 90px;
width: 120px;
}
.spreadsheet .col-sno{
min-width: 30px;
background-color: #EEE;
}
.spreadsheet.table .col-sno{
width: 20px;
background-color: #EEE;
}
.spreadsheet.table .row-header{
background-color: #EEE;
}