UNPKG

@skhemata/skhemata-form

Version:

Skhemata Form Web Component. This web component can be used as base web component when working with forms and inputs.

219 lines (217 loc) 8.6 kB
import { __decorate } from "tslib"; import { html, property, css, unsafeHTML } from '@skhemata/skhemata-base'; import { SkhemataFormInput } from './SkhemataFormInput'; import { SkhemataFormTextbox } from './SkhemataFormTextbox'; import { SkhemataFormTextarea } from './SkhemataFormTextarea'; import { SkhemataFormDropdown } from './SkhemataFormDropdown'; import { SkhemataFormDropzone } from './SkhemataFormDropzone'; import { SkhemataFormButton } from './SkhemataFormButton'; import { SkhemataFormQuill } from './SkhemataFormQuill'; import { SkhemataFormCheckbox } from './SkhemataFormCheckbox'; import { SkhemataFormToggle } from './SkhemataFormToggle'; import { SkhemataFormDatePicker } from './SkhemataFormDatePicker'; /** * Repeater component that repeats inputs passed in. */ export class SkhemataFormRepeat extends SkhemataFormInput { rowName = ""; addRowButtonText = "Add Row"; removeRowButtonText = "Remove Row"; rowLimit = 10; repeatedFields = []; type = "SKHEMATA-FORM-REPEAT"; fieldNodes = []; rowData = []; // Pair all component types with appropriate component allowedComponents = { textbox: 'skhemata-form-textbox', textarea: 'skhemata-form-textarea', dropdown: 'skhemata-form-dropdown', dropzone: 'skhemata-form-dropzone', button: 'skhemata-form-button', quill: 'skhemata-form-quill', checkbox: 'skhemata-form-checkbox', toggle: 'skhemata-form-toggle', datepicker: 'skhemata-form-date-picker' }; static get scopedElements() { return { 'skhemata-form-textbox': SkhemataFormTextbox, 'skhemata-form-textarea': SkhemataFormTextarea, 'skhemata-form-dropdown': SkhemataFormDropdown, 'skhemata-form-dropzone': SkhemataFormDropzone, 'skhemata-form-button': SkhemataFormButton, 'skhemata-form-quill': SkhemataFormQuill, 'skhemata-form-checkbox': SkhemataFormCheckbox, 'skhemata-form-toggle': SkhemataFormToggle, 'skhemata-form-date-picker': SkhemataFormDatePicker }; } constructor() { super(); // Event listener that updates local state based on skhemata-form state this.addEventListener('update-data', (e) => { this.rowData = e.detail.data[this.name]; this.value = this.rowData; }); } async firstUpdated() { await super.firstUpdated(); this.value = this.rowData; const currentNodes = this.shadowRoot.querySelectorAll('[data-row-num]'); currentNodes.forEach((row, index) => { const nodes = row.querySelectorAll('[skhemata-input]'); this.dispatchEvent(new CustomEvent('add-row', { detail: { name: this.name, rowIndex: index, nodes: nodes }, composed: true, bubbles: true, })); this.fieldNodes = Array.from(nodes); }); } /** * Appends a new row of inputs to the end of the component * */ addRow() { this.rowData.push({}); this.requestUpdate(); } /** * Removes row based on the index of the row */ removeRow(event) { // remove-row splices the appropriate data from skhemata-form data property this.dispatchEvent(new CustomEvent('remove-row', { detail: { name: this.name, rowIndex: event.originalTarget.parentNode.dataset.rowNum }, composed: true, bubbles: true, })); this.requestUpdate(); } static get styles() { return [ ...super.styles, css ` :host { width: 100%; } h3 { font-size: 1.5rem; margin-top: 0.5rem; margin-bottom: 0.5rem; font-weight: bold; }` ]; } /** * Renders component based on repeatedFields object * @param name * @param attributes * @param value * @param content content that is inserted to <slot></slot> * @returns HtmlTemplate */ renderComponent(name, attributes, value = '', content = '') { // add excception for date-picker value if (name == 'skhemata-form-date-picker') { if (value) { value = value.slice(0, 10); } } const templateString = `<${name} ${Object.keys(attributes).map(key => { return `${key}="${attributes[key]}"`; }).join(' ')} value="${value}" ${value == true ? 'checked="true"' : ''} skhemata-input>${content}</${name}>`; return html `${unsafeHTML(templateString)}`; } /** * Life cycle method that triggers whenever updated * if there are new rows added, trigger add row event */ updated() { const currentNodes = this.shadowRoot.querySelectorAll('[skhemata-input]'); // check if last row of rowdata is blank if (this.rowData != undefined) { if (this.rowData[this.rowData.length - 1] != undefined) { if (Object.keys(this.rowData[this.rowData.length - 1]).length == 0) { if (currentNodes.length > this.fieldNodes.length) { // Filter out previous nodes from currentNodes const newNodes = Array.from(currentNodes).filter(node => !this.fieldNodes.includes(node)); /** * Dispatch the add-row event * add-row attaches eventlisteners to newly created inputs */ this.dispatchEvent(new CustomEvent('add-row', { detail: { name: this.name, rowIndex: this.rowData?.length - 1, nodes: newNodes }, composed: true, bubbles: true, })); } } } } // Save the current nodes as previous nodes this.fieldNodes = Array.from(currentNodes); } render() { const field = html ` <div class="repeater-field field"> ${this.label && !this.horizontal ? html `<label class="label">${this.label}</label>` : null} ${this.description && !this.horizontal ? html `<p>${this.description}</p>` : null} ${this.rowData?.map((data, i) => html `<div data-row-num=${i}> <h3>${this.rowName} #${i + 1}</h3> ${this.repeatedFields.map((field, j) => html `${(field.type in this.allowedComponents && (field.hide === undefined || !field.hide)) ? this.renderComponent(this.allowedComponents[field.type], { ...field.attributes, 'row-index': i }, data[field.attributes.name], field.content) : ''}`)}<button class="button is-danger" @click=${(e) => this.removeRow(e)}>${this.removeRowButtonText}</button><hr></div>`)} ${this.rowData?.length < this.rowLimit ? html `<button class="button is-success" @click=${this.addRow}>${this.addRowButtonText}</button>` : ''} </div> `; const horizontalFieldLabel = html ` <div class="field-label column is-one-quarter" style="text-align: left"> ${this.label ? html `<label class="label">${this.label}</label>` : null} ${this.description ? html `<p>${this.description}</p>` : null} </div> `; const horizontalField = html ` <div class="field is-horizontal"> ${this.label || this.description ? horizontalFieldLabel : null} <div class="field-body column">${field}</div> </div> `; return this.horizontal ? horizontalField : field; } } __decorate([ property({ type: String }) ], SkhemataFormRepeat.prototype, "rowName", void 0); __decorate([ property({ type: String }) ], SkhemataFormRepeat.prototype, "addRowButtonText", void 0); __decorate([ property({ type: String }) ], SkhemataFormRepeat.prototype, "removeRowButtonText", void 0); __decorate([ property({ type: Number }) ], SkhemataFormRepeat.prototype, "rowLimit", void 0); __decorate([ property({ type: Array }) ], SkhemataFormRepeat.prototype, "repeatedFields", void 0); __decorate([ property({ type: Array }) ], SkhemataFormRepeat.prototype, "rowData", void 0); //# sourceMappingURL=SkhemataFormRepeat.js.map