Caleydo Web & AngularJS: Matrix
A simple demo on how to integrate Caleydo Web's matrix vis into an AngularJS app.
by flek
HTML
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stampit/2.1.0/stampit.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.14/d3.min.js"></script>
<script src="https://rawgit.com/Caleydo/caleydo_web/02056f5408fc33402f9de79aa75ddfd097188195/bundle.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.5.1/lodash.min.js"></script>
<div id="app" ng-app="matrixApp" ng-controller="matrixAppCtrl as app">
<table id="table" ng-mouseleave="app.leaveTableBody()">
<thead>
<tr>
<th
ng-if="app.matrix.columnIds || app.matrix.rowIds"
ng-mouseenter="app.leaveTableBody()"></th>
<th
ng-class="{ 'soft-highlight': app.hoveringColumn === $index, 'selected': app.columnSelected($index) }"
ng-click="app.selectColumn($index)"
ng-mouseenter="app.enterColumn($index)"
ng-repeat="id in app.matrix.columnIds track by $index" class="ids">{{ app.matrix.columnHeader($index) }}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in app.matrix.rowIds track by $index">
<td
class="ids"
ng-class="{ 'soft-highlight': app.hoveringRow === $parent.$index, 'selected': app.rowSelected($parent.$index) }"
ng-if="app.matrix.rowIds.length"
ng-mouseenter="app.enterRow($index)"
ng-click="app.selectRow($index)">{{ app.matrix.rowHeader($index) }}</td>
<td
class="cell"
ng-class="{
'soft-highlight-horizontally': app.hoveringRow === $parent.$index,
'soft-highlight-vertically': app.hoveringColumn === $index,
'selected': app.cellSelected($index, $parent.$index)
}"
ng-repeat="cell in app.matrix.columnIds track by $index"
ng-mouseenter="app.enterCell($parent.$index, $index)"
ng-click="app.selectCell($index, $parent.$index)">
...
SCSS
@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700);
$colorBlindBlue: #50b4e9;
$colorBlindYellow: #f0e442;
$colorDirectHoverBg: rgba(0, 0, 0, 0.2);
$colorIndirectHoverBg: rgba(0, 0, 0, 0.1);
$colorSelectedBg: $colorBlindYellow;
#app {
font-family: 'Source Sans Pro', sans-serif;
font-size: 14px;
}
#table {
.ids {
font-weight: bold;
color: #000;
&:hover {
cursor: pointer;
}
&.soft-highlight {
background: $colorIndirectHoverBg;
}
&.selected {
background: lighten($colorSelectedBg, 15%);
}
}
.cell {
color: #333;
&:hover {
color: #000;
background: $colorDirectHoverBg;
box-shadow: none !important;
}
&.soft-highlight-horizontally {
box-shadow: inset 0 2px 0 0 $colorIndirectHoverBg, inset 0 -2px 0 0 $colorIndirectHoverBg;
}
&.soft-select-horizontally {
box-shadow: inset 0 2px 0 0 $colorSelectedBg, inset 0 -2px 0 0 $colorSelectedBg;
}
&.soft-highlight-vertically {
box-shadow: inset 2px 0 0 0 $colorIndirectHoverBg, inset -2px 0 0 0 $colorIndirectHoverBg;
}
&.soft-select-vertically {
box-shadow: inset 2px 0 0 0 $colorSelectedBg, inset -2px 0 0 0 $colorSelectedBg;
}
&.soft-select-horizontally.soft-select-vertically {
box-shadow: inset 0 0 0 2px $colorSelectedBg;
}
&:last-child {
&.soft-highlight-horizontally {
box-shadow: inset 0 2px 0 0 $colorIndirectHoverBg, inset 0 -2px 0 0 $colorIndirectHoverBg, inset -2px 0 0 0 $colorIndirectHoverBg;
}
&.soft-select-horizontally {
box-shadow: inset 0 2px 0 0 $colorSelectedBg, inset 0 -2px 0 0 $colorSelectedBg, inset -2px 0 0 0 $colorSelectedBg;
}
}
&.selected {
background: $colorSelectedBg;
}
}
td, th {
min-width: 2rem;
padding: 0.25rem;
text-align: center;
}
input {
width: 2rem;
text-align: center;
...
JavaScript
// Dummy data
var DUMMY_DATA = [['ID','a','b','c','d','e'],
['1', 10, 9.14, 1, 2, 4],
['2', 8, 8.14, 1, 2, 4],
['3', 13, 8.74, 1, 2, 4],
['4', 9, 8.77, 1, 2, 4],
['5', 11, 9.26, 1, 2, 4],
['6', 14, 8.1, 1, 2, 4],
['7', 6, 6.13, 1, 2, 4],
['8', 4, 3.1, 1, 2, 4],
['9', 12, 9.13, 1, 2, 4],
['10', 7, 7.26, 1, 2, 4],
['11', 5, 4.74, 1, 2, 4]];
// Very simple event factory
var eventStamp = stampit.init(function() {
var stack = {};
this.on = function (event, callback) {
if (event in stack) {
stack[event].push(callback);
} else {
stack[event] = [callback];
}
return this;
};
this.trigger = function (event, payload) {
if (event in stack) {
for (var i = stack[event].length; i--;) {
stack[event][i](payload);
}
}
return this;
};
});
// Generic matrix factory function
var matrixStamp = stampit.init(function(stamp) {
var THAT = stamp.instance;
var data = stamp.args[0];
var columnIds = stamp.args[1];
var rowIds = stamp.args[2];
// Update the column sums
function updateColumnSums () {
var sums = [];
for (
var row = rowIds ? 1 : 0,
numRows = data.length;
row < numRows;
row++
) {
for (
var column = columnIds ? 1 : 0,
numColumns = data[row].length;
column < numColumns;
column++
) {
var realColumn = columnIds ? column - 1 : column;
sums[realColumn] = sums[realColumn] ? sums[realColumn] + data[row][column] : data[row][column];
}
}
THAT.columnSums = sums;
}
// Initialize column sums
updateColumnSums();
// Generates an empty array of length # columns
// Note that this is barely a helper array to support _ngRepeat_.
this.columnIds = (function () {
return [].constructor(columnIds && data.length ? rowIds ? data[0].length - 1 : data[0].length : 0);
}());
// Returns column header entry
this.columnHeader = function (column) {
return columnIds...