@teipublisher/pb-components
Version:
Collection of webcomponents underlying TEI Publisher
198 lines (180 loc) • 5.76 kB
JavaScript
import { html, css } from 'lit-element';
import '@polymer/iron-form';
import '@polymer/iron-ajax';
import { PbLoad } from './pb-load.js';
import './pb-dialog.js';
/**
* A custom form element which loads the actual form from a server-side script using AJAX.
* Emits `pb-search-resubmit` and `pb-submit` events, signalling the receiver that it should
* refresh.
*
* The component is currently used to implement the additional search facets on the start and
* search result page. It can also be combined with `pb-split-list` to contain an additional form
* with options.
*
* @fires pb-custom-form-loaded - Fired before the element updates its content
* @fires pb-search-resubmit - Fired when the form is submitted
* @fires pb-submit - Fired when the form is submitted
*/
export class PbCustomForm extends PbLoad {
firstUpdated() {
this.shadowRoot.getElementById('ironform').addEventListener('iron-form-presubmit', ev => {
ev.preventDefault();
this._submit();
});
this.addEventListener('click', e => {
if (e.target.slot === 'searchButtonTop') {
this.submit();
}
if (e.target.slot === 'searchButtonBottom') {
this.submit();
}
if (e.target.slot === 'resetButton') {
this._reset();
}
});
this._submissionHandlers();
}
render() {
return html`
<iron-form id="ironform">
<form action="" accept="text/html" method="GET">
<slot name="searchButtonTop"></slot>
<slot></slot>
<slot name="searchButtonBottom"></slot>
<slot name="resetButton"></slot>
</form>
</iron-form>
<iron-ajax
id="loadContent"
verbose
handle-as="text"
method="get"
with-credentials
@response="${this._handleContent}"
@error="${this._handleError}"
></iron-ajax>
<pb-dialog id="errorDialog" title="Error">
<p id="errorMessage"></p>
<div slot="footer">
<button rel="prev">Close</button>
</div>
</pb-dialog>
`;
}
static get styles() {
return css`
:host {
display: block;
}
`;
}
submit() {
this.shadowRoot.getElementById('ironform').submit();
}
_submit() {
const json = this.serializeForm();
this.emitTo('pb-search-resubmit', { params: json });
this.emitTo('pb-submit', { params: json });
}
_reset() {
this.shadowRoot.getElementById('ironform').reset();
}
/**
* serialize custom form to object with name value pairs
* empty, unselected and undifined inputs will be returned
* as null while disabled elements will still be omitted
* this allows url parameters to be reset in the URL
* as IronForm.serializeform will omit names without a value
* @returns {Object} name value pairs
*/
serializeForm() {
const elements = this.shadowRoot.getElementById('ironform')._getSubmittableElements();
const initial = {};
for (const element of elements) {
initial[element.name] = null;
}
return Object.assign(initial, this.shadowRoot.getElementById('ironform').serializeForm());
}
_parseHeaders(xhr) {
// overwrite to avoid `pb-results-received` event being sent
}
_onLoad(content) {
super._onLoad(content);
this.dispatchEvent(new CustomEvent('pb-custom-form-loaded', { detail: content }));
}
_handleError() {
this.emitTo('pb-end-update');
const loader = this.shadowRoot.getElementById('loadContent');
const { response } = loader.lastError;
if (this.silent) {
console.error('Request failed: %s', response ? response.description : '');
return;
}
let message;
if (response) {
message = response.description;
} else {
message = 'Server error occurred';
}
const dialog = this.shadowRoot.getElementById('errorDialog');
const messageElement = this.shadowRoot.getElementById('errorMessage');
messageElement.textContent = `Server error: ${message}`;
dialog.openDialog();
}
_submissionHandlers() {
if (!this.autoSubmit) {
return;
}
this.querySelectorAll(this.autoSubmit).forEach(control => {
const name = control.nodeName.toLowerCase();
let event = 'change';
if (
control instanceof HTMLButtonElement ||
name === 'paper-icon-button' ||
name === 'paper-button' ||
(name === 'input' &&
(control.type === 'button' || control.type === 'submit' || control.type === 'reset'))
) {
event = 'click';
} else if (
name === 'paper-input' ||
(control instanceof HTMLInputElement &&
(control.type === 'text' || control.type === 'search'))
) {
event = 'keyup';
} else if (name === 'paper-dropdown-menu') {
event = 'value-changed';
}
control.addEventListener(event, this._submit.bind(this));
});
}
static get properties() {
return {
/**
* Register event handlers on all descendant elements matching the given CSS selector and submit the form
* automatically if any of those changes. For button-like controls,
* a submit is triggered on click, for text input on keyUp, and for
* all other form components on change.
*/
autoSubmit: {
type: String,
attribute: 'auto-submit',
},
...super.properties,
};
}
/**
* Fired before the element updates its content
*
* @event pb-custom-form-loaded
* @param {string} the loaded content
*/
/**
* Fired when form is submitted
*
* @event pb-search-resubmit
* @param {object} params: serialized form parameters as json object
*/
}
customElements.define('pb-custom-form', PbCustomForm);