mirror of
https://github.com/nasa/openmct.git
synced 2025-01-31 08:25:31 +00:00
[Table] #798 Simplified markup, moved styles to external stylesheet
This commit is contained in:
parent
c591ade479
commit
23a8c305c1
@ -161,6 +161,12 @@ define([
|
|||||||
"key": "table-options-edit",
|
"key": "table-options-edit",
|
||||||
"templateUrl": "templates/table-options-edit.html"
|
"templateUrl": "templates/table-options-edit.html"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"stylesheets": [
|
||||||
|
{
|
||||||
|
"stylesheetUrl": "css/table.css",
|
||||||
|
"priority": "mandatory"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
50
platform/features/table/res/sass/table.scss
Normal file
50
platform/features/table/res/sass/table.scss
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
.sizing-table {
|
||||||
|
min-width: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
visibility: hidden;
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
//Add some padding to allow for decorations such as limits indicator
|
||||||
|
td {
|
||||||
|
padding-right: 15px;
|
||||||
|
padding-left: 10px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.mct-table {
|
||||||
|
table-layout: fixed;
|
||||||
|
th {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,22 +1,25 @@
|
|||||||
<div class="l-view-section scrolling"
|
<div class="l-view-section scrolling" style="overflow: auto;">
|
||||||
ng-style="overrideRowPositioning ?
|
<table class="sizing-table">
|
||||||
{'overflow': 'auto'} :
|
<tbody>
|
||||||
{'overflow': 'scroll'}"
|
<tr>
|
||||||
>
|
<td ng-repeat="header in displayHeaders">{{header}}</td>
|
||||||
<table class="filterable"
|
</tr>
|
||||||
ng-style="overrideRowPositioning && {
|
<tr><td ng-repeat="header in displayHeaders" >
|
||||||
|
{{sizingRow[header].text}}
|
||||||
|
</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table class="filterable mct-table"
|
||||||
|
ng-style="{
|
||||||
height: totalHeight + 'px',
|
height: totalHeight + 'px',
|
||||||
'table-layout': overrideRowPositioning ? 'fixed' : 'auto',
|
|
||||||
'max-width': totalWidth
|
'max-width': totalWidth
|
||||||
}">
|
}">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th ng-repeat="header in displayHeaders"
|
<th ng-repeat="header in displayHeaders"
|
||||||
ng-style="overrideRowPositioning && {
|
ng-style="{
|
||||||
width: columnWidths[$index] + 'px',
|
width: columnWidths[$index] + 'px',
|
||||||
'max-width': columnWidths[$index] + 'px',
|
'max-width': columnWidths[$index] + 'px',
|
||||||
overflow: 'none',
|
|
||||||
'box-sizing': 'border-box'
|
|
||||||
}"
|
}"
|
||||||
ng-class="[
|
ng-class="[
|
||||||
enableSort ? 'sortable' : '',
|
enableSort ? 'sortable' : '',
|
||||||
@ -29,11 +32,9 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr ng-if="enableFilter" class="s-filters">
|
<tr ng-if="enableFilter" class="s-filters">
|
||||||
<th ng-repeat="header in displayHeaders"
|
<th ng-repeat="header in displayHeaders"
|
||||||
ng-style="overrideRowPositioning && {
|
ng-style="{
|
||||||
width: columnWidths[$index] + 'px',
|
width: columnWidths[$index] + 'px',
|
||||||
'max-width': columnWidths[$index] + 'px',
|
'max-width': columnWidths[$index] + 'px',
|
||||||
overflow: 'none',
|
|
||||||
'box-sizing': 'border-box'
|
|
||||||
}">
|
}">
|
||||||
<input type="text"
|
<input type="text"
|
||||||
ng-model="filters[header]"/>
|
ng-model="filters[header]"/>
|
||||||
@ -41,21 +42,15 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<tbody ng-style="overrideRowPositioning ? '' : {
|
<tbody>
|
||||||
'opacity': '0.0'
|
|
||||||
}">
|
|
||||||
<tr ng-repeat="visibleRow in visibleRows track by visibleRow.rowIndex"
|
<tr ng-repeat="visibleRow in visibleRows track by visibleRow.rowIndex"
|
||||||
ng-style="overrideRowPositioning && {
|
ng-style="{
|
||||||
position: 'absolute',
|
|
||||||
top: visibleRow.offsetY + 'px',
|
top: visibleRow.offsetY + 'px',
|
||||||
}">
|
}">
|
||||||
<td ng-repeat="header in displayHeaders"
|
<td ng-repeat="header in displayHeaders"
|
||||||
ng-style="overrideRowPositioning && {
|
ng-style=" {
|
||||||
width: columnWidths[$index] + 'px',
|
width: columnWidths[$index] + 'px',
|
||||||
'white-space': 'nowrap',
|
|
||||||
'max-width': columnWidths[$index] + 'px',
|
'max-width': columnWidths[$index] + 'px',
|
||||||
overflow: 'hidden',
|
|
||||||
'box-sizing': 'border-box'
|
|
||||||
}"
|
}"
|
||||||
class="{{visibleRow.contents[header].cssClass}}">
|
class="{{visibleRow.contents[header].cssClass}}">
|
||||||
{{ visibleRow.contents[header].text }}
|
{{ visibleRow.contents[header].text }}
|
||||||
|
@ -23,10 +23,13 @@ define(
|
|||||||
this.maxDisplayRows = 50;
|
this.maxDisplayRows = 50;
|
||||||
|
|
||||||
this.scrollable = element.find('div');
|
this.scrollable = element.find('div');
|
||||||
|
this.thead = element.find('thead');
|
||||||
|
this.tbody = element.find('tbody');
|
||||||
|
this.$scope.sizingRow = {};
|
||||||
|
|
||||||
this.scrollable.on('scroll', this.onScroll.bind(this));
|
this.scrollable.on('scroll', this.onScroll.bind(this));
|
||||||
|
|
||||||
$scope.visibleRows = [];
|
$scope.visibleRows = [];
|
||||||
$scope.overrideRowPositioning = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set default values for optional parameters on a given scope
|
* Set default values for optional parameters on a given scope
|
||||||
@ -100,14 +103,31 @@ define(
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
MCTTableController.prototype.newRow = function (event, rowIndex) {
|
MCTTableController.prototype.newRow = function (event, rowIndex) {
|
||||||
var row = this.$scope.rows[rowIndex];
|
var self = this,
|
||||||
//Add row to the filtered, sorted list of all rows
|
row = this.$scope.rows[rowIndex],
|
||||||
if (this.filterRows([row]).length > 0) {
|
largestRow;
|
||||||
this.insertSorted(this.$scope.displayRows, row);
|
|
||||||
|
function sizeAndScroll () {
|
||||||
|
self.setElementSizes();
|
||||||
|
self.scrollToBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$timeout(this.setElementSizes.bind(this))
|
//Does the row pass the current filter?
|
||||||
.then(this.scrollToBottom.bind(this));
|
if (this.filterRows([row]).length === 1) {
|
||||||
|
this.insertSorted(this.$scope.displayRows, row);
|
||||||
|
|
||||||
|
//Calculate largest row
|
||||||
|
largestRow = this.buildLargestRow([this.$scope.sizingRow, row]);
|
||||||
|
|
||||||
|
// Has it changed? If so, set the the 'sizing' row which
|
||||||
|
// determines column widths
|
||||||
|
if (JSON.stringify(largestRow) !== JSON.stringify(this.$scope.sizingRow)){
|
||||||
|
this.$scope.sizingRow = largestRow;
|
||||||
|
this.$timeout(sizeAndScroll);
|
||||||
|
} else {
|
||||||
|
sizeAndScroll();
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -249,13 +269,12 @@ define(
|
|||||||
* for individual rows.
|
* for individual rows.
|
||||||
*/
|
*/
|
||||||
MCTTableController.prototype.setElementSizes = function () {
|
MCTTableController.prototype.setElementSizes = function () {
|
||||||
var self = this,
|
var thead = this.thead,
|
||||||
thead = this.element.find('thead'),
|
tbody = this.tbody,
|
||||||
tbody = this.element.find('tbody'),
|
|
||||||
firstRow = tbody.find('tr'),
|
firstRow = tbody.find('tr'),
|
||||||
column = firstRow.find('td'),
|
column = firstRow.find('td'),
|
||||||
headerHeight = thead.prop('offsetHeight'),
|
headerHeight = thead.prop('offsetHeight'),
|
||||||
rowHeight = 20,
|
rowHeight = firstRow.prop('offsetHeight'),
|
||||||
columnWidth,
|
columnWidth,
|
||||||
tableWidth = 0,
|
tableWidth = 0,
|
||||||
overallHeight = headerHeight + (rowHeight *
|
overallHeight = headerHeight + (rowHeight *
|
||||||
@ -279,8 +298,6 @@ define(
|
|||||||
} else {
|
} else {
|
||||||
this.$scope.totalWidth = 'none';
|
this.$scope.totalWidth = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$scope.overrideRowPositioning = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -400,43 +417,32 @@ define(
|
|||||||
* pre-calculate optimal column sizes without having to render
|
* pre-calculate optimal column sizes without having to render
|
||||||
* every row.
|
* every row.
|
||||||
*/
|
*/
|
||||||
MCTTableController.prototype.findLargestRow = function (rows) {
|
MCTTableController.prototype.buildLargestRow = function (rows) {
|
||||||
var largestRow = rows.reduce(function (largestRow, row) {
|
var largestRow = rows.reduce(function (prevLargest, row) {
|
||||||
Object.keys(row).forEach(function (key) {
|
Object.keys(row).forEach(function (key) {
|
||||||
var currentColumn = row[key].text,
|
var currentColumn,
|
||||||
|
currentColumnLength,
|
||||||
|
largestColumn,
|
||||||
|
largestColumnLength;
|
||||||
|
if (!row[key]){
|
||||||
|
//do nothing, no value for this column;
|
||||||
|
} else {
|
||||||
|
currentColumn = (row[key]).text;
|
||||||
currentColumnLength =
|
currentColumnLength =
|
||||||
(currentColumn && currentColumn.length) ?
|
(currentColumn && currentColumn.length) ?
|
||||||
currentColumn.length :
|
currentColumn.length :
|
||||||
currentColumn,
|
currentColumn;
|
||||||
largestColumn = largestRow[key].text,
|
largestColumn = prevLargest[key] ? prevLargest[key].text : "";
|
||||||
largestColumnLength =
|
largestColumnLength = largestColumn.length;
|
||||||
(largestColumn && largestColumn.length) ?
|
|
||||||
largestColumn.length :
|
|
||||||
largestColumn;
|
|
||||||
|
|
||||||
if (currentColumnLength > largestColumnLength) {
|
if (currentColumnLength > largestColumnLength) {
|
||||||
largestRow[key] = JSON.parse(JSON.stringify(row[key]));
|
prevLargest[key] = JSON.parse(JSON.stringify(row[key]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
return largestRow;
|
return prevLargest;
|
||||||
}, JSON.parse(JSON.stringify(rows[0] || {})));
|
}, JSON.parse(JSON.stringify(rows[0] || {})));
|
||||||
|
|
||||||
largestRow = JSON.parse(JSON.stringify(largestRow));
|
|
||||||
|
|
||||||
// Pad with characters to accomodate variable-width fonts,
|
|
||||||
// and remove characters that would allow word-wrapping.
|
|
||||||
Object.keys(largestRow).forEach(function (key) {
|
|
||||||
var padCharacters,
|
|
||||||
i;
|
|
||||||
|
|
||||||
largestRow[key].text = String(largestRow[key].text);
|
|
||||||
padCharacters = largestRow[key].text.length / 10;
|
|
||||||
for (i = 0; i < padCharacters; i++) {
|
|
||||||
largestRow[key].text = largestRow[key].text + 'W';
|
|
||||||
}
|
|
||||||
largestRow[key].text = largestRow[key].text
|
|
||||||
.replace(/[ \-_]/g, 'W');
|
|
||||||
});
|
|
||||||
return largestRow;
|
return largestRow;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -447,20 +453,13 @@ define(
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
MCTTableController.prototype.resize = function (){
|
MCTTableController.prototype.resize = function (){
|
||||||
var largestRow = this.findLargestRow(this.$scope.displayRows),
|
var self = this;
|
||||||
self = this;
|
|
||||||
this.$scope.visibleRows = [
|
this.$scope.sizingRow = this.buildLargestRow(this.$scope.displayRows);
|
||||||
{
|
|
||||||
rowIndex: 0,
|
|
||||||
offsetY: undefined,
|
|
||||||
contents: largestRow
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
//Wait a timeout to allow digest of previous change to visible
|
//Wait a timeout to allow digest of previous change to visible
|
||||||
// rows to happen.
|
// rows to happen.
|
||||||
this.$timeout(function () {
|
this.$timeout(function () {
|
||||||
//Remove temporary padding row used for setting column widths
|
|
||||||
self.$scope.visibleRows = [];
|
self.$scope.visibleRows = [];
|
||||||
self.setElementSizes();
|
self.setElementSizes();
|
||||||
});
|
});
|
||||||
@ -489,8 +488,6 @@ define(
|
|||||||
//Reset visible rows because new row data available.
|
//Reset visible rows because new row data available.
|
||||||
this.$scope.visibleRows = [];
|
this.$scope.visibleRows = [];
|
||||||
|
|
||||||
this.$scope.overrideRowPositioning = false;
|
|
||||||
|
|
||||||
//Nothing to show because no columns visible
|
//Nothing to show because no columns visible
|
||||||
if (!this.$scope.displayHeaders) {
|
if (!this.$scope.displayHeaders) {
|
||||||
return;
|
return;
|
||||||
|
@ -58,15 +58,18 @@ define(
|
|||||||
|
|
||||||
mockElement = jasmine.createSpyObj('element', [
|
mockElement = jasmine.createSpyObj('element', [
|
||||||
'find',
|
'find',
|
||||||
|
'prop',
|
||||||
'on'
|
'on'
|
||||||
]);
|
]);
|
||||||
mockElement.find.andReturn(mockElement);
|
mockElement.find.andReturn(mockElement);
|
||||||
|
mockElement.prop.andReturn(0);
|
||||||
|
|
||||||
mockScope.displayHeaders = true;
|
mockScope.displayHeaders = true;
|
||||||
mockTimeout = jasmine.createSpy('$timeout');
|
mockTimeout = jasmine.createSpy('$timeout');
|
||||||
mockTimeout.andReturn(promise(undefined));
|
mockTimeout.andReturn(promise(undefined));
|
||||||
|
|
||||||
controller = new MCTTableController(mockScope, mockTimeout, mockElement);
|
controller = new MCTTableController(mockScope, mockTimeout, mockElement);
|
||||||
|
spyOn(controller, 'setVisibleRows');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Reacts to changes to filters, headers, and rows', function() {
|
it('Reacts to changes to filters, headers, and rows', function() {
|
||||||
@ -138,8 +141,6 @@ define(
|
|||||||
var removeRowFunc = mockScope.$on.calls[mockScope.$on.calls.length-1].args[1];
|
var removeRowFunc = mockScope.$on.calls[mockScope.$on.calls.length-1].args[1];
|
||||||
controller.updateRows(testRows);
|
controller.updateRows(testRows);
|
||||||
expect(mockScope.displayRows.length).toBe(3);
|
expect(mockScope.displayRows.length).toBe(3);
|
||||||
spyOn(controller, 'setVisibleRows');
|
|
||||||
//controller.setVisibleRows.andReturn(undefined);
|
|
||||||
removeRowFunc(undefined, 2);
|
removeRowFunc(undefined, 2);
|
||||||
expect(mockScope.displayRows.length).toBe(2);
|
expect(mockScope.displayRows.length).toBe(2);
|
||||||
expect(controller.setVisibleRows).toHaveBeenCalled();
|
expect(controller.setVisibleRows).toHaveBeenCalled();
|
||||||
@ -266,6 +267,25 @@ define(
|
|||||||
expect(mockScope.displayRows[4].col2.text).toEqual('ggg');
|
expect(mockScope.displayRows[4].col2.text).toEqual('ggg');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Resizes columns if length of any columns in new' +
|
||||||
|
' row exceeds corresponding existing column', function() {
|
||||||
|
var row7 = {
|
||||||
|
'col1': {'text': 'row6 col1'},
|
||||||
|
'col2': {'text': 'some longer string'},
|
||||||
|
'col3': {'text': 'row6 col3'}
|
||||||
|
};
|
||||||
|
|
||||||
|
mockScope.sortColumn = undefined;
|
||||||
|
mockScope.sortDirection = undefined;
|
||||||
|
mockScope.filters = {};
|
||||||
|
|
||||||
|
mockScope.displayRows = testRows.slice(0);
|
||||||
|
|
||||||
|
mockScope.rows.push(row7);
|
||||||
|
controller.newRow(undefined, mockScope.rows.length-1);
|
||||||
|
expect(controller.$scope.sizingRow.col2).toEqual({text: 'some longer string'});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user