ModeratorElection/frontend/generated/jar-resources/flow-component-renderer.js
2024-06-06 17:45:46 +02:00

208 lines
5.9 KiB
JavaScript

import '@polymer/polymer/lib/elements/dom-if.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { Debouncer } from '@polymer/polymer/lib/utils/debounce.js';
import { idlePeriod } from '@polymer/polymer/lib/utils/async.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import { flowComponentDirective } from './flow-component-directive.js';
import { render, html as litHtml } from 'lit';
/**
* Returns the requested node in a form suitable for Lit template interpolation.
* @param {string} appid
* @param {number} nodeid
* @returns {any} a Lit directive
*/
function getNode(appid, nodeid) {
return flowComponentDirective(appid, nodeid);
}
/**
* Sets the nodes defined by the given node ids as the child nodes of the
* given root element.
* @param {string} appid
* @param {number[]} nodeIds
* @param {Element} root
*/
function setChildNodes(appid, nodeIds, root) {
render(litHtml`${nodeIds.map(id => flowComponentDirective(appid, id))}`, root);
}
/**
* SimpleElementBindingStrategy::addChildren uses insertBefore to add child
* elements to the container. When the children are manually placed under
* another element, the call to insertBefore can occasionally fail due to
* an invalid reference node.
*
* This is a temporary workaround which patches the container's native API
* to not fail when called with invalid arguments.
*/
function patchVirtualContainer(container) {
const originalInsertBefore = container.insertBefore;
container.insertBefore = function (newNode, referenceNode) {
if (referenceNode && referenceNode.parentNode === this) {
return originalInsertBefore.call(this, newNode, referenceNode);
} else {
return originalInsertBefore.call(this, newNode, null);
}
};
}
window.Vaadin ||= {};
window.Vaadin.FlowComponentHost ||= { patchVirtualContainer, getNode, setChildNodes };
class FlowComponentRenderer extends PolymerElement {
static get template() {
return html`
<style>
:host {
animation: 1ms flow-component-renderer-appear;
}
@keyframes flow-component-renderer-appear {
to {
opacity: 1;
}
}
</style>
<slot></slot>
`;
}
static get is() {
return 'flow-component-renderer';
}
static get properties() {
return {
nodeid: Number,
appid: String,
};
}
static get observers() {
return ['_attachRenderedComponentIfAble(appid, nodeid)'];
}
ready() {
super.ready();
this.addEventListener('click', function (event) {
if (
this.firstChild &&
typeof this.firstChild.click === 'function' &&
event.target === this
) {
event.stopPropagation();
this.firstChild.click();
}
});
this.addEventListener('animationend', this._onAnimationEnd);
}
_asyncAttachRenderedComponentIfAble() {
this._debouncer = Debouncer.debounce(this._debouncer, idlePeriod, () =>
this._attachRenderedComponentIfAble()
);
}
_attachRenderedComponentIfAble() {
if (this.appid == null) {
return;
}
if (this.nodeid == null) {
if (this.firstChild) {
this.removeChild(this.firstChild);
}
return;
}
const renderedComponent = this._getRenderedComponent();
if (this.firstChild) {
if (!renderedComponent) {
this._asyncAttachRenderedComponentIfAble();
} else if (this.firstChild !== renderedComponent) {
this.replaceChild(renderedComponent, this.firstChild);
this._defineFocusTarget();
this.onComponentRendered();
} else {
this._defineFocusTarget();
this.onComponentRendered();
}
} else {
if (renderedComponent) {
this.appendChild(renderedComponent);
this._defineFocusTarget();
this.onComponentRendered();
} else {
this._asyncAttachRenderedComponentIfAble();
}
}
}
_getRenderedComponent() {
try {
return window.Vaadin.Flow.clients[this.appid].getByNodeId(this.nodeid);
} catch (error) {
console.error(
'Could not get node %s from app %s',
this.nodeid,
this.appid
);
console.error(error);
}
return null;
}
onComponentRendered() {
// subclasses can override this method to execute custom logic on resize
}
/* Setting the `focus-target` attribute to the first focusable descendant
starting from the firstChild necessary for the focus to be delegated
within the flow-component-renderer when used inside a vaadin-grid cell */
_defineFocusTarget() {
var focusable = this._getFirstFocusableDescendant(this.firstChild);
if (focusable !== null) {
focusable.setAttribute('focus-target', 'true');
}
}
_getFirstFocusableDescendant(node) {
if (this._isFocusable(node)) {
return node;
}
if (node.hasAttribute && (node.hasAttribute('disabled') || node.hasAttribute('hidden'))) {
return null;
}
if (!node.children) {
return null;
}
for (var i = 0; i < node.children.length; i++) {
var focusable = this._getFirstFocusableDescendant(node.children[i]);
if (focusable !== null) {
return focusable;
}
}
return null;
}
_isFocusable(node) {
if (
node.hasAttribute &&
typeof node.hasAttribute === 'function' &&
(node.hasAttribute('disabled') || node.hasAttribute('hidden'))
) {
return false;
}
return node.tabIndex === 0;
}
_onAnimationEnd(e) {
// ShadyCSS applies scoping suffixes to animation names
// To ensure that child is attached once element is unhidden
// for when it was filtered out from, eg, ComboBox
// https://github.com/vaadin/vaadin-flow-components/issues/437
if (e.animationName.indexOf('flow-component-renderer-appear') === 0) {
this._attachRenderedComponentIfAble();
}
}
}
window.customElements.define(FlowComponentRenderer.is, FlowComponentRenderer);