Compare commits
	
		
			4 Commits
		
	
	
		
			vue-3-migr
			...
			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