mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-22 06:30:59 +01:00
Home: Fix portlet broken by Shoelace update to 1.8
They made HasSlotController internal only
This commit is contained in:
parent
8814f55ff3
commit
a9fcf05fab
@ -17,7 +17,7 @@ import type {InteractEvent} from "@interactjs/core/InteractEvent";
|
||||
import {egw} from "../../jsapi/egw_global";
|
||||
import {css, html, TemplateResult} from "lit";
|
||||
import {classMap} from "lit/directives/class-map.js";
|
||||
import type {HasSlotController} from "../../../../node_modules/@shoelace-style/shoelace/dist/internal/slot";
|
||||
import {HasSlotController} from "../Et2Widget/slot";
|
||||
import shoelace from "../Styles/shoelace";
|
||||
import {Et2Dialog} from "../Et2Dialog/Et2Dialog";
|
||||
import {et2_IResizeable} from "../et2_core_interfaces";
|
||||
@ -81,8 +81,7 @@ export class Et2Portlet extends Et2Widget(SlCard)
|
||||
font-size: var(--sl-font-size-medium);
|
||||
line-height: var(--sl-line-height-dense);
|
||||
padding: 0px;
|
||||
padding-left: var(--header-spacing);
|
||||
padding-right: calc(2em + var(--header-spacing));
|
||||
padding-left: 0px;
|
||||
margin: 0px;
|
||||
position: relative;
|
||||
}
|
||||
@ -111,8 +110,12 @@ export class Et2Portlet extends Et2Widget(SlCard)
|
||||
height: 100%
|
||||
}
|
||||
|
||||
.card_header {
|
||||
margin-right: calc(var(--sl-spacing-medium) + 1em);
|
||||
.card__header {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 0px;
|
||||
padding-left: var(--sl-spacing-medium);
|
||||
padding-right: calc(2em + var(--header-spacing));
|
||||
}
|
||||
|
||||
.card__body {
|
||||
|
129
api/js/etemplate/Et2Widget/slot.ts
Normal file
129
api/js/etemplate/Et2Widget/slot.ts
Normal file
@ -0,0 +1,129 @@
|
||||
import type {ReactiveController, ReactiveControllerHost} from 'lit';
|
||||
|
||||
/**
|
||||
* A reactive controller that determines when slots exist.
|
||||
*
|
||||
* Copied from Shoelace
|
||||
* /src/internal/slot.ts
|
||||
*/
|
||||
export class HasSlotController implements ReactiveController
|
||||
{
|
||||
host : ReactiveControllerHost & Element;
|
||||
slotNames : string[] = [];
|
||||
|
||||
constructor(host : ReactiveControllerHost & Element, ...slotNames : string[])
|
||||
{
|
||||
(this.host = host).addController(this);
|
||||
this.slotNames = slotNames;
|
||||
}
|
||||
|
||||
private hasDefaultSlot()
|
||||
{
|
||||
return [...this.host.childNodes].some(node =>
|
||||
{
|
||||
if(node.nodeType === node.TEXT_NODE && node.textContent!.trim() !== '')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if(node.nodeType === node.ELEMENT_NODE)
|
||||
{
|
||||
const el = node as HTMLElement;
|
||||
const tagName = el.tagName.toLowerCase();
|
||||
|
||||
// Ignore visually hidden elements since they aren't rendered
|
||||
if(tagName === 'sl-visually-hidden')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If it doesn't have a slot attribute, it's part of the default slot
|
||||
if(!el.hasAttribute('slot'))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private hasNamedSlot(name : string)
|
||||
{
|
||||
return this.host.querySelector(`:scope > [slot="${name}"]`) !== null;
|
||||
}
|
||||
|
||||
test(slotName : string)
|
||||
{
|
||||
return slotName === '[default]' ? this.hasDefaultSlot() : this.hasNamedSlot(slotName);
|
||||
}
|
||||
|
||||
hostConnected()
|
||||
{
|
||||
this.host.shadowRoot!.addEventListener('slotchange', this.handleSlotChange);
|
||||
}
|
||||
|
||||
hostDisconnected()
|
||||
{
|
||||
this.host.shadowRoot!.removeEventListener('slotchange', this.handleSlotChange);
|
||||
}
|
||||
|
||||
private handleSlotChange = (event : Event) =>
|
||||
{
|
||||
const slot = event.target as HTMLSlotElement;
|
||||
|
||||
if((this.slotNames.includes('[default]') && !slot.name) || (slot.name && this.slotNames.includes(slot.name)))
|
||||
{
|
||||
this.host.requestUpdate();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a slot, this function iterates over all of its assigned element and text nodes and returns the concatenated
|
||||
* HTML as a string. This is useful because we can't use slot.innerHTML as an alternative.
|
||||
*/
|
||||
export function getInnerHTML(slot : HTMLSlotElement) : string
|
||||
{
|
||||
const nodes = slot.assignedNodes({flatten: true});
|
||||
let html = '';
|
||||
|
||||
[...nodes].forEach(node =>
|
||||
{
|
||||
if(node.nodeType === Node.ELEMENT_NODE)
|
||||
{
|
||||
html += (node as HTMLElement).outerHTML;
|
||||
}
|
||||
|
||||
if(node.nodeType === Node.TEXT_NODE)
|
||||
{
|
||||
html += node.textContent;
|
||||
}
|
||||
});
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a slot, this function iterates over all of its assigned text nodes and returns the concatenated text as a
|
||||
* string. This is useful because we can't use slot.textContent as an alternative.
|
||||
*/
|
||||
export function getTextContent(slot : HTMLSlotElement | undefined | null) : string
|
||||
{
|
||||
if(!slot)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
const nodes = slot.assignedNodes({flatten: true});
|
||||
let text = '';
|
||||
|
||||
[...nodes].forEach(node =>
|
||||
{
|
||||
if(node.nodeType === Node.TEXT_NODE)
|
||||
{
|
||||
text += node.textContent;
|
||||
}
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
Loading…
Reference in New Issue
Block a user