Compare commits
4 Commits
fix/nested
...
table-chil
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6daf2dd1b | ||
|
|
e4782c5e40 | ||
|
|
d342950f12 | ||
|
|
cd6079fa6f |
157
src/plugins/telemetryTable/components/table-column-header.vue
Normal file
157
src/plugins/telemetryTable/components/table-column-header.vue
Normal file
@@ -0,0 +1,157 @@
|
||||
/*****************************************************************************
|
||||
* 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 :class="[
|
||||
isSortable ? 'is-sortable' : '',
|
||||
isSortable && sortOptions.key === headerKey ? 'is-sorting' : '',
|
||||
isSortable && sortOptions.direction].join(' ')"
|
||||
:style="{ width: columnWidths[headerKey] + 'px', 'max-width': columnWidths[headerKey] + 'px'}"
|
||||
draggable="true"
|
||||
@mouseup="sort"
|
||||
@dragstart="columnMoveStart"
|
||||
@drop="columnMoveEnd"
|
||||
@dragleave="hideDropTarget"
|
||||
@dragover="dragOverColumn($event.currentTarget, $event)">
|
||||
<div class="c-telemetry-table__headers__content">
|
||||
<div class="c-telemetry-table__resize-hotzone c-telemetry-table__resize-hotzone--right"
|
||||
@mousedown="startResizeColumn"
|
||||
></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 - $hotzone-size / 2;
|
||||
}
|
||||
th:last-child .c-telemetry-table__resize-hotzone {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import _ from 'lodash';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
headerKey: String,
|
||||
headerIndex: Number,
|
||||
isHeaderTitle: Boolean,
|
||||
sortOptions: Object,
|
||||
columnWidths: Object,
|
||||
hotzone: Boolean
|
||||
},
|
||||
computed: {
|
||||
isSortable() {
|
||||
return this.sortOptions !== undefined;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
startResizeColumn($event) {
|
||||
this.resizeStartX = event.clientX;
|
||||
this.resizeStartWidth = this.columnWidths[this.headerKey];
|
||||
|
||||
document.addEventListener('mouseup', ()=>{
|
||||
this.resizeStartX = undefined;
|
||||
this.resizeStartWidth = undefined;
|
||||
document.removeEventListener('mousemove', this.resizeColumn);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}, {once: true, capture: true});
|
||||
document.addEventListener('mousemove', this.resizeColumn);
|
||||
event.preventDefault();
|
||||
},
|
||||
resizeColumn(event) {
|
||||
let delta = event.clientX - this.resizeStartX;
|
||||
let newWidth = this.resizeStartWidth + delta;
|
||||
this.$emit('resizeColumn', this.headerKey, newWidth);
|
||||
},
|
||||
columnMoveStart(event) {
|
||||
event.dataTransfer.setData('moveColumnFromIndex', this.headerIndex);
|
||||
},
|
||||
dragOverColumn(element, event) {
|
||||
event.preventDefault();
|
||||
this.updateDropOffset(element, event.clientX);
|
||||
},
|
||||
updateDropOffset(element, clientX) {
|
||||
let thClientLeft = element.getBoundingClientRect().x;
|
||||
let offsetInHeader = clientX - thClientLeft;
|
||||
|
||||
this.calculate
|
||||
let dropOffsetLeft;
|
||||
|
||||
if (offsetInHeader < element.offsetWidth / 2) {
|
||||
dropOffsetLeft = element.offsetLeft;
|
||||
} else {
|
||||
dropOffsetLeft = element.offsetLeft + element.offsetWidth;
|
||||
}
|
||||
this.$parent.$emit('drop-target-offset-changed', dropOffsetLeft);
|
||||
this.$parent.$emit('drop-target-active', true);
|
||||
},
|
||||
hideDropTarget(){
|
||||
this.$parent.$emit('drop-target-active', false);
|
||||
},
|
||||
columnMoveEnd(){
|
||||
let toIndex = this.headerIndex;
|
||||
let fromIndex = event.dataTransfer.getData('moveColumnFromIndex');
|
||||
if (event.offsetX < event.target.offsetWidth / 2) {
|
||||
if (toIndex > fromIndex){
|
||||
toIndex--;
|
||||
}
|
||||
} else {
|
||||
if (toIndex < fromIndex){
|
||||
toIndex++;
|
||||
}
|
||||
}
|
||||
if (toIndex !== fromIndex) {
|
||||
this.$parent.$emit('move-column', fromIndex, toIndex);
|
||||
}
|
||||
},
|
||||
sort(){
|
||||
this.$emit("sort");
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.resizeColumn = _.throttle(this.resizeColumn, 50);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,9 +1,31 @@
|
||||
/*****************************************************************************
|
||||
* 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]}"
|
||||
<template v-for="(title, key) in headers">
|
||||
<td :style="{ width: columnWidths[key] + 'px', 'max-width': columnWidths[key] + 'px'}"
|
||||
:title="formattedRow[key]"
|
||||
:class="cellLimitClasses[key]">{{formattedRow[key]}}</td>
|
||||
</template>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
@@ -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,35 +29,46 @@
|
||||
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">
|
||||
<div class="c-telemetry-table__headers-w js-table__headers-w" ref="headersTable">
|
||||
<table class="c-table__headers c-telemetry-table__headers"
|
||||
:style="{ 'max-width': totalWidth + 'px'}">
|
||||
<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>
|
||||
<template v-for="(title, key, headerIndex) in headers">
|
||||
<table-column-header
|
||||
:key="key"
|
||||
:headerKey="key"
|
||||
:headerIndex="headerIndex"
|
||||
@sort="sortBy(key)"
|
||||
@resizeColumn="resizeColumn"
|
||||
:columnWidths="columnWidths"
|
||||
:sortOptions="sortOptions"
|
||||
>{{title}}</table-column-header>
|
||||
</template>
|
||||
</tr>
|
||||
<tr>
|
||||
<th v-for="(title, key, headerIndex) in headers"
|
||||
:style="{
|
||||
width: columnWidths[headerIndex],
|
||||
'max-width': columnWidths[headerIndex],
|
||||
}">
|
||||
<template v-for="(title, key, headerIndex) in headers">
|
||||
<table-column-header
|
||||
:key="key"
|
||||
:headerKey="key"
|
||||
:headerIndex="headerIndex"
|
||||
@resizeColumn="resizeColumn"
|
||||
:columnWidths="columnWidths">
|
||||
<search class="c-table__search"
|
||||
v-model="filters[key]"
|
||||
v-on:input="filterChanged(key)"
|
||||
v-on:clear="clearFilter(key)" />
|
||||
</th>
|
||||
</table-column-header>
|
||||
</template>
|
||||
</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-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'}">
|
||||
<tbody>
|
||||
@@ -46,8 +78,7 @@
|
||||
:rowIndex="rowIndex"
|
||||
:rowOffset="rowOffset"
|
||||
:rowHeight="rowHeight"
|
||||
:row="row"
|
||||
>
|
||||
:row="row">
|
||||
</telemetry-table-row>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -56,10 +87,13 @@
|
||||
<table class="c-telemetry-table__sizing js-telemetry-table__sizing"
|
||||
:style="{width: calcTableWidth}">
|
||||
<tr>
|
||||
<th v-for="(title, key, headerIndex) in headers">{{title}}</th>
|
||||
<template v-for="(title, key) in headers">
|
||||
<th :key="key">{{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 +104,16 @@
|
||||
@import "~styles/sass-base";
|
||||
@import "~styles/table";
|
||||
|
||||
.c-telemetry-table__drop-target {
|
||||
position: absolute;
|
||||
width: 2px;
|
||||
top: 27px;
|
||||
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;
|
||||
@@ -101,12 +145,6 @@
|
||||
thead {
|
||||
display: block;
|
||||
}
|
||||
|
||||
th {
|
||||
&:not(:first-child) {
|
||||
border-left: 1px solid $colorTabHeaderBorder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************* ELEMENTS */
|
||||
@@ -178,16 +216,20 @@
|
||||
<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;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TelemetryTableRow,
|
||||
TableColumnHeader,
|
||||
search
|
||||
},
|
||||
inject: ['table', 'openmct', 'csvExporter'],
|
||||
@@ -196,7 +238,8 @@ export default {
|
||||
return {
|
||||
headers: {},
|
||||
visibleRows: [],
|
||||
columnWidths: [],
|
||||
columnWidths: {},
|
||||
configuredColumnWidths: {},
|
||||
sizingRows: {},
|
||||
rowHeight: ROW_HEIGHT,
|
||||
scrollOffset: 0,
|
||||
@@ -212,7 +255,18 @@ export default {
|
||||
headersHolderEl: undefined,
|
||||
calcTableWidth: '100%',
|
||||
processingScroll: false,
|
||||
updatingView: false
|
||||
updatingView: false,
|
||||
dropOffsetLeft: undefined,
|
||||
isDropTargetActive: false,
|
||||
lastHeaderKey: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
dropTargetStyle() {
|
||||
return {
|
||||
height: this.totalHeight + 47 + 'px',
|
||||
left: this.dropOffsetLeft && this.dropOffsetLeft + 'px'
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -256,8 +310,9 @@ export default {
|
||||
},
|
||||
updateHeaders() {
|
||||
let headers = this.table.configuration.getVisibleHeaders();
|
||||
|
||||
this.headers = headers;
|
||||
let headerKeys = Object.keys(this.headers);
|
||||
this.lastHeaderKey = headerKeys[headerKeys.length - 1];
|
||||
this.$nextTick().then(this.calculateColumnWidths);
|
||||
},
|
||||
setSizingTableWidth() {
|
||||
@@ -268,16 +323,17 @@ export default {
|
||||
}
|
||||
},
|
||||
calculateColumnWidths() {
|
||||
let columnWidths = [];
|
||||
let columnWidths = {};
|
||||
let totalWidth = 0;
|
||||
let sizingRowEl = this.sizingTable.children[0];
|
||||
let sizingCells = Array.from(sizingRowEl.children);
|
||||
|
||||
sizingCells.forEach((cell) => {
|
||||
Object.keys(this.headers).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;
|
||||
@@ -404,12 +460,53 @@ export default {
|
||||
let objectKeyString = this.openmct.objects.makeKeyString(objectIdentifier);
|
||||
delete this.sizingRows[objectKeyString];
|
||||
this.updateHeaders();
|
||||
},
|
||||
resizeColumn(key, newWidth) {
|
||||
let delta = newWidth - this.columnWidths[key];
|
||||
this.columnWidths[key] = newWidth;
|
||||
this.$set(this.configuredColumnWidths, key, newWidth);
|
||||
|
||||
this.columnWidths[this.lastHeaderKey] = this.columnWidths[this.lastHeaderKey] - delta;
|
||||
},
|
||||
setDropTargetOffset(dropOffsetLeft) {
|
||||
this.dropOffsetLeft = dropOffsetLeft;
|
||||
},
|
||||
moveColumn(from, to) {
|
||||
let newHeaderKeys = Object.keys(this.headers);
|
||||
let moveFromKey = newHeaderKeys[from];
|
||||
let moveFromWidth = columnWidths[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.headers = newHeaders;
|
||||
this.dropOffsetLeft = undefined;
|
||||
this.lastHeaderKey = newHeaderKeys[newHeaderKeys.length - 1];
|
||||
|
||||
this.dropTargetActive(false);
|
||||
},
|
||||
dropTargetActive(isActive) {
|
||||
this.isDropTargetActive = isActive;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.filterChanged = _.debounce(this.filterChanged, 500);
|
||||
},
|
||||
mounted() {
|
||||
this.$on('drop-target-offset-changed', this.setDropTargetOffset);
|
||||
this.$on('drop-target-active', this.dropTargetActive);
|
||||
this.$on('move-column', this.moveColumn);
|
||||
|
||||
this.table.on('object-added', this.addObject);
|
||||
this.table.on('object-removed', this.removeObject);
|
||||
this.table.on('outstanding-requests', this.outstandingRequests);
|
||||
|
||||
Reference in New Issue
Block a user