2021-12-17 22:47:19 +01:00
|
|
|
/**
|
|
|
|
* 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";
|
2023-09-13 19:55:33 +02:00
|
|
|
import {css, html, LitElement, render} from "lit";
|
2021-12-17 22:47:19 +01:00
|
|
|
import {et2_IDetachedDOM} from "../et2_core_interfaces";
|
|
|
|
import {activateLinks} from "../ActivateLinksDirective";
|
2022-08-19 00:22:39 +02:00
|
|
|
import {et2_csvSplit} from "../et2_core_common";
|
2024-04-25 21:05:15 +02:00
|
|
|
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
2021-12-17 22:47:19 +01:00
|
|
|
|
|
|
|
export class Et2Description extends Et2Widget(LitElement) implements et2_IDetachedDOM
|
|
|
|
{
|
|
|
|
|
|
|
|
protected _value : string = "";
|
|
|
|
|
2024-04-25 21:05:15 +02:00
|
|
|
protected _forTarget : Et2InputWidget = null;
|
|
|
|
|
2021-12-17 22:47:19 +01:00
|
|
|
static get styles()
|
|
|
|
{
|
|
|
|
return [
|
|
|
|
...super.styles,
|
|
|
|
css`
|
2022-04-21 18:41:30 +02:00
|
|
|
* {
|
|
|
|
white-space: pre-wrap;
|
2021-12-20 23:15:32 +01:00
|
|
|
}
|
2022-08-31 12:27:52 +02:00
|
|
|
:host {
|
|
|
|
display:flex;
|
2024-01-19 22:25:43 +01:00
|
|
|
flex-direction: row;
|
|
|
|
justify-content: flex-start;
|
2022-08-31 12:27:52 +02:00
|
|
|
flex: 0 1 auto !important;
|
|
|
|
}
|
2024-01-19 22:08:28 +01:00
|
|
|
|
|
|
|
label {
|
|
|
|
padding-inline-end: 1ex;
|
|
|
|
}
|
|
|
|
|
|
|
|
.split-label label {
|
|
|
|
display: contents;
|
|
|
|
}
|
2023-03-10 13:39:58 +01:00
|
|
|
::slotted(a) {
|
2021-12-20 23:15:32 +01:00
|
|
|
cursor: pointer;
|
2024-07-12 00:46:22 +02:00
|
|
|
color: var(--sl-color-primary-700);
|
2021-12-20 23:15:32 +01:00
|
|
|
text-decoration: none;
|
2023-03-10 13:39:58 +01:00
|
|
|
display: inherit;
|
2021-12-17 22:47:19 +01:00
|
|
|
}`
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
static get properties()
|
|
|
|
{
|
|
|
|
return {
|
|
|
|
...super.properties,
|
2021-12-20 23:15:32 +01:00
|
|
|
/**
|
|
|
|
* Scan the value, and if there are any links (URL, mailto:) then wrap them in a clickable
|
|
|
|
* <a/> tag
|
|
|
|
*/
|
2023-02-01 22:31:17 +01:00
|
|
|
activateLinks: {
|
2021-12-20 23:15:32 +01:00
|
|
|
type: Boolean,
|
|
|
|
reflect: true
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Extra link target
|
|
|
|
* Goes with href. If provided, that's the target for opening the link.
|
|
|
|
*/
|
2022-07-21 17:57:50 +02:00
|
|
|
extraLinkTarget: {
|
2021-12-21 19:16:58 +01:00
|
|
|
type: String,
|
|
|
|
reflect: true
|
2021-12-20 23:15:32 +01:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
* widthxheight, if popup should be used, eg. 640x480
|
|
|
|
*/
|
2022-07-21 17:57:50 +02:00
|
|
|
extraLinkPopup: {
|
2021-12-21 19:16:58 +01:00
|
|
|
type: String,
|
|
|
|
reflect: true
|
2021-12-20 23:15:32 +01:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Link URL
|
|
|
|
* If provided, will be clickable and open this URL
|
|
|
|
*/
|
|
|
|
href: {
|
2021-12-21 19:16:58 +01:00
|
|
|
type: String,
|
|
|
|
reflect: true
|
2021-12-20 23:15:32 +01:00
|
|
|
},
|
2022-09-12 19:25:47 +02:00
|
|
|
value: {
|
|
|
|
type: String,
|
|
|
|
noAccessor: true
|
|
|
|
},
|
2024-04-25 21:05:15 +02:00
|
|
|
for: { type: String}
|
2021-12-17 22:47:19 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-20 23:15:32 +01:00
|
|
|
constructor()
|
|
|
|
{
|
|
|
|
super();
|
|
|
|
|
|
|
|
// Initialize properties
|
2023-02-01 22:31:17 +01:00
|
|
|
this.activateLinks = false;
|
2022-07-21 17:57:50 +02:00
|
|
|
this.extraLinkPopup = "";
|
|
|
|
this.extraLinkTarget = "_browser";
|
2022-02-03 21:52:14 +01:00
|
|
|
// Don't initialize this to avoid href(unknown) when rendered
|
|
|
|
//this.href = "";
|
2021-12-20 23:15:32 +01:00
|
|
|
this.value = "";
|
2021-12-21 18:52:39 +01:00
|
|
|
|
|
|
|
this._handleClick = this._handleClick.bind(this);
|
2021-12-20 23:15:32 +01:00
|
|
|
}
|
|
|
|
|
2022-04-21 00:23:53 +02:00
|
|
|
connectedCallback()
|
|
|
|
{
|
|
|
|
super.connectedCallback();
|
|
|
|
|
2024-04-25 21:05:15 +02:00
|
|
|
if (this.for)
|
|
|
|
{
|
|
|
|
this._forTarget = this.getRoot().getWidgetById(this.for);
|
|
|
|
|
|
|
|
if (this._forTarget)
|
|
|
|
{
|
|
|
|
this._forTarget.ariaLabel = this.value;
|
|
|
|
}
|
|
|
|
}
|
2022-04-21 00:23:53 +02:00
|
|
|
// Put content directly in DOM
|
|
|
|
if(this.value)
|
|
|
|
{
|
|
|
|
render(this._renderContent(), this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-17 22:47:19 +01:00
|
|
|
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()?
|
2022-07-21 17:57:50 +02:00
|
|
|
if(_value && !this.noLang)
|
2021-12-17 22:47:19 +01:00
|
|
|
{
|
|
|
|
_value = this.egw().lang(_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(_value && (_value + "").indexOf('%s') != -1)
|
|
|
|
{
|
|
|
|
_value = _value.replace(/%s/g, _value);
|
|
|
|
}
|
|
|
|
|
|
|
|
this._value = _value;
|
|
|
|
this.requestUpdate('value', oldValue);
|
2024-04-25 21:05:15 +02:00
|
|
|
|
|
|
|
if (this._forTarget)
|
|
|
|
{
|
|
|
|
this._forTarget.ariaLabel = this._value;
|
|
|
|
}
|
2021-12-17 22:47:19 +01:00
|
|
|
}
|
|
|
|
|
2023-11-03 23:15:53 +01:00
|
|
|
updated(changedProperties)
|
2022-05-11 21:58:01 +02:00
|
|
|
{
|
2023-11-03 23:15:53 +01:00
|
|
|
super.updated(changedProperties);
|
2022-05-11 21:58:01 +02:00
|
|
|
// 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
|
2023-11-03 23:15:53 +01:00
|
|
|
if((changedProperties.has("value") || changedProperties.has("href") || changedProperties.has("activateLinks")) && this.parentNode)
|
2022-05-11 21:58:01 +02:00
|
|
|
{
|
2023-11-03 23:15:53 +01:00
|
|
|
render(this._renderContent(), <HTMLElement><unknown>this);
|
2022-05-11 21:58:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-21 00:23:53 +02:00
|
|
|
_renderContent()
|
2021-12-17 22:47:19 +01:00
|
|
|
{
|
2021-12-20 23:15:32 +01:00
|
|
|
let render = null;
|
2021-12-17 22:47:19 +01:00
|
|
|
|
|
|
|
// Add hover action button (Edit)
|
|
|
|
if(this.hover_action)
|
|
|
|
{
|
|
|
|
// TODO
|
|
|
|
}
|
2021-12-20 23:15:32 +01:00
|
|
|
|
2021-12-17 22:47:19 +01:00
|
|
|
|
|
|
|
// If there's a link, wrap that
|
2022-05-05 21:48:39 +02:00
|
|
|
if(this.href && this.value)
|
2021-12-17 22:47:19 +01:00
|
|
|
{
|
2022-05-05 21:48:39 +02:00
|
|
|
render = this.wrapLink(this.href, this.value);
|
2021-12-17 22:47:19 +01:00
|
|
|
}
|
|
|
|
// If we want to activate links inside, do that
|
2023-02-01 22:31:17 +01:00
|
|
|
else if(this.activateLinks && this.value)
|
2021-12-17 22:47:19 +01:00
|
|
|
{
|
2022-07-21 17:57:50 +02:00
|
|
|
render = this.getActivatedValue(this.value, this.href ? this.extraLinkTarget : '_blank');
|
2021-12-17 22:47:19 +01:00
|
|
|
}
|
|
|
|
// Just do the value
|
|
|
|
else
|
|
|
|
{
|
2022-05-05 21:48:39 +02:00
|
|
|
render = html`${this.value}`;
|
2021-12-20 23:15:32 +01:00
|
|
|
}
|
2024-08-07 23:15:58 +02:00
|
|
|
return html`<span part="content" class="description--content">${render}</span>`;
|
2021-12-20 23:15:32 +01:00
|
|
|
}
|
|
|
|
|
2022-04-21 00:23:53 +02:00
|
|
|
render()
|
2022-04-13 00:28:36 +02:00
|
|
|
{
|
2022-08-19 00:22:39 +02:00
|
|
|
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[1]}</label>`;
|
|
|
|
label = parts[0];
|
|
|
|
}
|
|
|
|
}
|
2022-04-21 18:41:30 +02:00
|
|
|
// Turn off IDE reformatting, or it will add an extra line break into the template
|
|
|
|
// @formatter:off
|
2024-01-19 22:08:28 +01:00
|
|
|
return html`<slot part="form-control-label" name="label" class=${after ? "split-label" : ""}><label>${label}</label></slot><slot part="form-control-value"></slot>${after}`;
|
2022-04-21 18:41:30 +02:00
|
|
|
// @formatter:on
|
2022-04-13 00:28:36 +02:00
|
|
|
}
|
|
|
|
|
2022-04-21 00:23:53 +02:00
|
|
|
|
2021-12-20 23:15:32 +01:00
|
|
|
async firstUpdated()
|
|
|
|
{
|
2021-12-21 18:52:39 +01:00
|
|
|
this.removeEventListener('click.extra_link', this._handleClick);
|
2022-07-21 17:57:50 +02:00
|
|
|
if(this.extraLinkPopup || this.mime)
|
2021-12-20 23:15:32 +01:00
|
|
|
{
|
|
|
|
// Add click listener
|
2021-12-21 18:52:39 +01:00
|
|
|
this.addEventListener('click.extra_link', this._handleClick);
|
2021-12-20 23:15:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_handleClick(_ev : MouseEvent) : boolean
|
|
|
|
{
|
2022-02-25 11:33:04 +01:00
|
|
|
// call super to get the onclick handling running
|
2024-04-25 21:05:15 +02:00
|
|
|
if (super._handleClick(_ev) && !_ev.defaultPrevented && this._forTarget?.focus)
|
|
|
|
{
|
|
|
|
this._forTarget.focus();
|
|
|
|
_ev.preventDefault();
|
|
|
|
return false;
|
|
|
|
}
|
2022-02-25 11:33:04 +01:00
|
|
|
|
2022-07-21 17:57:50 +02:00
|
|
|
if(this.mimeData || this.href)
|
2021-12-20 23:15:32 +01:00
|
|
|
{
|
2022-07-21 17:57:50 +02:00
|
|
|
egw(window).open_link(this.mimeData || this.href, this.extraLinkTarget, this.extraLinkPopup, null, null, this.mime);
|
2023-04-04 22:08:24 +02:00
|
|
|
_ev.preventDefault();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if(_ev.target.nodeName !== "A")
|
|
|
|
{
|
|
|
|
// If it's not an activated link, just stop
|
|
|
|
_ev.preventDefault();
|
|
|
|
return false;
|
2021-12-17 22:47:19 +01:00
|
|
|
}
|
2023-04-04 22:08:24 +02:00
|
|
|
// Let links (present if activateLinks = true) do their thing normally
|
2021-12-17 22:47:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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`<a href="${href}" target="${this.target ?? "_blank"}">${value}</a>`;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected getActivatedValue(value, target)
|
|
|
|
{
|
|
|
|
return html`${activateLinks(value, target)}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
getDetachedAttributes(attrs)
|
|
|
|
{
|
2023-04-25 21:53:16 +02:00
|
|
|
attrs.push("id", "label", "value", "class", "href", "statustext");
|
2021-12-17 22:47:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
getDetachedNodes() : HTMLElement[]
|
|
|
|
{
|
|
|
|
return [<HTMLElement><unknown>this];
|
|
|
|
}
|
|
|
|
|
|
|
|
setDetachedAttributes(_nodes : HTMLElement[], _values : object, _data? : any) : void
|
|
|
|
{
|
2022-05-03 19:01:42 +02:00
|
|
|
for(let attr in _values)
|
|
|
|
{
|
|
|
|
this[attr] = _values[attr];
|
|
|
|
}
|
2021-12-17 22:47:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
loadFromXML()
|
|
|
|
{
|
|
|
|
// nope
|
|
|
|
}
|
|
|
|
}
|
2022-05-13 11:59:13 +02:00
|
|
|
// @ts-ignore TypeScript is not recognizing that this widget is a LitElement
|
|
|
|
customElements.define("et2-description", Et2Description);
|
2021-12-17 22:47:19 +01:00
|
|
|
|
2022-05-13 11:59:13 +02:00
|
|
|
export class Et2Label extends Et2Description {}
|
2021-12-17 22:47:19 +01:00
|
|
|
// @ts-ignore TypeScript is not recognizing that this widget is a LitElement
|
2022-05-13 11:59:13 +02:00
|
|
|
customElements.define("et2-label", Et2Label);
|