mirror of
https://github.com/nasa/openmct.git
synced 2025-02-21 01:42:31 +00:00
[Table] Custom column widths and order (#2204)
* Renders with resize hotzones * Implemented basic reordering of columns * Refactored column headers into component * Custom column widths persist during resize * Initial working version of fixed column sizes * Only calculate default sizes when first data received. * Further fixes for fixed column widths. Add option to switch to auto-widths * Fixed bug with table auto-sizing * Only allow reorder and resize in edit mode * Bug fixes * Allow for scroll bar width * Bug fix with tables reverting to 100% width * Fixed bug with drop position indicator when scrolled * Moved events on to component * Do not throttle mouse events. Let Vue throttle them * Do not hard code vertical offset for drop target * Addressed review issues * Clarified mouse event handling on column resize
This commit is contained in:
parent
ed8137726d
commit
c073a21ba6
@ -46,6 +46,10 @@ define([
|
||||
getConfiguration() {
|
||||
let configuration = this.domainObject.configuration || {};
|
||||
configuration.hiddenColumns = configuration.hiddenColumns || {};
|
||||
configuration.columnWidths = configuration.columnWidths || {};
|
||||
configuration.columnOrder = configuration.columnOrder || [];
|
||||
configuration.autosize = configuration.autosize === undefined ? true : configuration.autosize;
|
||||
|
||||
return configuration;
|
||||
}
|
||||
|
||||
@ -113,7 +117,7 @@ define([
|
||||
let headers = _.uniq(flattenedColumns, false, column => column.getKey())
|
||||
.reduce(fromColumnsToHeadersMap, {});
|
||||
|
||||
function fromColumnsToHeadersMap(headersMap, column){
|
||||
function fromColumnsToHeadersMap(headersMap, column) {
|
||||
headersMap[column.getKey()] = column.getTitle();
|
||||
return headersMap;
|
||||
}
|
||||
@ -122,16 +126,42 @@ define([
|
||||
}
|
||||
|
||||
getVisibleHeaders() {
|
||||
let headers = this.getAllHeaders();
|
||||
let allHeaders = this.getAllHeaders();
|
||||
let configuration = this.getConfiguration();
|
||||
|
||||
Object.keys(headers).forEach((headerKey) => {
|
||||
if (configuration.hiddenColumns[headerKey] === true) {
|
||||
delete headers[headerKey];
|
||||
}
|
||||
});
|
||||
let orderedColumns = this.getColumnOrder();
|
||||
let unorderedColumns = _.difference(Object.keys(allHeaders), orderedColumns);
|
||||
|
||||
return headers;
|
||||
return orderedColumns.concat(unorderedColumns)
|
||||
.filter((headerKey) => {
|
||||
return configuration.hiddenColumns[headerKey] !== true;
|
||||
})
|
||||
.reduce((headers, headerKey) => {
|
||||
headers[headerKey] = allHeaders[headerKey];
|
||||
return headers;
|
||||
}, {});
|
||||
}
|
||||
|
||||
getColumnWidths() {
|
||||
let configuration = this.getConfiguration();
|
||||
return configuration.columnWidths;
|
||||
}
|
||||
|
||||
setColumnWidths(columnWidths) {
|
||||
let configuration = this.getConfiguration();
|
||||
configuration.columnWidths = columnWidths;
|
||||
this.updateConfiguration(configuration);
|
||||
}
|
||||
|
||||
getColumnOrder() {
|
||||
let configuration = this.getConfiguration();
|
||||
return configuration.columnOrder;
|
||||
}
|
||||
|
||||
setColumnOrder(columnOrder) {
|
||||
let configuration = this.getConfiguration();
|
||||
configuration.columnOrder = columnOrder;
|
||||
this.updateConfiguration(configuration);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
@ -140,4 +170,4 @@ define([
|
||||
}
|
||||
|
||||
return TelemetryTableConfiguration;
|
||||
});
|
||||
});
|
||||
|
@ -29,7 +29,8 @@ define(function () {
|
||||
initialize(domainObject) {
|
||||
domainObject.composition = [];
|
||||
domainObject.configuration = {
|
||||
columns: {}
|
||||
columnWidths: {},
|
||||
hiddenColumns: {}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
182
src/plugins/telemetryTable/components/table-column-header.vue
Normal file
182
src/plugins/telemetryTable/components/table-column-header.vue
Normal file
@ -0,0 +1,182 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT 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 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.
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<th
|
||||
:style="{ width: columnWidth + 'px', 'max-width': columnWidth + 'px'}"
|
||||
:draggable="isEditing"
|
||||
@mouseup="sort"
|
||||
v-on="isEditing ? {
|
||||
dragstart: columnMoveStart,
|
||||
drop: columnMoveEnd,
|
||||
dragleave: hideDropTarget,
|
||||
dragover: dragOverColumn
|
||||
} : {}">
|
||||
<div class="c-telemetry-table__headers__content" :class="[
|
||||
isSortable ? 'is-sortable' : '',
|
||||
isSortable && sortOptions.key === headerKey ? 'is-sorting' : '',
|
||||
isSortable && sortOptions.direction].join(' ')">
|
||||
<div v-if="isEditing" class="c-telemetry-table__resize-hotzone c-telemetry-table__resize-hotzone--right"
|
||||
@mousedown="resizeColumnStart"
|
||||
></div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</th>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
@import "~styles/sass-base";
|
||||
@import "~styles/table";
|
||||
|
||||
$hotzone-size: 6px;
|
||||
|
||||
.c-telemetry-table__headers__content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.c-table.c-telemetry-table {
|
||||
.c-telemetry-table__resize-hotzone {
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: $hotzone-size;
|
||||
min-width: $hotzone-size;
|
||||
cursor: col-resize;
|
||||
border: none;
|
||||
right: 0px;
|
||||
margin-right: -$tabularTdPadLR - 1 - $hotzone-size / 2;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import _ from 'lodash';
|
||||
const MOVE_COLUMN_DT_TYPE = 'movecolumnfromindex';
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
data() {
|
||||
return {
|
||||
isEditing: this.openmct.editor.isEditing()
|
||||
}
|
||||
},
|
||||
props: {
|
||||
headerKey: String,
|
||||
headerIndex: Number,
|
||||
isHeaderTitle: Boolean,
|
||||
sortOptions: Object,
|
||||
columnWidth: Object,
|
||||
hotzone: Boolean
|
||||
},
|
||||
computed: {
|
||||
isSortable() {
|
||||
return this.sortOptions !== undefined;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resizeColumnStart(event) {
|
||||
this.resizeStartX = event.clientX;
|
||||
this.resizeStartWidth = this.columnWidth;
|
||||
|
||||
document.addEventListener('mouseup', this.resizeColumnEnd, {once: true, capture: true});
|
||||
document.addEventListener('mousemove', this.resizeColumn);
|
||||
event.preventDefault();
|
||||
},
|
||||
resizeColumnEnd(event) {
|
||||
this.resizeStartX = undefined;
|
||||
this.resizeStartWidth = undefined;
|
||||
document.removeEventListener('mousemove', this.resizeColumn);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
this.$emit('resizeColumnEnd');
|
||||
},
|
||||
resizeColumn(event) {
|
||||
let delta = event.clientX - this.resizeStartX;
|
||||
let newWidth = this.resizeStartWidth + delta;
|
||||
let minWidth = parseInt(window.getComputedStyle(this.$el).minWidth);
|
||||
if (newWidth > minWidth) {
|
||||
this.$emit('resizeColumn', this.headerKey, newWidth);
|
||||
}
|
||||
},
|
||||
columnMoveStart(event) {
|
||||
event.dataTransfer.setData(MOVE_COLUMN_DT_TYPE, this.headerIndex);
|
||||
},
|
||||
isColumnMoveEvent(event) {
|
||||
return [...event.dataTransfer.types].includes(MOVE_COLUMN_DT_TYPE);
|
||||
},
|
||||
dragOverColumn(event) {
|
||||
if (this.isColumnMoveEvent(event)){
|
||||
event.preventDefault();
|
||||
this.updateDropOffset(event.currentTarget, event.clientX);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
updateDropOffset(element, clientX) {
|
||||
let thClientLeft = element.getBoundingClientRect().x;
|
||||
let offsetInHeader = clientX - thClientLeft;
|
||||
let dropOffsetLeft;
|
||||
|
||||
if (offsetInHeader < element.offsetWidth / 2) {
|
||||
dropOffsetLeft = element.offsetLeft;
|
||||
} else {
|
||||
dropOffsetLeft = element.offsetLeft + element.offsetWidth;
|
||||
}
|
||||
this.$emit('dropTargetOffsetChanged', dropOffsetLeft);
|
||||
this.$emit('dropTargetActive', true);
|
||||
},
|
||||
hideDropTarget(){
|
||||
this.$emit('dropTargetActive', false);
|
||||
},
|
||||
columnMoveEnd(event){
|
||||
if (this.isColumnMoveEvent(event)){
|
||||
let toIndex = this.headerIndex;
|
||||
let fromIndex = event.dataTransfer.getData(MOVE_COLUMN_DT_TYPE);
|
||||
if (event.offsetX < event.target.offsetWidth / 2) {
|
||||
if (toIndex > fromIndex){
|
||||
toIndex--;
|
||||
}
|
||||
} else {
|
||||
if (toIndex < fromIndex){
|
||||
toIndex++;
|
||||
}
|
||||
}
|
||||
if (toIndex !== fromIndex) {
|
||||
this.$emit('reorderColumn', fromIndex, toIndex);
|
||||
}
|
||||
}
|
||||
},
|
||||
sort() {
|
||||
this.$emit("sort");
|
||||
},
|
||||
toggleEditMode(isEditing) {
|
||||
this.isEditing = isEditing;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.openmct.editor.on('isEditing', this.toggleEditMode);
|
||||
},
|
||||
destroyed() {
|
||||
this.openmct.editor.off('isEditing', this.toggleEditMode);
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,6 +1,13 @@
|
||||
<template>
|
||||
<div class="c-properties" v-if="isEditing">
|
||||
<div class="c-properties__header">Table Columns</div>
|
||||
<div class="c-properties__header">Table Column Size</div>
|
||||
<ul class="c-properties__section">
|
||||
<li class="c-properties__row">
|
||||
<div class="c-properties__label" title="Show or Hide Column"><label for="AutoSizeControl">Auto-size</label></div>
|
||||
<div class="c-properties__value"><input type="checkbox" id="AutoSizeControl" :checked="configuration.autosize !== false" @change="toggleAutosize()"></div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="c-properties__header">Table Column Visibility</div>
|
||||
<ul class="c-properties__section">
|
||||
<li class="c-properties__row" v-for="(title, key) in headers">
|
||||
<div class="c-properties__label" title="Show or Hide Column"><label :for="key + 'ColumnControl'">{{title}}</label></div>
|
||||
@ -43,6 +50,10 @@ export default {
|
||||
},
|
||||
toggleEdit(isEditing) {
|
||||
this.isEditing = isEditing;
|
||||
},
|
||||
toggleAutosize() {
|
||||
this.configuration.autosize = !this.configuration.autosize;
|
||||
this.tableConfiguration.updateConfiguration(this.configuration);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
@ -1,7 +1,29 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT 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 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.
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<tr :style="{ top: rowTop }" :class="rowLimitClass">
|
||||
<td v-for="(title, key, headerIndex) in headers"
|
||||
:style="{ width: columnWidths[headerIndex], 'max-width': columnWidths[headerIndex]}"
|
||||
<td v-for="(title, key) in headers"
|
||||
:key="key"
|
||||
:style="columnWidths[key] === undefined ? {} : { width: columnWidths[key] + 'px', 'max-width': columnWidths[key] + 'px'}"
|
||||
:title="formattedRow[key]"
|
||||
:class="cellLimitClasses[key]">{{formattedRow[key]}}</td>
|
||||
</tr>
|
||||
@ -30,11 +52,8 @@ export default {
|
||||
required: true
|
||||
},
|
||||
columnWidths: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
rowIndex: {
|
||||
type: Number,
|
||||
|
@ -1,3 +1,24 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT 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 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.
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<div class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar"
|
||||
:class="{'loading': loading}">
|
||||
@ -8,37 +29,53 @@
|
||||
Export As CSV
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="isDropTargetActive" class="c-telemetry-table__drop-target" :style="dropTargetStyle"></div>
|
||||
<!-- Headers table -->
|
||||
<div class="c-telemetry-table__headers-w js-table__headers-w">
|
||||
<table class="c-table__headers c-telemetry-table__headers"
|
||||
:style="{ 'max-width': totalWidth + 'px'}">
|
||||
<div class="c-telemetry-table__headers-w js-table__headers-w" ref="headersTable" :style="{ 'max-width': widthWithScroll}">
|
||||
<table class="c-table__headers c-telemetry-table__headers">
|
||||
<thead>
|
||||
<tr>
|
||||
<th v-for="(title, key, headerIndex) in headers"
|
||||
v-on:click="sortBy(key)"
|
||||
:class="['is-sortable', sortOptions.key === key ? 'is-sorting' : '', sortOptions.direction].join(' ')"
|
||||
:style="{ width: columnWidths[headerIndex], 'max-width': columnWidths[headerIndex]}">{{title}}</th>
|
||||
<table-column-header
|
||||
v-for="(title, key, headerIndex) in headers"
|
||||
:key="key"
|
||||
:headerKey="key"
|
||||
:headerIndex="headerIndex"
|
||||
@sort="sortBy(key)"
|
||||
@resizeColumn="resizeColumn"
|
||||
@dropTargetOffsetChanged="setDropTargetOffset"
|
||||
@dropTargetActive="dropTargetActive"
|
||||
@reorderColumn="reorderColumn"
|
||||
@resizeColumnEnd="updateConfiguredColumnWidths"
|
||||
:columnWidth="columnWidths[key]"
|
||||
:sortOptions="sortOptions"
|
||||
>{{title}}</table-column-header>
|
||||
</tr>
|
||||
<tr>
|
||||
<th v-for="(title, key, headerIndex) in headers"
|
||||
:style="{
|
||||
width: columnWidths[headerIndex],
|
||||
'max-width': columnWidths[headerIndex],
|
||||
}">
|
||||
<table-column-header
|
||||
v-for="(title, key, headerIndex) in headers"
|
||||
:key="key"
|
||||
:headerKey="key"
|
||||
:headerIndex="headerIndex"
|
||||
@resizeColumn="resizeColumn"
|
||||
@dropTargetOffsetChanged="setDropTargetOffset"
|
||||
@dropTargetActive="dropTargetActive"
|
||||
@reorderColumn="reorderColumn"
|
||||
@resizeColumnEnd="updateConfiguredColumnWidths"
|
||||
:columnWidth="columnWidths[key]">
|
||||
<search class="c-table__search"
|
||||
v-model="filters[key]"
|
||||
v-on:input="filterChanged(key)"
|
||||
v-on:clear="clearFilter(key)" />
|
||||
</th>
|
||||
</table-column-header>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
<!-- Content table -->
|
||||
<div class="c-table__body-w c-telemetry-table__body-w js-telemetry-table__body-w" @scroll="scroll">
|
||||
<div class="c-telemetry-table__scroll-forcer" :style="{ width: totalWidth }"></div>
|
||||
<div class="c-table__body-w c-telemetry-table__body-w js-telemetry-table__body-w" @scroll="scroll" :style="{ 'max-width': widthWithScroll}">
|
||||
<div class="c-telemetry-table__scroll-forcer" :style="{ width: totalWidth + 'px' }"></div>
|
||||
<table class="c-table__body c-telemetry-table__body"
|
||||
:style="{ height: totalHeight + 'px', 'max-width': totalWidth + 'px'}">
|
||||
:style="{ height: totalHeight + 'px'}">
|
||||
<tbody>
|
||||
<telemetry-table-row v-for="(row, rowIndex) in visibleRows"
|
||||
:headers="headers"
|
||||
@ -46,20 +83,21 @@
|
||||
:rowIndex="rowIndex"
|
||||
:rowOffset="rowOffset"
|
||||
:rowHeight="rowHeight"
|
||||
:row="row"
|
||||
>
|
||||
:row="row">
|
||||
</telemetry-table-row>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- Sizing table -->
|
||||
<table class="c-telemetry-table__sizing js-telemetry-table__sizing"
|
||||
:style="{width: calcTableWidth}">
|
||||
<table class="c-telemetry-table__sizing js-telemetry-table__sizing" :style="sizingTableWidth">
|
||||
<tr>
|
||||
<th v-for="(title, key, headerIndex) in headers">{{title}}</th>
|
||||
<template v-for="(title, key) in headers">
|
||||
<th :key="key" :style="{ width: configuredColumnWidths[key] + 'px', 'max-width': configuredColumnWidths[key] + 'px'}">{{title}}</th>
|
||||
</template>
|
||||
</tr>
|
||||
<telemetry-table-row v-for="(sizingRowData, objectKeyString) in sizingRows"
|
||||
:headers="headers"
|
||||
:columnWidths="configuredColumnWidths"
|
||||
:row="sizingRowData">
|
||||
</telemetry-table-row>
|
||||
</table>
|
||||
@ -70,6 +108,15 @@
|
||||
@import "~styles/sass-base";
|
||||
@import "~styles/table";
|
||||
|
||||
.c-telemetry-table__drop-target {
|
||||
position: absolute;
|
||||
width: 2px;
|
||||
background-color: $editColor;
|
||||
box-shadow: rgba($editColor, 0.5) 0 0 10px;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.c-telemetry-table {
|
||||
// Table that displays telemetry in a scrolling body area
|
||||
overflow: hidden;
|
||||
@ -77,6 +124,7 @@
|
||||
th, td {
|
||||
display: block;
|
||||
flex: 1 0 auto;
|
||||
width: 100px;
|
||||
vertical-align: middle; // This is crucial to hiding f**king 4px height injected by browser by default
|
||||
}
|
||||
|
||||
@ -101,12 +149,6 @@
|
||||
thead {
|
||||
display: block;
|
||||
}
|
||||
|
||||
th {
|
||||
&:not(:first-child) {
|
||||
border-left: 1px solid $colorTabHeaderBorder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************* ELEMENTS */
|
||||
@ -178,24 +220,32 @@
|
||||
<script>
|
||||
import TelemetryTableRow from './table-row.vue';
|
||||
import search from '../../../ui/components/controls/search.vue';
|
||||
import TableColumnHeader from './table-column-header.vue';
|
||||
import _ from 'lodash';
|
||||
|
||||
const VISIBLE_ROW_COUNT = 100;
|
||||
const ROW_HEIGHT = 17;
|
||||
const RESIZE_POLL_INTERVAL = 200;
|
||||
const AUTO_SCROLL_TRIGGER_HEIGHT = 20;
|
||||
const RESIZE_HOT_ZONE = 10;
|
||||
const MOVE_TRIGGER_WAIT = 500;
|
||||
const VERTICAL_SCROLL_WIDTH = 30;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TelemetryTableRow,
|
||||
TableColumnHeader,
|
||||
search
|
||||
},
|
||||
inject: ['table', 'openmct', 'csvExporter'],
|
||||
data() {
|
||||
let configuration = this.table.configuration.getConfiguration();
|
||||
|
||||
return {
|
||||
headers: {},
|
||||
visibleRows: [],
|
||||
columnWidths: [],
|
||||
columnWidths: {},
|
||||
configuredColumnWidths: configuration.columnWidths,
|
||||
sizingRows: {},
|
||||
rowHeight: ROW_HEIGHT,
|
||||
scrollOffset: 0,
|
||||
@ -209,9 +259,43 @@ export default {
|
||||
scrollable: undefined,
|
||||
tableEl: undefined,
|
||||
headersHolderEl: undefined,
|
||||
calcTableWidth: '100%',
|
||||
processingScroll: false,
|
||||
updatingView: false
|
||||
updatingView: false,
|
||||
dropOffsetLeft: undefined,
|
||||
isDropTargetActive: false,
|
||||
isAutosizeEnabled: configuration.autosize,
|
||||
scrollW: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
dropTargetStyle() {
|
||||
return {
|
||||
top: this.$refs.headersTable.offsetTop + 'px',
|
||||
height: this.totalHeight + this.$refs.headersTable.offsetHeight + 'px',
|
||||
left: this.dropOffsetLeft && this.dropOffsetLeft + 'px'
|
||||
}
|
||||
},
|
||||
lastHeaderKey() {
|
||||
let headerKeys = Object.keys(this.headers);
|
||||
return headerKeys[headerKeys.length - 1];
|
||||
},
|
||||
widthWithScroll() {
|
||||
return this.totalWidth + this.scrollW + "px";
|
||||
},
|
||||
sizingTableWidth() {
|
||||
let style;
|
||||
|
||||
if (this.isAutosizeEnabled) {
|
||||
style = { width: "calc(100% - " + this.scrollW + "px)" };
|
||||
} else {
|
||||
let totalWidth = Object.keys(this.headers).reduce((total, key) => {
|
||||
total += this.configuredColumnWidths[key];
|
||||
return total;
|
||||
}, 0);
|
||||
|
||||
style = {width: totalWidth + 'px'};
|
||||
}
|
||||
return style;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -254,32 +338,30 @@ export default {
|
||||
return Math.floor(bottomScroll / this.rowHeight);
|
||||
},
|
||||
updateHeaders() {
|
||||
let headers = this.table.configuration.getVisibleHeaders();
|
||||
|
||||
this.headers = headers;
|
||||
this.$nextTick().then(this.calculateColumnWidths);
|
||||
this.headers = this.table.configuration.getVisibleHeaders();
|
||||
},
|
||||
setSizingTableWidth() {
|
||||
let scrollW = this.scrollable.offsetWidth - this.scrollable.clientWidth;
|
||||
|
||||
if (scrollW && scrollW > 0) {
|
||||
this.calcTableWidth = 'calc(100% - ' + scrollW + 'px)';
|
||||
}
|
||||
calculateScrollbarWidth() {
|
||||
// Scroll width seems to vary by a pixel for reasons that are not clear.
|
||||
this.scrollW = (this.scrollable.offsetWidth - this.scrollable.clientWidth) + 1;
|
||||
},
|
||||
calculateColumnWidths() {
|
||||
let columnWidths = [];
|
||||
let columnWidths = {};
|
||||
let totalWidth = 0;
|
||||
let sizingRowEl = this.sizingTable.children[0];
|
||||
let sizingCells = Array.from(sizingRowEl.children);
|
||||
let headerKeys = Object.keys(this.headers);
|
||||
|
||||
sizingCells.forEach((cell) => {
|
||||
headerKeys.forEach((headerKey, headerIndex)=>{
|
||||
let cell = sizingCells[headerIndex];
|
||||
let columnWidth = cell.offsetWidth;
|
||||
columnWidths.push(columnWidth + 'px');
|
||||
columnWidths[headerKey] = columnWidth;
|
||||
totalWidth += columnWidth;
|
||||
});
|
||||
|
||||
this.columnWidths = columnWidths;
|
||||
this.totalWidth = totalWidth;
|
||||
|
||||
this.calculateScrollbarWidth();
|
||||
},
|
||||
sortBy(columnKey) {
|
||||
// If sorting by the same column, flip the sort direction.
|
||||
@ -376,39 +458,88 @@ export default {
|
||||
this.loading = loading;
|
||||
},
|
||||
calculateTableSize() {
|
||||
this.setSizingTableWidth();
|
||||
this.$nextTick().then(this.calculateColumnWidths);
|
||||
},
|
||||
updateConfiguration(configuration) {
|
||||
this.isAutosizeEnabled = configuration.autosize;
|
||||
|
||||
this.updateHeaders();
|
||||
this.$nextTick().then(this.calculateColumnWidths);
|
||||
},
|
||||
addObject() {
|
||||
this.updateHeaders();
|
||||
this.$nextTick().then(this.calculateColumnWidths);
|
||||
},
|
||||
removeObject(objectIdentifier) {
|
||||
let objectKeyString = this.openmct.objects.makeKeyString(objectIdentifier);
|
||||
delete this.sizingRows[objectKeyString];
|
||||
this.updateHeaders();
|
||||
this.$nextTick().then(this.calculateColumnWidths);
|
||||
},
|
||||
resizeColumn(key, newWidth) {
|
||||
let delta = newWidth - this.columnWidths[key];
|
||||
this.columnWidths[key] = newWidth;
|
||||
this.totalWidth += delta;
|
||||
},
|
||||
updateConfiguredColumnWidths() {
|
||||
this.configuredColumnWidths = this.columnWidths;
|
||||
|
||||
let configuration = this.table.configuration.getConfiguration();
|
||||
configuration.autosize = false;
|
||||
configuration.columnWidths = this.configuredColumnWidths;
|
||||
|
||||
this.table.configuration.updateConfiguration(configuration);
|
||||
},
|
||||
setDropTargetOffset(dropOffsetLeft) {
|
||||
this.dropOffsetLeft = dropOffsetLeft - this.scrollable.scrollLeft;
|
||||
},
|
||||
reorderColumn(from, to) {
|
||||
let newHeaderKeys = Object.keys(this.headers);
|
||||
let moveFromKey = newHeaderKeys[from];
|
||||
|
||||
if (to < from) {
|
||||
newHeaderKeys.splice(from, 1);
|
||||
newHeaderKeys.splice(to, 0, moveFromKey);
|
||||
} else {
|
||||
newHeaderKeys.splice(from, 1);
|
||||
newHeaderKeys.splice(to, 0, moveFromKey);
|
||||
}
|
||||
|
||||
let newHeaders = newHeaderKeys.reduce((headers, headerKey)=>{
|
||||
headers[headerKey] = this.headers[headerKey];
|
||||
return headers;
|
||||
}, {});
|
||||
|
||||
this.table.configuration.setColumnOrder(Object.keys(newHeaders));
|
||||
|
||||
this.headers = newHeaders;
|
||||
this.dropOffsetLeft = undefined;
|
||||
|
||||
this.dropTargetActive(false);
|
||||
},
|
||||
dropTargetActive(isActive) {
|
||||
this.isDropTargetActive = isActive;
|
||||
},
|
||||
pollForResize() {
|
||||
let el = this.$el;
|
||||
let width = el.clientWidth;
|
||||
let height = el.clientHeight;
|
||||
|
||||
this.resizePollHandle = setInterval(() => {
|
||||
if (el.clientWidth !== width || el.clientHeight !== height) {
|
||||
if ((el.clientWidth !== width || el.clientHeight !== height) && this.isAutosizeEnabled) {
|
||||
this.calculateTableSize();
|
||||
width = el.clientWidth;
|
||||
height = el.clientHeight;
|
||||
}
|
||||
}, RESIZE_POLL_INTERVAL);
|
||||
},
|
||||
updateConfiguration(configuration) {
|
||||
this.configuration = configuration;
|
||||
this.updateHeaders();
|
||||
},
|
||||
addObject() {
|
||||
this.updateHeaders();
|
||||
},
|
||||
removeObject(objectIdentifier) {
|
||||
let objectKeyString = this.openmct.objects.makeKeyString(objectIdentifier);
|
||||
delete this.sizingRows[objectKeyString];
|
||||
this.updateHeaders();
|
||||
}
|
||||
|
||||
},
|
||||
created() {
|
||||
this.filterChanged = _.debounce(this.filterChanged, 500);
|
||||
},
|
||||
mounted() {
|
||||
|
||||
this.table.on('object-added', this.addObject);
|
||||
this.table.on('object-removed', this.removeObject);
|
||||
this.table.on('outstanding-requests', this.outstandingRequests);
|
||||
@ -428,6 +559,7 @@ export default {
|
||||
|
||||
this.calculateTableSize();
|
||||
this.pollForResize();
|
||||
this.calculateScrollbarWidth();
|
||||
|
||||
this.table.initialize();
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user