| /** | |
| * @license | |
| * Copyright 2018 Google LLC | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| import '@polymer/iron-input/iron-input'; | |
| import '@polymer/paper-toggle-button/paper-toggle-button'; | |
| import '../../shared/gr-button/gr-button'; | |
| import { | |
| PluginConfigOptionsChangedEventDetail, | |
| ArrayPluginOption, | |
| } from '../gr-repo-plugin-config/gr-repo-plugin-config-types'; | |
| import {formStyles} from '../../../styles/gr-form-styles'; | |
| import {sharedStyles} from '../../../styles/shared-styles'; | |
| import {LitElement, html, css} from 'lit'; | |
| import {customElement, property, state} from 'lit/decorators.js'; | |
| import {BindValueChangeEvent} from '../../../types/events'; | |
| import {fireNoBubbleNoCompose} from '../../../utils/event-util'; | |
| declare global { | |
| interface HTMLElementTagNameMap { | |
| 'gr-plugin-config-array-editor': GrPluginConfigArrayEditor; | |
| } | |
| interface HTMLElementEventMap { | |
| 'plugin-config-option-changed': CustomEvent<PluginConfigOptionsChangedEventDetail>; | |
| } | |
| } | |
| @customElement('gr-plugin-config-array-editor') | |
| export class GrPluginConfigArrayEditor extends LitElement { | |
| // private but used in test | |
| @state() newValue = ''; | |
| // This property is never null, since this component in only about operations | |
| // on pluginOption. | |
| @property({type: Object}) | |
| pluginOption!: ArrayPluginOption; | |
| @property({type: Boolean, reflect: true}) | |
| disabled = false; | |
| static override get styles() { | |
| return [ | |
| sharedStyles, | |
| formStyles, | |
| css` | |
| .wrapper { | |
| width: 30em; | |
| } | |
| .existingItems { | |
| background: var(--table-header-background-color); | |
| border: 1px solid var(--border-color); | |
| border-radius: var(--border-radius); | |
| } | |
| gr-button { | |
| float: right; | |
| margin-left: var(--spacing-m); | |
| width: 4.5em; | |
| } | |
| .row { | |
| align-items: center; | |
| display: flex; | |
| justify-content: space-between; | |
| padding: var(--spacing-m) 0; | |
| width: 100%; | |
| } | |
| .existingItems .row { | |
| padding: var(--spacing-m); | |
| } | |
| .existingItems .row:not(:first-of-type) { | |
| border-top: 1px solid var(--border-color); | |
| } | |
| input { | |
| flex-grow: 1; | |
| } | |
| .hide { | |
| display: none; | |
| } | |
| .placeholder { | |
| color: var(--deemphasized-text-color); | |
| padding-top: var(--spacing-m); | |
| } | |
| `, | |
| ]; | |
| } | |
| override render() { | |
| return html` | |
| <div class="wrapper gr-form-styles"> | |
| ${this.renderPluginOptions()} | |
| <div class="row ${this.disabled ? 'hide' : ''}"> | |
| <iron-input | |
| .bindValue=${this.newValue} | |
| @bind-value-changed=${this.handleBindValueChangedNewValue} | |
| > | |
| <input | |
| id="input" | |
| @keydown=${this.handleInputKeydown} | |
| ?disabled=${this.disabled} | |
| /> | |
| </iron-input> | |
| <gr-button | |
| id="addButton" | |
| ?disabled=${!this.newValue.length} | |
| link | |
| @click=${this.handleAddTap} | |
| >Add</gr-button | |
| > | |
| </div> | |
| </div> | |
| `; | |
| } | |
| private renderPluginOptions() { | |
| if (!this.pluginOption?.info?.values?.length) { | |
| return html`<div class="row placeholder">None configured.</div>`; | |
| } | |
| return html` | |
| <div class="existingItems"> | |
| ${this.pluginOption.info.values.map(item => | |
| this.renderPluginOptionValue(item) | |
| )} | |
| </div> | |
| `; | |
| } | |
| private renderPluginOptionValue(item: string) { | |
| return html` | |
| <div class="row"> | |
| <span>${item}</span> | |
| <gr-button | |
| link | |
| ?disabled=${this.disabled} | |
| @click=${() => this.handleDelete(item)} | |
| >Delete</gr-button | |
| > | |
| </div> | |
| `; | |
| } | |
| private handleAddTap(e: MouseEvent) { | |
| e.preventDefault(); | |
| this.handleAdd(); | |
| } | |
| private handleInputKeydown(e: KeyboardEvent) { | |
| if (e.key === 'Enter') { | |
| e.preventDefault(); | |
| this.handleAdd(); | |
| } | |
| } | |
| private handleAdd() { | |
| if (!this.newValue.length) { | |
| return; | |
| } | |
| this.dispatchChanged(this.pluginOption.info.values.concat([this.newValue])); | |
| this.newValue = ''; | |
| } | |
| private handleDelete(value: string) { | |
| this.dispatchChanged( | |
| this.pluginOption.info.values.filter(str => str !== value) | |
| ); | |
| } | |
| // private but used in test | |
| dispatchChanged(values: string[]) { | |
| const {_key, info} = this.pluginOption; | |
| const detail: PluginConfigOptionsChangedEventDetail = { | |
| _key, | |
| info: {...info, values}, | |
| notifyPath: `${_key}.values`, | |
| }; | |
| fireNoBubbleNoCompose(this, 'plugin-config-option-changed', detail); | |
| } | |
| private handleBindValueChangedNewValue(e: BindValueChangeEvent) { | |
| this.newValue = e.detail.value ?? ''; | |
| } | |
| } |