import {customElement} from "lit/decorators/custom-element.js"; import {css, html, LitElement, nothing, PropertyValueMap, render} from "lit"; import {classMap} from "lit/directives/class-map.js"; import {unsafeHTML} from "lit/directives/unsafe-html.js"; import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget"; import * as Diff2Html from "diff2html"; import {Diff2HtmlConfig} from "diff2html"; import {ColorSchemeType} from "diff2html/lib/types"; import {property} from "lit/decorators/property.js"; import shoelace from "../Styles/shoelace"; import {Et2Dialog} from "../Et2Dialog/Et2Dialog"; /** * Show a nicely formatted diff */ @customElement("et2-diff") export class Et2Diff extends Et2InputWidget(LitElement) { @property({type: Boolean, reflect: true}) open = false; /** * Disable the dialog and show the whole diff * * @type {boolean} */ @property({type: Boolean, reflect: true}) noDialog = false; // CSS in etemplate2.css due to library static get styles() { return [ shoelace, ...super.styles, css` :host { position: relative; } .expand-icon { display: none; position: absolute; bottom: var(--sl-spacing-medium); right: var(--sl-spacing-medium); background-color: var(--sl-panel-background-color); z-index: 1; } :host(:hover) { .expand-icon { display: initial; } } :host(:not([open])) { cursor: pointer; } :host(:not([noDialog])) .form-control-input { max-height: 9em; overflow: hidden; } ` ]; } private readonly diff_options : Diff2HtmlConfig = { matching: "words", drawFileList: false, colorScheme: ColorSchemeType.AUTO }; updated(changedProperties : PropertyValueMap) { if(changedProperties.has("value") || this.value && this.childElementCount == 0) { // Put diff into lightDOM so styles can leak, since we can't import the library CSS into the component render(html`${unsafeHTML(Diff2Html.html(this.value ?? "", this.diff_options))}`, this, {host: this}); } } set value(value : string) { if(typeof value == 'string') { // Diff2Html likes to have files, we don't have them if(value.indexOf('---') !== 0) { value = "--- diff\n+++ diff\n" + value; } super.value = value; this.requestUpdate("value"); } } _handleClick(e) { const oldValue = this.getAttribute("open") this.toggleAttribute("open"); this.requestUpdate("open", oldValue); } getDetachedAttributes(attrs) { attrs.push("id", "value", "class"); } getDetachedNodes() : HTMLElement[] { return [this]; } setDetachedAttributes(_nodes : HTMLElement[], _values : object, _data? : any) : void { for(let attr in _values) { this[attr] = _values[attr]; } } render() { const labelTemplate = this._labelTemplate(); const helpTextTemplate = this._helpTextTemplate(); return html`
${labelTemplate}
${this.open && !this.noDialog ? html` { // Stop bubble or it will re-show dialog e.stopPropagation() }} @close=${() => { this.removeAttribute("open"); this.requestUpdate("open", true); }} > ` : html` ${!this.noDialog ? html` ` : nothing} ` }
${helpTextTemplate}
`; } }