kempo-ui 0.2.4 → 0.3.1
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/Dialog.js +2 -2
- package/dist/components/Table.js +6 -5
- package/docs/components/dialog.html +26 -6
- package/docs/components/table.html +12 -1
- package/docs/components/tableControls.html +1 -1
- package/docs/components/tableCustomFields.html +27 -2
- package/docs/components/tableFetchRecords.html +1 -1
- package/docs/components/tableFieldSortHide.html +1 -1
- package/docs/components/tablePagination.html +1 -1
- package/docs/components/tableRecordEditing.html +32 -1
- package/docs/components/tableRecordFiltering.html +1 -1
- package/docs/components/tableRecordHiding.html +1 -1
- package/docs/components/tableRecordSearching.html +1 -1
- package/docs/components/tableRecordSelection.html +1 -1
- package/docs/components/tableRowControls.html +1 -1
- package/docs/components/tableSorting.html +1 -1
- package/docs/src/components/Dialog.js +2 -2
- package/docs/src/components/Table.js +6 -5
- package/package.json +1 -1
- package/src/components/Dialog.js +7 -3
- package/src/components/Table.js +7 -4
- package/tests/components/Dialog.browser-test.js +97 -0
- package/tests/components/Table.browser-test.js +132 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-all.min.js";import{boolExists}from"../utils/propConverters.js";import"./Icon.js";import"./FocusCapture.js";const firstFocusable=e=>e.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')[0];export default class Dialog extends ShadowComponent{static properties={opened:{type:Boolean,reflect:!0,converter:boolExists},closeBtn:{type:Boolean,reflect:!0,attribute:"close-btn",converter:boolExists},overlayClose:{type:Boolean,reflect:!0,attribute:"overlay-close",converter:boolExists},disableKeyboardClose:{type:Boolean,reflect:!0,attribute:"disable-keyboard-close",converter:boolExists},confirmText:{type:String,reflect:!0,attribute:"confirm-text"},confirmClasses:{type:String,reflect:!0,attribute:"confirm-classes"},cancelText:{type:String,reflect:!0,attribute:"cancel-text"},cancelClasses:{type:String,reflect:!0,attribute:"cancel-classes"}};constructor(){super(),this.opened=!1,this.closeBtn=!0,this.overlayClose=!0,this.disableKeyboardClose=!1,this.confirmText="",this.confirmClasses="success ml",this.cancelText="",this.cancelClasses="",this.confirmAction=()=>{},this.cancelAction=()=>{},this.closeCallback=()=>{},this.previousFocus=null}handleClick=e=>{const{target:t}=e,o=t.id||t.closest("[id]")?.id;"overlay"===o&&this.overlayClose||"close"===o?this.close():"cancel"===o&&(this.cancelAction(e),e.defaultPrevented||this.close())};handleSubmit=e=>{e.preventDefault(),this.confirmAction(e),e.keepDialogOpen||this.close()};handleSlotKeydown=e=>{if("Enter"===e.key&&this.confirmText){const t=e.target;if("INPUT"===t.tagName||"TEXTAREA"===t.tagName){e.preventDefault();const t=this.shadowRoot.querySelector("form");t&&t.requestSubmit()}}};handleKeydown=e=>{this.disableKeyboardClose||27!==e.keyCode||this.close()};connectedCallback(){super.connectedCallback()}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("keydown",this.handleKeydown)}updated(e){super.updated(e),e.has("opened")&&this.dispatchEvent(new CustomEvent(this.opened?"opened":"close"))}open(){this.opened=!0,window.addEventListener("keydown",this.handleKeydown),document.body.classList.add("no-scroll");const e=this.updateComplete||this.requestUpdate();e&&e.then?e.then(()=>{const e=this.shadowRoot.querySelector("[autofocus]")||firstFocusable(this.shadowRoot);e&&e.focus()}):setTimeout(()=>{const e=this.shadowRoot.querySelector("[autofocus]")||firstFocusable(this.shadowRoot);e&&e.focus()},0)}close(){this.opened=!1,this.blur(),this.closeCallback(),window.removeEventListener("keydown",this.handleKeydown),document.body.classList.remove("no-scroll")}toggle(){this.opened?this.close():this.open()}focus(){const e=firstFocusable(this.shadowRoot);e&&(this.previousFocus=document.activeElement,e.focus())}blur(){this.previousFocus&&this.previousFocus.focus()}hasTitle(){return!!this.querySelector('[slot="title"]')}static styles=css`
|
|
1
|
+
import ShadowComponent from"./ShadowComponent.js";import{html,css,render}from"../lit-all.min.js";import{boolExists}from"../utils/propConverters.js";import"./Icon.js";import"./FocusCapture.js";const firstFocusable=e=>e.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')[0];export default class Dialog extends ShadowComponent{static properties={opened:{type:Boolean,reflect:!0,converter:boolExists},closeBtn:{type:Boolean,reflect:!0,attribute:"close-btn",converter:boolExists},overlayClose:{type:Boolean,reflect:!0,attribute:"overlay-close",converter:boolExists},disableKeyboardClose:{type:Boolean,reflect:!0,attribute:"disable-keyboard-close",converter:boolExists},confirmText:{type:String,reflect:!0,attribute:"confirm-text"},confirmClasses:{type:String,reflect:!0,attribute:"confirm-classes"},cancelText:{type:String,reflect:!0,attribute:"cancel-text"},cancelClasses:{type:String,reflect:!0,attribute:"cancel-classes"}};constructor(){super(),this.opened=!1,this.closeBtn=!0,this.overlayClose=!0,this.disableKeyboardClose=!1,this.confirmText="",this.confirmClasses="success ml",this.cancelText="",this.cancelClasses="",this.confirmAction=()=>{},this.cancelAction=()=>{},this.closeCallback=()=>{},this.previousFocus=null}handleClick=e=>{const{target:t}=e,o=t.id||t.closest("[id]")?.id;"overlay"===o&&this.overlayClose||"close"===o?this.close():"cancel"===o&&(this.cancelAction(e),e.defaultPrevented||this.close())};handleSubmit=e=>{e.preventDefault(),this.confirmAction(e),e.keepDialogOpen||this.close()};handleSlotKeydown=e=>{if("Enter"===e.key&&this.confirmText){const t=e.target;if("INPUT"===t.tagName||"TEXTAREA"===t.tagName){e.preventDefault();const t=this.shadowRoot.querySelector("form");t&&t.requestSubmit()}}};handleKeydown=e=>{this.disableKeyboardClose||27!==e.keyCode||this.close()};connectedCallback(){super.connectedCallback()}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("keydown",this.handleKeydown)}updated(e){super.updated(e),e.has("opened")&&this.dispatchEvent(new CustomEvent(this.opened?"opened":"close"))}open(){this.opened=!0,window.addEventListener("keydown",this.handleKeydown),document.body.classList.add("no-scroll");const e=this.updateComplete||this.requestUpdate();e&&e.then?e.then(()=>{const e=this.shadowRoot.querySelector("[autofocus]")||firstFocusable(this.shadowRoot);e&&e.focus()}):setTimeout(()=>{const e=this.shadowRoot.querySelector("[autofocus]")||firstFocusable(this.shadowRoot);e&&e.focus()},0)}close(){this.opened=!1,this.blur(),this.closeCallback(),window.removeEventListener("keydown",this.handleKeydown),document.body.classList.remove("no-scroll")}toggle(){this.opened?this.close():this.open()}focus(){const e=firstFocusable(this.shadowRoot);e&&(this.previousFocus=document.activeElement,e.focus())}blur(){this.previousFocus&&this.previousFocus.focus()}hasTitle(){return!!this.querySelector('[slot="title"]')}static styles=css`
|
|
2
2
|
:host {
|
|
3
3
|
position: fixed;
|
|
4
4
|
top: 0;
|
|
@@ -118,4 +118,4 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-al
|
|
|
118
118
|
</form>
|
|
119
119
|
</k-focus-capture>
|
|
120
120
|
</div>
|
|
121
|
-
`}static create(e="",t={}){!1!==t.closeExisting&&document.querySelectorAll("k-dialog").forEach(e=>e.close());const{removeOnClose:o=!0,closeCallback:s=()=>{},title:i="",titleClasses:l="pyh px m0"}=t,
|
|
121
|
+
`}static create(e="",t={}){!1!==t.closeExisting&&document.querySelectorAll("k-dialog").forEach(e=>e.close());const{removeOnClose:o=!0,closeCallback:s=()=>{},title:i="",titleClasses:l="pyh px m0"}=t,n=new Dialog;if(Object.assign(n,{opened:!0,...t,closeCallback:(...e)=>{o&&n.remove(),s(...e)}}),i){const e=document.createElement("h5");e.slot="title",e.className=l,i instanceof HTMLElement?e.appendChild(i):e.innerHTML=i,n.appendChild(e)}if(void 0!==e?._$litType$){const t=document.createElement("div");t.style.display="contents",render(e,t),n.appendChild(t)}else if(e instanceof HTMLElement||e instanceof DocumentFragment)n.appendChild(e);else if(e){if(/<[^>]+>/.test(e))n.innerHTML+=e;else{const t=document.createElement("p");t.className="p",t.textContent=e,n.appendChild(t)}}return t.width&&n.style.setProperty("--width",t.width),t.minWidth&&n.style.setProperty("--min_width",t.minWidth),t.maxWidth&&n.style.setProperty("--max_width",t.maxWidth),t.height&&n.style.setProperty("--height",t.height),t.minHeight&&n.style.setProperty("--min_height",t.minHeight),t.maxHeight&&n.style.setProperty("--max_height",t.maxHeight),document.body.appendChild(n),n.open(),n}static confirm(e,t,o={}){return Dialog.create(e,{title:o.title||"Confirm",closeBtn:!1,overlayClose:!1,disableKeyboardClose:!0,confirmText:"Yes",confirmClasses:"success ml",confirmAction:()=>t(!0),cancelText:"No",cancelClasses:"danger",cancelAction:()=>t(!1),...o})}static alert(e,t=()=>{},o={}){return Dialog.create(e,{title:o.title||"Alert",closeCallback:t,cancelText:"Ok",...o})}static error(e,t=()=>{},o={}){return Dialog.create(e,{title:o.title||"Error",titleClasses:"pyh px m0 tc-danger",closeCallback:t,cancelText:"Ok",...o})}static success(e,t=()=>{},o={}){return Dialog.create(e,{title:o.title||"Success",titleClasses:"pyh px m0 tc-success",closeCallback:t,cancelText:"Ok",...o})}}customElements.define("k-dialog",Dialog);
|
package/dist/components/Table.js
CHANGED
|
@@ -19,15 +19,15 @@ import{html,css,unsafeStatic,literal}from"../lit-all.min.js";import ShadowCompon
|
|
|
19
19
|
@change=${t=>this.handleRecordSelectionChange(e,t)}
|
|
20
20
|
/>
|
|
21
21
|
</td>
|
|
22
|
-
`),this.hasBeforeControls()&&t.push(this.renderBeforeControlsTemplate()),this.fields.forEach(({name:s,formatter:i,calculator:r,type:o,editor:n,hidden:l})=>{if(l)return;let
|
|
22
|
+
`),this.hasBeforeControls()&&t.push(this.renderBeforeControlsTemplate()),this.fields.forEach(({name:s,formatter:i,calculator:r,type:o,editor:n,hidden:l,editable:a})=>{if(l)return;let d=e[s]||"";t.push(html`
|
|
23
23
|
<td class="cell" data-field=${s}>
|
|
24
|
-
${e[editing]?this.renderEditingCell(e,s,
|
|
24
|
+
${e[editing]?this.renderEditingCell(e,s,d,r,n,o,a):this.renderDisplayCell(e,s,d,r,i)}
|
|
25
25
|
</td>
|
|
26
26
|
`)}),this.hasAfterControls()&&t.push(this.renderAfterControlsTemplate()),html`
|
|
27
27
|
<tr class="record ${e[editing]?"editing":""}" data-index=${e[index]}>
|
|
28
28
|
${t}
|
|
29
29
|
</tr>
|
|
30
|
-
`}renderEditingCell(e,t,s,i,r,o){if(i)return html`<input disabled .value=${i(e,this)} />`;if(r){const e=r(s);return html`${e}`}switch(o||typeof s){case"number":return html`<input type="number" .value=${s} />`;case"date":return html`<input type="date" .value=${s} />`;case"boolean":return html`
|
|
30
|
+
`}renderEditingCell(e,t,s,i,r,o,n){if(!1===n)return this.renderDisplayCell(e,t,s,i,null);if(i)return html`<input disabled .value=${i(e,this)} />`;if(r){const e=r(s);return html`${e}`}switch(o||typeof s){case"number":return html`<input type="number" .value=${s} />`;case"date":return html`<input type="date" .value=${s} />`;case"boolean":return html`
|
|
31
31
|
<select .value=${s}>
|
|
32
32
|
<option value="true" ?selected=${s}>True</option>
|
|
33
33
|
<option value="false" ?selected=${!s}>False</option>
|
|
@@ -40,7 +40,7 @@ import{html,css,unsafeStatic,literal}from"../lit-all.min.js";import ShadowCompon
|
|
|
40
40
|
<td class="cell controls controls-after">
|
|
41
41
|
<div>${e}</div>
|
|
42
42
|
</td>
|
|
43
|
-
`}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]}"]`);t&&(t.classList.add("editing"),t.setAttribute("editing","true"),t.querySelectorAll(".cell[data-field]").forEach(t=>{const s=t.dataset.field,i=this.fields.find(e=>e.name===s);if(i){const r=e[s]||"";if(t.innerHTML="",i.calculator){const s=document.createElement("input");s.disabled=!0,s.value=i.calculator(e,this),t.appendChild(s)}else if(i.editor)t.appendChild(i.editor(r));else{const e=i.type||typeof r,s=Table.editors[e]||Table.editors.string;t.appendChild(s(r))}}})),this.dispatchEvent(new CustomEvent("editingChange",{detail:{record:e,editing:!0},bubbles:!0}))}saveEditedRecord(e){const t=this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`),s={};t&&t.querySelectorAll(".cell[data-field]").forEach(e=>{const t=e.dataset.field,i=this.fields.find(e=>e.name===t);if(i&&!i.calculator){const i=e.querySelector("input, select");i&&(s[t]=i.value)}});const i=()=>{Object.assign(e,s),e[editing]=!1,t&&(t.classList.remove("editing"),t.removeAttribute("editing"),t.querySelectorAll(".cell[data-field]").forEach(t=>{const s=t.dataset.field,i=this.fields.find(e=>e.name===s);if(i){const r=e[s]||"";i.calculator?t.textContent=i.calculator(e,this):i.formatter?t.innerHTML=i.formatter(r):t.textContent=r}})),this.dispatchEvent(new CustomEvent("editingChange",{detail:{record:e,editing:!1},bubbles:!0}))};if(Object.keys(s).some(t=>String(e[t]??"")!==s[t]))if(this.requestEdit){t?.classList.add("pending");const r=()=>{t?.classList.remove("pending"),i()},o=()=>{t?.classList.remove("pending")};this.dispatchEvent(new CustomEvent("requestSave",{detail:{record:e,newData:s,approve:r,reject:o},bubbles:!0}))}else i();else this.cancelEditedRecord(e)}cancelEditedRecord(e){e[editing]=!1;const t=this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`);t&&(t.classList.remove("editing"),t.removeAttribute("editing"),t.querySelectorAll(".cell[data-field]").forEach(t=>{const s=t.dataset.field,i=this.fields.find(e=>e.name===s);if(i){const r=e[s]||"";i.calculator?t.textContent=i.calculator(e,this):i.formatter?t.innerHTML=i.formatter(r):t.textContent=r}})),this.dispatchEvent(new CustomEvent("editingChange",{detail:{record:e,editing:!1},bubbles:!0}))}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.requestUpdate(),this.dispatchEvent(new CustomEvent("pageChange",{bubbles:!0})))}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.requestUpdate(),this.dispatchEvent(new CustomEvent("pageSizeChange",{bubbles:!0}))}getPageSize(){return this.pageSize}getPageSizeOptions(){return this.pageSizeOptions}getFieldLabel(e){const t=this.fields.find(t=>t.name===e);return t?t.label:toTitleCase(e)}setPageSizeOptions(e){this.pageSizeOptions=e,this.requestUpdate()}setData({records:e=!1,fields:t=!1,pageSize:s=!1,pageSizeOptions:i=!1,currentPage:r=!1,enableSelection:o}={}){let n=!1,l=this.getTotalPages(),a=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),n=!0),s&&(this.pageSize=s,n=!0),i&&(this.pageSizeOptions=i),r&&(this.currentPage=r,n=!0),void 0!==o&&(this.enableSelection=o,n=!0),n&&this.requestUpdate();const d=this.getTotalPages();d!==l&&this.dispatchEvent(new CustomEvent("pageCountChanged",{detail:{totalPages:this.getTotalPages()},bubbles:!0})),a>d&&this.setPage(d)}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.requestUpdate(),this.dispatchEvent(new CustomEvent("recordsSet",{detail:{records:e},bubbles:!0}));const r=this.getTotalPages();r!==s&&this.dispatchEvent(new CustomEvent("pageCountChanged",{detail:{totalPages:this.getTotalPages()},bubbles:!0})),i>r&&this.setPage(r)}setupFetchRecords(e,t){const s=this.records.length,i=this.getTotalPages();if(s<e){this.records.length=e,this.records.fill(null,s),this.requestUpdate();const t=this.getTotalPages();t!==i&&setTimeout(()=>{this.dispatchEvent(new CustomEvent("pageCountChanged",{detail:{totalPages:t},bubbles:!0}))},0)}this.addEventListener("fetchRecords",async e=>{if(this.fetchPending)return;this.fetchPending=!0;const{start:s,count:i}=e.detail,r=await t(s,i);r.forEach((e,t)=>{e[index]=s+t,void 0===e[selected]&&(e[selected]=!1),void 0===e[hidden]&&(e[hidden]=!1),void 0===e[editing]&&(e[editing]=!1)}),this.records.splice(s,r.length,...r),this.fetchPending=!1,this.requestUpdate()})}addRecord(e){e[selected]=!1,e[hidden]=!1,e[index]=this.records.length,this.records.push(e),this.requestUpdate(),this.dispatchEvent(new CustomEvent("recordAdded",{detail:{record:e},bubbles:!0}))}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.requestUpdate()}}deleteRecord(e){let t=this.records.find(t=>t===e);const s=this.getTotalPages();if(t||void 0===e[index]||(t=this.records[e[index]]),!t)return;const i=()=>{const e=this.records.indexOf(t);this.records.splice(e,1),this.records.forEach((e,t)=>{e[index]=t}),this.requestUpdate(),this.dispatchEvent(new CustomEvent("selectionChange",{bubbles:!0})),this.dispatchEvent(new CustomEvent("recordDeleted",{detail:{index:e},bubbles:!0}));const i=this.getTotalPages();this.currentPage>i&&this.setPage(i),i!==s&&this.dispatchEvent(new CustomEvent("pageCountChanged",{detail:{totalPages:i},bubbles:!0}))};if(this.requestDelete){const e=this.shadowRoot.querySelector(`.record[data-index="${t[index]}"]`);e?.classList.add("pending");const s=()=>{e?.classList.remove("pending"),i()},r=()=>{e?.classList.remove("pending")};this.dispatchEvent(new CustomEvent("requestDelete",{detail:{records:[t],approve:s,reject:r},bubbles:!0}))}else i()}deleteSelected(){const e=this.getTotalPages(),t=this.getSelectedRecords().map(e=>this.records.find(t=>t===e)??(void 0!==e[index]?this.records[e[index]]:null)).filter(Boolean);if(!t.length)return;const s=()=>{t.forEach(e=>{const t=this.records.indexOf(e);-1!==t&&this.records.splice(t,1)}),this.records.forEach((e,t)=>{e[index]=t}),this.requestUpdate();const s=this.getTotalPages();this.currentPage>s&&this.setPage(s),s!==e&&this.dispatchEvent(new CustomEvent("pageCountChanged",{detail:{totalPages:s},bubbles:!0})),this.dispatchEvent(new CustomEvent("selectionChange",{bubbles:!0}))};if(this.requestDelete){const e=t.map(e=>this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`)).filter(Boolean);e.forEach(e=>e.classList.add("pending"));const i=()=>{e.forEach(e=>e.classList.remove("pending")),s()},r=()=>{e.forEach(e=>e.classList.remove("pending"))};this.dispatchEvent(new CustomEvent("requestDelete",{detail:{records:t,approve:i,reject:r},bubbles:!0}))}else s()}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.requestUpdate(),setTimeout(()=>{this.dispatchEvent(new CustomEvent("selectionChange",{bubbles:!0}))},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.requestUpdate(),setTimeout(()=>{this.dispatchEvent(new CustomEvent("selectionChange",{bubbles:!0}))},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.requestUpdate()}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.requestUpdate(),this.dispatchEvent(new CustomEvent("recordHidden",{bubbles:!0})))}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.requestUpdate(),this.dispatchEvent(new CustomEvent("recordShown",{bubbles:!0})))}showAllRecords(){this.records.forEach(e=>{e[hidden]=!1}),this.filters.length&&(this.filters=[],this.dispatchEvent(new CustomEvent("filterRemoved",{bubbles:!0})),this.dispatchEvent(new CustomEvent("filterChange",{bubbles:!0}))),this.requestUpdate(),this.dispatchEvent(new CustomEvent("recordShown",{bubbles:!0})),this.dispatchEvent(new CustomEvent("allRecordsShown",{bubbles:!0}))}addFilter(e,t,s){this.filters.push({field:e,condition:t,value:s}),this.dispatchEvent(new CustomEvent("filterAdded",{bubbles:!0})),this.dispatchEvent(new CustomEvent("filterChange",{bubbles:!0})),this.requestUpdate()}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),this.dispatchEvent(new CustomEvent("filterRemoved",{bubbles:!0})),this.dispatchEvent(new CustomEvent("filterChange",{bubbles:!0})),i&&this.requestUpdate())}testFilter(e,t,s,i){let r=e[t],o=i;switch(this.caseSensitiveFilters||"string"!=typeof r||"string"!=typeof i||(r=r.toLowerCase(),o=i.toLowerCase()),s){case"equals":return r===o;case"not-equals":return r!==o;case"contains":return r.includes(o);case"not-contains":return!r.includes(o);case"greater-than":return r>o;case"less-than":return r<o;case"greater-than-or-equal":return r>=o;case"less-than-or-equal":return r<=o;default:return!0}}removeAllFilters(){this.filters.length&&([...this.filters].forEach(({field:e,condition:t,value:s})=>{this.removeFilter(e,t,s,!1)}),this.requestUpdate())}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&&(this.dispatchEvent(new CustomEvent("recordHidden",{bubbles:!0})),this.requestUpdate()),this.dispatchEvent(new CustomEvent("search",{detail:{term:e},bubbles:!0}))}getDisplayedRecords(){this.filters.forEach(({field:e,condition:t,value:s})=>{this.records.forEach(i=>{null!==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)=>null===e||null===i?0: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(){const e=Array.from(this.querySelectorAll('[slot="before"]')),t=Array.from(this.querySelectorAll('[slot="after"]')),s={beforeControls:e.reduce((e,t)=>e+(t.maxWidth||40),0),afterControls:t.reduce((e,t)=>e+(t.maxWidth||40),0)},i=[...e,...t].some(e=>void 0===e.maxWidth);return JSON.stringify(this.columnSizes)!==JSON.stringify(s)&&(this.columnSizes=s),i&&setTimeout(()=>this.calculateColumnSizes(),0),this.columnSizes}setFieldHiddenState(e,t){const s=this.fields.find(t=>t.name===e);s&&(s.hidden=t,this.calculateColumnSizes(),this.requestUpdate(),this.dispatchEvent(new CustomEvent("fieldVisibilityChanged",{detail:{field:s},bubbles:!0})),this.dispatchEvent(new CustomEvent(t?"fieldHidden":"fieldShown",{detail:{field:s},bubbles:!0})))}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.requestUpdate()}render(){return this.records&&this.fields?(this.calculateColumnSizes(),this.hasTopControls()?this.setAttribute("top-controls","true"):this.removeAttribute("top-controls"),this.hasBottomControls()?this.setAttribute("bottom-controls","true"):this.removeAttribute("bottom-controls"),html`
|
|
43
|
+
`}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]}"]`);t&&(t.classList.add("editing"),t.setAttribute("editing","true"),t.querySelectorAll(".cell[data-field]").forEach(t=>{const s=t.dataset.field,i=this.fields.find(e=>e.name===s);if(i){if(!1===i.editable)return;const r=e[s]||"";if(t.innerHTML="",i.calculator){const s=document.createElement("input");s.disabled=!0,s.value=i.calculator(e,this),t.appendChild(s)}else if(i.editor)t.appendChild(i.editor(r));else{const e=i.type||typeof r,s=Table.editors[e]||Table.editors.string;t.appendChild(s(r))}}})),this.dispatchEvent(new CustomEvent("editingChange",{detail:{record:e,editing:!0},bubbles:!0}))}saveEditedRecord(e){const t=this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`),s={};t&&t.querySelectorAll(".cell[data-field]").forEach(e=>{const t=e.dataset.field,i=this.fields.find(e=>e.name===t);if(i&&!i.calculator){const i=e.querySelector("input, select");i&&(s[t]=i.value)}});const i=()=>{Object.assign(e,s),e[editing]=!1,t&&(t.classList.remove("editing"),t.removeAttribute("editing"),t.querySelectorAll(".cell[data-field]").forEach(t=>{const s=t.dataset.field,i=this.fields.find(e=>e.name===s);if(i){const r=e[s]||"";i.calculator?t.textContent=i.calculator(e,this):i.formatter?t.innerHTML=i.formatter(r):t.textContent=r}})),this.dispatchEvent(new CustomEvent("editingChange",{detail:{record:e,editing:!1},bubbles:!0}))};if(Object.keys(s).some(t=>String(e[t]??"")!==s[t]))if(this.requestEdit){t?.classList.add("pending");const r=()=>{t?.classList.remove("pending"),i()},o=()=>{t?.classList.remove("pending")};this.dispatchEvent(new CustomEvent("requestSave",{detail:{record:e,newData:s,approve:r,reject:o},bubbles:!0}))}else i();else this.cancelEditedRecord(e)}cancelEditedRecord(e){e[editing]=!1;const t=this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`);t&&(t.classList.remove("editing"),t.removeAttribute("editing"),t.querySelectorAll(".cell[data-field]").forEach(t=>{const s=t.dataset.field,i=this.fields.find(e=>e.name===s);if(i){const r=e[s]||"";i.calculator?t.textContent=i.calculator(e,this):i.formatter?t.innerHTML=i.formatter(r):t.textContent=r}})),this.dispatchEvent(new CustomEvent("editingChange",{detail:{record:e,editing:!1},bubbles:!0}))}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.requestUpdate(),this.dispatchEvent(new CustomEvent("pageChange",{bubbles:!0})))}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.requestUpdate(),this.dispatchEvent(new CustomEvent("pageSizeChange",{bubbles:!0}))}getPageSize(){return this.pageSize}getPageSizeOptions(){return this.pageSizeOptions}getFieldLabel(e){const t=this.fields.find(t=>t.name===e);return t?t.label:toTitleCase(e)}setPageSizeOptions(e){this.pageSizeOptions=e,this.requestUpdate()}setData({records:e=!1,fields:t=!1,pageSize:s=!1,pageSizeOptions:i=!1,currentPage:r=!1,enableSelection:o}={}){let n=!1,l=this.getTotalPages(),a=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),n=!0),s&&(this.pageSize=s,n=!0),i&&(this.pageSizeOptions=i),r&&(this.currentPage=r,n=!0),void 0!==o&&(this.enableSelection=o,n=!0),n&&this.requestUpdate();const d=this.getTotalPages();d!==l&&this.dispatchEvent(new CustomEvent("pageCountChanged",{detail:{totalPages:this.getTotalPages()},bubbles:!0})),a>d&&this.setPage(d)}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.requestUpdate(),this.dispatchEvent(new CustomEvent("recordsSet",{detail:{records:e},bubbles:!0}));const r=this.getTotalPages();r!==s&&this.dispatchEvent(new CustomEvent("pageCountChanged",{detail:{totalPages:this.getTotalPages()},bubbles:!0})),i>r&&this.setPage(r)}setupFetchRecords(e,t){const s=this.records.length,i=this.getTotalPages();if(s<e){this.records.length=e,this.records.fill(null,s),this.requestUpdate();const t=this.getTotalPages();t!==i&&setTimeout(()=>{this.dispatchEvent(new CustomEvent("pageCountChanged",{detail:{totalPages:t},bubbles:!0}))},0)}this.addEventListener("fetchRecords",async e=>{if(this.fetchPending)return;this.fetchPending=!0;const{start:s,count:i}=e.detail,r=await t(s,i);r.forEach((e,t)=>{e[index]=s+t,void 0===e[selected]&&(e[selected]=!1),void 0===e[hidden]&&(e[hidden]=!1),void 0===e[editing]&&(e[editing]=!1)}),this.records.splice(s,r.length,...r),this.fetchPending=!1,this.requestUpdate()})}addRecord(e){e[selected]=!1,e[hidden]=!1,e[index]=this.records.length,this.records.push(e),this.requestUpdate(),this.dispatchEvent(new CustomEvent("recordAdded",{detail:{record:e},bubbles:!0}))}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.requestUpdate()}}deleteRecord(e){let t=this.records.find(t=>t===e);const s=this.getTotalPages();if(t||void 0===e[index]||(t=this.records[e[index]]),!t)return;const i=()=>{const e=this.records.indexOf(t);this.records.splice(e,1),this.records.forEach((e,t)=>{e[index]=t}),this.requestUpdate(),this.dispatchEvent(new CustomEvent("selectionChange",{bubbles:!0})),this.dispatchEvent(new CustomEvent("recordDeleted",{detail:{index:e},bubbles:!0}));const i=this.getTotalPages();this.currentPage>i&&this.setPage(i),i!==s&&this.dispatchEvent(new CustomEvent("pageCountChanged",{detail:{totalPages:i},bubbles:!0}))};if(this.requestDelete){const e=this.shadowRoot.querySelector(`.record[data-index="${t[index]}"]`);e?.classList.add("pending");const s=()=>{e?.classList.remove("pending"),i()},r=()=>{e?.classList.remove("pending")};this.dispatchEvent(new CustomEvent("requestDelete",{detail:{records:[t],approve:s,reject:r},bubbles:!0}))}else i()}deleteSelected(){const e=this.getTotalPages(),t=this.getSelectedRecords().map(e=>this.records.find(t=>t===e)??(void 0!==e[index]?this.records[e[index]]:null)).filter(Boolean);if(!t.length)return;const s=()=>{t.forEach(e=>{const t=this.records.indexOf(e);-1!==t&&this.records.splice(t,1)}),this.records.forEach((e,t)=>{e[index]=t}),this.requestUpdate();const s=this.getTotalPages();this.currentPage>s&&this.setPage(s),s!==e&&this.dispatchEvent(new CustomEvent("pageCountChanged",{detail:{totalPages:s},bubbles:!0})),this.dispatchEvent(new CustomEvent("selectionChange",{bubbles:!0}))};if(this.requestDelete){const e=t.map(e=>this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`)).filter(Boolean);e.forEach(e=>e.classList.add("pending"));const i=()=>{e.forEach(e=>e.classList.remove("pending")),s()},r=()=>{e.forEach(e=>e.classList.remove("pending"))};this.dispatchEvent(new CustomEvent("requestDelete",{detail:{records:t,approve:i,reject:r},bubbles:!0}))}else s()}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.requestUpdate(),setTimeout(()=>{this.dispatchEvent(new CustomEvent("selectionChange",{bubbles:!0}))},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.requestUpdate(),setTimeout(()=>{this.dispatchEvent(new CustomEvent("selectionChange",{bubbles:!0}))},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.requestUpdate()}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.requestUpdate(),this.dispatchEvent(new CustomEvent("recordHidden",{bubbles:!0})))}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.requestUpdate(),this.dispatchEvent(new CustomEvent("recordShown",{bubbles:!0})))}showAllRecords(){this.records.forEach(e=>{e[hidden]=!1}),this.filters.length&&(this.filters=[],this.dispatchEvent(new CustomEvent("filterRemoved",{bubbles:!0})),this.dispatchEvent(new CustomEvent("filterChange",{bubbles:!0}))),this.requestUpdate(),this.dispatchEvent(new CustomEvent("recordShown",{bubbles:!0})),this.dispatchEvent(new CustomEvent("allRecordsShown",{bubbles:!0}))}addFilter(e,t,s){this.filters.push({field:e,condition:t,value:s}),this.dispatchEvent(new CustomEvent("filterAdded",{bubbles:!0})),this.dispatchEvent(new CustomEvent("filterChange",{bubbles:!0})),this.requestUpdate()}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),this.dispatchEvent(new CustomEvent("filterRemoved",{bubbles:!0})),this.dispatchEvent(new CustomEvent("filterChange",{bubbles:!0})),i&&this.requestUpdate())}testFilter(e,t,s,i){let r=e[t],o=i;switch(this.caseSensitiveFilters||"string"!=typeof r||"string"!=typeof i||(r=r.toLowerCase(),o=i.toLowerCase()),s){case"equals":return r===o;case"not-equals":return r!==o;case"contains":return r.includes(o);case"not-contains":return!r.includes(o);case"greater-than":return r>o;case"less-than":return r<o;case"greater-than-or-equal":return r>=o;case"less-than-or-equal":return r<=o;default:return!0}}removeAllFilters(){this.filters.length&&([...this.filters].forEach(({field:e,condition:t,value:s})=>{this.removeFilter(e,t,s,!1)}),this.requestUpdate())}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&&(this.dispatchEvent(new CustomEvent("recordHidden",{bubbles:!0})),this.requestUpdate()),this.dispatchEvent(new CustomEvent("search",{detail:{term:e},bubbles:!0}))}getDisplayedRecords(){this.filters.forEach(({field:e,condition:t,value:s})=>{this.records.forEach(i=>{null!==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)=>null===e||null===i?0: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(){const e=Array.from(this.querySelectorAll('[slot="before"]')),t=Array.from(this.querySelectorAll('[slot="after"]')),s={beforeControls:e.reduce((e,t)=>e+(t.maxWidth||40),0),afterControls:t.reduce((e,t)=>e+(t.maxWidth||40),0)},i=[...e,...t].some(e=>void 0===e.maxWidth);return JSON.stringify(this.columnSizes)!==JSON.stringify(s)&&(this.columnSizes=s),i&&setTimeout(()=>this.calculateColumnSizes(),0),this.columnSizes}setFieldHiddenState(e,t){const s=this.fields.find(t=>t.name===e);s&&(s.hidden=t,this.calculateColumnSizes(),this.requestUpdate(),this.dispatchEvent(new CustomEvent("fieldVisibilityChanged",{detail:{field:s},bubbles:!0})),this.dispatchEvent(new CustomEvent(t?"fieldHidden":"fieldShown",{detail:{field:s},bubbles:!0})))}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.requestUpdate()}render(){return this.records&&this.fields?(this.calculateColumnSizes(),this.hasTopControls()?this.setAttribute("top-controls","true"):this.removeAttribute("top-controls"),this.hasBottomControls()?this.setAttribute("bottom-controls","true"):this.removeAttribute("bottom-controls"),html`
|
|
44
44
|
<div id="wrapper">
|
|
45
45
|
<div id="top"><slot name="top"></slot></div>
|
|
46
46
|
<div id="table-container">
|
|
@@ -122,7 +122,8 @@ import{html,css,unsafeStatic,literal}from"../lit-all.min.js";import ShadowCompon
|
|
|
122
122
|
tbody tr:last-child td {
|
|
123
123
|
border-bottom: none;
|
|
124
124
|
}
|
|
125
|
-
tr.editing td.cell[data-field]
|
|
125
|
+
tr.editing td.cell[data-field]:has(input),
|
|
126
|
+
tr.editing td.cell[data-field]:has(select) {
|
|
126
127
|
padding: 0;
|
|
127
128
|
}
|
|
128
129
|
tr.editing td.cell[data-field] input,
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
<a href="#actionButtons">Action Buttons</a><br />
|
|
28
28
|
<a href="#disableKeyboardClose">Disabling Keyboard Close</a><br />
|
|
29
29
|
<a href="#jsUsage">JavaScript Usage</a><br />
|
|
30
|
+
<a href="#litTemplate">Lit Template Content</a><br />
|
|
30
31
|
<a href="#confirmDialog">Confirm Dialog</a><br />
|
|
31
32
|
|
|
32
33
|
<h6 class="mt"><a href="#jsRef" class="no-link">JavaScript Reference</a></h6>
|
|
@@ -135,6 +136,25 @@
|
|
|
135
136
|
});
|
|
136
137
|
</script>
|
|
137
138
|
|
|
139
|
+
<h3 id="litTemplate"><a href="#litTemplate" class="no-link">Lit Template Content</a></h3>
|
|
140
|
+
<p>You can pass a Lit <code>html</code> tagged template literal directly as the content to any of the static methods (<code>Dialog.create</code>, <code>Dialog.confirm</code>, <code>Dialog.alert</code>, <code>Dialog.error</code>, <code>Dialog.success</code>).</p>
|
|
141
|
+
<pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"d11"</span>></span>Open Dialog with Lit Template<span class="hljs-tag"></<span class="hljs-name">button</span>></span><br /><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span>></span><span class="javascript"><br /> <span class="hljs-keyword">import</span> Dialog <span class="hljs-keyword">from</span> <span class="hljs-string">'/src/components/Dialog.js'</span>;<br /> <span class="hljs-keyword">import</span> { html } <span class="hljs-keyword">from</span> <span class="hljs-string">'/src/lit-all.min.js'</span>;<br /> <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'d11'</span>).addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function">() =></span> {<br /> Dialog.create(html<span class="hljs-string">`<br /> <div class="p"><br /> <h6>Custom Lit Content</h6><br /> <p>This was created with a Lit template.</p><br /> </div><br /> `</span>, {<br /> <span class="hljs-attr">title</span>: <span class="hljs-string">'Lit Template Dialog'</span><br /> });<br /> });<br /></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span></code></pre>
|
|
142
|
+
<button id="d11" class="mb">Open Dialog with Lit Template</button>
|
|
143
|
+
<script type="module">
|
|
144
|
+
import Dialog from '../src/components/Dialog.js';
|
|
145
|
+
import { html } from '../src/lit-all.min.js';
|
|
146
|
+
document.getElementById('d11').addEventListener('click', () => {
|
|
147
|
+
Dialog.create(html`
|
|
148
|
+
<div class="p">
|
|
149
|
+
<h6>Custom Lit Content</h6>
|
|
150
|
+
<p>This was created with a Lit template.</p>
|
|
151
|
+
</div>
|
|
152
|
+
`, {
|
|
153
|
+
title: 'Lit Template Dialog'
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
</script>
|
|
157
|
+
|
|
138
158
|
<h3 id="confirmDialog"><a href="#confirmDialog" class="no-link">Confirm Dialog</a></h3>
|
|
139
159
|
<p>Use the <code>Dialog.confirm</code> static method to create a confirmation dialog. This method takes a text message, a response callback, and an options object.</p>
|
|
140
160
|
<pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"d9"</span>></span>Open Confirm Dialog<span class="hljs-tag"></<span class="hljs-name">button</span>></span><br /><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span>></span><span class="javascript"><br /> <span class="hljs-keyword">import</span> Dialog <span class="hljs-keyword">from</span> <span class="hljs-string">'/src/components/Dialog.js'</span>;<br /> <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'d9'</span>).addEventListener(<span class="hljs-string">'click'</span>, () => {<br /> Dialog.confirm(<span class="hljs-string">"Are you sure you want to proceed?"</span>, <span class="hljs-function"><span class="hljs-params">response</span> =></span> {<br /> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"User response:"</span>, response);<br /> });<br /> });<br /></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span></code></pre>
|
|
@@ -240,11 +260,11 @@
|
|
|
240
260
|
<h3 id="staticMethods"><a href="#staticMethods" class="no-link">Static Methods</a></h3>
|
|
241
261
|
<h5><code>Dialog.create(contents, options)<i>: Dialog</i></code></h5>
|
|
242
262
|
<p>Creates and opens a new dialog with the specified contents and options. Returns the created Dialog instance.</p>
|
|
243
|
-
<p>If <code>contents</code> is plain text (no HTML tags), it will automatically be wrapped in a <code><p class="p"></code> tag. If it contains HTML, it will be inserted as-is. If it's an HTMLElement or DocumentFragment, it will be appended directly
|
|
263
|
+
<p>If <code>contents</code> is plain text (no HTML tags), it will automatically be wrapped in a <code><p class="p"></code> tag. If it contains HTML, it will be inserted as-is. If it's an HTMLElement or DocumentFragment, it will be appended directly. You can also pass a Lit <code>html</code> tagged template literal (<code>TemplateResult</code>), which will be rendered using Lit's <code>render()</code>.</p>
|
|
244
264
|
|
|
245
265
|
<h6>Parameters</h6>
|
|
246
266
|
<ul>
|
|
247
|
-
<li><code>contents<i>: string | HTMLElement | DocumentFragment</i></code> - The content to display in the dialog. Plain text will be wrapped in a paragraph tag automatically.</li>
|
|
267
|
+
<li><code>contents<i>: string | HTMLElement | DocumentFragment | TemplateResult</i></code> - The content to display in the dialog. Plain text will be wrapped in a paragraph tag automatically.</li>
|
|
248
268
|
<li><code>options<i>: object</i></code> - Configuration options for the dialog.</li>
|
|
249
269
|
</ul>
|
|
250
270
|
|
|
@@ -278,7 +298,7 @@
|
|
|
278
298
|
|
|
279
299
|
<h6>Parameters</h6>
|
|
280
300
|
<ul>
|
|
281
|
-
<li><code>text<i>: string</i></code> - The message to display in the dialog.</li>
|
|
301
|
+
<li><code>text<i>: string | TemplateResult</i></code> - The message to display in the dialog.</li>
|
|
282
302
|
<li><code>responseCallback<i>: function</i></code> - Function called with the user's response (<code>true</code> for confirm, <code>false</code> for cancel).</li>
|
|
283
303
|
<li><code>options<i>: object</i></code> - Configuration options.</li>
|
|
284
304
|
</ul>
|
|
@@ -300,7 +320,7 @@
|
|
|
300
320
|
|
|
301
321
|
<h6>Parameters</h6>
|
|
302
322
|
<ul>
|
|
303
|
-
<li><code>text<i>: string</i></code> - The message to display in the dialog.</li>
|
|
323
|
+
<li><code>text<i>: string | TemplateResult</i></code> - The message to display in the dialog.</li>
|
|
304
324
|
<li><code>responseCallback<i>: function</i></code> - Function called when the dialog is closed.</li>
|
|
305
325
|
<li><code>options<i>: object</i></code> - Configuration options.</li>
|
|
306
326
|
</ul>
|
|
@@ -316,7 +336,7 @@
|
|
|
316
336
|
|
|
317
337
|
<h6>Parameters</h6>
|
|
318
338
|
<ul>
|
|
319
|
-
<li><code>text<i>: string</i></code> - The error message to display in the dialog.</li>
|
|
339
|
+
<li><code>text<i>: string | TemplateResult</i></code> - The error message to display in the dialog.</li>
|
|
320
340
|
<li><code>responseCallback<i>: function</i></code> - Function called when the dialog is closed.</li>
|
|
321
341
|
<li><code>options<i>: object</i></code> - Configuration options.</li>
|
|
322
342
|
</ul>
|
|
@@ -332,7 +352,7 @@
|
|
|
332
352
|
|
|
333
353
|
<h6>Parameters</h6>
|
|
334
354
|
<ul>
|
|
335
|
-
<li><code>text<i>: string</i></code> - The success message to display in the dialog.</li>
|
|
355
|
+
<li><code>text<i>: string | TemplateResult</i></code> - The success message to display in the dialog.</li>
|
|
336
356
|
<li><code>responseCallback<i>: function</i></code> - Function called when the dialog is closed.</li>
|
|
337
357
|
<li><code>options<i>: object</i></code> - Configuration options.</li>
|
|
338
358
|
</ul>
|
|
@@ -134,7 +134,18 @@
|
|
|
134
134
|
|
|
135
135
|
<h3 id="properties"><a href="#properties" class="no-link">Properties</a></h3>
|
|
136
136
|
<h5><code>fields<i>: <Array>object</i></code></h5>
|
|
137
|
-
<p>An array of objects
|
|
137
|
+
<p>An array of objects that represent the fields to be displayed. Each field object supports the following properties:</p>
|
|
138
|
+
<ul>
|
|
139
|
+
<li><code>name</code> <i>(string, required)</i>: The key in the record object to display.</li>
|
|
140
|
+
<li><code>label</code> <i>(string)</i>: The column header label. Defaults to <code>toTitleCase(name)</code>.</li>
|
|
141
|
+
<li><code>size</code> <i>(number)</i>: The column width in pixels.</li>
|
|
142
|
+
<li><code>hidden</code> <i>(boolean)</i>: Hides the column when <code>true</code>.</li>
|
|
143
|
+
<li><code>editable</code> <i>(boolean)</i>: Set to <code>false</code> to make the field read-only during record editing. Defaults to <code>true</code>.</li>
|
|
144
|
+
<li><code>type</code> <i>(string)</i>: The editor type to use (<code>"string"</code>, <code>"number"</code>, <code>"date"</code>, <code>"boolean"</code>). Inferred from the value if omitted.</li>
|
|
145
|
+
<li><code>formatter</code> <i>(function)</i>: A function <code>(value) => displayString</code> that formats the display value.</li>
|
|
146
|
+
<li><code>calculator</code> <i>(function)</i>: A function <code>(record, table) => value</code> that computes a derived value. Calculated fields are always read-only.</li>
|
|
147
|
+
<li><code>editor</code> <i>(function)</i>: A custom editor generator function <code>(value) => HTMLInputElement</code> for per-field editing.</li>
|
|
148
|
+
</ul>
|
|
138
149
|
|
|
139
150
|
<h5><code>records<i>: <Array>object</i></code></h5>
|
|
140
151
|
<p>An array of objects containing the data that will be the records of the table, where the object keys match the field names.</p>
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
<h1 class="ta-center">Table - Top and Bottom Controls</h1>
|
|
55
55
|
<k-main>
|
|
56
56
|
|
|
57
|
-
<a href="./
|
|
57
|
+
<a href="./table.html"><h5>Back to Table Component Documentation</h5></a>
|
|
58
58
|
|
|
59
59
|
<p>The table component supports controls at both the top and bottom. These controls can handle pagination, adding new records, or other actions. They are web components that interact with their parent <code>k-table</code> by accessing its methods and listening for its events.</p>
|
|
60
60
|
<p>Controls without a slot will be placed at the bottom of the table by default, to place a control on the top of the table give it a <code>slot="top"</code>.</p>
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
<h1 class="ta-center">Table - Custom Fields</h1>
|
|
55
55
|
<k-main>
|
|
56
56
|
|
|
57
|
-
<a href="./
|
|
57
|
+
<a href="./table.html"><h5>Back to Table Component Documentation</h5></a>
|
|
58
58
|
|
|
59
|
-
<p>Use the <code>fields</code> property of the options object in <code>setData</code> to define custom fields. This is useful for hiding fields, reordering fields, changing
|
|
59
|
+
<p>Use the <code>fields</code> property of the options object in <code>setData</code> to define custom fields. This is useful for hiding fields, reordering fields, changing labels, formatting output, creating calculated fields, or marking fields as non-editable.</p>
|
|
60
60
|
|
|
61
61
|
<pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">k-table</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"customFieldsExample"</span>></span><span class="hljs-tag"></<span class="hljs-name">k-table</span>></span><br /><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span>></span><span class="javascript"><br /> <span class="hljs-keyword">await</span> <span class="hljs-built_in">window</span>.customElements.whenDefined(<span class="hljs-string">'k-table'</span>);<br /> <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'customFieldsExample'</span>).setData({<br /> <span class="hljs-attr">records</span>: contacts,<br /> <span class="hljs-attr">fields</span>: [<br /> {<br /> <span class="hljs-attr">name</span>: <span class="hljs-string">"name"</span>,<br /> <span class="hljs-attr">label</span>: <span class="hljs-string">"Name"</span>,<br /> <span class="hljs-attr">size</span>: <span class="hljs-number">300</span><br /> },<br /> {<br /> <span class="hljs-attr">name</span>: <span class="hljs-string">'phoneNumber'</span>,<br /> <span class="hljs-attr">label</span>: <span class="hljs-string">'Phone Number'</span><br /> },<br /> {<br /> <span class="hljs-attr">name</span>: <span class="hljs-string">'email'</span>,<br /> <span class="hljs-attr">label</span>: <span class="hljs-string">'Email Address'</span><br /> },<br /> {<br /> <span class="hljs-attr">name</span>: <span class="hljs-string">'birthday'</span>,<br /> <span class="hljs-attr">label</span>: <span class="hljs-string">'Birthday'</span><br /> },<br /> {<br /> <span class="hljs-attr">name</span>: <span class="hljs-string">'age'</span>,<br /> <span class="hljs-attr">label</span>: <span class="hljs-string">'Age'</span>,<br /> <span class="hljs-attr">calculator</span>: <span class="hljs-function">(<span class="hljs-params">record</span>) =></span> {<br /> <span class="hljs-comment">// record.birthday is a string in the format "YYYY-MM-DD"</span><br /> <span class="hljs-keyword">const</span> today = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>();<br /> <span class="hljs-keyword">const</span> birthDate = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(record.birthday);<br /> <span class="hljs-keyword">let</span> age = today.getFullYear() - birthDate.getFullYear();<br /> <span class="hljs-keyword">const</span> monthDifference = today.getMonth() - birthDate.getMonth();<br /> <span class="hljs-keyword">if</span> (monthDifference < <span class="hljs-number">0</span> || (monthDifference === <span class="hljs-number">0</span> && today.getDate() < birthDate.getDate())) {<br /> age--;<br /> }<br /> <span class="hljs-keyword">return</span> age;<br /> }<br /> },<br /> {<br /> <span class="hljs-attr">name</span>: <span class="hljs-string">'gender'</span>,<br /> <span class="hljs-attr">label</span>: <span class="hljs-string">'Gender'</span>,<br /> <span class="hljs-attr">formatter</span>: <span class="hljs-function">(<span class="hljs-params">v</span>) =></span> v===<span class="hljs-string">'m'</span>?<span class="hljs-string">'Male'</span>:<span class="hljs-string">'Female'</span> <span class="hljs-comment">// original value is "m" or "f"</span><br /> }<br /> ]<br /> });<br /></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span></code></pre>
|
|
62
62
|
|
|
@@ -106,11 +106,36 @@
|
|
|
106
106
|
]
|
|
107
107
|
});
|
|
108
108
|
</script>
|
|
109
|
+
|
|
110
|
+
<h3>Non-Editable Fields</h3>
|
|
111
|
+
<p>Set <code>editable: false</code> on a field definition to make it read-only when a row is in edit mode. The cell displays its value normally — no input is rendered. This is useful for fields like IDs that should never be changed by the user.</p>
|
|
112
|
+
|
|
113
|
+
<pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">k-table</span> <span class="hljs-attr">id</span>=<span class="hljs-string">editableFieldExample</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">k-tc-edit</span> <span class="hljs-attr">slot</span>=<span class="hljs-string">after</span>></span><span class="hljs-tag"></<span class="hljs-name">k-tc-edit</span>></span><br><span class="hljs-tag"></<span class="hljs-name">k-table</span>></span><br><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">module</span>></span><span class="language-javascript"><br> <span class="hljs-keyword">await</span> <span class="hljs-variable language_">window</span>.<span class="hljs-property">customElements</span>.<span class="hljs-title function_">whenDefined</span>(<span class="hljs-string">'k-table'</span>);<br> <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">'editableFieldExample'</span>).<span class="hljs-title function_">setData</span>({<br> <span class="hljs-attr">records</span>: [{<br> <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>,<br> <span class="hljs-attr">name</span>: <span class="hljs-string">'Alice'</span>,<br> <span class="hljs-attr">city</span>: <span class="hljs-string">'New York'</span><br> }, {<br> <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>,<br> <span class="hljs-attr">name</span>: <span class="hljs-string">'Bob'</span>,<br> <span class="hljs-attr">city</span>: <span class="hljs-string">'Los Angeles'</span><br> }, {<br> <span class="hljs-attr">id</span>: <span class="hljs-number">3</span>,<br> <span class="hljs-attr">name</span>: <span class="hljs-string">'Charlie'</span>,<br> <span class="hljs-attr">city</span>: <span class="hljs-string">'Chicago'</span><br> }],<br> <span class="hljs-attr">fields</span>: [{<br> <span class="hljs-attr">name</span>: <span class="hljs-string">'id'</span>,<br> <span class="hljs-attr">label</span>: <span class="hljs-string">'ID'</span>,<br> <span class="hljs-attr">editable</span>: <span class="hljs-literal">false</span><br> }, {<br> <span class="hljs-attr">name</span>: <span class="hljs-string">'name'</span>,<br> <span class="hljs-attr">label</span>: <span class="hljs-string">'Name'</span><br> }, {<br> <span class="hljs-attr">name</span>: <span class="hljs-string">'city'</span>,<br> <span class="hljs-attr">label</span>: <span class="hljs-string">'City'</span><br> }]<br> });<br></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span></code></pre>
|
|
114
|
+
|
|
115
|
+
<k-table id="editableFieldExample">
|
|
116
|
+
<k-tc-edit slot="after"></k-tc-edit>
|
|
117
|
+
</k-table>
|
|
118
|
+
<script type="module">
|
|
119
|
+
await window.customElements.whenDefined('k-table');
|
|
120
|
+
document.getElementById('editableFieldExample').setData({
|
|
121
|
+
records: [
|
|
122
|
+
{ id: 1, name: 'Alice', city: 'New York' },
|
|
123
|
+
{ id: 2, name: 'Bob', city: 'Los Angeles' },
|
|
124
|
+
{ id: 3, name: 'Charlie', city: 'Chicago' }
|
|
125
|
+
],
|
|
126
|
+
fields: [
|
|
127
|
+
{ name: 'id', label: 'ID', editable: false },
|
|
128
|
+
{ name: 'name', label: 'Name' },
|
|
129
|
+
{ name: 'city', label: 'City' }
|
|
130
|
+
]
|
|
131
|
+
});
|
|
132
|
+
</script>
|
|
109
133
|
</k-main>
|
|
110
134
|
<div style="height: 33.333vh"></div>
|
|
111
135
|
<script type="module" src="../src/components/Import.js"></script>
|
|
112
136
|
<script type="module" src="../src/components/Main.js"></script>
|
|
113
137
|
<div style="height:33vh"></div>
|
|
114
138
|
<script type="module" src="../src/components/Table.js"></script>
|
|
139
|
+
<script type="module" src="../src/components/tableControls/Edit.js"></script>
|
|
115
140
|
</body>
|
|
116
141
|
</html>
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
<k-import src="../nav-1.inc.html"></k-import>
|
|
55
55
|
<h1 class="ta-center">Table - Fetch Records</h1>
|
|
56
56
|
<k-main>
|
|
57
|
-
<a href="./
|
|
57
|
+
<a href="./table.html"><h5>Back to Table Component Documentation</h5></a>
|
|
58
58
|
|
|
59
59
|
<p>The <code>setupFetchRecords</code> function is used to configure the table to fetch records dynamically. It takes two parameters: the total number of records and a function that fetching records. It should be an asynconous function that returns/resolves an array of records that will populate the table.</p>
|
|
60
60
|
<p>In this example, the <code>simulateFetchRecords</code> function generates a specified number of random contact records and resolves the promise after a delay, simulating an asynchronous fetch operation.
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
<h1 class="ta-center">Table - Field Sort and Hide</h1>
|
|
56
56
|
<k-main>
|
|
57
57
|
|
|
58
|
-
<a href="./
|
|
58
|
+
<a href="./table.html"><h5>Back to Table Component Documentation</h5></a>
|
|
59
59
|
|
|
60
60
|
<p>This example shows how to use the <code><k-tc-field-sort-hide></code> component to allow users to show or hide fields in the table, and sort fields.</p>
|
|
61
61
|
<pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">k-table</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"fieldVisibilityExample"</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">k-tc-field-sort-hide</span> <span class="hljs-attr">slot</span>=<span class="hljs-string">"top"</span>></span><span class="hljs-tag"></<span class="hljs-name">k-tc-field-sort-hide</span>></span><br /><span class="hljs-tag"></<span class="hljs-name">k-table</span>></span><br /><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/src/components/tableControls/FieldSortHide.js"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span><br /><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span>></span><span class="javascript"><br /> <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'fieldVisibilityExample'</span>).setRecords(contacts);<br /></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span></code></pre>
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
<k-import src="../nav-1.inc.html"></k-import>
|
|
54
54
|
<h1 class="ta-center">Table - Pagination</h1>
|
|
55
55
|
<k-main>
|
|
56
|
-
<a href="./
|
|
56
|
+
<a href="./table.html"><h5>Back to Table Component Documentation</h5></a>
|
|
57
57
|
<p>The table component supports pagination, enable it by adding the <code>enable-pages</code> attribute to the <code>k-table</code> element.</p>
|
|
58
58
|
<p>You can optionally set the page size and page size options by setting the <code>page-size</code> and <code>page-size-options</code> attributes respectively. <code>page-size-options</code> must be in the format of an array of numbers (with brackets).</p>
|
|
59
59
|
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
<k-import src="../nav-1.inc.html"></k-import>
|
|
54
54
|
<h1 class="ta-center">Table - Record Editing</h1>
|
|
55
55
|
<k-main>
|
|
56
|
-
<a href="./
|
|
56
|
+
<a href="./table.html"><h5>Back to Table Component Documentation</h5></a>
|
|
57
57
|
|
|
58
58
|
<p>The table component supports record editing. To allow for editing add the "edit" control to the "before" or "after" controls. You may define a "type" on the field, which will tell the table to use a specific editor for that field. If you do not define a "type" it will attempt to find the correct type using the value.</p>
|
|
59
59
|
<p>You can also define a custom editor by adding a generator function to the <code>Table.editors</code> object that will generate an input (or select). This will be global to all tables on the page.</p>
|
|
@@ -136,6 +136,37 @@
|
|
|
136
136
|
]
|
|
137
137
|
});
|
|
138
138
|
</script>
|
|
139
|
+
|
|
140
|
+
<h3>Non-Editable Fields</h3>
|
|
141
|
+
<p>Set <code>editable: false</code> on a field definition to make it read-only during record editing. The cell will display its value normally while the row is in edit mode. This is useful for fields like IDs or calculated values that should not be changed directly.</p>
|
|
142
|
+
|
|
143
|
+
<pre><code class="hljs javascript"><span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">'nonEditableFieldExample'</span>).<span class="hljs-title function_">setData</span>({<br> <span class="hljs-attr">records</span>: contacts,<br> <span class="hljs-attr">fields</span>: [{<br> <span class="hljs-attr">name</span>: <span class="hljs-string">'name'</span>,<br> <span class="hljs-attr">label</span>: <span class="hljs-string">'Name'</span>,<br> <span class="hljs-attr">editable</span>: <span class="hljs-literal">false</span><br> }, {<br> <span class="hljs-attr">name</span>: <span class="hljs-string">'email'</span>,<br> <span class="hljs-attr">label</span>: <span class="hljs-string">'Email'</span><br> }, {<br> <span class="hljs-attr">name</span>: <span class="hljs-string">'birthday'</span>,<br> <span class="hljs-attr">label</span>: <span class="hljs-string">'Birthday'</span>,<br> <span class="hljs-attr">type</span>: <span class="hljs-string">'date'</span><br> }]<br>});</code></pre>
|
|
144
|
+
|
|
145
|
+
<k-table id="nonEditableFieldExample">
|
|
146
|
+
<k-tc-edit slot="after"></k-tc-edit>
|
|
147
|
+
</k-table>
|
|
148
|
+
<script type="module">
|
|
149
|
+
await window.customElements.whenDefined('k-table');
|
|
150
|
+
document.getElementById('nonEditableFieldExample').setData({
|
|
151
|
+
records: contacts,
|
|
152
|
+
fields: [
|
|
153
|
+
{
|
|
154
|
+
name: 'name',
|
|
155
|
+
label: 'Name',
|
|
156
|
+
editable: false
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
name: 'email',
|
|
160
|
+
label: 'Email'
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: 'birthday',
|
|
164
|
+
label: 'Birthday',
|
|
165
|
+
type: 'date'
|
|
166
|
+
}
|
|
167
|
+
]
|
|
168
|
+
});
|
|
169
|
+
</script>
|
|
139
170
|
</k-main>
|
|
140
171
|
<div style="height: 33.333vh"></div>
|
|
141
172
|
<script type="module" src="../src/components/Import.js"></script>
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
<h1 class="ta-center">Table - Record Filtering</h1>
|
|
56
56
|
<k-main>
|
|
57
57
|
|
|
58
|
-
<a href="./
|
|
58
|
+
<a href="./table.html"><h5>Back to Table Component Documentation</h5></a>
|
|
59
59
|
|
|
60
60
|
<p>Record filtering is a feature that allows users to filter the records displayed in the table. The table component provides a filter control that can be used to filter records based on the values in the columns. This control uses a Dialog to display the current filters and allows the user to create filters.</p>
|
|
61
61
|
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
<h1 class="ta-center">Table - Record Hiding</h1>
|
|
55
55
|
<k-main>
|
|
56
56
|
|
|
57
|
-
<a href="./
|
|
57
|
+
<a href="./table.html"><h5>Back to Table Component Documentation</h5></a>
|
|
58
58
|
|
|
59
59
|
<p>The table component supports record hiding. To allow for hiding add the "hide" control to the "before" or "after" controls. There is a built-in "showAll" control that can be added to the "top" or "bottom" controls to reveal previously hidden fields.</p>
|
|
60
60
|
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
<h1 class="ta-center">Table - Record Searching</h1>
|
|
56
56
|
<k-main>
|
|
57
57
|
|
|
58
|
-
<a href="./
|
|
58
|
+
<a href="./table.html"><h5>Back to Table Component Documentation</h5></a>
|
|
59
59
|
|
|
60
60
|
<p>The search control (aka quick filter) is a feature that allows users to filter the records displayed in the table. The control is a search input that can be used to filter records based on the values in the columns.</p>
|
|
61
61
|
<pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">k-table</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"quickFilterExample"</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">k-tc-search</span> <span class="hljs-attr">slot</span>=<span class="hljs-string">"top"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"width: 20rem"</span>></span><span class="hljs-tag"></<span class="hljs-name">k-tc-search</span>></span><br /><span class="hljs-tag"></<span class="hljs-name">k-table</span>></span><br /><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/src/components/tableControls/Search.js"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span><br /><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span>></span><span class="javascript"><br /> <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'quickFilterExample'</span>).setRecords(contacts);<br /></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span></code></pre>
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
<k-import src="../nav-1.inc.html"></k-import>
|
|
54
54
|
<h1 class="ta-center">Table - Record Selection</h1>
|
|
55
55
|
<k-main>
|
|
56
|
-
<a href="./
|
|
56
|
+
<a href="./table.html"><h5>Back to Table Component Documentation</h5></a>
|
|
57
57
|
|
|
58
58
|
<p>The table component supports record selection. You can enable selection by setting the <code>enable-selection</code> attribute to <code>true</code>. This will add a checkbox to each row and a "Select All" checkbox to the header.</p>
|
|
59
59
|
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
<h1 class="ta-center">Table - Row Controls</h1>
|
|
55
55
|
<k-main>
|
|
56
56
|
|
|
57
|
-
<a href="./
|
|
57
|
+
<a href="./table.html"><h5>Back to Table Component Documentation</h5></a>
|
|
58
58
|
<p>The table component can display controls before or after each row. Each control is defined by an icon name, a callback function, and optionally an HTML string or a render function. Use <code>slot="before"</code> to add the control before each row, and use <code>slot="after"</code> to add the control after each row.</p>
|
|
59
59
|
<pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">k-table</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"rowControlsExample"</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">k-tc-export-json</span> <span class="hljs-attr">slot</span>=<span class="hljs-string">"before"</span> <span class="hljs-attr">max-width</span>=<span class="hljs-string">"40"</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">k-icon</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"export-file"</span>></span><span class="hljs-tag"></<span class="hljs-name">k-icon</span>></span><br /> <span class="hljs-tag"></<span class="hljs-name">k-tc-export-json</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">k-tc-delete-record</span> <span class="hljs-attr">slot</span>=<span class="hljs-string">"after"</span>></span><span class="hljs-tag"></<span class="hljs-name">k-tc-delete-record</span>></span><br /><span class="hljs-tag"></<span class="hljs-name">k-table</span>></span><br /><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span>></span><span class="javascript"><br /> <span class="hljs-keyword">await</span> <span class="hljs-built_in">window</span>.customElements.whenDefined(<span class="hljs-string">'k-table'</span>);<br /> <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'rowControlsExample'</span>).setRecords(contacts);<br /></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span></code></pre>
|
|
60
60
|
<k-table id="rowControlsExample">
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
<h1 class="ta-center">Table - Sorting</h1>
|
|
56
56
|
<k-main>
|
|
57
57
|
|
|
58
|
-
<a href="./
|
|
58
|
+
<a href="./table.html"><h5>Back to Table Component Documentation</h5></a>
|
|
59
59
|
|
|
60
60
|
<p>The sorting feature allows users to sort the records displayed in the table by clicking on the column headers. This is enabled by setting the <code>enable-sorting="true"</code> attribute.</p>
|
|
61
61
|
<p>Once enabled the user can sort the records by clicking on the fields.</p>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-all.min.js";import{boolExists}from"../utils/propConverters.js";import"./Icon.js";import"./FocusCapture.js";const firstFocusable=e=>e.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')[0];export default class Dialog extends ShadowComponent{static properties={opened:{type:Boolean,reflect:!0,converter:boolExists},closeBtn:{type:Boolean,reflect:!0,attribute:"close-btn",converter:boolExists},overlayClose:{type:Boolean,reflect:!0,attribute:"overlay-close",converter:boolExists},disableKeyboardClose:{type:Boolean,reflect:!0,attribute:"disable-keyboard-close",converter:boolExists},confirmText:{type:String,reflect:!0,attribute:"confirm-text"},confirmClasses:{type:String,reflect:!0,attribute:"confirm-classes"},cancelText:{type:String,reflect:!0,attribute:"cancel-text"},cancelClasses:{type:String,reflect:!0,attribute:"cancel-classes"}};constructor(){super(),this.opened=!1,this.closeBtn=!0,this.overlayClose=!0,this.disableKeyboardClose=!1,this.confirmText="",this.confirmClasses="success ml",this.cancelText="",this.cancelClasses="",this.confirmAction=()=>{},this.cancelAction=()=>{},this.closeCallback=()=>{},this.previousFocus=null}handleClick=e=>{const{target:t}=e,o=t.id||t.closest("[id]")?.id;"overlay"===o&&this.overlayClose||"close"===o?this.close():"cancel"===o&&(this.cancelAction(e),e.defaultPrevented||this.close())};handleSubmit=e=>{e.preventDefault(),this.confirmAction(e),e.keepDialogOpen||this.close()};handleSlotKeydown=e=>{if("Enter"===e.key&&this.confirmText){const t=e.target;if("INPUT"===t.tagName||"TEXTAREA"===t.tagName){e.preventDefault();const t=this.shadowRoot.querySelector("form");t&&t.requestSubmit()}}};handleKeydown=e=>{this.disableKeyboardClose||27!==e.keyCode||this.close()};connectedCallback(){super.connectedCallback()}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("keydown",this.handleKeydown)}updated(e){super.updated(e),e.has("opened")&&this.dispatchEvent(new CustomEvent(this.opened?"opened":"close"))}open(){this.opened=!0,window.addEventListener("keydown",this.handleKeydown),document.body.classList.add("no-scroll");const e=this.updateComplete||this.requestUpdate();e&&e.then?e.then(()=>{const e=this.shadowRoot.querySelector("[autofocus]")||firstFocusable(this.shadowRoot);e&&e.focus()}):setTimeout(()=>{const e=this.shadowRoot.querySelector("[autofocus]")||firstFocusable(this.shadowRoot);e&&e.focus()},0)}close(){this.opened=!1,this.blur(),this.closeCallback(),window.removeEventListener("keydown",this.handleKeydown),document.body.classList.remove("no-scroll")}toggle(){this.opened?this.close():this.open()}focus(){const e=firstFocusable(this.shadowRoot);e&&(this.previousFocus=document.activeElement,e.focus())}blur(){this.previousFocus&&this.previousFocus.focus()}hasTitle(){return!!this.querySelector('[slot="title"]')}static styles=css`
|
|
1
|
+
import ShadowComponent from"./ShadowComponent.js";import{html,css,render}from"../lit-all.min.js";import{boolExists}from"../utils/propConverters.js";import"./Icon.js";import"./FocusCapture.js";const firstFocusable=e=>e.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')[0];export default class Dialog extends ShadowComponent{static properties={opened:{type:Boolean,reflect:!0,converter:boolExists},closeBtn:{type:Boolean,reflect:!0,attribute:"close-btn",converter:boolExists},overlayClose:{type:Boolean,reflect:!0,attribute:"overlay-close",converter:boolExists},disableKeyboardClose:{type:Boolean,reflect:!0,attribute:"disable-keyboard-close",converter:boolExists},confirmText:{type:String,reflect:!0,attribute:"confirm-text"},confirmClasses:{type:String,reflect:!0,attribute:"confirm-classes"},cancelText:{type:String,reflect:!0,attribute:"cancel-text"},cancelClasses:{type:String,reflect:!0,attribute:"cancel-classes"}};constructor(){super(),this.opened=!1,this.closeBtn=!0,this.overlayClose=!0,this.disableKeyboardClose=!1,this.confirmText="",this.confirmClasses="success ml",this.cancelText="",this.cancelClasses="",this.confirmAction=()=>{},this.cancelAction=()=>{},this.closeCallback=()=>{},this.previousFocus=null}handleClick=e=>{const{target:t}=e,o=t.id||t.closest("[id]")?.id;"overlay"===o&&this.overlayClose||"close"===o?this.close():"cancel"===o&&(this.cancelAction(e),e.defaultPrevented||this.close())};handleSubmit=e=>{e.preventDefault(),this.confirmAction(e),e.keepDialogOpen||this.close()};handleSlotKeydown=e=>{if("Enter"===e.key&&this.confirmText){const t=e.target;if("INPUT"===t.tagName||"TEXTAREA"===t.tagName){e.preventDefault();const t=this.shadowRoot.querySelector("form");t&&t.requestSubmit()}}};handleKeydown=e=>{this.disableKeyboardClose||27!==e.keyCode||this.close()};connectedCallback(){super.connectedCallback()}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("keydown",this.handleKeydown)}updated(e){super.updated(e),e.has("opened")&&this.dispatchEvent(new CustomEvent(this.opened?"opened":"close"))}open(){this.opened=!0,window.addEventListener("keydown",this.handleKeydown),document.body.classList.add("no-scroll");const e=this.updateComplete||this.requestUpdate();e&&e.then?e.then(()=>{const e=this.shadowRoot.querySelector("[autofocus]")||firstFocusable(this.shadowRoot);e&&e.focus()}):setTimeout(()=>{const e=this.shadowRoot.querySelector("[autofocus]")||firstFocusable(this.shadowRoot);e&&e.focus()},0)}close(){this.opened=!1,this.blur(),this.closeCallback(),window.removeEventListener("keydown",this.handleKeydown),document.body.classList.remove("no-scroll")}toggle(){this.opened?this.close():this.open()}focus(){const e=firstFocusable(this.shadowRoot);e&&(this.previousFocus=document.activeElement,e.focus())}blur(){this.previousFocus&&this.previousFocus.focus()}hasTitle(){return!!this.querySelector('[slot="title"]')}static styles=css`
|
|
2
2
|
:host {
|
|
3
3
|
position: fixed;
|
|
4
4
|
top: 0;
|
|
@@ -118,4 +118,4 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-al
|
|
|
118
118
|
</form>
|
|
119
119
|
</k-focus-capture>
|
|
120
120
|
</div>
|
|
121
|
-
`}static create(e="",t={}){!1!==t.closeExisting&&document.querySelectorAll("k-dialog").forEach(e=>e.close());const{removeOnClose:o=!0,closeCallback:s=()=>{},title:i="",titleClasses:l="pyh px m0"}=t,
|
|
121
|
+
`}static create(e="",t={}){!1!==t.closeExisting&&document.querySelectorAll("k-dialog").forEach(e=>e.close());const{removeOnClose:o=!0,closeCallback:s=()=>{},title:i="",titleClasses:l="pyh px m0"}=t,n=new Dialog;if(Object.assign(n,{opened:!0,...t,closeCallback:(...e)=>{o&&n.remove(),s(...e)}}),i){const e=document.createElement("h5");e.slot="title",e.className=l,i instanceof HTMLElement?e.appendChild(i):e.innerHTML=i,n.appendChild(e)}if(void 0!==e?._$litType$){const t=document.createElement("div");t.style.display="contents",render(e,t),n.appendChild(t)}else if(e instanceof HTMLElement||e instanceof DocumentFragment)n.appendChild(e);else if(e){if(/<[^>]+>/.test(e))n.innerHTML+=e;else{const t=document.createElement("p");t.className="p",t.textContent=e,n.appendChild(t)}}return t.width&&n.style.setProperty("--width",t.width),t.minWidth&&n.style.setProperty("--min_width",t.minWidth),t.maxWidth&&n.style.setProperty("--max_width",t.maxWidth),t.height&&n.style.setProperty("--height",t.height),t.minHeight&&n.style.setProperty("--min_height",t.minHeight),t.maxHeight&&n.style.setProperty("--max_height",t.maxHeight),document.body.appendChild(n),n.open(),n}static confirm(e,t,o={}){return Dialog.create(e,{title:o.title||"Confirm",closeBtn:!1,overlayClose:!1,disableKeyboardClose:!0,confirmText:"Yes",confirmClasses:"success ml",confirmAction:()=>t(!0),cancelText:"No",cancelClasses:"danger",cancelAction:()=>t(!1),...o})}static alert(e,t=()=>{},o={}){return Dialog.create(e,{title:o.title||"Alert",closeCallback:t,cancelText:"Ok",...o})}static error(e,t=()=>{},o={}){return Dialog.create(e,{title:o.title||"Error",titleClasses:"pyh px m0 tc-danger",closeCallback:t,cancelText:"Ok",...o})}static success(e,t=()=>{},o={}){return Dialog.create(e,{title:o.title||"Success",titleClasses:"pyh px m0 tc-success",closeCallback:t,cancelText:"Ok",...o})}}customElements.define("k-dialog",Dialog);
|
|
@@ -19,15 +19,15 @@ import{html,css,unsafeStatic,literal}from"../lit-all.min.js";import ShadowCompon
|
|
|
19
19
|
@change=${t=>this.handleRecordSelectionChange(e,t)}
|
|
20
20
|
/>
|
|
21
21
|
</td>
|
|
22
|
-
`),this.hasBeforeControls()&&t.push(this.renderBeforeControlsTemplate()),this.fields.forEach(({name:s,formatter:i,calculator:r,type:o,editor:n,hidden:l})=>{if(l)return;let
|
|
22
|
+
`),this.hasBeforeControls()&&t.push(this.renderBeforeControlsTemplate()),this.fields.forEach(({name:s,formatter:i,calculator:r,type:o,editor:n,hidden:l,editable:a})=>{if(l)return;let d=e[s]||"";t.push(html`
|
|
23
23
|
<td class="cell" data-field=${s}>
|
|
24
|
-
${e[editing]?this.renderEditingCell(e,s,
|
|
24
|
+
${e[editing]?this.renderEditingCell(e,s,d,r,n,o,a):this.renderDisplayCell(e,s,d,r,i)}
|
|
25
25
|
</td>
|
|
26
26
|
`)}),this.hasAfterControls()&&t.push(this.renderAfterControlsTemplate()),html`
|
|
27
27
|
<tr class="record ${e[editing]?"editing":""}" data-index=${e[index]}>
|
|
28
28
|
${t}
|
|
29
29
|
</tr>
|
|
30
|
-
`}renderEditingCell(e,t,s,i,r,o){if(i)return html`<input disabled .value=${i(e,this)} />`;if(r){const e=r(s);return html`${e}`}switch(o||typeof s){case"number":return html`<input type="number" .value=${s} />`;case"date":return html`<input type="date" .value=${s} />`;case"boolean":return html`
|
|
30
|
+
`}renderEditingCell(e,t,s,i,r,o,n){if(!1===n)return this.renderDisplayCell(e,t,s,i,null);if(i)return html`<input disabled .value=${i(e,this)} />`;if(r){const e=r(s);return html`${e}`}switch(o||typeof s){case"number":return html`<input type="number" .value=${s} />`;case"date":return html`<input type="date" .value=${s} />`;case"boolean":return html`
|
|
31
31
|
<select .value=${s}>
|
|
32
32
|
<option value="true" ?selected=${s}>True</option>
|
|
33
33
|
<option value="false" ?selected=${!s}>False</option>
|
|
@@ -40,7 +40,7 @@ import{html,css,unsafeStatic,literal}from"../lit-all.min.js";import ShadowCompon
|
|
|
40
40
|
<td class="cell controls controls-after">
|
|
41
41
|
<div>${e}</div>
|
|
42
42
|
</td>
|
|
43
|
-
`}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]}"]`);t&&(t.classList.add("editing"),t.setAttribute("editing","true"),t.querySelectorAll(".cell[data-field]").forEach(t=>{const s=t.dataset.field,i=this.fields.find(e=>e.name===s);if(i){const r=e[s]||"";if(t.innerHTML="",i.calculator){const s=document.createElement("input");s.disabled=!0,s.value=i.calculator(e,this),t.appendChild(s)}else if(i.editor)t.appendChild(i.editor(r));else{const e=i.type||typeof r,s=Table.editors[e]||Table.editors.string;t.appendChild(s(r))}}})),this.dispatchEvent(new CustomEvent("editingChange",{detail:{record:e,editing:!0},bubbles:!0}))}saveEditedRecord(e){const t=this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`),s={};t&&t.querySelectorAll(".cell[data-field]").forEach(e=>{const t=e.dataset.field,i=this.fields.find(e=>e.name===t);if(i&&!i.calculator){const i=e.querySelector("input, select");i&&(s[t]=i.value)}});const i=()=>{Object.assign(e,s),e[editing]=!1,t&&(t.classList.remove("editing"),t.removeAttribute("editing"),t.querySelectorAll(".cell[data-field]").forEach(t=>{const s=t.dataset.field,i=this.fields.find(e=>e.name===s);if(i){const r=e[s]||"";i.calculator?t.textContent=i.calculator(e,this):i.formatter?t.innerHTML=i.formatter(r):t.textContent=r}})),this.dispatchEvent(new CustomEvent("editingChange",{detail:{record:e,editing:!1},bubbles:!0}))};if(Object.keys(s).some(t=>String(e[t]??"")!==s[t]))if(this.requestEdit){t?.classList.add("pending");const r=()=>{t?.classList.remove("pending"),i()},o=()=>{t?.classList.remove("pending")};this.dispatchEvent(new CustomEvent("requestSave",{detail:{record:e,newData:s,approve:r,reject:o},bubbles:!0}))}else i();else this.cancelEditedRecord(e)}cancelEditedRecord(e){e[editing]=!1;const t=this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`);t&&(t.classList.remove("editing"),t.removeAttribute("editing"),t.querySelectorAll(".cell[data-field]").forEach(t=>{const s=t.dataset.field,i=this.fields.find(e=>e.name===s);if(i){const r=e[s]||"";i.calculator?t.textContent=i.calculator(e,this):i.formatter?t.innerHTML=i.formatter(r):t.textContent=r}})),this.dispatchEvent(new CustomEvent("editingChange",{detail:{record:e,editing:!1},bubbles:!0}))}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.requestUpdate(),this.dispatchEvent(new CustomEvent("pageChange",{bubbles:!0})))}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.requestUpdate(),this.dispatchEvent(new CustomEvent("pageSizeChange",{bubbles:!0}))}getPageSize(){return this.pageSize}getPageSizeOptions(){return this.pageSizeOptions}getFieldLabel(e){const t=this.fields.find(t=>t.name===e);return t?t.label:toTitleCase(e)}setPageSizeOptions(e){this.pageSizeOptions=e,this.requestUpdate()}setData({records:e=!1,fields:t=!1,pageSize:s=!1,pageSizeOptions:i=!1,currentPage:r=!1,enableSelection:o}={}){let n=!1,l=this.getTotalPages(),a=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),n=!0),s&&(this.pageSize=s,n=!0),i&&(this.pageSizeOptions=i),r&&(this.currentPage=r,n=!0),void 0!==o&&(this.enableSelection=o,n=!0),n&&this.requestUpdate();const d=this.getTotalPages();d!==l&&this.dispatchEvent(new CustomEvent("pageCountChanged",{detail:{totalPages:this.getTotalPages()},bubbles:!0})),a>d&&this.setPage(d)}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.requestUpdate(),this.dispatchEvent(new CustomEvent("recordsSet",{detail:{records:e},bubbles:!0}));const r=this.getTotalPages();r!==s&&this.dispatchEvent(new CustomEvent("pageCountChanged",{detail:{totalPages:this.getTotalPages()},bubbles:!0})),i>r&&this.setPage(r)}setupFetchRecords(e,t){const s=this.records.length,i=this.getTotalPages();if(s<e){this.records.length=e,this.records.fill(null,s),this.requestUpdate();const t=this.getTotalPages();t!==i&&setTimeout(()=>{this.dispatchEvent(new CustomEvent("pageCountChanged",{detail:{totalPages:t},bubbles:!0}))},0)}this.addEventListener("fetchRecords",async e=>{if(this.fetchPending)return;this.fetchPending=!0;const{start:s,count:i}=e.detail,r=await t(s,i);r.forEach((e,t)=>{e[index]=s+t,void 0===e[selected]&&(e[selected]=!1),void 0===e[hidden]&&(e[hidden]=!1),void 0===e[editing]&&(e[editing]=!1)}),this.records.splice(s,r.length,...r),this.fetchPending=!1,this.requestUpdate()})}addRecord(e){e[selected]=!1,e[hidden]=!1,e[index]=this.records.length,this.records.push(e),this.requestUpdate(),this.dispatchEvent(new CustomEvent("recordAdded",{detail:{record:e},bubbles:!0}))}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.requestUpdate()}}deleteRecord(e){let t=this.records.find(t=>t===e);const s=this.getTotalPages();if(t||void 0===e[index]||(t=this.records[e[index]]),!t)return;const i=()=>{const e=this.records.indexOf(t);this.records.splice(e,1),this.records.forEach((e,t)=>{e[index]=t}),this.requestUpdate(),this.dispatchEvent(new CustomEvent("selectionChange",{bubbles:!0})),this.dispatchEvent(new CustomEvent("recordDeleted",{detail:{index:e},bubbles:!0}));const i=this.getTotalPages();this.currentPage>i&&this.setPage(i),i!==s&&this.dispatchEvent(new CustomEvent("pageCountChanged",{detail:{totalPages:i},bubbles:!0}))};if(this.requestDelete){const e=this.shadowRoot.querySelector(`.record[data-index="${t[index]}"]`);e?.classList.add("pending");const s=()=>{e?.classList.remove("pending"),i()},r=()=>{e?.classList.remove("pending")};this.dispatchEvent(new CustomEvent("requestDelete",{detail:{records:[t],approve:s,reject:r},bubbles:!0}))}else i()}deleteSelected(){const e=this.getTotalPages(),t=this.getSelectedRecords().map(e=>this.records.find(t=>t===e)??(void 0!==e[index]?this.records[e[index]]:null)).filter(Boolean);if(!t.length)return;const s=()=>{t.forEach(e=>{const t=this.records.indexOf(e);-1!==t&&this.records.splice(t,1)}),this.records.forEach((e,t)=>{e[index]=t}),this.requestUpdate();const s=this.getTotalPages();this.currentPage>s&&this.setPage(s),s!==e&&this.dispatchEvent(new CustomEvent("pageCountChanged",{detail:{totalPages:s},bubbles:!0})),this.dispatchEvent(new CustomEvent("selectionChange",{bubbles:!0}))};if(this.requestDelete){const e=t.map(e=>this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`)).filter(Boolean);e.forEach(e=>e.classList.add("pending"));const i=()=>{e.forEach(e=>e.classList.remove("pending")),s()},r=()=>{e.forEach(e=>e.classList.remove("pending"))};this.dispatchEvent(new CustomEvent("requestDelete",{detail:{records:t,approve:i,reject:r},bubbles:!0}))}else s()}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.requestUpdate(),setTimeout(()=>{this.dispatchEvent(new CustomEvent("selectionChange",{bubbles:!0}))},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.requestUpdate(),setTimeout(()=>{this.dispatchEvent(new CustomEvent("selectionChange",{bubbles:!0}))},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.requestUpdate()}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.requestUpdate(),this.dispatchEvent(new CustomEvent("recordHidden",{bubbles:!0})))}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.requestUpdate(),this.dispatchEvent(new CustomEvent("recordShown",{bubbles:!0})))}showAllRecords(){this.records.forEach(e=>{e[hidden]=!1}),this.filters.length&&(this.filters=[],this.dispatchEvent(new CustomEvent("filterRemoved",{bubbles:!0})),this.dispatchEvent(new CustomEvent("filterChange",{bubbles:!0}))),this.requestUpdate(),this.dispatchEvent(new CustomEvent("recordShown",{bubbles:!0})),this.dispatchEvent(new CustomEvent("allRecordsShown",{bubbles:!0}))}addFilter(e,t,s){this.filters.push({field:e,condition:t,value:s}),this.dispatchEvent(new CustomEvent("filterAdded",{bubbles:!0})),this.dispatchEvent(new CustomEvent("filterChange",{bubbles:!0})),this.requestUpdate()}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),this.dispatchEvent(new CustomEvent("filterRemoved",{bubbles:!0})),this.dispatchEvent(new CustomEvent("filterChange",{bubbles:!0})),i&&this.requestUpdate())}testFilter(e,t,s,i){let r=e[t],o=i;switch(this.caseSensitiveFilters||"string"!=typeof r||"string"!=typeof i||(r=r.toLowerCase(),o=i.toLowerCase()),s){case"equals":return r===o;case"not-equals":return r!==o;case"contains":return r.includes(o);case"not-contains":return!r.includes(o);case"greater-than":return r>o;case"less-than":return r<o;case"greater-than-or-equal":return r>=o;case"less-than-or-equal":return r<=o;default:return!0}}removeAllFilters(){this.filters.length&&([...this.filters].forEach(({field:e,condition:t,value:s})=>{this.removeFilter(e,t,s,!1)}),this.requestUpdate())}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&&(this.dispatchEvent(new CustomEvent("recordHidden",{bubbles:!0})),this.requestUpdate()),this.dispatchEvent(new CustomEvent("search",{detail:{term:e},bubbles:!0}))}getDisplayedRecords(){this.filters.forEach(({field:e,condition:t,value:s})=>{this.records.forEach(i=>{null!==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)=>null===e||null===i?0: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(){const e=Array.from(this.querySelectorAll('[slot="before"]')),t=Array.from(this.querySelectorAll('[slot="after"]')),s={beforeControls:e.reduce((e,t)=>e+(t.maxWidth||40),0),afterControls:t.reduce((e,t)=>e+(t.maxWidth||40),0)},i=[...e,...t].some(e=>void 0===e.maxWidth);return JSON.stringify(this.columnSizes)!==JSON.stringify(s)&&(this.columnSizes=s),i&&setTimeout(()=>this.calculateColumnSizes(),0),this.columnSizes}setFieldHiddenState(e,t){const s=this.fields.find(t=>t.name===e);s&&(s.hidden=t,this.calculateColumnSizes(),this.requestUpdate(),this.dispatchEvent(new CustomEvent("fieldVisibilityChanged",{detail:{field:s},bubbles:!0})),this.dispatchEvent(new CustomEvent(t?"fieldHidden":"fieldShown",{detail:{field:s},bubbles:!0})))}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.requestUpdate()}render(){return this.records&&this.fields?(this.calculateColumnSizes(),this.hasTopControls()?this.setAttribute("top-controls","true"):this.removeAttribute("top-controls"),this.hasBottomControls()?this.setAttribute("bottom-controls","true"):this.removeAttribute("bottom-controls"),html`
|
|
43
|
+
`}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]}"]`);t&&(t.classList.add("editing"),t.setAttribute("editing","true"),t.querySelectorAll(".cell[data-field]").forEach(t=>{const s=t.dataset.field,i=this.fields.find(e=>e.name===s);if(i){if(!1===i.editable)return;const r=e[s]||"";if(t.innerHTML="",i.calculator){const s=document.createElement("input");s.disabled=!0,s.value=i.calculator(e,this),t.appendChild(s)}else if(i.editor)t.appendChild(i.editor(r));else{const e=i.type||typeof r,s=Table.editors[e]||Table.editors.string;t.appendChild(s(r))}}})),this.dispatchEvent(new CustomEvent("editingChange",{detail:{record:e,editing:!0},bubbles:!0}))}saveEditedRecord(e){const t=this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`),s={};t&&t.querySelectorAll(".cell[data-field]").forEach(e=>{const t=e.dataset.field,i=this.fields.find(e=>e.name===t);if(i&&!i.calculator){const i=e.querySelector("input, select");i&&(s[t]=i.value)}});const i=()=>{Object.assign(e,s),e[editing]=!1,t&&(t.classList.remove("editing"),t.removeAttribute("editing"),t.querySelectorAll(".cell[data-field]").forEach(t=>{const s=t.dataset.field,i=this.fields.find(e=>e.name===s);if(i){const r=e[s]||"";i.calculator?t.textContent=i.calculator(e,this):i.formatter?t.innerHTML=i.formatter(r):t.textContent=r}})),this.dispatchEvent(new CustomEvent("editingChange",{detail:{record:e,editing:!1},bubbles:!0}))};if(Object.keys(s).some(t=>String(e[t]??"")!==s[t]))if(this.requestEdit){t?.classList.add("pending");const r=()=>{t?.classList.remove("pending"),i()},o=()=>{t?.classList.remove("pending")};this.dispatchEvent(new CustomEvent("requestSave",{detail:{record:e,newData:s,approve:r,reject:o},bubbles:!0}))}else i();else this.cancelEditedRecord(e)}cancelEditedRecord(e){e[editing]=!1;const t=this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`);t&&(t.classList.remove("editing"),t.removeAttribute("editing"),t.querySelectorAll(".cell[data-field]").forEach(t=>{const s=t.dataset.field,i=this.fields.find(e=>e.name===s);if(i){const r=e[s]||"";i.calculator?t.textContent=i.calculator(e,this):i.formatter?t.innerHTML=i.formatter(r):t.textContent=r}})),this.dispatchEvent(new CustomEvent("editingChange",{detail:{record:e,editing:!1},bubbles:!0}))}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.requestUpdate(),this.dispatchEvent(new CustomEvent("pageChange",{bubbles:!0})))}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.requestUpdate(),this.dispatchEvent(new CustomEvent("pageSizeChange",{bubbles:!0}))}getPageSize(){return this.pageSize}getPageSizeOptions(){return this.pageSizeOptions}getFieldLabel(e){const t=this.fields.find(t=>t.name===e);return t?t.label:toTitleCase(e)}setPageSizeOptions(e){this.pageSizeOptions=e,this.requestUpdate()}setData({records:e=!1,fields:t=!1,pageSize:s=!1,pageSizeOptions:i=!1,currentPage:r=!1,enableSelection:o}={}){let n=!1,l=this.getTotalPages(),a=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),n=!0),s&&(this.pageSize=s,n=!0),i&&(this.pageSizeOptions=i),r&&(this.currentPage=r,n=!0),void 0!==o&&(this.enableSelection=o,n=!0),n&&this.requestUpdate();const d=this.getTotalPages();d!==l&&this.dispatchEvent(new CustomEvent("pageCountChanged",{detail:{totalPages:this.getTotalPages()},bubbles:!0})),a>d&&this.setPage(d)}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.requestUpdate(),this.dispatchEvent(new CustomEvent("recordsSet",{detail:{records:e},bubbles:!0}));const r=this.getTotalPages();r!==s&&this.dispatchEvent(new CustomEvent("pageCountChanged",{detail:{totalPages:this.getTotalPages()},bubbles:!0})),i>r&&this.setPage(r)}setupFetchRecords(e,t){const s=this.records.length,i=this.getTotalPages();if(s<e){this.records.length=e,this.records.fill(null,s),this.requestUpdate();const t=this.getTotalPages();t!==i&&setTimeout(()=>{this.dispatchEvent(new CustomEvent("pageCountChanged",{detail:{totalPages:t},bubbles:!0}))},0)}this.addEventListener("fetchRecords",async e=>{if(this.fetchPending)return;this.fetchPending=!0;const{start:s,count:i}=e.detail,r=await t(s,i);r.forEach((e,t)=>{e[index]=s+t,void 0===e[selected]&&(e[selected]=!1),void 0===e[hidden]&&(e[hidden]=!1),void 0===e[editing]&&(e[editing]=!1)}),this.records.splice(s,r.length,...r),this.fetchPending=!1,this.requestUpdate()})}addRecord(e){e[selected]=!1,e[hidden]=!1,e[index]=this.records.length,this.records.push(e),this.requestUpdate(),this.dispatchEvent(new CustomEvent("recordAdded",{detail:{record:e},bubbles:!0}))}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.requestUpdate()}}deleteRecord(e){let t=this.records.find(t=>t===e);const s=this.getTotalPages();if(t||void 0===e[index]||(t=this.records[e[index]]),!t)return;const i=()=>{const e=this.records.indexOf(t);this.records.splice(e,1),this.records.forEach((e,t)=>{e[index]=t}),this.requestUpdate(),this.dispatchEvent(new CustomEvent("selectionChange",{bubbles:!0})),this.dispatchEvent(new CustomEvent("recordDeleted",{detail:{index:e},bubbles:!0}));const i=this.getTotalPages();this.currentPage>i&&this.setPage(i),i!==s&&this.dispatchEvent(new CustomEvent("pageCountChanged",{detail:{totalPages:i},bubbles:!0}))};if(this.requestDelete){const e=this.shadowRoot.querySelector(`.record[data-index="${t[index]}"]`);e?.classList.add("pending");const s=()=>{e?.classList.remove("pending"),i()},r=()=>{e?.classList.remove("pending")};this.dispatchEvent(new CustomEvent("requestDelete",{detail:{records:[t],approve:s,reject:r},bubbles:!0}))}else i()}deleteSelected(){const e=this.getTotalPages(),t=this.getSelectedRecords().map(e=>this.records.find(t=>t===e)??(void 0!==e[index]?this.records[e[index]]:null)).filter(Boolean);if(!t.length)return;const s=()=>{t.forEach(e=>{const t=this.records.indexOf(e);-1!==t&&this.records.splice(t,1)}),this.records.forEach((e,t)=>{e[index]=t}),this.requestUpdate();const s=this.getTotalPages();this.currentPage>s&&this.setPage(s),s!==e&&this.dispatchEvent(new CustomEvent("pageCountChanged",{detail:{totalPages:s},bubbles:!0})),this.dispatchEvent(new CustomEvent("selectionChange",{bubbles:!0}))};if(this.requestDelete){const e=t.map(e=>this.shadowRoot.querySelector(`.record[data-index="${e[index]}"]`)).filter(Boolean);e.forEach(e=>e.classList.add("pending"));const i=()=>{e.forEach(e=>e.classList.remove("pending")),s()},r=()=>{e.forEach(e=>e.classList.remove("pending"))};this.dispatchEvent(new CustomEvent("requestDelete",{detail:{records:t,approve:i,reject:r},bubbles:!0}))}else s()}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.requestUpdate(),setTimeout(()=>{this.dispatchEvent(new CustomEvent("selectionChange",{bubbles:!0}))},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.requestUpdate(),setTimeout(()=>{this.dispatchEvent(new CustomEvent("selectionChange",{bubbles:!0}))},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.requestUpdate()}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.requestUpdate(),this.dispatchEvent(new CustomEvent("recordHidden",{bubbles:!0})))}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.requestUpdate(),this.dispatchEvent(new CustomEvent("recordShown",{bubbles:!0})))}showAllRecords(){this.records.forEach(e=>{e[hidden]=!1}),this.filters.length&&(this.filters=[],this.dispatchEvent(new CustomEvent("filterRemoved",{bubbles:!0})),this.dispatchEvent(new CustomEvent("filterChange",{bubbles:!0}))),this.requestUpdate(),this.dispatchEvent(new CustomEvent("recordShown",{bubbles:!0})),this.dispatchEvent(new CustomEvent("allRecordsShown",{bubbles:!0}))}addFilter(e,t,s){this.filters.push({field:e,condition:t,value:s}),this.dispatchEvent(new CustomEvent("filterAdded",{bubbles:!0})),this.dispatchEvent(new CustomEvent("filterChange",{bubbles:!0})),this.requestUpdate()}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),this.dispatchEvent(new CustomEvent("filterRemoved",{bubbles:!0})),this.dispatchEvent(new CustomEvent("filterChange",{bubbles:!0})),i&&this.requestUpdate())}testFilter(e,t,s,i){let r=e[t],o=i;switch(this.caseSensitiveFilters||"string"!=typeof r||"string"!=typeof i||(r=r.toLowerCase(),o=i.toLowerCase()),s){case"equals":return r===o;case"not-equals":return r!==o;case"contains":return r.includes(o);case"not-contains":return!r.includes(o);case"greater-than":return r>o;case"less-than":return r<o;case"greater-than-or-equal":return r>=o;case"less-than-or-equal":return r<=o;default:return!0}}removeAllFilters(){this.filters.length&&([...this.filters].forEach(({field:e,condition:t,value:s})=>{this.removeFilter(e,t,s,!1)}),this.requestUpdate())}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&&(this.dispatchEvent(new CustomEvent("recordHidden",{bubbles:!0})),this.requestUpdate()),this.dispatchEvent(new CustomEvent("search",{detail:{term:e},bubbles:!0}))}getDisplayedRecords(){this.filters.forEach(({field:e,condition:t,value:s})=>{this.records.forEach(i=>{null!==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)=>null===e||null===i?0: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(){const e=Array.from(this.querySelectorAll('[slot="before"]')),t=Array.from(this.querySelectorAll('[slot="after"]')),s={beforeControls:e.reduce((e,t)=>e+(t.maxWidth||40),0),afterControls:t.reduce((e,t)=>e+(t.maxWidth||40),0)},i=[...e,...t].some(e=>void 0===e.maxWidth);return JSON.stringify(this.columnSizes)!==JSON.stringify(s)&&(this.columnSizes=s),i&&setTimeout(()=>this.calculateColumnSizes(),0),this.columnSizes}setFieldHiddenState(e,t){const s=this.fields.find(t=>t.name===e);s&&(s.hidden=t,this.calculateColumnSizes(),this.requestUpdate(),this.dispatchEvent(new CustomEvent("fieldVisibilityChanged",{detail:{field:s},bubbles:!0})),this.dispatchEvent(new CustomEvent(t?"fieldHidden":"fieldShown",{detail:{field:s},bubbles:!0})))}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.requestUpdate()}render(){return this.records&&this.fields?(this.calculateColumnSizes(),this.hasTopControls()?this.setAttribute("top-controls","true"):this.removeAttribute("top-controls"),this.hasBottomControls()?this.setAttribute("bottom-controls","true"):this.removeAttribute("bottom-controls"),html`
|
|
44
44
|
<div id="wrapper">
|
|
45
45
|
<div id="top"><slot name="top"></slot></div>
|
|
46
46
|
<div id="table-container">
|
|
@@ -122,7 +122,8 @@ import{html,css,unsafeStatic,literal}from"../lit-all.min.js";import ShadowCompon
|
|
|
122
122
|
tbody tr:last-child td {
|
|
123
123
|
border-bottom: none;
|
|
124
124
|
}
|
|
125
|
-
tr.editing td.cell[data-field]
|
|
125
|
+
tr.editing td.cell[data-field]:has(input),
|
|
126
|
+
tr.editing td.cell[data-field]:has(select) {
|
|
126
127
|
padding: 0;
|
|
127
128
|
}
|
|
128
129
|
tr.editing td.cell[data-field] input,
|
package/package.json
CHANGED
package/src/components/Dialog.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import ShadowComponent from './ShadowComponent.js';
|
|
2
|
-
import { html, css } from '../lit-all.min.js';
|
|
2
|
+
import { html, css, render } from '../lit-all.min.js';
|
|
3
3
|
import { boolExists } from '../utils/propConverters.js';
|
|
4
4
|
import './Icon.js';
|
|
5
5
|
import './FocusCapture.js';
|
|
@@ -330,10 +330,14 @@ export default class Dialog extends ShadowComponent {
|
|
|
330
330
|
}
|
|
331
331
|
|
|
332
332
|
// Set content
|
|
333
|
-
if(contents
|
|
333
|
+
if(contents?._$litType$ !== undefined) {
|
|
334
|
+
const container = document.createElement('div');
|
|
335
|
+
container.style.display = 'contents';
|
|
336
|
+
render(contents, container);
|
|
337
|
+
dialog.appendChild(container);
|
|
338
|
+
} else if(contents instanceof HTMLElement || contents instanceof DocumentFragment) {
|
|
334
339
|
dialog.appendChild(contents);
|
|
335
340
|
} else if(contents) {
|
|
336
|
-
// Check if content is plain text (no HTML tags)
|
|
337
341
|
const hasHtmlTags = /<[^>]+>/.test(contents);
|
|
338
342
|
if(hasHtmlTags) {
|
|
339
343
|
dialog.innerHTML += contents;
|
package/src/components/Table.js
CHANGED
|
@@ -209,12 +209,12 @@ export default class Table extends ShadowComponent {
|
|
|
209
209
|
recordCells.push(this.renderBeforeControlsTemplate());
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
this.fields.forEach(({ name, formatter, calculator, type, editor, hidden }) => {
|
|
212
|
+
this.fields.forEach(({ name, formatter, calculator, type, editor, hidden, editable }) => {
|
|
213
213
|
if(hidden) return;
|
|
214
214
|
let value = record[name] || '';
|
|
215
215
|
recordCells.push(html`
|
|
216
216
|
<td class="cell" data-field=${name}>
|
|
217
|
-
${record[editing] ? this.renderEditingCell(record, name, value, calculator, editor, type) : this.renderDisplayCell(record, name, value, calculator, formatter)}
|
|
217
|
+
${record[editing] ? this.renderEditingCell(record, name, value, calculator, editor, type, editable) : this.renderDisplayCell(record, name, value, calculator, formatter)}
|
|
218
218
|
</td>
|
|
219
219
|
`);
|
|
220
220
|
});
|
|
@@ -230,7 +230,8 @@ export default class Table extends ShadowComponent {
|
|
|
230
230
|
`;
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
-
renderEditingCell(record, name, value, calculator, editor, type) {
|
|
233
|
+
renderEditingCell(record, name, value, calculator, editor, type, editable) {
|
|
234
|
+
if(editable === false) return this.renderDisplayCell(record, name, value, calculator, null);
|
|
234
235
|
if (calculator) {
|
|
235
236
|
return html`<input disabled .value=${calculator(record, this)} />`;
|
|
236
237
|
} else if (editor) {
|
|
@@ -337,6 +338,7 @@ export default class Table extends ShadowComponent {
|
|
|
337
338
|
const field = $cell.dataset.field;
|
|
338
339
|
const fieldDef = this.fields.find(f => f.name === field);
|
|
339
340
|
if(fieldDef){
|
|
341
|
+
if(fieldDef.editable === false) return;
|
|
340
342
|
const value = record[field] || '';
|
|
341
343
|
$cell.innerHTML = '';
|
|
342
344
|
if(fieldDef.calculator){
|
|
@@ -1099,7 +1101,8 @@ export default class Table extends ShadowComponent {
|
|
|
1099
1101
|
tbody tr:last-child td {
|
|
1100
1102
|
border-bottom: none;
|
|
1101
1103
|
}
|
|
1102
|
-
tr.editing td.cell[data-field]
|
|
1104
|
+
tr.editing td.cell[data-field]:has(input),
|
|
1105
|
+
tr.editing td.cell[data-field]:has(select) {
|
|
1103
1106
|
padding: 0;
|
|
1104
1107
|
}
|
|
1105
1108
|
tr.editing td.cell[data-field] input,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Dialog from '../../src/components/Dialog.js';
|
|
2
|
+
import { html } from '../../src/lit-all.min.js';
|
|
2
3
|
|
|
3
4
|
const createDialog = async (options = {}) => {
|
|
4
5
|
const container = document.createElement('div');
|
|
@@ -673,5 +674,101 @@ export default {
|
|
|
673
674
|
|
|
674
675
|
cleanup(container);
|
|
675
676
|
pass('Default content slot rendered');
|
|
677
|
+
},
|
|
678
|
+
|
|
679
|
+
/*
|
|
680
|
+
Lit TemplateResult Content Tests
|
|
681
|
+
*/
|
|
682
|
+
'Dialog.create should accept Lit TemplateResult as content': async ({pass, fail}) => {
|
|
683
|
+
const dialog = Dialog.create(html`<p class="p">Lit template content</p>`);
|
|
684
|
+
await dialog.updateComplete;
|
|
685
|
+
|
|
686
|
+
const container = dialog.querySelector('div[style="display: contents;"]');
|
|
687
|
+
if(!container){
|
|
688
|
+
cleanupAllDialogs();
|
|
689
|
+
fail('Should render TemplateResult into a display:contents wrapper');
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
const p = container.querySelector('p');
|
|
694
|
+
if(!p || p.textContent !== 'Lit template content'){
|
|
695
|
+
cleanupAllDialogs();
|
|
696
|
+
fail(`Expected paragraph with "Lit template content", got "${p?.textContent}"`);
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
cleanupAllDialogs();
|
|
701
|
+
pass('Dialog.create accepts Lit TemplateResult');
|
|
702
|
+
},
|
|
703
|
+
|
|
704
|
+
'Dialog.confirm should accept Lit TemplateResult as content': async ({pass, fail}) => {
|
|
705
|
+
let response = null;
|
|
706
|
+
const dialog = Dialog.confirm(
|
|
707
|
+
html`<p class="p">Confirm this?</p>`,
|
|
708
|
+
r => { response = r; }
|
|
709
|
+
);
|
|
710
|
+
await dialog.updateComplete;
|
|
711
|
+
|
|
712
|
+
const container = dialog.querySelector('div[style="display: contents;"]');
|
|
713
|
+
if(!container){
|
|
714
|
+
cleanupAllDialogs();
|
|
715
|
+
fail('Should render TemplateResult in confirm dialog');
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
const p = container.querySelector('p');
|
|
720
|
+
if(!p || p.textContent !== 'Confirm this?'){
|
|
721
|
+
cleanupAllDialogs();
|
|
722
|
+
fail(`Expected paragraph with "Confirm this?", got "${p?.textContent}"`);
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
cleanupAllDialogs();
|
|
727
|
+
pass('Dialog.confirm accepts Lit TemplateResult');
|
|
728
|
+
},
|
|
729
|
+
|
|
730
|
+
'Dialog.alert should accept Lit TemplateResult as content': async ({pass, fail}) => {
|
|
731
|
+
const dialog = Dialog.alert(html`<p class="p">Alert content</p>`);
|
|
732
|
+
await dialog.updateComplete;
|
|
733
|
+
|
|
734
|
+
const container = dialog.querySelector('div[style="display: contents;"]');
|
|
735
|
+
if(!container){
|
|
736
|
+
cleanupAllDialogs();
|
|
737
|
+
fail('Should render TemplateResult in alert dialog');
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
cleanupAllDialogs();
|
|
742
|
+
pass('Dialog.alert accepts Lit TemplateResult');
|
|
743
|
+
},
|
|
744
|
+
|
|
745
|
+
'Dialog.error should accept Lit TemplateResult as content': async ({pass, fail}) => {
|
|
746
|
+
const dialog = Dialog.error(html`<p class="p">Error content</p>`);
|
|
747
|
+
await dialog.updateComplete;
|
|
748
|
+
|
|
749
|
+
const container = dialog.querySelector('div[style="display: contents;"]');
|
|
750
|
+
if(!container){
|
|
751
|
+
cleanupAllDialogs();
|
|
752
|
+
fail('Should render TemplateResult in error dialog');
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
cleanupAllDialogs();
|
|
757
|
+
pass('Dialog.error accepts Lit TemplateResult');
|
|
758
|
+
},
|
|
759
|
+
|
|
760
|
+
'Dialog.success should accept Lit TemplateResult as content': async ({pass, fail}) => {
|
|
761
|
+
const dialog = Dialog.success(html`<p class="p">Success content</p>`);
|
|
762
|
+
await dialog.updateComplete;
|
|
763
|
+
|
|
764
|
+
const container = dialog.querySelector('div[style="display: contents;"]');
|
|
765
|
+
if(!container){
|
|
766
|
+
cleanupAllDialogs();
|
|
767
|
+
fail('Should render TemplateResult in success dialog');
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
cleanupAllDialogs();
|
|
772
|
+
pass('Dialog.success accepts Lit TemplateResult');
|
|
676
773
|
}
|
|
677
774
|
};
|
|
@@ -1115,5 +1115,137 @@ export default {
|
|
|
1115
1115
|
}
|
|
1116
1116
|
|
|
1117
1117
|
pass('Table has static editors');
|
|
1118
|
+
},
|
|
1119
|
+
|
|
1120
|
+
/*
|
|
1121
|
+
Non-Editable Field Tests
|
|
1122
|
+
*/
|
|
1123
|
+
'should not create input for editable:false field when editing': async ({pass, fail}) => {
|
|
1124
|
+
const table = await createTable();
|
|
1125
|
+
table.setData({
|
|
1126
|
+
fields: [
|
|
1127
|
+
{ name: 'id', label: 'ID', editable: false },
|
|
1128
|
+
{ name: 'name', label: 'Name' },
|
|
1129
|
+
{ name: 'age', label: 'Age' }
|
|
1130
|
+
],
|
|
1131
|
+
records: [...sampleRecords]
|
|
1132
|
+
});
|
|
1133
|
+
await table.updateComplete;
|
|
1134
|
+
|
|
1135
|
+
const record = table.records[0];
|
|
1136
|
+
table.editRecord(record);
|
|
1137
|
+
|
|
1138
|
+
const idCell = table.shadowRoot.querySelector(`.record[data-index="0"] .cell[data-field="id"]`);
|
|
1139
|
+
if(!idCell){
|
|
1140
|
+
cleanup(table);
|
|
1141
|
+
fail('Could not find id cell');
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
const input = idCell.querySelector('input, select');
|
|
1146
|
+
if(input){
|
|
1147
|
+
cleanup(table);
|
|
1148
|
+
fail('Non-editable field should not have an input in edit mode');
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
cleanup(table);
|
|
1153
|
+
pass('editable:false field does not show input in edit mode');
|
|
1154
|
+
},
|
|
1155
|
+
|
|
1156
|
+
'should preserve editable:false field value when saving': async ({pass, fail}) => {
|
|
1157
|
+
const table = await createTable();
|
|
1158
|
+
table.setData({
|
|
1159
|
+
fields: [
|
|
1160
|
+
{ name: 'id', label: 'ID', editable: false },
|
|
1161
|
+
{ name: 'name', label: 'Name' },
|
|
1162
|
+
{ name: 'age', label: 'Age' }
|
|
1163
|
+
],
|
|
1164
|
+
records: [...sampleRecords]
|
|
1165
|
+
});
|
|
1166
|
+
await table.updateComplete;
|
|
1167
|
+
|
|
1168
|
+
const record = table.records[0];
|
|
1169
|
+
const originalId = record.id;
|
|
1170
|
+
table.editRecord(record);
|
|
1171
|
+
|
|
1172
|
+
const nameCell = table.shadowRoot.querySelector(`.record[data-index="0"] .cell[data-field="name"]`);
|
|
1173
|
+
const nameInput = nameCell ? nameCell.querySelector('input') : null;
|
|
1174
|
+
if(nameInput) nameInput.value = 'Updated Name';
|
|
1175
|
+
|
|
1176
|
+
table.saveEditedRecord(record);
|
|
1177
|
+
await table.updateComplete;
|
|
1178
|
+
|
|
1179
|
+
if(record.id !== originalId){
|
|
1180
|
+
cleanup(table);
|
|
1181
|
+
fail(`Non-editable field 'id' should not change: expected ${originalId}, got ${record.id}`);
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
cleanup(table);
|
|
1186
|
+
pass('editable:false field value is preserved after save');
|
|
1187
|
+
},
|
|
1188
|
+
|
|
1189
|
+
'should display value for editable:false field during edit mode': async ({pass, fail}) => {
|
|
1190
|
+
const table = await createTable();
|
|
1191
|
+
table.setData({
|
|
1192
|
+
fields: [
|
|
1193
|
+
{ name: 'id', label: 'ID', editable: false },
|
|
1194
|
+
{ name: 'name', label: 'Name' }
|
|
1195
|
+
],
|
|
1196
|
+
records: [...sampleRecords]
|
|
1197
|
+
});
|
|
1198
|
+
await table.updateComplete;
|
|
1199
|
+
|
|
1200
|
+
const record = table.records[0];
|
|
1201
|
+
table.editRecord(record);
|
|
1202
|
+
|
|
1203
|
+
const idCell = table.shadowRoot.querySelector(`.record[data-index="0"] .cell[data-field="id"]`);
|
|
1204
|
+
if(!idCell){
|
|
1205
|
+
cleanup(table);
|
|
1206
|
+
fail('Could not find id cell');
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
if(!idCell.textContent.trim()){
|
|
1211
|
+
cleanup(table);
|
|
1212
|
+
fail('Non-editable field cell should display its value during edit mode');
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
cleanup(table);
|
|
1217
|
+
pass('editable:false field displays its value during edit mode');
|
|
1218
|
+
},
|
|
1219
|
+
|
|
1220
|
+
'should treat fields without editable property as editable by default': async ({pass, fail}) => {
|
|
1221
|
+
const table = await createTable();
|
|
1222
|
+
table.setData({
|
|
1223
|
+
fields: [
|
|
1224
|
+
{ name: 'id', label: 'ID' },
|
|
1225
|
+
{ name: 'name', label: 'Name' }
|
|
1226
|
+
],
|
|
1227
|
+
records: [...sampleRecords]
|
|
1228
|
+
});
|
|
1229
|
+
await table.updateComplete;
|
|
1230
|
+
|
|
1231
|
+
const record = table.records[0];
|
|
1232
|
+
table.editRecord(record);
|
|
1233
|
+
|
|
1234
|
+
const idCell = table.shadowRoot.querySelector(`.record[data-index="0"] .cell[data-field="id"]`);
|
|
1235
|
+
if(!idCell){
|
|
1236
|
+
cleanup(table);
|
|
1237
|
+
fail('Could not find id cell');
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
const input = idCell.querySelector('input, select');
|
|
1242
|
+
if(!input){
|
|
1243
|
+
cleanup(table);
|
|
1244
|
+
fail('Field without editable property should default to editable (have an input)');
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
cleanup(table);
|
|
1249
|
+
pass('Field without editable property defaults to editable');
|
|
1118
1250
|
}
|
|
1119
1251
|
};
|