kempo 2.10.2 → 2.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/components/Table.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import Component from"./Component.js";import{toTitleCase}from"../utils/string.js";import{onEvent,offEvent,dispatchEvent}from"../utils/element.js";const selected=Symbol("selected"),hidden=Symbol("hidden"),index=Symbol("index"),editing=Symbol("editing");export default class Table extends Component{constructor({records:e=[],fields:t=[],filters:s=[],enablePages:i=!1,pageSize:r=50,pageSizeOptions:n=[10,25,50,100,500],currentPage:o=1,enableSelection:d=!1,enableSorting:l=!1}={}){super(),this.registerAttributes({enablePages:i,pageSize:r,currentPage:o,pageSizeOptions:n,enableSelection:d,enableSorting:l}),this.registerProps({fields:t,records:e,filters:s,sort:[],columnSizes:{},fetchPending:!1})}async render(e){return!!await super.render(e)&&(this.setData({records:this.records,fields:this.fields,filters:this.filters}),!0)}renderFields(){if(!this.rendered)return;this.calculateColumnSizes(),this.hasTopControls()?this.setAttribute("top-controls","true"):this.removeAttribute("top-controls"),this.hasBottomControls()?this.setAttribute("bottom-controls","true"):this.removeAttribute("bottom-controls");const e=this.shadowRoot.getElementById("fields");if(e.innerHTML="",this.enableSelection){const t=document.createElement("div");t.classList.add("field","controls","cell","field-select"),t.style.width="40px",t.innerHTML='<input type="checkbox" id="select-all" />',e.appendChild(t)}if(this.hasBeforeControls()){const t=document.createElement("div");t.classList.add("field","cell","field-before-controls"),t.style.width=this.columnSizes.beforeControls+"px",e.appendChild(t)}if(this.fields.forEach((({name:t,label:s,hidden:i})=>{if(i)return;const r=document.createElement("div");if(r.classList.add("field","cell"),r.style.width=this.columnSizes[t]+"px",r.innerHTML=s,this.enableSorting){r.style.cursor="pointer";const e=this.sort.find((e=>e.name===t));if(e){r.classList.add(e.asc?"sort-asc":"sort-desc");const t=document.createElement("k-icon");t.classList.add("icon-sort"),t.setAttribute("name",e.asc?"arrow-down":"arrow-up"),r.appendChild(t)}r.addEventListener("click",(()=>{const e=this.sort.find((e=>e.name===t)),s=!e||!e.asc;this.sortBy(t,s)}))}e.appendChild(r)})),this.hasAfterControls()){const t=document.createElement("div");t.classList.add("field","cell","field-after-controls"),t.style.width=this.columnSizes.afterControls+"px",e.appendChild(t)}if(e.style.width=this.shadowRoot.getElementById("top").style.width=this.shadowRoot.getElementById("bottom").style.width=this.shadowRoot.getElementById("records").style.width=this.columnSizes.total+"px",this.enableSelection){const e=this.shadowRoot.getElementById("select-all");e.addEventListener("change",(()=>{e.checked?this.selectAllOnPage():this.deselectAllOnPage()})),onEvent(this,"selectionChange pageChange",(()=>{this.allOnPageSelected()?e.checked=!0:e.checked=!1}))}}renderRecords(){if(!this.rendered)return;const e=this.shadowRoot.getElementById("records");e.innerHTML="";let t=this.getDisplayedRecords(),s=0,i=this.pageSize;this.enablePages&&(s=(this.currentPage-1)*this.pageSize,i=s+this.pageSize,t=t.slice(s,i));let r,n=0;t.forEach(((t,i)=>{if(null!==t)e.appendChild(this.renderRecord(t));else{r||(r=s+i),n++;const t=document.createElement("div");t.classList.add("record","fetching"),t.innerHTML='<div class="cell">Loading...</div>',e.appendChild(t)}})),r&&!this.fetchPending&&dispatchEvent(this,"fetchRecords",{start:r,count:n})}renderRecord(e){const t=document.createElement("div");if(t.classList.add("record"),t.dataset.index=e[index],this.enableSelection){const s=document.createElement("div");s.style.width="40px",s.classList.add("cell","selection","controls");const i=document.createElement("input");i.type="checkbox",i.checked=e[selected],i.addEventListener("change",(t=>{e[selected]=!!t.target.checked,dispatchEvent(this,"selectionChange")})),s.appendChild(i),t.appendChild(s)}return this.hasBeforeControls()&&t.appendChild(this.renderBeforeControls()),this.fields.forEach((({name:s,formatter:i,calculator:r,type:n,editor:o,hidden:d})=>{if(d)return;const l=document.createElement("div");l.classList.add("cell"),l.dataset.field=s,l.style.width=this.columnSizes[s]+"px";let a=e[s]||"";if(e[editing])if(t.setAttribute("editing",!0),r)l.appendChild(Table.editors.calculated(r(e,this)));else if(o)l.appendChild(o(a));else{let e=Table.editors[n||typeof a];e||(e=Table.editors.string),l.appendChild(e(a))}else l.innerHTML=r?r(e,this):i?i(a):a;t.appendChild(l)})),this.hasAfterControls()&&t.appendChild(this.renderAfterControls()),t}renderBeforeControls(){const e=document.createElement("div");return e.style.width=this.columnSizes.beforeControls+"px",e.classList.add("cell","controls","controls-before"),this.querySelectorAll('[slot="before"]').forEach((t=>{e.appendChild(t.cloneNode(!0))})),e}renderAfterControls(){const e=document.createElement("div");return e.style.width=this.columnSizes.afterControls+"px",e.classList.add("cell","controls","controls-after"),this.querySelectorAll('[slot="after"]').forEach((t=>{e.appendChild(t.cloneNode(!0))})),e}hasBeforeControls(){return!!this.querySelector('[slot="before"]')}hasAfterControls(){return!!this.querySelector('[slot="after"]')}hasTopControls(){return!!this.querySelector('[slot="top"]')}hasBottomControls(){return!!this.querySelector(":scope > :not([slot])")}editRecord(e){e[editing]=!0;const t=this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`),s=this.renderRecord(e);t.replaceWith(s),dispatchEvent(this,"editingChange")}saveEditedRecord(e){e[editing]=!1;const t=this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`);t.querySelectorAll(".cell").forEach((t=>{const s=t.dataset.field,i=this.fields.find((e=>e.name===s));if(i&&!i.calculator){const i=1===t.children.length?t.firstChild:t.querySelector("input, select");i&&(e[s]=i.value)}})),t.replaceWith(this.renderRecord(e))}cancelEditedRecord(e){e[editing]=!1,this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`).replaceWith(this.renderRecord(e))}recordIsEditing(e){return e[editing]}getCurrentPage(){return this.currentPage}getTotalPages(){return Math.ceil(this.getDisplayedRecords().length/this.pageSize)}setPage(e){e<1||e>this.getTotalPages()||(this.currentPage=e,this.renderRecords(),dispatchEvent(this,"pageChange"))}firstPage(){1!==this.currentPage&&this.setPage(1)}nextPage(){this.currentPage<this.getTotalPages()&&this.setPage(this.currentPage+1)}prevPage(){this.currentPage>1&&this.setPage(this.currentPage-1)}lastPage(){this.currentPage!==this.getTotalPages()&&this.setPage(this.getTotalPages())}setPageSize(e){this.pageSize=e,this.currentPage=1,this.renderRecords(),dispatchEvent(this,"pageSizeChange")}setData({records:e=!1,fields:t=!1,pageSize:s=!1,pageSizeOptions:i=!1,currentPage:r=!1,enableSelection:n}={}){let o=!1,d=this.getTotalPages(),l=this.currentPage;e&&(this.records=e.map((e=>({...e}))),this.records.forEach(((e,t)=>{e[index]=t,e[selected]=!1,e[hidden]=!1,e[editing]=!1})),this.fields=t||Table.extractFieldsFromRecords(this.records),o=!0),s&&(this.pageSize=s,o=!0),i&&(this.pageSizeOptions=i),r&&(this.currentPage=r,o=!0),void 0!==n&&(this.enableSelection=n,o=!0),o&&(this.renderFields(),this.renderRecords());const a=this.getTotalPages();a!==d&&dispatchEvent(this,"pageCountChanged",{totalPages:this.getTotalPages()}),l>a&&this.setPage(a)}setRecords(e,t){let s=this.getTotalPages(),i=this.currentPage;this.records=e.map((e=>({...e}))),this.records.forEach(((e,t)=>{e[index]=t,e[selected]=!1,e[hidden]=!1,e[editing]=!1})),this.fields=t||Table.extractFieldsFromRecords(this.records),this.renderFields(),this.renderRecords(),dispatchEvent(this,"recordsSet",{records:e});const r=this.getTotalPages();r!==s&&dispatchEvent(this,"pageCountChanged",{totalPages:this.getTotalPages()}),i>r&&this.setPage(r)}setupFetchRecords(e,t){const s=this.records.length;s<e&&(this.records.length=e,this.records.fill(null,s)),onEvent(this,"fetchRecords",(async e=>{if(this.fetchPending)return;this.fetchPending=!0;const{start:s,count:i}=e.detail,r=await t(s,i);this.records.splice(s,r.length,...r),this.fetchPending=!1,this.renderRecords()}))}addRecord(e){e[selected]=!1,e[hidden]=!1,e[index]=this.records.length,this.records.push(e),this.renderRecords(),dispatchEvent(this,"recordAdded",{record:e})}updateRecord(e,t){let s=!1,i=this.records.find((t=>t===e));if(i||void 0===e[index]||(i=this.records[e[index]]),Object.keys(t).forEach((e=>{i.hasOwnProperty(e)&&(i[e]=t[e],s=!0)})),s){const e=(this.currentPage-1)*this.pageSize,t=e+this.pageSize;(!this.enablePages||i[index]>=e&&i[index]<t)&&this.renderRecords()}}deleteRecord(e){let t=this.records.find((t=>t===e)),s=this.getTotalPages();if(t||void 0===e[index]||(t=this.records[e[index]]),t){const e=this.records.indexOf(t);this.records.splice(e,1),this.records.forEach(((e,t)=>{e[index]=t})),this.renderRecords(),dispatchEvent(this,"selectionChange"),dispatchEvent(this,"recordDeleted",{index:e});const i=this.getTotalPages();this.currentPage>i&&this.setPage(i),i!==s&&dispatchEvent(this,"pageCountChanged",{totalPages:i})}}deleteSelected(){let e=this.getTotalPages();this.getSelectedRecords().forEach((e=>{let t=this.records.find((t=>t===e));if(t||void 0===e[index]||(t=this.records[e[index]]),t){const e=this.records.indexOf(t);this.records.splice(e,1)}})),this.records.forEach(((e,t)=>{e[index]=t})),this.renderRecords();const t=this.getTotalPages();this.currentPage>t&&this.setPage(t),t!==e&&dispatchEvent(this,"pageCountChanged",{totalPages:t}),dispatchEvent(this,"selectionChange")}getSelectedRecords(){return this.records.filter((e=>e[selected]))}selectAllOnPage(){const e=(this.currentPage-1)*this.pageSize,t=Math.min(e+this.pageSize,this.records.length);for(let s=e;s<t;s++)this.records[s][selected]=!0;this.renderRecords(),setTimeout((()=>{dispatchEvent(this,"selectionChange")}),0)}deselectAllOnPage(){const e=(this.currentPage-1)*this.pageSize,t=Math.min(e+this.pageSize,this.records.length);for(let s=e;s<t;s++)this.records[s][selected]=!1;this.renderRecords(),setTimeout((()=>{dispatchEvent(this,"selectionChange")}),0)}allOnPageSelected(){const e=(this.currentPage-1)*this.pageSize,t=Math.min(e+this.pageSize,this.records.length);for(let s=e;s<t;s++)if(!this.records[s][selected])return!1;return!0}sortBy(e,t=!0){this.sort=this.sort.filter((t=>t.name!==e)),this.sort.push({name:e,asc:t}),this.renderFields(),this.renderRecords()}hideRecord(e){let t=this.records.find((t=>t===e));t||void 0===e[index]||(t=this.records[e[index]]),t&&(t[hidden]=!0,this.renderRecords(),dispatchEvent(this,"recordHidden"))}showRecord(e){let t=this.records.find((t=>t===e));t||void 0===e[index]||(t=this.records[e[index]]),t&&(t[hidden]=!1,this.renderRecords(),dispatchEvent(this,"recordShown"))}showAllRecords(){this.records.forEach((e=>{e[hidden]=!1})),this.filters.length&&(this.filters=[],dispatchEvent(this,"filterRemoved filterChange")),this.renderRecords(),dispatchEvent(this,"recordShown allRecordsShown")}addFilter(e,t,s){this.filters.push({field:e,condition:t,value:s}),dispatchEvent(this,"filterAdded filterChange"),this.renderRecords()}removeFilter(e,t,s,i=!0){const r=this.filters.findIndex((i=>i.field===e&&i.condition===t&&i.value===s));-1!==r&&(this.records.forEach((i=>{this.testFilter(i,e,t,s)||(i[hidden]=!1)})),this.filters.splice(r,1),dispatchEvent(this,"filterRemoved filterChange"),i&&this.renderRecords())}testFilter(e,t,s,i){const r=e[t];switch(s){case"equals":return r===i;case"not-equals":return r!==i;case"contains":return r.includes(i);case"not-contains":return!r.includes(i);case"greater-than":return r>i;case"less-than":return r<i;case"greater-than-or-equal":return r>=i;case"less-than-or-equal":return r<=i;default:return!0}}removeAllFilters(){this.filters.length&&(this.filters.forEach((({field:e,condition:t,value:s})=>{this.removeFilter(e,t,s,!1)})),this.renderRecords())}search(e){const t=e.trim().toLowerCase();let s=!1;this.records.forEach((e=>{if(e[hidden])return;let i=!1;this.fields.forEach((({name:s})=>{(e[s]?.toString().toLowerCase()||"").includes(t)&&(i=!0)})),e[hidden]!==!i&&(e[hidden]=!i,s=!0)})),s&&(dispatchEvent(this,"recordHidden"),this.renderRecords()),dispatchEvent(this,"search",{term:e})}getDisplayedRecords(){this.filters.forEach((({field:e,condition:t,value:s})=>{this.records.forEach((i=>{this.testFilter(i,e,t,s)||(i[hidden]=!0)}))}));let e=this.records.filter((e=>null===e||!e[hidden]));return this.sort.forEach((({name:t,asc:s})=>{e.sort(((e,i)=>e[t]<i[t]?s?-1:1:e[t]>i[t]?s?1:-1:0))})),e}getHiddenRecords(){return this.records.filter((e=>e[hidden]))}calculateColumnSizes(){return this.columnSizes={},this.columnSizes.total=0,this.enableSelection&&(this.columnSizes.total+=40),this.columnSizes.beforeControls=Array.from(this.querySelectorAll('[slot="before"]')).reduce(((e,t)=>e+(t.maxWidth||40)),0),this.columnSizes.afterControls=Array.from(this.querySelectorAll('[slot="after"]')).reduce(((e,t)=>e+(t.maxWidth||40)),0),this.hasBeforeControls()&&(this.columnSizes.total+=this.columnSizes.beforeControls),this.hasAfterControls()&&(this.columnSizes.total+=this.columnSizes.afterControls),this.fields.forEach((e=>{if(e.size)this.columnSizes[e.name]=e.size;else{let t=0;this.records.slice(0,100).forEach((s=>{let i=s[e.name];e.calculator&&(i=e.calculator(s,this)),e.formatter&&(i=e.formatter(i)),i&&i.toString().length>t&&(t=i.toString().length)})),this.columnSizes[e.name]=Math.max(10*t+32,128),e.hidden||(this.columnSizes.total+=this.columnSizes[e.name])}})),this.columnSizes}setFieldHiddenState(e,t){const s=this.fields.find((t=>t.name===e));s&&(s.hidden=t,this.calculateColumnSizes(),this.renderFields(),this.renderRecords(),dispatchEvent(this,"fieldVisibilityChanged "+(t?"fieldHidden":"fieldShown"),{field:s}))}hideField(e){this.setFieldHiddenState(e,!0)}showField(e){this.setFieldHiddenState(e,!1)}reorderFields(e){const t=[];e.forEach((e=>{const s=this.fields.find((t=>t.name===e));s&&t.push(s)})),this.fields=t,this.renderFields(),this.renderRecords()}get shadowTemplate(){return`\n <div id="wrapper">\n <div id="top"><slot name="top"></slot></div>\n <div id="table">\n <div id="fields"></div>\n <div id="records"></div>\n </div>\n <div id="bottom">${super.shadowTemplate}</div>\n </div>\n <div style="display: none">\n <slot name="before"></slot>\n <slot name="after"></slot>\n </div>\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n display: block;\n width: 100%;\n overflow: auto;\n margin-bottom: var(--spacer);\n }\n #wrapper {\n width: min-content;\n border: 1px solid var(--c_border);\n border-radius: var(--radius);\n }\n #table {\n width: min-content;\n }\n #fields,\n .record {\n display: flex;\n }\n #fields {\n background-color: var(--c_bg__alt);\n border-bottom: 1px solid var(--c_border);\n }\n .record:not([editing="true"]) .cell:not(.controls),\n #fields .cell:not(.controls) {\n padding: calc(0.5 * var(--spacer)) var(--spacer);\n }\n .cell:not(:first-child) {\n border-left: 1px solid var(--c_border);\n }\n .record:not(:last-child) .cell {\n border-bottom: 1px solid var(--c_border);\n }\n #top, #bottom {\n display: flex;\n width: 100%;\n }\n #top slot {\n display: block;\n width: 100%;\n border-bottom: 1px solid var(--c_border);\n }\n #bottom slot {\n display: block;\n width: 100%;\n border-top: 1px solid var(--c_border);\n }\n :host(:not([top-controls])) #top,\n :host(:not([bottom-controls])) #bottom {\n display: none;\n }\n .field-select,\n .selection {\n display: flex;\n justify-content: center;\n align-items: center;\n }\n .field-select input,\n .selection input {\n width: 1.25rem;\n height: 1.25rem;\n }\n .icon-sort {\n float: right;\n opacity: 0.5;\n }\n `}static extractFieldsFromRecords(e,t=100){const s=new Set;return e.slice(0,t).forEach((e=>{Object.keys(e).forEach((e=>s.add(e)))})),[...s].map((e=>({name:e,label:toTitleCase(e)})))}static format(e){return(Array.isArray(e)?Table.formatters.array:Table.formatters[typeof e])(e)}static formatters={string:e=>e,number:e=>`${e}`,date:e=>e.toLocaleDateString(),boolean:e=>e?"True":"False",array:e=>e.map((e=>Table.format(e))).join(", "),undefined:e=>"",null:e=>"<code>null</code>"};static editors={string:e=>{const t=document.createElement("input");return t.value=e,t},number:e=>{const t=document.createElement("input");return t.type="number",t.value=e,t},date:e=>{const t=document.createElement("input");return t.type="date",t.value=e,t},boolean:e=>{const t=document.createElement("select");return t.innerHTML=`\n <option value="true" ${e?"selected":""}>True</option>\n <option value="false" ${e?"":"selected"}>False</option>\n `,t.value=e,t},calculated:e=>{const t=document.createElement("input");return t.disabled=!0,t.value=e,t}}}window.customElements.define("k-table",Table);
|
|
1
|
+
import Component from"./Component.js";import{toTitleCase}from"../utils/string.js";import{onEvent,offEvent,dispatchEvent}from"../utils/element.js";const selected=Symbol("selected"),hidden=Symbol("hidden"),index=Symbol("index"),editing=Symbol("editing");export default class Table extends Component{constructor({records:e=[],fields:t=[],filters:s=[],enablePages:i=!1,pageSize:r=50,pageSizeOptions:n=[10,25,50,100,500],currentPage:o=1,enableSelection:d=!1,enableSorting:l=!1}={}){super(),this.registerAttributes({enablePages:i,pageSize:r,currentPage:o,pageSizeOptions:n,enableSelection:d,enableSorting:l}),this.registerProps({fields:t,records:e,filters:s,sort:[],columnSizes:{},fetchPending:!1})}async render(e){return!!await super.render(e)&&(this.setData({records:this.records,fields:this.fields,filters:this.filters}),!0)}renderFields(){if(!this.rendered)return;this.calculateColumnSizes(),this.hasTopControls()?this.setAttribute("top-controls","true"):this.removeAttribute("top-controls"),this.hasBottomControls()?this.setAttribute("bottom-controls","true"):this.removeAttribute("bottom-controls");const e=this.shadowRoot.getElementById("fields");if(e.innerHTML="",this.enableSelection){const t=document.createElement("div");t.classList.add("field","controls","cell","field-select"),t.style.width="40px",t.innerHTML='<input type="checkbox" id="select-all" />',e.appendChild(t)}if(this.hasBeforeControls()){const t=document.createElement("div");t.classList.add("field","cell","field-before-controls"),t.style.width=this.columnSizes.beforeControls+"px",e.appendChild(t)}if(this.fields.forEach((({name:t,label:s,hidden:i})=>{if(i)return;const r=document.createElement("div");if(r.classList.add("field","cell"),r.style.width=this.columnSizes[t]+"px",r.innerHTML=s,this.enableSorting){r.style.cursor="pointer";const e=this.sort.find((e=>e.name===t));if(e){r.classList.add(e.asc?"sort-asc":"sort-desc");const t=document.createElement("k-icon");t.classList.add("icon-sort"),t.setAttribute("name",e.asc?"arrow-down":"arrow-up"),r.appendChild(t)}r.addEventListener("click",(()=>{const e=this.sort.find((e=>e.name===t)),s=!e||!e.asc;this.sortBy(t,s)}))}e.appendChild(r)})),this.hasAfterControls()){const t=document.createElement("div");t.classList.add("field","cell","field-after-controls"),t.style.width=this.columnSizes.afterControls+"px",e.appendChild(t)}if(e.style.width=this.shadowRoot.getElementById("top").style.width=this.shadowRoot.getElementById("bottom").style.width=this.shadowRoot.getElementById("records").style.width=this.columnSizes.total+"px",this.enableSelection){const e=this.shadowRoot.getElementById("select-all");e.addEventListener("change",(()=>{e.checked?this.selectAllOnPage():this.deselectAllOnPage()})),onEvent(this,"selectionChange pageChange",(()=>{this.allOnPageSelected()?e.checked=!0:e.checked=!1}))}}renderRecords(){if(!this.rendered)return;const e=this.shadowRoot.getElementById("records");e.innerHTML="";let t=this.getDisplayedRecords(),s=0,i=this.pageSize;this.enablePages&&(s=(this.currentPage-1)*this.pageSize,i=s+this.pageSize,t=t.slice(s,i));let r,n=0;t.forEach(((t,i)=>{if(null!==t)e.appendChild(this.renderRecord(t));else{r||(r=s+i),n++;const t=document.createElement("div");t.classList.add("record","fetching"),t.innerHTML='<div class="cell">Loading...</div>',e.appendChild(t)}})),r&&!this.fetchPending&&dispatchEvent(this,"fetchRecords",{start:r,count:n})}renderRecord(e){const t=document.createElement("div");if(t.classList.add("record"),t.dataset.index=e[index],this.enableSelection){const s=document.createElement("div");s.style.width="40px",s.classList.add("cell","selection","controls");const i=document.createElement("input");i.type="checkbox",i.checked=e[selected],i.addEventListener("change",(t=>{e[selected]=!!t.target.checked,dispatchEvent(this,"selectionChange")})),s.appendChild(i),t.appendChild(s)}return this.hasBeforeControls()&&t.appendChild(this.renderBeforeControls()),this.fields.forEach((({name:s,formatter:i,calculator:r,type:n,editor:o,hidden:d})=>{if(d)return;const l=document.createElement("div");l.classList.add("cell"),l.dataset.field=s,l.style.width=this.columnSizes[s]+"px";let a=e[s]||"";if(e[editing])if(t.setAttribute("editing",!0),r)l.appendChild(Table.editors.calculated(r(e,this)));else if(o)l.appendChild(o(a));else{let e=Table.editors[n||typeof a];e||(e=Table.editors.string),l.appendChild(e(a))}else l.innerHTML=r?r(e,this):i?i(a):a;t.appendChild(l)})),this.hasAfterControls()&&t.appendChild(this.renderAfterControls()),t}renderBeforeControls(){const e=document.createElement("div");return e.style.width=this.columnSizes.beforeControls+"px",e.classList.add("cell","controls","controls-before"),this.querySelectorAll('[slot="before"]').forEach((t=>{e.appendChild(t.cloneNode(!0))})),e}renderAfterControls(){const e=document.createElement("div");return e.style.width=this.columnSizes.afterControls+"px",e.classList.add("cell","controls","controls-after"),this.querySelectorAll('[slot="after"]').forEach((t=>{e.appendChild(t.cloneNode(!0))})),e}hasBeforeControls(){return!!this.querySelector('[slot="before"]')}hasAfterControls(){return!!this.querySelector('[slot="after"]')}hasTopControls(){return!!this.querySelector('[slot="top"]')}hasBottomControls(){return!!this.querySelector(":scope > :not([slot])")}editRecord(e){e[editing]=!0;const t=this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`),s=this.renderRecord(e);t.replaceWith(s),dispatchEvent(this,"editingChange")}saveEditedRecord(e){e[editing]=!1;const t=this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`);t.querySelectorAll(".cell").forEach((t=>{const s=t.dataset.field,i=this.fields.find((e=>e.name===s));if(i&&!i.calculator){const i=1===t.children.length?t.firstChild:t.querySelector("input, select");i&&(e[s]=i.value)}})),t.replaceWith(this.renderRecord(e))}cancelEditedRecord(e){e[editing]=!1,this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`).replaceWith(this.renderRecord(e))}recordIsEditing(e){return e[editing]}getCurrentPage(){return this.currentPage}getTotalPages(){return Math.ceil(this.getDisplayedRecords().length/this.pageSize)}setPage(e){e<1||e>this.getTotalPages()||(this.currentPage=e,this.renderRecords(),dispatchEvent(this,"pageChange"))}firstPage(){1!==this.currentPage&&this.setPage(1)}nextPage(){this.currentPage<this.getTotalPages()&&this.setPage(this.currentPage+1)}prevPage(){this.currentPage>1&&this.setPage(this.currentPage-1)}lastPage(){this.currentPage!==this.getTotalPages()&&this.setPage(this.getTotalPages())}setPageSize(e){this.pageSize=e,this.currentPage=1,this.renderRecords(),dispatchEvent(this,"pageSizeChange")}setData({records:e=!1,fields:t=!1,pageSize:s=!1,pageSizeOptions:i=!1,currentPage:r=!1,enableSelection:n}={}){let o=!1,d=this.getTotalPages(),l=this.currentPage;e&&(this.records=e.map((e=>({...e}))),this.records.forEach(((e,t)=>{e[index]=t,e[selected]=!1,e[hidden]=!1,e[editing]=!1})),this.fields=t||Table.extractFieldsFromRecords(this.records),o=!0),s&&(this.pageSize=s,o=!0),i&&(this.pageSizeOptions=i),r&&(this.currentPage=r,o=!0),void 0!==n&&(this.enableSelection=n,o=!0),o&&(this.renderFields(),this.renderRecords());const a=this.getTotalPages();a!==d&&dispatchEvent(this,"pageCountChanged",{totalPages:this.getTotalPages()}),l>a&&this.setPage(a)}setRecords(e,t){let s=this.getTotalPages(),i=this.currentPage;this.records=e.map((e=>({...e}))),this.records.forEach(((e,t)=>{e[index]=t,e[selected]=!1,e[hidden]=!1,e[editing]=!1})),this.fields=t||Table.extractFieldsFromRecords(this.records),this.renderFields(),this.renderRecords(),dispatchEvent(this,"recordsSet",{records:e});const r=this.getTotalPages();r!==s&&dispatchEvent(this,"pageCountChanged",{totalPages:this.getTotalPages()}),i>r&&this.setPage(r)}setupFetchRecords(e,t){const s=this.records.length;s<e&&(this.records.length=e,this.records.fill(null,s)),onEvent(this,"fetchRecords",(async e=>{if(this.fetchPending)return;this.fetchPending=!0;const{start:s,count:i}=e.detail,r=await t(s,i);this.records.splice(s,r.length,...r),this.fetchPending=!1,this.renderRecords()}))}addRecord(e){e[selected]=!1,e[hidden]=!1,e[index]=this.records.length,this.records.push(e),this.renderRecords(),dispatchEvent(this,"recordAdded",{record:e})}updateRecord(e,t){let s=!1,i=this.records.find((t=>t===e));if(i||void 0===e[index]||(i=this.records[e[index]]),Object.keys(t).forEach((e=>{i.hasOwnProperty(e)&&(i[e]=t[e],s=!0)})),s){const e=(this.currentPage-1)*this.pageSize,t=e+this.pageSize;(!this.enablePages||i[index]>=e&&i[index]<t)&&this.renderRecords()}}deleteRecord(e){let t=this.records.find((t=>t===e)),s=this.getTotalPages();if(t||void 0===e[index]||(t=this.records[e[index]]),t){const e=this.records.indexOf(t);this.records.splice(e,1),this.records.forEach(((e,t)=>{e[index]=t})),this.renderRecords(),dispatchEvent(this,"selectionChange"),dispatchEvent(this,"recordDeleted",{index:e});const i=this.getTotalPages();this.currentPage>i&&this.setPage(i),i!==s&&dispatchEvent(this,"pageCountChanged",{totalPages:i})}}deleteSelected(){let e=this.getTotalPages();this.getSelectedRecords().forEach((e=>{let t=this.records.find((t=>t===e));if(t||void 0===e[index]||(t=this.records[e[index]]),t){const e=this.records.indexOf(t);this.records.splice(e,1)}})),this.records.forEach(((e,t)=>{e[index]=t})),this.renderRecords();const t=this.getTotalPages();this.currentPage>t&&this.setPage(t),t!==e&&dispatchEvent(this,"pageCountChanged",{totalPages:t}),dispatchEvent(this,"selectionChange")}getSelectedRecords(){return this.records.filter((e=>e[selected]))}selectAllOnPage(){const e=(this.currentPage-1)*this.pageSize,t=Math.min(e+this.pageSize,this.records.length);for(let s=e;s<t;s++)this.records[s][selected]=!0;this.renderRecords(),setTimeout((()=>{dispatchEvent(this,"selectionChange")}),0)}deselectAllOnPage(){const e=(this.currentPage-1)*this.pageSize,t=Math.min(e+this.pageSize,this.records.length);for(let s=e;s<t;s++)this.records[s][selected]=!1;this.renderRecords(),setTimeout((()=>{dispatchEvent(this,"selectionChange")}),0)}allOnPageSelected(){const e=(this.currentPage-1)*this.pageSize,t=Math.min(e+this.pageSize,this.records.length);for(let s=e;s<t;s++)if(!this.records[s][selected])return!1;return!0}sortBy(e,t=!0){this.sort=this.sort.filter((t=>t.name!==e)),this.sort.push({name:e,asc:t}),this.renderFields(),this.renderRecords()}hideRecord(e){let t=this.records.find((t=>t===e));t||void 0===e[index]||(t=this.records[e[index]]),t&&(t[hidden]=!0,this.renderRecords(),dispatchEvent(this,"recordHidden"))}showRecord(e){let t=this.records.find((t=>t===e));t||void 0===e[index]||(t=this.records[e[index]]),t&&(t[hidden]=!1,this.renderRecords(),dispatchEvent(this,"recordShown"))}showAllRecords(){this.records.forEach((e=>{e[hidden]=!1})),this.filters.length&&(this.filters=[],dispatchEvent(this,"filterRemoved filterChange")),this.renderRecords(),dispatchEvent(this,"recordShown allRecordsShown")}addFilter(e,t,s){this.filters.push({field:e,condition:t,value:s}),dispatchEvent(this,"filterAdded filterChange"),this.renderRecords()}removeFilter(e,t,s,i=!0){const r=this.filters.findIndex((i=>i.field===e&&i.condition===t&&i.value===s));-1!==r&&(this.records.forEach((i=>{this.testFilter(i,e,t,s)||(i[hidden]=!1)})),this.filters.splice(r,1),dispatchEvent(this,"filterRemoved filterChange"),i&&this.renderRecords())}testFilter(e,t,s,i){const r=e[t];switch(s){case"equals":return r===i;case"not-equals":return r!==i;case"contains":return r.includes(i);case"not-contains":return!r.includes(i);case"greater-than":return r>i;case"less-than":return r<i;case"greater-than-or-equal":return r>=i;case"less-than-or-equal":return r<=i;default:return!0}}removeAllFilters(){this.filters.length&&(this.filters.forEach((({field:e,condition:t,value:s})=>{this.removeFilter(e,t,s,!1)})),this.renderRecords())}search(e){const t=e.trim().toLowerCase();let s=!1;this.records.forEach((e=>{if(e[hidden])return;let i=!1;this.fields.forEach((({name:s})=>{(e[s]?.toString().toLowerCase()||"").includes(t)&&(i=!0)})),e[hidden]!==!i&&(e[hidden]=!i,s=!0)})),s&&(dispatchEvent(this,"recordHidden"),this.renderRecords()),dispatchEvent(this,"search",{term:e})}getDisplayedRecords(){this.filters.forEach((({field:e,condition:t,value:s})=>{this.records.forEach((i=>{this.testFilter(i,e,t,s)||(i[hidden]=!0)}))}));let e=this.records.filter((e=>null===e||!e[hidden]));return this.sort.forEach((({name:t,asc:s})=>{e.sort(((e,i)=>e[t]<i[t]?s?-1:1:e[t]>i[t]?s?1:-1:0))})),e}getHiddenRecords(){return this.records.filter((e=>e[hidden]))}calculateColumnSizes(){return this.columnSizes={},this.columnSizes.total=0,this.enableSelection&&(this.columnSizes.total+=40),this.columnSizes.beforeControls=Array.from(this.querySelectorAll('[slot="before"]')).reduce(((e,t)=>e+(t.maxWidth||40)),0),this.columnSizes.afterControls=Array.from(this.querySelectorAll('[slot="after"]')).reduce(((e,t)=>e+(t.maxWidth||40)),0),this.hasBeforeControls()&&(this.columnSizes.total+=this.columnSizes.beforeControls),this.hasAfterControls()&&(this.columnSizes.total+=this.columnSizes.afterControls),this.fields.forEach((e=>{if(e.size)this.columnSizes[e.name]=e.size,this.columnSizes.total+=e.size;else{let t=0;this.records.slice(0,100).forEach((s=>{let i=s[e.name];e.calculator&&(i=e.calculator(s,this)),e.formatter&&(i=e.formatter(i)),i&&i.toString().length>t&&(t=i.toString().length)})),this.columnSizes[e.name]=Math.max(10*t+32,128),e.hidden||(this.columnSizes.total+=this.columnSizes[e.name])}})),this.columnSizes}setFieldHiddenState(e,t){const s=this.fields.find((t=>t.name===e));s&&(s.hidden=t,this.calculateColumnSizes(),this.renderFields(),this.renderRecords(),dispatchEvent(this,"fieldVisibilityChanged "+(t?"fieldHidden":"fieldShown"),{field:s}))}hideField(e){this.setFieldHiddenState(e,!0)}showField(e){this.setFieldHiddenState(e,!1)}reorderFields(e){const t=[];e.forEach((e=>{const s=this.fields.find((t=>t.name===e));s&&t.push(s)})),this.fields=t,this.renderFields(),this.renderRecords()}get shadowTemplate(){return`\n <div id="wrapper">\n <div id="top"><slot name="top"></slot></div>\n <div id="table">\n <div id="fields"></div>\n <div id="records"></div>\n </div>\n <div id="bottom">${super.shadowTemplate}</div>\n </div>\n <div style="display: none">\n <slot name="before"></slot>\n <slot name="after"></slot>\n </div>\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n display: block;\n width: 100%;\n overflow: auto;\n margin-bottom: var(--spacer);\n }\n #wrapper {\n width: min-content;\n border: 1px solid var(--c_border);\n border-radius: var(--radius);\n }\n #table {\n width: min-content;\n }\n #fields,\n .record {\n display: flex;\n }\n #fields {\n background-color: var(--c_bg__alt);\n border-bottom: 1px solid var(--c_border);\n }\n .record:not([editing="true"]) .cell:not(.controls),\n #fields .cell:not(.controls) {\n padding: calc(0.5 * var(--spacer)) var(--spacer);\n }\n .cell:not(:first-child) {\n border-left: 1px solid var(--c_border);\n }\n .record:not(:last-child) .cell {\n border-bottom: 1px solid var(--c_border);\n }\n #top, #bottom {\n display: flex;\n width: 100%;\n }\n #top slot {\n display: block;\n width: 100%;\n border-bottom: 1px solid var(--c_border);\n }\n #bottom slot {\n display: block;\n width: 100%;\n border-top: 1px solid var(--c_border);\n }\n :host(:not([top-controls])) #top,\n :host(:not([bottom-controls])) #bottom {\n display: none;\n }\n .field-select,\n .selection {\n display: flex;\n justify-content: center;\n align-items: center;\n }\n .field-select input,\n .selection input {\n width: 1.25rem;\n height: 1.25rem;\n }\n .icon-sort {\n float: right;\n opacity: 0.5;\n }\n `}static extractFieldsFromRecords(e,t=100){const s=new Set;return e.slice(0,t).forEach((e=>{Object.keys(e).forEach((e=>s.add(e)))})),[...s].map((e=>({name:e,label:toTitleCase(e)})))}static format(e){return(Array.isArray(e)?Table.formatters.array:Table.formatters[typeof e])(e)}static formatters={string:e=>e,number:e=>`${e}`,date:e=>e.toLocaleDateString(),boolean:e=>e?"True":"False",array:e=>e.map((e=>Table.format(e))).join(", "),undefined:e=>"",null:e=>"<code>null</code>"};static editors={string:e=>{const t=document.createElement("input");return t.value=e,t},number:e=>{const t=document.createElement("input");return t.type="number",t.value=e,t},date:e=>{const t=document.createElement("input");return t.type="date",t.value=e,t},boolean:e=>{const t=document.createElement("select");return t.innerHTML=`\n <option value="true" ${e?"selected":""}>True</option>\n <option value="false" ${e?"":"selected"}>False</option>\n `,t.value=e,t},calculated:e=>{const t=document.createElement("input");return t.disabled=!0,t.value=e,t}}}window.customElements.define("k-table",Table);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import Component from"./Component.js";import{toTitleCase}from"../utils/string.js";import{onEvent,offEvent,dispatchEvent}from"../utils/element.js";const selected=Symbol("selected"),hidden=Symbol("hidden"),index=Symbol("index"),editing=Symbol("editing");export default class Table extends Component{constructor({records:e=[],fields:t=[],filters:s=[],enablePages:i=!1,pageSize:r=50,pageSizeOptions:n=[10,25,50,100,500],currentPage:o=1,enableSelection:d=!1,enableSorting:l=!1}={}){super(),this.registerAttributes({enablePages:i,pageSize:r,currentPage:o,pageSizeOptions:n,enableSelection:d,enableSorting:l}),this.registerProps({fields:t,records:e,filters:s,sort:[],columnSizes:{},fetchPending:!1})}async render(e){return!!await super.render(e)&&(this.setData({records:this.records,fields:this.fields,filters:this.filters}),!0)}renderFields(){if(!this.rendered)return;this.calculateColumnSizes(),this.hasTopControls()?this.setAttribute("top-controls","true"):this.removeAttribute("top-controls"),this.hasBottomControls()?this.setAttribute("bottom-controls","true"):this.removeAttribute("bottom-controls");const e=this.shadowRoot.getElementById("fields");if(e.innerHTML="",this.enableSelection){const t=document.createElement("div");t.classList.add("field","controls","cell","field-select"),t.style.width="40px",t.innerHTML='<input type="checkbox" id="select-all" />',e.appendChild(t)}if(this.hasBeforeControls()){const t=document.createElement("div");t.classList.add("field","cell","field-before-controls"),t.style.width=this.columnSizes.beforeControls+"px",e.appendChild(t)}if(this.fields.forEach((({name:t,label:s,hidden:i})=>{if(i)return;const r=document.createElement("div");if(r.classList.add("field","cell"),r.style.width=this.columnSizes[t]+"px",r.innerHTML=s,this.enableSorting){r.style.cursor="pointer";const e=this.sort.find((e=>e.name===t));if(e){r.classList.add(e.asc?"sort-asc":"sort-desc");const t=document.createElement("k-icon");t.classList.add("icon-sort"),t.setAttribute("name",e.asc?"arrow-down":"arrow-up"),r.appendChild(t)}r.addEventListener("click",(()=>{const e=this.sort.find((e=>e.name===t)),s=!e||!e.asc;this.sortBy(t,s)}))}e.appendChild(r)})),this.hasAfterControls()){const t=document.createElement("div");t.classList.add("field","cell","field-after-controls"),t.style.width=this.columnSizes.afterControls+"px",e.appendChild(t)}if(e.style.width=this.shadowRoot.getElementById("top").style.width=this.shadowRoot.getElementById("bottom").style.width=this.shadowRoot.getElementById("records").style.width=this.columnSizes.total+"px",this.enableSelection){const e=this.shadowRoot.getElementById("select-all");e.addEventListener("change",(()=>{e.checked?this.selectAllOnPage():this.deselectAllOnPage()})),onEvent(this,"selectionChange pageChange",(()=>{this.allOnPageSelected()?e.checked=!0:e.checked=!1}))}}renderRecords(){if(!this.rendered)return;const e=this.shadowRoot.getElementById("records");e.innerHTML="";let t=this.getDisplayedRecords(),s=0,i=this.pageSize;this.enablePages&&(s=(this.currentPage-1)*this.pageSize,i=s+this.pageSize,t=t.slice(s,i));let r,n=0;t.forEach(((t,i)=>{if(null!==t)e.appendChild(this.renderRecord(t));else{r||(r=s+i),n++;const t=document.createElement("div");t.classList.add("record","fetching"),t.innerHTML='<div class="cell">Loading...</div>',e.appendChild(t)}})),r&&!this.fetchPending&&dispatchEvent(this,"fetchRecords",{start:r,count:n})}renderRecord(e){const t=document.createElement("div");if(t.classList.add("record"),t.dataset.index=e[index],this.enableSelection){const s=document.createElement("div");s.style.width="40px",s.classList.add("cell","selection","controls");const i=document.createElement("input");i.type="checkbox",i.checked=e[selected],i.addEventListener("change",(t=>{e[selected]=!!t.target.checked,dispatchEvent(this,"selectionChange")})),s.appendChild(i),t.appendChild(s)}return this.hasBeforeControls()&&t.appendChild(this.renderBeforeControls()),this.fields.forEach((({name:s,formatter:i,calculator:r,type:n,editor:o,hidden:d})=>{if(d)return;const l=document.createElement("div");l.classList.add("cell"),l.dataset.field=s,l.style.width=this.columnSizes[s]+"px";let a=e[s]||"";if(e[editing])if(t.setAttribute("editing",!0),r)l.appendChild(Table.editors.calculated(r(e,this)));else if(o)l.appendChild(o(a));else{let e=Table.editors[n||typeof a];e||(e=Table.editors.string),l.appendChild(e(a))}else l.innerHTML=r?r(e,this):i?i(a):a;t.appendChild(l)})),this.hasAfterControls()&&t.appendChild(this.renderAfterControls()),t}renderBeforeControls(){const e=document.createElement("div");return e.style.width=this.columnSizes.beforeControls+"px",e.classList.add("cell","controls","controls-before"),this.querySelectorAll('[slot="before"]').forEach((t=>{e.appendChild(t.cloneNode(!0))})),e}renderAfterControls(){const e=document.createElement("div");return e.style.width=this.columnSizes.afterControls+"px",e.classList.add("cell","controls","controls-after"),this.querySelectorAll('[slot="after"]').forEach((t=>{e.appendChild(t.cloneNode(!0))})),e}hasBeforeControls(){return!!this.querySelector('[slot="before"]')}hasAfterControls(){return!!this.querySelector('[slot="after"]')}hasTopControls(){return!!this.querySelector('[slot="top"]')}hasBottomControls(){return!!this.querySelector(":scope > :not([slot])")}editRecord(e){e[editing]=!0;const t=this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`),s=this.renderRecord(e);t.replaceWith(s),dispatchEvent(this,"editingChange")}saveEditedRecord(e){e[editing]=!1;const t=this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`);t.querySelectorAll(".cell").forEach((t=>{const s=t.dataset.field,i=this.fields.find((e=>e.name===s));if(i&&!i.calculator){const i=1===t.children.length?t.firstChild:t.querySelector("input, select");i&&(e[s]=i.value)}})),t.replaceWith(this.renderRecord(e))}cancelEditedRecord(e){e[editing]=!1,this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`).replaceWith(this.renderRecord(e))}recordIsEditing(e){return e[editing]}getCurrentPage(){return this.currentPage}getTotalPages(){return Math.ceil(this.getDisplayedRecords().length/this.pageSize)}setPage(e){e<1||e>this.getTotalPages()||(this.currentPage=e,this.renderRecords(),dispatchEvent(this,"pageChange"))}firstPage(){1!==this.currentPage&&this.setPage(1)}nextPage(){this.currentPage<this.getTotalPages()&&this.setPage(this.currentPage+1)}prevPage(){this.currentPage>1&&this.setPage(this.currentPage-1)}lastPage(){this.currentPage!==this.getTotalPages()&&this.setPage(this.getTotalPages())}setPageSize(e){this.pageSize=e,this.currentPage=1,this.renderRecords(),dispatchEvent(this,"pageSizeChange")}setData({records:e=!1,fields:t=!1,pageSize:s=!1,pageSizeOptions:i=!1,currentPage:r=!1,enableSelection:n}={}){let o=!1,d=this.getTotalPages(),l=this.currentPage;e&&(this.records=e.map((e=>({...e}))),this.records.forEach(((e,t)=>{e[index]=t,e[selected]=!1,e[hidden]=!1,e[editing]=!1})),this.fields=t||Table.extractFieldsFromRecords(this.records),o=!0),s&&(this.pageSize=s,o=!0),i&&(this.pageSizeOptions=i),r&&(this.currentPage=r,o=!0),void 0!==n&&(this.enableSelection=n,o=!0),o&&(this.renderFields(),this.renderRecords());const a=this.getTotalPages();a!==d&&dispatchEvent(this,"pageCountChanged",{totalPages:this.getTotalPages()}),l>a&&this.setPage(a)}setRecords(e,t){let s=this.getTotalPages(),i=this.currentPage;this.records=e.map((e=>({...e}))),this.records.forEach(((e,t)=>{e[index]=t,e[selected]=!1,e[hidden]=!1,e[editing]=!1})),this.fields=t||Table.extractFieldsFromRecords(this.records),this.renderFields(),this.renderRecords(),dispatchEvent(this,"recordsSet",{records:e});const r=this.getTotalPages();r!==s&&dispatchEvent(this,"pageCountChanged",{totalPages:this.getTotalPages()}),i>r&&this.setPage(r)}setupFetchRecords(e,t){const s=this.records.length;s<e&&(this.records.length=e,this.records.fill(null,s)),onEvent(this,"fetchRecords",(async e=>{if(this.fetchPending)return;this.fetchPending=!0;const{start:s,count:i}=e.detail,r=await t(s,i);this.records.splice(s,r.length,...r),this.fetchPending=!1,this.renderRecords()}))}addRecord(e){e[selected]=!1,e[hidden]=!1,e[index]=this.records.length,this.records.push(e),this.renderRecords(),dispatchEvent(this,"recordAdded",{record:e})}updateRecord(e,t){let s=!1,i=this.records.find((t=>t===e));if(i||void 0===e[index]||(i=this.records[e[index]]),Object.keys(t).forEach((e=>{i.hasOwnProperty(e)&&(i[e]=t[e],s=!0)})),s){const e=(this.currentPage-1)*this.pageSize,t=e+this.pageSize;(!this.enablePages||i[index]>=e&&i[index]<t)&&this.renderRecords()}}deleteRecord(e){let t=this.records.find((t=>t===e)),s=this.getTotalPages();if(t||void 0===e[index]||(t=this.records[e[index]]),t){const e=this.records.indexOf(t);this.records.splice(e,1),this.records.forEach(((e,t)=>{e[index]=t})),this.renderRecords(),dispatchEvent(this,"selectionChange"),dispatchEvent(this,"recordDeleted",{index:e});const i=this.getTotalPages();this.currentPage>i&&this.setPage(i),i!==s&&dispatchEvent(this,"pageCountChanged",{totalPages:i})}}deleteSelected(){let e=this.getTotalPages();this.getSelectedRecords().forEach((e=>{let t=this.records.find((t=>t===e));if(t||void 0===e[index]||(t=this.records[e[index]]),t){const e=this.records.indexOf(t);this.records.splice(e,1)}})),this.records.forEach(((e,t)=>{e[index]=t})),this.renderRecords();const t=this.getTotalPages();this.currentPage>t&&this.setPage(t),t!==e&&dispatchEvent(this,"pageCountChanged",{totalPages:t}),dispatchEvent(this,"selectionChange")}getSelectedRecords(){return this.records.filter((e=>e[selected]))}selectAllOnPage(){const e=(this.currentPage-1)*this.pageSize,t=Math.min(e+this.pageSize,this.records.length);for(let s=e;s<t;s++)this.records[s][selected]=!0;this.renderRecords(),setTimeout((()=>{dispatchEvent(this,"selectionChange")}),0)}deselectAllOnPage(){const e=(this.currentPage-1)*this.pageSize,t=Math.min(e+this.pageSize,this.records.length);for(let s=e;s<t;s++)this.records[s][selected]=!1;this.renderRecords(),setTimeout((()=>{dispatchEvent(this,"selectionChange")}),0)}allOnPageSelected(){const e=(this.currentPage-1)*this.pageSize,t=Math.min(e+this.pageSize,this.records.length);for(let s=e;s<t;s++)if(!this.records[s][selected])return!1;return!0}sortBy(e,t=!0){this.sort=this.sort.filter((t=>t.name!==e)),this.sort.push({name:e,asc:t}),this.renderFields(),this.renderRecords()}hideRecord(e){let t=this.records.find((t=>t===e));t||void 0===e[index]||(t=this.records[e[index]]),t&&(t[hidden]=!0,this.renderRecords(),dispatchEvent(this,"recordHidden"))}showRecord(e){let t=this.records.find((t=>t===e));t||void 0===e[index]||(t=this.records[e[index]]),t&&(t[hidden]=!1,this.renderRecords(),dispatchEvent(this,"recordShown"))}showAllRecords(){this.records.forEach((e=>{e[hidden]=!1})),this.filters.length&&(this.filters=[],dispatchEvent(this,"filterRemoved filterChange")),this.renderRecords(),dispatchEvent(this,"recordShown allRecordsShown")}addFilter(e,t,s){this.filters.push({field:e,condition:t,value:s}),dispatchEvent(this,"filterAdded filterChange"),this.renderRecords()}removeFilter(e,t,s,i=!0){const r=this.filters.findIndex((i=>i.field===e&&i.condition===t&&i.value===s));-1!==r&&(this.records.forEach((i=>{this.testFilter(i,e,t,s)||(i[hidden]=!1)})),this.filters.splice(r,1),dispatchEvent(this,"filterRemoved filterChange"),i&&this.renderRecords())}testFilter(e,t,s,i){const r=e[t];switch(s){case"equals":return r===i;case"not-equals":return r!==i;case"contains":return r.includes(i);case"not-contains":return!r.includes(i);case"greater-than":return r>i;case"less-than":return r<i;case"greater-than-or-equal":return r>=i;case"less-than-or-equal":return r<=i;default:return!0}}removeAllFilters(){this.filters.length&&(this.filters.forEach((({field:e,condition:t,value:s})=>{this.removeFilter(e,t,s,!1)})),this.renderRecords())}search(e){const t=e.trim().toLowerCase();let s=!1;this.records.forEach((e=>{if(e[hidden])return;let i=!1;this.fields.forEach((({name:s})=>{(e[s]?.toString().toLowerCase()||"").includes(t)&&(i=!0)})),e[hidden]!==!i&&(e[hidden]=!i,s=!0)})),s&&(dispatchEvent(this,"recordHidden"),this.renderRecords()),dispatchEvent(this,"search",{term:e})}getDisplayedRecords(){this.filters.forEach((({field:e,condition:t,value:s})=>{this.records.forEach((i=>{this.testFilter(i,e,t,s)||(i[hidden]=!0)}))}));let e=this.records.filter((e=>null===e||!e[hidden]));return this.sort.forEach((({name:t,asc:s})=>{e.sort(((e,i)=>e[t]<i[t]?s?-1:1:e[t]>i[t]?s?1:-1:0))})),e}getHiddenRecords(){return this.records.filter((e=>e[hidden]))}calculateColumnSizes(){return this.columnSizes={},this.columnSizes.total=0,this.enableSelection&&(this.columnSizes.total+=40),this.columnSizes.beforeControls=Array.from(this.querySelectorAll('[slot="before"]')).reduce(((e,t)=>e+(t.maxWidth||40)),0),this.columnSizes.afterControls=Array.from(this.querySelectorAll('[slot="after"]')).reduce(((e,t)=>e+(t.maxWidth||40)),0),this.hasBeforeControls()&&(this.columnSizes.total+=this.columnSizes.beforeControls),this.hasAfterControls()&&(this.columnSizes.total+=this.columnSizes.afterControls),this.fields.forEach((e=>{if(e.size)this.columnSizes[e.name]=e.size;else{let t=0;this.records.slice(0,100).forEach((s=>{let i=s[e.name];e.calculator&&(i=e.calculator(s,this)),e.formatter&&(i=e.formatter(i)),i&&i.toString().length>t&&(t=i.toString().length)})),this.columnSizes[e.name]=Math.max(10*t+32,128),e.hidden||(this.columnSizes.total+=this.columnSizes[e.name])}})),this.columnSizes}setFieldHiddenState(e,t){const s=this.fields.find((t=>t.name===e));s&&(s.hidden=t,this.calculateColumnSizes(),this.renderFields(),this.renderRecords(),dispatchEvent(this,"fieldVisibilityChanged "+(t?"fieldHidden":"fieldShown"),{field:s}))}hideField(e){this.setFieldHiddenState(e,!0)}showField(e){this.setFieldHiddenState(e,!1)}reorderFields(e){const t=[];e.forEach((e=>{const s=this.fields.find((t=>t.name===e));s&&t.push(s)})),this.fields=t,this.renderFields(),this.renderRecords()}get shadowTemplate(){return`\n <div id="wrapper">\n <div id="top"><slot name="top"></slot></div>\n <div id="table">\n <div id="fields"></div>\n <div id="records"></div>\n </div>\n <div id="bottom">${super.shadowTemplate}</div>\n </div>\n <div style="display: none">\n <slot name="before"></slot>\n <slot name="after"></slot>\n </div>\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n display: block;\n width: 100%;\n overflow: auto;\n margin-bottom: var(--spacer);\n }\n #wrapper {\n width: min-content;\n border: 1px solid var(--c_border);\n border-radius: var(--radius);\n }\n #table {\n width: min-content;\n }\n #fields,\n .record {\n display: flex;\n }\n #fields {\n background-color: var(--c_bg__alt);\n border-bottom: 1px solid var(--c_border);\n }\n .record:not([editing="true"]) .cell:not(.controls),\n #fields .cell:not(.controls) {\n padding: calc(0.5 * var(--spacer)) var(--spacer);\n }\n .cell:not(:first-child) {\n border-left: 1px solid var(--c_border);\n }\n .record:not(:last-child) .cell {\n border-bottom: 1px solid var(--c_border);\n }\n #top, #bottom {\n display: flex;\n width: 100%;\n }\n #top slot {\n display: block;\n width: 100%;\n border-bottom: 1px solid var(--c_border);\n }\n #bottom slot {\n display: block;\n width: 100%;\n border-top: 1px solid var(--c_border);\n }\n :host(:not([top-controls])) #top,\n :host(:not([bottom-controls])) #bottom {\n display: none;\n }\n .field-select,\n .selection {\n display: flex;\n justify-content: center;\n align-items: center;\n }\n .field-select input,\n .selection input {\n width: 1.25rem;\n height: 1.25rem;\n }\n .icon-sort {\n float: right;\n opacity: 0.5;\n }\n `}static extractFieldsFromRecords(e,t=100){const s=new Set;return e.slice(0,t).forEach((e=>{Object.keys(e).forEach((e=>s.add(e)))})),[...s].map((e=>({name:e,label:toTitleCase(e)})))}static format(e){return(Array.isArray(e)?Table.formatters.array:Table.formatters[typeof e])(e)}static formatters={string:e=>e,number:e=>`${e}`,date:e=>e.toLocaleDateString(),boolean:e=>e?"True":"False",array:e=>e.map((e=>Table.format(e))).join(", "),undefined:e=>"",null:e=>"<code>null</code>"};static editors={string:e=>{const t=document.createElement("input");return t.value=e,t},number:e=>{const t=document.createElement("input");return t.type="number",t.value=e,t},date:e=>{const t=document.createElement("input");return t.type="date",t.value=e,t},boolean:e=>{const t=document.createElement("select");return t.innerHTML=`\n <option value="true" ${e?"selected":""}>True</option>\n <option value="false" ${e?"":"selected"}>False</option>\n `,t.value=e,t},calculated:e=>{const t=document.createElement("input");return t.disabled=!0,t.value=e,t}}}window.customElements.define("k-table",Table);
|
|
1
|
+
import Component from"./Component.js";import{toTitleCase}from"../utils/string.js";import{onEvent,offEvent,dispatchEvent}from"../utils/element.js";const selected=Symbol("selected"),hidden=Symbol("hidden"),index=Symbol("index"),editing=Symbol("editing");export default class Table extends Component{constructor({records:e=[],fields:t=[],filters:s=[],enablePages:i=!1,pageSize:r=50,pageSizeOptions:n=[10,25,50,100,500],currentPage:o=1,enableSelection:d=!1,enableSorting:l=!1}={}){super(),this.registerAttributes({enablePages:i,pageSize:r,currentPage:o,pageSizeOptions:n,enableSelection:d,enableSorting:l}),this.registerProps({fields:t,records:e,filters:s,sort:[],columnSizes:{},fetchPending:!1})}async render(e){return!!await super.render(e)&&(this.setData({records:this.records,fields:this.fields,filters:this.filters}),!0)}renderFields(){if(!this.rendered)return;this.calculateColumnSizes(),this.hasTopControls()?this.setAttribute("top-controls","true"):this.removeAttribute("top-controls"),this.hasBottomControls()?this.setAttribute("bottom-controls","true"):this.removeAttribute("bottom-controls");const e=this.shadowRoot.getElementById("fields");if(e.innerHTML="",this.enableSelection){const t=document.createElement("div");t.classList.add("field","controls","cell","field-select"),t.style.width="40px",t.innerHTML='<input type="checkbox" id="select-all" />',e.appendChild(t)}if(this.hasBeforeControls()){const t=document.createElement("div");t.classList.add("field","cell","field-before-controls"),t.style.width=this.columnSizes.beforeControls+"px",e.appendChild(t)}if(this.fields.forEach((({name:t,label:s,hidden:i})=>{if(i)return;const r=document.createElement("div");if(r.classList.add("field","cell"),r.style.width=this.columnSizes[t]+"px",r.innerHTML=s,this.enableSorting){r.style.cursor="pointer";const e=this.sort.find((e=>e.name===t));if(e){r.classList.add(e.asc?"sort-asc":"sort-desc");const t=document.createElement("k-icon");t.classList.add("icon-sort"),t.setAttribute("name",e.asc?"arrow-down":"arrow-up"),r.appendChild(t)}r.addEventListener("click",(()=>{const e=this.sort.find((e=>e.name===t)),s=!e||!e.asc;this.sortBy(t,s)}))}e.appendChild(r)})),this.hasAfterControls()){const t=document.createElement("div");t.classList.add("field","cell","field-after-controls"),t.style.width=this.columnSizes.afterControls+"px",e.appendChild(t)}if(e.style.width=this.shadowRoot.getElementById("top").style.width=this.shadowRoot.getElementById("bottom").style.width=this.shadowRoot.getElementById("records").style.width=this.columnSizes.total+"px",this.enableSelection){const e=this.shadowRoot.getElementById("select-all");e.addEventListener("change",(()=>{e.checked?this.selectAllOnPage():this.deselectAllOnPage()})),onEvent(this,"selectionChange pageChange",(()=>{this.allOnPageSelected()?e.checked=!0:e.checked=!1}))}}renderRecords(){if(!this.rendered)return;const e=this.shadowRoot.getElementById("records");e.innerHTML="";let t=this.getDisplayedRecords(),s=0,i=this.pageSize;this.enablePages&&(s=(this.currentPage-1)*this.pageSize,i=s+this.pageSize,t=t.slice(s,i));let r,n=0;t.forEach(((t,i)=>{if(null!==t)e.appendChild(this.renderRecord(t));else{r||(r=s+i),n++;const t=document.createElement("div");t.classList.add("record","fetching"),t.innerHTML='<div class="cell">Loading...</div>',e.appendChild(t)}})),r&&!this.fetchPending&&dispatchEvent(this,"fetchRecords",{start:r,count:n})}renderRecord(e){const t=document.createElement("div");if(t.classList.add("record"),t.dataset.index=e[index],this.enableSelection){const s=document.createElement("div");s.style.width="40px",s.classList.add("cell","selection","controls");const i=document.createElement("input");i.type="checkbox",i.checked=e[selected],i.addEventListener("change",(t=>{e[selected]=!!t.target.checked,dispatchEvent(this,"selectionChange")})),s.appendChild(i),t.appendChild(s)}return this.hasBeforeControls()&&t.appendChild(this.renderBeforeControls()),this.fields.forEach((({name:s,formatter:i,calculator:r,type:n,editor:o,hidden:d})=>{if(d)return;const l=document.createElement("div");l.classList.add("cell"),l.dataset.field=s,l.style.width=this.columnSizes[s]+"px";let a=e[s]||"";if(e[editing])if(t.setAttribute("editing",!0),r)l.appendChild(Table.editors.calculated(r(e,this)));else if(o)l.appendChild(o(a));else{let e=Table.editors[n||typeof a];e||(e=Table.editors.string),l.appendChild(e(a))}else l.innerHTML=r?r(e,this):i?i(a):a;t.appendChild(l)})),this.hasAfterControls()&&t.appendChild(this.renderAfterControls()),t}renderBeforeControls(){const e=document.createElement("div");return e.style.width=this.columnSizes.beforeControls+"px",e.classList.add("cell","controls","controls-before"),this.querySelectorAll('[slot="before"]').forEach((t=>{e.appendChild(t.cloneNode(!0))})),e}renderAfterControls(){const e=document.createElement("div");return e.style.width=this.columnSizes.afterControls+"px",e.classList.add("cell","controls","controls-after"),this.querySelectorAll('[slot="after"]').forEach((t=>{e.appendChild(t.cloneNode(!0))})),e}hasBeforeControls(){return!!this.querySelector('[slot="before"]')}hasAfterControls(){return!!this.querySelector('[slot="after"]')}hasTopControls(){return!!this.querySelector('[slot="top"]')}hasBottomControls(){return!!this.querySelector(":scope > :not([slot])")}editRecord(e){e[editing]=!0;const t=this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`),s=this.renderRecord(e);t.replaceWith(s),dispatchEvent(this,"editingChange")}saveEditedRecord(e){e[editing]=!1;const t=this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`);t.querySelectorAll(".cell").forEach((t=>{const s=t.dataset.field,i=this.fields.find((e=>e.name===s));if(i&&!i.calculator){const i=1===t.children.length?t.firstChild:t.querySelector("input, select");i&&(e[s]=i.value)}})),t.replaceWith(this.renderRecord(e))}cancelEditedRecord(e){e[editing]=!1,this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`).replaceWith(this.renderRecord(e))}recordIsEditing(e){return e[editing]}getCurrentPage(){return this.currentPage}getTotalPages(){return Math.ceil(this.getDisplayedRecords().length/this.pageSize)}setPage(e){e<1||e>this.getTotalPages()||(this.currentPage=e,this.renderRecords(),dispatchEvent(this,"pageChange"))}firstPage(){1!==this.currentPage&&this.setPage(1)}nextPage(){this.currentPage<this.getTotalPages()&&this.setPage(this.currentPage+1)}prevPage(){this.currentPage>1&&this.setPage(this.currentPage-1)}lastPage(){this.currentPage!==this.getTotalPages()&&this.setPage(this.getTotalPages())}setPageSize(e){this.pageSize=e,this.currentPage=1,this.renderRecords(),dispatchEvent(this,"pageSizeChange")}setData({records:e=!1,fields:t=!1,pageSize:s=!1,pageSizeOptions:i=!1,currentPage:r=!1,enableSelection:n}={}){let o=!1,d=this.getTotalPages(),l=this.currentPage;e&&(this.records=e.map((e=>({...e}))),this.records.forEach(((e,t)=>{e[index]=t,e[selected]=!1,e[hidden]=!1,e[editing]=!1})),this.fields=t||Table.extractFieldsFromRecords(this.records),o=!0),s&&(this.pageSize=s,o=!0),i&&(this.pageSizeOptions=i),r&&(this.currentPage=r,o=!0),void 0!==n&&(this.enableSelection=n,o=!0),o&&(this.renderFields(),this.renderRecords());const a=this.getTotalPages();a!==d&&dispatchEvent(this,"pageCountChanged",{totalPages:this.getTotalPages()}),l>a&&this.setPage(a)}setRecords(e,t){let s=this.getTotalPages(),i=this.currentPage;this.records=e.map((e=>({...e}))),this.records.forEach(((e,t)=>{e[index]=t,e[selected]=!1,e[hidden]=!1,e[editing]=!1})),this.fields=t||Table.extractFieldsFromRecords(this.records),this.renderFields(),this.renderRecords(),dispatchEvent(this,"recordsSet",{records:e});const r=this.getTotalPages();r!==s&&dispatchEvent(this,"pageCountChanged",{totalPages:this.getTotalPages()}),i>r&&this.setPage(r)}setupFetchRecords(e,t){const s=this.records.length;s<e&&(this.records.length=e,this.records.fill(null,s)),onEvent(this,"fetchRecords",(async e=>{if(this.fetchPending)return;this.fetchPending=!0;const{start:s,count:i}=e.detail,r=await t(s,i);this.records.splice(s,r.length,...r),this.fetchPending=!1,this.renderRecords()}))}addRecord(e){e[selected]=!1,e[hidden]=!1,e[index]=this.records.length,this.records.push(e),this.renderRecords(),dispatchEvent(this,"recordAdded",{record:e})}updateRecord(e,t){let s=!1,i=this.records.find((t=>t===e));if(i||void 0===e[index]||(i=this.records[e[index]]),Object.keys(t).forEach((e=>{i.hasOwnProperty(e)&&(i[e]=t[e],s=!0)})),s){const e=(this.currentPage-1)*this.pageSize,t=e+this.pageSize;(!this.enablePages||i[index]>=e&&i[index]<t)&&this.renderRecords()}}deleteRecord(e){let t=this.records.find((t=>t===e)),s=this.getTotalPages();if(t||void 0===e[index]||(t=this.records[e[index]]),t){const e=this.records.indexOf(t);this.records.splice(e,1),this.records.forEach(((e,t)=>{e[index]=t})),this.renderRecords(),dispatchEvent(this,"selectionChange"),dispatchEvent(this,"recordDeleted",{index:e});const i=this.getTotalPages();this.currentPage>i&&this.setPage(i),i!==s&&dispatchEvent(this,"pageCountChanged",{totalPages:i})}}deleteSelected(){let e=this.getTotalPages();this.getSelectedRecords().forEach((e=>{let t=this.records.find((t=>t===e));if(t||void 0===e[index]||(t=this.records[e[index]]),t){const e=this.records.indexOf(t);this.records.splice(e,1)}})),this.records.forEach(((e,t)=>{e[index]=t})),this.renderRecords();const t=this.getTotalPages();this.currentPage>t&&this.setPage(t),t!==e&&dispatchEvent(this,"pageCountChanged",{totalPages:t}),dispatchEvent(this,"selectionChange")}getSelectedRecords(){return this.records.filter((e=>e[selected]))}selectAllOnPage(){const e=(this.currentPage-1)*this.pageSize,t=Math.min(e+this.pageSize,this.records.length);for(let s=e;s<t;s++)this.records[s][selected]=!0;this.renderRecords(),setTimeout((()=>{dispatchEvent(this,"selectionChange")}),0)}deselectAllOnPage(){const e=(this.currentPage-1)*this.pageSize,t=Math.min(e+this.pageSize,this.records.length);for(let s=e;s<t;s++)this.records[s][selected]=!1;this.renderRecords(),setTimeout((()=>{dispatchEvent(this,"selectionChange")}),0)}allOnPageSelected(){const e=(this.currentPage-1)*this.pageSize,t=Math.min(e+this.pageSize,this.records.length);for(let s=e;s<t;s++)if(!this.records[s][selected])return!1;return!0}sortBy(e,t=!0){this.sort=this.sort.filter((t=>t.name!==e)),this.sort.push({name:e,asc:t}),this.renderFields(),this.renderRecords()}hideRecord(e){let t=this.records.find((t=>t===e));t||void 0===e[index]||(t=this.records[e[index]]),t&&(t[hidden]=!0,this.renderRecords(),dispatchEvent(this,"recordHidden"))}showRecord(e){let t=this.records.find((t=>t===e));t||void 0===e[index]||(t=this.records[e[index]]),t&&(t[hidden]=!1,this.renderRecords(),dispatchEvent(this,"recordShown"))}showAllRecords(){this.records.forEach((e=>{e[hidden]=!1})),this.filters.length&&(this.filters=[],dispatchEvent(this,"filterRemoved filterChange")),this.renderRecords(),dispatchEvent(this,"recordShown allRecordsShown")}addFilter(e,t,s){this.filters.push({field:e,condition:t,value:s}),dispatchEvent(this,"filterAdded filterChange"),this.renderRecords()}removeFilter(e,t,s,i=!0){const r=this.filters.findIndex((i=>i.field===e&&i.condition===t&&i.value===s));-1!==r&&(this.records.forEach((i=>{this.testFilter(i,e,t,s)||(i[hidden]=!1)})),this.filters.splice(r,1),dispatchEvent(this,"filterRemoved filterChange"),i&&this.renderRecords())}testFilter(e,t,s,i){const r=e[t];switch(s){case"equals":return r===i;case"not-equals":return r!==i;case"contains":return r.includes(i);case"not-contains":return!r.includes(i);case"greater-than":return r>i;case"less-than":return r<i;case"greater-than-or-equal":return r>=i;case"less-than-or-equal":return r<=i;default:return!0}}removeAllFilters(){this.filters.length&&(this.filters.forEach((({field:e,condition:t,value:s})=>{this.removeFilter(e,t,s,!1)})),this.renderRecords())}search(e){const t=e.trim().toLowerCase();let s=!1;this.records.forEach((e=>{if(e[hidden])return;let i=!1;this.fields.forEach((({name:s})=>{(e[s]?.toString().toLowerCase()||"").includes(t)&&(i=!0)})),e[hidden]!==!i&&(e[hidden]=!i,s=!0)})),s&&(dispatchEvent(this,"recordHidden"),this.renderRecords()),dispatchEvent(this,"search",{term:e})}getDisplayedRecords(){this.filters.forEach((({field:e,condition:t,value:s})=>{this.records.forEach((i=>{this.testFilter(i,e,t,s)||(i[hidden]=!0)}))}));let e=this.records.filter((e=>null===e||!e[hidden]));return this.sort.forEach((({name:t,asc:s})=>{e.sort(((e,i)=>e[t]<i[t]?s?-1:1:e[t]>i[t]?s?1:-1:0))})),e}getHiddenRecords(){return this.records.filter((e=>e[hidden]))}calculateColumnSizes(){return this.columnSizes={},this.columnSizes.total=0,this.enableSelection&&(this.columnSizes.total+=40),this.columnSizes.beforeControls=Array.from(this.querySelectorAll('[slot="before"]')).reduce(((e,t)=>e+(t.maxWidth||40)),0),this.columnSizes.afterControls=Array.from(this.querySelectorAll('[slot="after"]')).reduce(((e,t)=>e+(t.maxWidth||40)),0),this.hasBeforeControls()&&(this.columnSizes.total+=this.columnSizes.beforeControls),this.hasAfterControls()&&(this.columnSizes.total+=this.columnSizes.afterControls),this.fields.forEach((e=>{if(e.size)this.columnSizes[e.name]=e.size,this.columnSizes.total+=e.size;else{let t=0;this.records.slice(0,100).forEach((s=>{let i=s[e.name];e.calculator&&(i=e.calculator(s,this)),e.formatter&&(i=e.formatter(i)),i&&i.toString().length>t&&(t=i.toString().length)})),this.columnSizes[e.name]=Math.max(10*t+32,128),e.hidden||(this.columnSizes.total+=this.columnSizes[e.name])}})),this.columnSizes}setFieldHiddenState(e,t){const s=this.fields.find((t=>t.name===e));s&&(s.hidden=t,this.calculateColumnSizes(),this.renderFields(),this.renderRecords(),dispatchEvent(this,"fieldVisibilityChanged "+(t?"fieldHidden":"fieldShown"),{field:s}))}hideField(e){this.setFieldHiddenState(e,!0)}showField(e){this.setFieldHiddenState(e,!1)}reorderFields(e){const t=[];e.forEach((e=>{const s=this.fields.find((t=>t.name===e));s&&t.push(s)})),this.fields=t,this.renderFields(),this.renderRecords()}get shadowTemplate(){return`\n <div id="wrapper">\n <div id="top"><slot name="top"></slot></div>\n <div id="table">\n <div id="fields"></div>\n <div id="records"></div>\n </div>\n <div id="bottom">${super.shadowTemplate}</div>\n </div>\n <div style="display: none">\n <slot name="before"></slot>\n <slot name="after"></slot>\n </div>\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n display: block;\n width: 100%;\n overflow: auto;\n margin-bottom: var(--spacer);\n }\n #wrapper {\n width: min-content;\n border: 1px solid var(--c_border);\n border-radius: var(--radius);\n }\n #table {\n width: min-content;\n }\n #fields,\n .record {\n display: flex;\n }\n #fields {\n background-color: var(--c_bg__alt);\n border-bottom: 1px solid var(--c_border);\n }\n .record:not([editing="true"]) .cell:not(.controls),\n #fields .cell:not(.controls) {\n padding: calc(0.5 * var(--spacer)) var(--spacer);\n }\n .cell:not(:first-child) {\n border-left: 1px solid var(--c_border);\n }\n .record:not(:last-child) .cell {\n border-bottom: 1px solid var(--c_border);\n }\n #top, #bottom {\n display: flex;\n width: 100%;\n }\n #top slot {\n display: block;\n width: 100%;\n border-bottom: 1px solid var(--c_border);\n }\n #bottom slot {\n display: block;\n width: 100%;\n border-top: 1px solid var(--c_border);\n }\n :host(:not([top-controls])) #top,\n :host(:not([bottom-controls])) #bottom {\n display: none;\n }\n .field-select,\n .selection {\n display: flex;\n justify-content: center;\n align-items: center;\n }\n .field-select input,\n .selection input {\n width: 1.25rem;\n height: 1.25rem;\n }\n .icon-sort {\n float: right;\n opacity: 0.5;\n }\n `}static extractFieldsFromRecords(e,t=100){const s=new Set;return e.slice(0,t).forEach((e=>{Object.keys(e).forEach((e=>s.add(e)))})),[...s].map((e=>({name:e,label:toTitleCase(e)})))}static format(e){return(Array.isArray(e)?Table.formatters.array:Table.formatters[typeof e])(e)}static formatters={string:e=>e,number:e=>`${e}`,date:e=>e.toLocaleDateString(),boolean:e=>e?"True":"False",array:e=>e.map((e=>Table.format(e))).join(", "),undefined:e=>"",null:e=>"<code>null</code>"};static editors={string:e=>{const t=document.createElement("input");return t.value=e,t},number:e=>{const t=document.createElement("input");return t.type="number",t.value=e,t},date:e=>{const t=document.createElement("input");return t.type="date",t.value=e,t},boolean:e=>{const t=document.createElement("select");return t.innerHTML=`\n <option value="true" ${e?"selected":""}>True</option>\n <option value="false" ${e?"":"selected"}>False</option>\n `,t.value=e,t},calculated:e=>{const t=document.createElement("input");return t.disabled=!0,t.value=e,t}}}window.customElements.define("k-table",Table);
|