go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/milo/ui/src/common/components/column_header.ts (about) 1 // Copyright 2021 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 import '@material/mwc-menu'; 16 import { MobxLitElement } from '@adobe/lit-mobx'; 17 import { css, html } from 'lit'; 18 import { customElement } from 'lit/decorators.js'; 19 import { styleMap } from 'lit/directives/style-map.js'; 20 import { computed, makeObservable, observable } from 'mobx'; 21 22 import '@/generic_libs/components/drag_tracker'; 23 import { commonStyles } from '@/common/styles/stylesheets'; 24 import { DragEvent } from '@/generic_libs/components/drag_tracker'; 25 26 @customElement('milo-column-header') 27 export class ColumnHeaderElement extends MobxLitElement { 28 @observable.ref label!: string; 29 @observable.ref tooltip!: string; 30 31 // finalized is set to true when the user stopped dragging. 32 @observable.ref resizeColumn?: (delta: number, finalized: boolean) => void; 33 @observable.ref sortByColumn?: (ascending: boolean) => void; 34 @observable.ref groupByColumn?: () => void; 35 @observable.ref hideColumn?: () => void; 36 37 @observable.ref private menuIsOpen = false; 38 39 @computed private get canResize() { 40 return Boolean(this.resizeColumn); 41 } 42 @computed private get canSort() { 43 return Boolean(this.sortByColumn); 44 } 45 @computed private get canGroup() { 46 return Boolean(this.groupByColumn); 47 } 48 @computed private get canHide() { 49 return Boolean(this.hideColumn); 50 } 51 52 constructor() { 53 super(); 54 makeObservable(this); 55 } 56 57 private renderResizer() { 58 if (!this.canResize) { 59 return html``; 60 } 61 let widthDiff = 0; 62 return html` 63 <milo-drag-tracker 64 id="resizer" 65 @drag=${(e: DragEvent) => { 66 widthDiff = e.detail.dx; 67 this.resizeColumn!(widthDiff, false); 68 }} 69 @dragend=${() => this.resizeColumn!(widthDiff, true)} 70 ></milo-drag-tracker> 71 `; 72 } 73 74 protected render() { 75 return html` 76 <div 77 id="prop-label" 78 title=${this.tooltip} 79 @click=${() => (this.menuIsOpen = !this.menuIsOpen)} 80 > 81 ${this.label} 82 </div> 83 <div id="padding"></div> 84 ${this.renderResizer()} 85 <mwc-menu 86 x="0" 87 y="20" 88 ?open=${this.menuIsOpen} 89 @closed=${() => (this.menuIsOpen = false)} 90 > 91 <mwc-list-item 92 style=${styleMap({ display: this.canSort ? '' : 'none' })} 93 @click=${() => this.sortByColumn?.(true)} 94 > 95 Sort in ascending order 96 </mwc-list-item> 97 <mwc-list-item 98 style=${styleMap({ display: this.canSort ? '' : 'none' })} 99 @click=${() => this.sortByColumn?.(false)} 100 > 101 Sort in descending order 102 </mwc-list-item> 103 <mwc-list-item 104 style=${styleMap({ display: this.canGroup ? '' : 'none' })} 105 @click=${() => this.groupByColumn?.()} 106 > 107 Group by this column 108 </mwc-list-item> 109 <mwc-list-item 110 style=${styleMap({ display: this.canHide ? '' : 'none' })} 111 @click=${() => this.hideColumn?.()} 112 > 113 Hide column 114 </mwc-list-item> 115 </mwc-menu> 116 `; 117 } 118 119 static styles = [ 120 commonStyles, 121 css` 122 :host { 123 display: flex; 124 position: relative; 125 } 126 127 #prop-label { 128 flex: 0 1 auto; 129 cursor: pointer; 130 color: var(--active-text-color); 131 overflow: hidden; 132 text-overflow: ellipsis; 133 } 134 #padding { 135 flex: 1 1 auto; 136 } 137 #resizer { 138 flex: 0 0 auto; 139 background: linear-gradient(var(--divider-color), var(--divider-color)) 140 2px 0/1px 100% no-repeat; 141 width: 5px; 142 cursor: col-resize; 143 } 144 `, 145 ]; 146 }