/** * @license * Copyright 2022 Superflow.dev * SPDX-License-Identifier: MIT */ import {LitElement, html, css, PropertyValueMap} from 'lit'; import {customElement, query, property, queryAssignedElements} from 'lit/decorators.js'; import Util from './util'; import {SfISelect} from 'sf-i-select'; // import {LitElement, html, css} from 'lit'; // import {customElement} from 'lit/decorators.js'; /** * SfISubSelect element. * @fires renderComplete - When the list is populated * @fires valueChanged - When the value is changed * @property apiId - backend api id * @property label - select input label * @property name - name of the input * @property selectedId - id (primary key) to be preselected * @property filterId - filter by the foreign key id * @property selectedValue - value of the selected item * @property repopulate - repopulate the list */ @customElement('sf-i-sub-select') export class SfISubSelect extends LitElement { @property() apiId!: string; @property() label!: string; @property() name!: string; @property() mode!: string; @property() flow!: string; @property() selectedId: string[] = []; @property() filterId!: string; @property() removedValues: string[] = []; @property() selectedTextPhrase: string = ""; selectedIndex = () => { let index = 0; const len = this._SfInputSelect.options.length; for (var i = 0; i < len; i++) { const opt = this._SfInputSelect.options[i]; if (opt.selected && opt.value != "noselect") { index = i; } } return index; } selectedValues = () => { const values = []; const len = this._SfInputSelect.options.length; for (var i = 0; i < len; i++) { const opt = this._SfInputSelect.options[i]; if (opt.selected && opt.value != "noselect") { values.push(opt.value); } } return values; } selectedTexts = () => { const values = []; const len = this._SfInputSelect.options.length; for (var i = 0; i < len; i++) { const opt = this._SfInputSelect.options[i]; if (opt.selected && opt.value != "noselect") { values.push(this._SfInputSelect.options[i].text) } } return values; } static override styles = css` .SfISubSelectC { display: flex; flex-direction: column; align-items: stretch; justify-content: space-between; } .SfISubSelectCAdmin { padding: 10px 20px; } .SfISubSelectC label{ padding-bottom: 5px; } .SfISubSelectC div{ display: flex; align-items: center; } .SfISubSelectC > div > select{ flex-grow: 1; } .table-action-button { margin-left: 15px; } .link { text-decoration: none; color: inherit; cursor: pointer; font-size: 90%; } .loader-element { } .lds-dual-ring { display: inline-block; width: 20px; height: 20px; } .lds-dual-ring:after { content: " "; display: block; width: 15px; height: 15px; margin: 0px; border-radius: 50%; border: 2px solid #fff; border-color: #888 #ddd #888 #ddd; animation: lds-dual-ring 0.8s linear infinite; } .lds-dual-ring-lg { display: inline-block; width: 20px; height: 20px; } .lds-dual-ring-lg:after { content: " "; display: block; width: 15px; height: 15px; margin: 0px; border-radius: 50%; border: 3px solid #fff; border-color: #888 #ddd #888 #ddd; animation: lds-dual-ring 0.8s linear infinite; } .button-action { margin: 5px; } .div-row-error { display: flex; justify-content: center; position: fixed; position: fixed; top: 0px; right: 0px; margin-top: 20px; margin-right: 20px; display: none; align-items:center; background-color: white; border: dashed 1px red; padding: 20px; } .div-row-error-message { color: red; padding: 5px; background-color: white; text-align: center; } .div-row-success { display: flex; justify-content: center; position: fixed; top: 0px; right: 0px; margin-top: 20px; margin-right: 20px; display: none; align-items:center; background-color: white; border: dashed 1px green; padding: 20px; } .div-row-success-message { color: green; padding: 5px; background-color: white; text-align: center; } .d-flex { display: flex; } .justify-center { justify-content: center; } .justify-end { justify-content: flex-end; } .justify-between { justify-content: space-between; } .align-center { align-items: center; } .flex-col { flex-direction: column; } .justify-end { justify-content: flex-end; } .mb5 { margin-bottom: 5px; } .mt5 { margin-top: 5px; } .mt10 { margin-top: 10px; } @keyframes lds-dual-ring { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .hide { display: none; } .badge { margin-top: -20px; } .lb { width: 5% } .rb { width: 5% } .tcId { min-width: 300px; } .tcName { min-width: 200px; } .tcActions { min-width: 150px; text-align: right; } .SfISubSelectCAdmin th { border-bottom: solid 1px #aaa } .SfISubSelectCAdmin td { border-bottom: solid 1px #aaa } .tableC { overflow-x: auto; } @media (orientation: landscape) { .lb { width: 25% } .rb { width: 25% } } `; @query('.SfISubSelectC select') _sfSelect: any; @query('.loader-element') _SfLoader: any; @query('.tableC') _SfTableC: any; @query('.newC') _SfNewC: any; @query('.input-new') _SfInputNew: any; @query('.input-select') _SfInputSelect: any; @query('.div-row-error') _SfRowError: any; @query('.div-row-error-message') _SfRowErrorMessage: any; @query('.div-row-success') _SfRowSuccess: any; @query('.div-row-success-message') _SfRowSuccessMessage: any; @queryAssignedElements({slot: 'fk'}) _sfSlottedFk: any; clearMessages = () => { this._SfRowError.style.display = 'none'; this._SfRowErrorMessage.innerHTML = ''; this._SfRowSuccess.style.display = 'none'; this._SfRowSuccessMessage.innerHTML = ''; } setError = (msg: string) => { this._SfRowError.style.display = 'flex'; this._SfRowErrorMessage.innerHTML = msg; this._SfRowSuccess.style.display = 'none'; this._SfRowSuccessMessage.innerHTML = ''; } setSuccess = (msg: string) => { this._SfRowError.style.display = 'none'; this._SfRowErrorMessage.innerHTML = ''; this._SfRowSuccess.style.display = 'flex'; this._SfRowSuccessMessage.innerHTML = msg; } clearSelection = () => { this._SfInputSelect.value = 'noselect'; } prepareXhr = async (data: any, url: string, loaderElement: any, authorization: any) => { if(loaderElement != null) { loaderElement.innerHTML = '
'; } return await Util.callApi(url, data, authorization); } submitNew = () => { } fetchList = async () => { const retVals = []; var retString = ""; console.log('pop list'); const body = {"fk": this.filterId}; const authorization = btoa(Util.readCookie('email') + ":" + Util.readCookie('accessToken')); const xhr : any = (await this.prepareXhr(body, "https://"+this.apiId+"/list", this._SfLoader, authorization)) as any; this._SfLoader.innerHTML = ''; if(xhr.status == 200) { const jsonRespose = JSON.parse(xhr.responseText); const values = jsonRespose.data.values; console.log('values', values); for(var i = 0; i < values.length; i++) { if(this.selectedId != null && this.selectedId.length > 0) { if(this.selectedId.includes(values[i].id)) { retVals.push(values[i].name) continue; } } } } for(var i = 0; i < retVals.length; i++) { retString += retVals[i]; if(i < retVals.length - 1) { retString += '; '; } } return retString; } renderList = (values: Array) => { if(this.mode == "admin") { var innerHTML = ''; innerHTML = '' for(var i = 0; i < values.length; i++) { innerHTML += ''; innerHTML += ''; innerHTML += ''; innerHTML += ''; innerHTML += ''; } this._SfTableC.innerHTML = innerHTML; for(var i = 0; i < values.length; i++) { this._SfTableC.querySelector('#edit-'+values[i].id+'').addEventListener('click', (event: any)=> { const id = event.target?.id.replace('edit-', ''); this._SfTableC.querySelector('#edit-'+id+'').style.display = 'none'; this._SfTableC.querySelector('#delete-'+id+'').style.display = 'none'; this._SfTableC.querySelector('#text-'+id+'').style.display = 'none'; this._SfTableC.querySelector('#cancel-'+id+'').style.display = 'inline'; this._SfTableC.querySelector('#submit-'+id+'').style.display = 'inline'; this._SfTableC.querySelector('#input-'+id+'').style.display = 'inline'; }) this._SfTableC.querySelector('#cancel-'+values[i].id+'').addEventListener('click', (event: any)=> { const id = event.target?.id.replace('cancel-', ''); this._SfTableC.querySelector('#edit-'+id+'').style.display = 'inline'; this._SfTableC.querySelector('#delete-'+id+'').style.display = 'inline'; this._SfTableC.querySelector('#text-'+id+'').style.display = 'inline'; this._SfTableC.querySelector('#cancel-'+id+'').style.display = 'none'; this._SfTableC.querySelector('#submit-'+id+'').style.display = 'none'; this._SfTableC.querySelector('#input-'+id+'').style.display = 'none'; }) this._SfTableC.querySelector('#input-'+values[i].id+'').addEventListener('keyup', (event: any)=> { const id = event.target?.id.replace('input-', ''); const name = event.target.value; if(Util.validateName(name)) { this._SfTableC.querySelector('#submit-'+id+'').removeAttribute('disabled'); } else { this._SfTableC.querySelector('#submit-'+id+'').setAttribute('disabled', true); } }); this._SfTableC.querySelector('#confirm-'+values[i].id+'').addEventListener('click', async (event: any)=> { this.clearMessages(); const id = event.target?.id.replace('confirm-', ''); const selectedValue = (document.querySelector('.input-parent-select') as SfISelect).selectedValues()[0]; const body = {"fk": selectedValue, "id": id}; const authorization = btoa(Util.readCookie('email') + ":" + Util.readCookie('accessToken')); const xhr : any = (await this.prepareXhr(body, "https://"+this.apiId+"/delete", this._SfLoader, authorization)) as any; this._SfLoader.innerHTML = ''; if(xhr.status == 200) { this.setSuccess('Operation Successful!'); setTimeout(() => { this.clearMessages(); this._SfNewC.querySelector('.input-new').value = ""; this.populateList(); }, 1000); } else { const jsonRespose = JSON.parse(xhr.responseText); this.setError(jsonRespose.error); setTimeout(() => { this.clearMessages(); }, 5000); } }); this._SfTableC.querySelector('#delete-'+values[i].id+'').addEventListener('click', async (event: any)=> { const id = event.target?.id.replace('delete-', ''); event.target.style.display = 'none'; this._SfTableC.querySelector('#edit-'+id+'').style.display = 'none'; this._SfTableC.querySelector('#confirm-'+id+'').style.display = 'inline'; this._SfTableC.querySelector('#canceld-'+id+'').style.display = 'inline'; }); this._SfTableC.querySelector('#canceld-'+values[i].id+'').addEventListener('click', async (event: any)=> { const id = event.target?.id.replace('canceld-', ''); event.target.style.display = 'none'; this._SfTableC.querySelector('#edit-'+id+'').style.display = 'inline'; this._SfTableC.querySelector('#delete-'+id+'').style.display = 'inline'; this._SfTableC.querySelector('#confirm-'+id+'').style.display = 'none'; this._SfTableC.querySelector('#canceld-'+id+'').style.display = 'none'; }); this._SfTableC.querySelector('#submit-'+values[i].id+'').addEventListener('click', async (event: any)=> { this.clearMessages(); const id = event.target?.id.replace('submit-', ''); const selectedValue = (document.querySelector('.input-parent-select') as SfISelect).selectedValues()[0]; const name = this._SfTableC.querySelector('#input-'+id+'').value; const body = {"name": name, "fk": selectedValue, "id": id}; const authorization = btoa(Util.readCookie('email') + ":" + Util.readCookie('accessToken')); const xhr : any = (await this.prepareXhr(body, "https://"+this.apiId+"/update", this._SfLoader, authorization)) as any; this._SfLoader.innerHTML = ''; if(xhr.status == 200) { this.setSuccess('Operation Successful!'); setTimeout(() => { this.clearMessages(); this._SfNewC.querySelector('.input-new').value = ""; this.populateList(); }, 1000); } else { const jsonRespose = JSON.parse(xhr.responseText); this.setError(jsonRespose.error); setTimeout(() => { this.clearMessages(); }, 5000); } }); } } else { var innerHTML = ''; innerHTML += '' for(var i = 0; i < values.length; i++) { if(this.removedValues.includes(values[i].id)) continue; if(this.selectedId != null && this.selectedId.length > 0) { if(this.selectedId.includes(values[i].id)) { innerHTML += '' this.dispatchMyEvent("valueChanged", {newValue: values[i].id, newText: values[i].name}) continue; } else { innerHTML += ''; } } else { innerHTML += ''; } // if(i === 0) { // innerHTML += '' // this.dispatchMyEvent("valueChanged", {newValue: values[i].id, newText: values[i].name}) // } else { // // } } this._sfSelect.innerHTML = innerHTML; console.log('renderlist', innerHTML); this.dispatchMyEvent("renderComplete", {}); } } populateList = async () => { console.log('pop list'); const body = {"fk": this.mode == "admin" ? (document.querySelector('.input-parent-select') as SfISelect).selectedValues()[0] : this.filterId}; const authorization = btoa(Util.readCookie('email') + ":" + Util.readCookie('accessToken')); const xhr : any = (await this.prepareXhr(body, "https://"+this.apiId+"/list", this._SfLoader, authorization)) as any; this._SfLoader.innerHTML = ''; if(xhr.status == 200) { const jsonRespose = JSON.parse(xhr.responseText); const values = jsonRespose.data.values; console.log('values', values); this.renderList(values) } } showNew = () => { this._SfNewC.querySelector('#button-new').setAttribute('disabled', true); this._SfNewC.querySelector('#button-view').removeAttribute('disabled'); this._SfNewC.querySelector('.new-section').style.display = 'flex'; this._SfNewC.querySelector('.view-section').style.display = 'none'; } showView = () => { this._SfNewC.querySelector('#button-view').setAttribute('disabled', true); this._SfNewC.querySelector('#button-new').removeAttribute('disabled'); this._SfNewC.querySelector('.view-section').style.display = 'flex'; this._SfNewC.querySelector('.new-section').style.display = 'none'; } removeItemByValue = (value: string) => { if(!this.removedValues.includes(value)) { this.removedValues.push(value); } } dispatchMyEvent = (ev: string, args?: any) => { console.log('dispatching event', ev); const event = new CustomEvent(ev, {detail: args, bubbles: true, composed: true}); this.dispatchEvent(event); } onChangeSelect = (ev: any) => { this.dispatchMyEvent("valueChanged", {newValue: ev.target.value, newText: ev.target.options[ev.target.selectedIndex].text}); } initState = async () => { console.log('mode', this.mode); if(this.flow == "read") { this._sfSelect.setAttribute("disabled", true); } else { this._sfSelect?.removeAttribute("disabled"); } } initListeners = () => { if(this.mode == "admin") { this._SfNewC.querySelector('#button-new').addEventListener('click', ()=> { this.showNew(); }) this._SfNewC.querySelector('#button-view').addEventListener('click', ()=> { this.showView(); this.populateList(); }) this._SfNewC.querySelector('.input-new').addEventListener('keyup', ()=> { const name = this._SfNewC.querySelector('.input-new').value; if(Util.validateName(name)) { this._SfNewC.querySelector('#button-submit-new').removeAttribute('disabled'); } else { this._SfNewC.querySelector('#button-submit-new').setAttribute('disabled', true); } }); this._SfNewC.querySelector('#button-submit-new').addEventListener('click', async ()=> { this.clearMessages(); const selectedValue = (document.querySelector('.input-parent-select') as SfISelect).selectedValues()[0]; const body = {"name": this._SfNewC.querySelector('.input-new').value, "fk": selectedValue}; const authorization = btoa(Util.readCookie('email') + ":" + Util.readCookie('accessToken')); const xhr : any = (await this.prepareXhr(body, "https://"+this.apiId+"/create", this._SfLoader, authorization)) as any; this._SfLoader.innerHTML = ''; if(xhr.status == 200) { this.setSuccess('Operation Successful!'); setTimeout(() => { this.clearMessages(); this._SfNewC.querySelector('.input-new').value = ""; }, 1000); } else { const jsonRespose = JSON.parse(xhr.responseText); this.setError(jsonRespose.error); setTimeout(() => { this.clearMessages(); }, 5000); } }); this._sfSlottedFk[0].addEventListener('valueChanged', () => { this.populateList(); }) } } loadMode = async () => { if(this.mode == "text") { this.selectedTextPhrase = await this.fetchList(); } else { this.initState(); this.initListeners(); if(this.mode != "admin") { this.populateList(); } } } constructor() { super(); } protected override firstUpdated(_changedProperties: PropertyValueMap | Map): void { this.loadMode(); } override connectedCallback() { super.connectedCallback() } override render() { if(this.mode == "admin") { return html`

${this.name}

Admin





`; } else if(this.mode == "text") { return html`
${this.selectedTextPhrase}
`; } else if(this.mode == "multi") { return html`
`; } else { return html`
`; } } } declare global { interface HTMLElementTagNameMap { 'sf-i-sub-select': SfISubSelect; } }
IdNameAction
'; innerHTML += values[i].id; innerHTML += ''; innerHTML += '' + values[i].name + ''; innerHTML += ''; innerHTML += ''; innerHTML += ''; innerHTML += ''; innerHTML += ''; innerHTML += ''; innerHTML += ''; innerHTML += ''; innerHTML += '