/** * EGroupware eTemplate2 - Description WebComponent * * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @package api * @link https://www.egroupware.org * @author Nathan Gray */ import {Et2Widget} from "../Et2Widget/Et2Widget"; import {css, html, LitElement, render} from "lit"; import {et2_IDetachedDOM} from "../et2_core_interfaces"; import {activateLinks} from "../ActivateLinksDirective"; import {et2_csvSplit} from "../et2_core_common"; import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget"; export class Et2Description extends Et2Widget(LitElement) implements et2_IDetachedDOM { protected _value : string = ""; protected _forTarget : Et2InputWidget = null; static get styles() { return [ ...super.styles, css` * { white-space: pre-wrap; } :host { display:flex; flex-direction: row; justify-content: flex-start; flex: 0 1 auto !important; } label { padding-inline-end: 1ex; } .split-label label { display: contents; } ::slotted(a) { cursor: pointer; color: var(--sl-color-primary-700); text-decoration: none; display: inherit; }` ]; } static get properties() { return { ...super.properties, /** * Scan the value, and if there are any links (URL, mailto:) then wrap them in a clickable * tag */ activateLinks: { type: Boolean, reflect: true }, /** * Extra link target * Goes with href. If provided, that's the target for opening the link. */ extraLinkTarget: { type: String, reflect: true }, /** * widthxheight, if popup should be used, eg. 640x480 */ extraLinkPopup: { type: String, reflect: true }, /** * Link URL * If provided, will be clickable and open this URL */ href: { type: String, reflect: true }, value: { type: String, noAccessor: true }, for: { type: String} } } constructor() { super(); // Initialize properties this.activateLinks = false; this.extraLinkPopup = ""; this.extraLinkTarget = "_browser"; // Don't initialize this to avoid href(unknown) when rendered //this.href = ""; this.value = ""; this._handleClick = this._handleClick.bind(this); } connectedCallback() { super.connectedCallback(); if (this.for) { this._forTarget = this.getRoot().getWidgetById(this.for); if (this._forTarget) { this._forTarget.ariaLabel = this.value; } } // Put content directly in DOM if(this.value) { render(this._renderContent(), this); } } set_value(value) { this.value = value; } get value() { return this._value; } set value(_value : string) { let oldValue = this.value; if(!_value) { _value = ""; } // Do we do this here, or in transformAttributes()? if(_value && !this.noLang) { _value = this.egw().lang(_value); } if(_value && (_value + "").indexOf('%s') != -1) { _value = _value.replace(/%s/g, _value); } this._value = _value; this.requestUpdate('value', oldValue); if (this._forTarget) { this._forTarget.ariaLabel = this._value; } } updated(changedProperties) { super.updated(changedProperties); // Due to how we do the rendering into the light DOM (not sure it's right) we need this after // value change or it won't actually show up if((changedProperties.has("value") || changedProperties.has("href") || changedProperties.has("activateLinks")) && this.parentNode) { render(this._renderContent(), this); } } _renderContent() { let render = null; // Add hover action button (Edit) if(this.hover_action) { // TODO } // If there's a link, wrap that if(this.href && this.value) { render = this.wrapLink(this.href, this.value); } // If we want to activate links inside, do that else if(this.activateLinks && this.value) { render = this.getActivatedValue(this.value, this.href ? this.extraLinkTarget : '_blank'); } // Just do the value else { render = html`${this.value}`; } return html`${render}`; } render() { let label = this.label; let after; if(label) { // Split the label at the "%s" let parts = et2_csvSplit(label, 2, "%s"); if(parts.length > 1) { after = html``; label = parts[0]; } } // Turn off IDE reformatting, or it will add an extra line break into the template // @formatter:off return html`${after}`; // @formatter:on } async firstUpdated() { this.removeEventListener('click.extra_link', this._handleClick); if(this.extraLinkPopup || this.mime) { // Add click listener this.addEventListener('click.extra_link', this._handleClick); } } _handleClick(_ev : MouseEvent) : boolean { // call super to get the onclick handling running if (super._handleClick(_ev) && !_ev.defaultPrevented && this._forTarget?.focus) { this._forTarget.focus(); _ev.preventDefault(); return false; } if(this.mimeData || this.href) { egw(window).open_link(this.mimeData || this.href, this.extraLinkTarget, this.extraLinkPopup, null, null, this.mime); _ev.preventDefault(); return false; } else if(_ev.target.nodeName !== "A") { // If it's not an activated link, just stop _ev.preventDefault(); return false; } // Let links (present if activateLinks = true) do their thing normally } protected wrapLink(href, value) { if(href.indexOf('/') == -1 && href.split('.').length >= 3 && !(href.indexOf('mailto:') != -1 || href.indexOf('://') != -1 || href.indexOf('javascript:') != -1) ) { href = "/index.php?menuaction=" + href; } if(href.charAt(0) == '/') // link relative to eGW { href = egw.link(href); } return html`${value}`; } protected getActivatedValue(value, target) { return html`${activateLinks(value, target)}`; } getDetachedAttributes(attrs) { attrs.push("id", "label", "value", "class", "href", "statustext"); } getDetachedNodes() : HTMLElement[] { return [this]; } setDetachedAttributes(_nodes : HTMLElement[], _values : object, _data? : any) : void { for(let attr in _values) { this[attr] = _values[attr]; } } loadFromXML() { // nope } } // @ts-ignore TypeScript is not recognizing that this widget is a LitElement customElements.define("et2-description", Et2Description); export class Et2Label extends Et2Description {} // @ts-ignore TypeScript is not recognizing that this widget is a LitElement customElements.define("et2-label", Et2Label);