blob: 89ae10552d5043a24ec7805bdd8e9a6dd8015e5a [file] [log] [blame]
Copybara854996b2021-09-07 19:36:02 +00001// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import {LitElement, html, css} from 'lit-element';
6import './mr-comment-content.js';
7import './mr-attachment.js';
8
9import {relativeTime} from
10 'elements/chops/chops-timestamp/chops-timestamp-helpers';
11
12
13/**
14 * `<mr-description>`
15 *
16 * Element for displaying a description or survey.
17 *
18 */
19export class MrDescription extends LitElement {
20 /** @override */
21 constructor() {
22 super();
23
24 this.descriptionList = [];
25 this.selectedIndex = 0;
26 }
27
28 /** @override */
29 static get properties() {
30 return {
31 descriptionList: {type: Array},
32 selectedIndex: {type: Number},
33 };
34 }
35
36 /** @override */
37 updated(changedProperties) {
38 super.updated(changedProperties);
39
40 if (changedProperties.has('descriptionList')) {
41 if (!this.descriptionList || !this.descriptionList.length) return;
42 this.selectedIndex = this.descriptionList.length - 1;
43 }
44 }
45
46 /** @override */
47 static get styles() {
48 return css`
49 .select-container {
50 text-align: right;
51 }
52 `;
53 }
54
55 /** @override */
56 render() {
57 const selectedDescription = this.selectedDescription;
58
59 return html`
60 <div class="select-container">
61 <select
62 @change=${this._selectChanged}
63 ?hidden=${!this.descriptionList || this.descriptionList.length <= 1}
64 aria-label="Description history menu">
65 ${this.descriptionList.map((desc, i) => this._renderDescriptionOption(desc, i))}
66 </select>
67 </div>
68 <mr-comment-content
69 .content=${selectedDescription.content}
70 .author=${selectedDescription.commenter.displayName}
71 ></mr-comment-content>
72 <div>
73 ${(selectedDescription.attachments || []).map((attachment) => html`
74 <mr-attachment
75 .attachment=${attachment}
76 .projectName=${selectedDescription.projectName}
77 .localId=${selectedDescription.localId}
78 .sequenceNum=${selectedDescription.sequenceNum}
79 .canDelete=${selectedDescription.canDelete}
80 ></mr-attachment>
81 `)}
82 </div>
83 `;
84 }
85
86 /**
87 * Getter for the currently viewed description.
88 * @return {Comment} The description object.
89 */
90 get selectedDescription() {
91 const descriptions = this.descriptionList || [];
92 const index = Math.max(
93 Math.min(this.selectedIndex, descriptions.length - 1),
94 0);
95 return descriptions[index] || {};
96 }
97
98 /**
99 * Helper to render a <select> <option> for a single description, for our
100 * description selector.
101 * @param {Comment} description
102 * @param {Number} index
103 * @return {TemplateResult}
104 * @private
105 */
106 _renderDescriptionOption(description, index) {
107 const {commenter, timestamp} = description || {};
108 const byLine = commenter ? `by ${commenter.displayName}` : '';
109 return html`
110 <option value=${index} ?selected=${index === this.selectedIndex}>
111 Description #${index + 1} ${byLine} (${_relativeTime(timestamp)})
112 </option>
113 `;
114 }
115
116 /**
117 * Updates the element's selectedIndex when the user changes the select menu.
118 * @param {Event} evt
119 */
120 _selectChanged(evt) {
121 if (!evt || !evt.target) return;
122 this.selectedIndex = Number.parseInt(evt.target.value);
123 }
124}
125
126/**
127 * Template helper for rendering relative time.
128 * @param {number} unixTime Unix timestamp in seconds.
129 * @return {string} human readable timestamp.
130 */
131function _relativeTime(unixTime) {
132 unixTime = Number.parseInt(unixTime);
133 if (Number.isNaN(unixTime)) return;
134 return relativeTime(new Date(unixTime * 1000));
135}
136
137customElements.define('mr-description', MrDescription);