kempo-ui 0.2.3 → 0.3.0

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.
Files changed (36) hide show
  1. package/dist/components/Context.js +1 -0
  2. package/dist/components/Table.js +6 -5
  3. package/docs/components/aside-items.html +1 -0
  4. package/docs/components/aside-menu.html +1 -0
  5. package/docs/components/aside-push.html +1 -0
  6. package/docs/components/aside.html +6 -2
  7. package/docs/components/context.html +231 -0
  8. package/docs/components/nav.html +7 -2
  9. package/docs/components/table.html +13 -1
  10. package/docs/components/tableControls.html +2 -1
  11. package/docs/components/tableCustomFields.html +28 -2
  12. package/docs/components/tableFetchRecords.html +2 -1
  13. package/docs/components/tableFieldSortHide.html +2 -1
  14. package/docs/components/tablePagination.html +2 -1
  15. package/docs/components/tableRecordEditing.html +33 -1
  16. package/docs/components/tableRecordFiltering.html +2 -1
  17. package/docs/components/tableRecordHiding.html +2 -1
  18. package/docs/components/tableRecordSearching.html +2 -1
  19. package/docs/components/tableRecordSelection.html +2 -1
  20. package/docs/components/tableRowControls.html +2 -1
  21. package/docs/components/tableServerSync.html +1 -0
  22. package/docs/components/tableSorting.html +2 -1
  23. package/docs/icons/npm.svg +1 -0
  24. package/docs/index.html +6 -0
  25. package/docs/nav-1.inc.html +5 -2
  26. package/docs/nav.inc.html +4 -1
  27. package/docs/src/components/Context.js +1 -0
  28. package/docs/src/components/Table.js +6 -5
  29. package/docs/utils/elevation.html +6 -2
  30. package/icons/npm.svg +1 -0
  31. package/llm.txt +1 -0
  32. package/package.json +1 -1
  33. package/src/components/Context.js +84 -0
  34. package/src/components/Table.js +7 -4
  35. package/tests/components/Context.browser-test.js +238 -0
  36. package/tests/components/Table.browser-test.js +132 -0
@@ -0,0 +1 @@
1
+ import{html}from"../lit-all.min.js";import LightComponent from"./LightComponent.js";export default class Context extends LightComponent{static properties={data:{type:String,reflect:!0}};constructor(){super(),this.data="{}"}connectedCallback(){super.connectedCallback(),this.style.display="contents"}set(t,e){const s=JSON.parse(this.data||"{}"),a=!(t in s),n=s[t];s[t]=e,this.data=JSON.stringify(s),this.dispatchEvent(new CustomEvent(a?"context:create":"context:set",{detail:a?{key:t,value:e}:{key:t,value:e,oldValue:n},bubbles:!0,composed:!0}))}get(t){return JSON.parse(this.data||"{}")[t]}has(t){return t in JSON.parse(this.data||"{}")}delete(t){const e=JSON.parse(this.data||"{}");if(!(t in e))return;const s=e[t];delete e[t],this.data=JSON.stringify(e),this.dispatchEvent(new CustomEvent("context:delete",{detail:{key:t,value:s},bubbles:!0,composed:!0}))}clear(){for(const t of Object.keys(JSON.parse(this.data||"{}")))this.delete(t)}getData(){return JSON.parse(this.data||"{}")}renderLightDom(){return html``}}customElements.define("k-context",Context);
@@ -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 a=e[s]||"";t.push(html`
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,a,r,n,o):this.renderDisplayCell(e,s,a,r,i)}
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,
@@ -12,6 +12,7 @@
12
12
  theme.init();
13
13
  </script>
14
14
  <script type="module" src="../src/components/Aside.js"></script>
15
+ <div style="height:33vh"></div>
15
16
  <script type="module" src="../src/components/Main.js"></script>
16
17
  <style>
17
18
  body {
@@ -12,6 +12,7 @@
12
12
  theme.init();
13
13
  </script>
14
14
  <script type="module" src="../src/components/Aside.js"></script>
15
+ <div style="height:33vh"></div>
15
16
  <script type="module" src="../src/components/Main.js"></script>
16
17
  <style>
17
18
  body {
@@ -21,6 +21,7 @@
21
21
  });
22
22
  </script>
23
23
  <script type="module" src="../src/components/Aside.js"></script>
24
+ <div style="height:33vh"></div>
24
25
  <script type="module" src="../src/components/Main.js"></script>
25
26
  </body>
26
27
  </html>
@@ -521,7 +521,8 @@
521
521
 
522
522
  <h3 id="asideItem"><a href="#asideItem" class="no-link">k-aside-item</a></h3>
523
523
  <p>A navigation link that reacts to the parent aside's collapsed/expanded state. Shows icon + text when expanded, icon-only when collapsed.</p>
524
- <table class="b r">
524
+ <div class="table-wrapper mb">
525
+ <table>
525
526
  <thead><tr><th>Attribute</th><th>Type</th><th>Default</th><th>Description</th></tr></thead>
526
527
  <tbody>
527
528
  <tr><td><code>icon</code></td><td>string</td><td><code>""</code></td><td>Icon name (renders <code>k-icon</code>). Shows a dot when collapsed if no icon set.</td></tr>
@@ -531,6 +532,7 @@
531
532
  <tr><td><code>hide-when-collapsed</code></td><td>boolean</td><td><code>false</code></td><td>Hides the item entirely when collapsed.</td></tr>
532
533
  </tbody>
533
534
  </table>
535
+ </div>
534
536
 
535
537
  <h3 id="asideLabel"><a href="#asideLabel" class="no-link">k-aside-label</a></h3>
536
538
  <p>A section label. Displays text when expanded, an <code>&lt;hr&gt;</code> divider when collapsed.</p>
@@ -538,7 +540,8 @@
538
540
 
539
541
  <h3 id="asideMenu"><a href="#asideMenu" class="no-link">k-aside-menu</a></h3>
540
542
  <p>An expandable sub-menu that toggles open/closed. When the aside is collapsed, clicking the menu icon expands the aside.</p>
541
- <table class="b r">
543
+ <div class="table-wrapper mb">
544
+ <table>
542
545
  <thead><tr><th>Attribute</th><th>Type</th><th>Default</th><th>Description</th></tr></thead>
543
546
  <tbody>
544
547
  <tr><td><code>icon</code></td><td>string</td><td><code>""</code></td><td>Icon name for the menu header.</td></tr>
@@ -548,6 +551,7 @@
548
551
  <tr><td><code>hide-when-collapsed</code></td><td>boolean</td><td><code>false</code></td><td>Hides the menu entirely when collapsed.</td></tr>
549
552
  </tbody>
550
553
  </table>
554
+ </div>
551
555
  <p>Slot: Place <code>k-aside-item</code> elements inside for sub-menu links.</p>
552
556
 
553
557
  <h3 id="asideSpacer"><a href="#asideSpacer" class="no-link">k-aside-spacer</a></h3>
@@ -0,0 +1,231 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Context - Components - Kempo Docs - A Web Components Solution</title>
7
+ <link rel="icon" type="image/png" sizes="48x48" href="../media/icon48.png">
8
+ <link rel="manifest" href="../manifest.json" />
9
+ <link rel="stylesheet" href="../kempo-vars.css" />
10
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/kempo-css@2/dist/kempo.min.css" />
11
+ <link rel="stylesheet" href="../kempo-hljs.css" />
12
+ <link rel="stylesheet" href="../styles.css" />
13
+ <script>window.litDisableBundleWarning = true;</script>
14
+ </head>
15
+ <body>
16
+ <k-import src="../nav-1.inc.html"></k-import>
17
+ <h1 class="ta-center">Context</h1>
18
+ <k-main>
19
+ <k-accordion persistent-id="toc" class="b r mb">
20
+ <k-accordion-header for-panel="toc-panel">Table of Contents</k-accordion-header>
21
+ <k-accordion-panel name="toc-panel">
22
+ <div class="m">
23
+ <h6>Examples</h6>
24
+ <a href="#basicUsage">Basic Usage</a><br />
25
+ <a href="#events">Listening to Events</a><br />
26
+ <a href="#usingInComponents">Using in Components</a><br />
27
+ <a href="#interactiveDemo">Interactive Demo</a><br />
28
+
29
+ <h6 class="mt"><a href="#jsRef" class="no-link">JavaScript Reference</a></h6>
30
+ <a href="#constructor">Constructor</a><br />
31
+ <a href="#attributes">Attributes</a><br />
32
+ <a href="#methods">Methods</a><br />
33
+ <a href="#eventsRef">Events</a><br />
34
+ </div>
35
+ </k-accordion-panel>
36
+ </k-accordion>
37
+
38
+ <p><code>k-context</code> is a non-rendering state container inspired by React's <code>useContext</code>. Place it in the DOM as an ancestor of components that need shared state. Descendant components locate the nearest <code>k-context</code> via <code>closest('k-context')</code>, read and write data with its methods, and react to changes by listening for its events.</p>
39
+
40
+ <h3 id="basicUsage"><a href="#basicUsage" class="no-link">Basic Usage</a></h3>
41
+ <p>Declare a <code>&lt;k-context&gt;</code> element in your markup. It is invisible and renders nothing; its <code>display: contents</code> style ensures its children remain in the normal document flow.</p>
42
+ <div class="row -mx">
43
+ <div class="col m-span-12 px">
44
+ <k-card label="HTML">
45
+ <pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">k-context</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;myContext&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-context</span>&gt;</span></code></pre>
46
+ </k-card>
47
+ </div>
48
+ </div>
49
+ <p>You can pre-populate the store declaratively by setting the <code>data</code> attribute to a valid JSON string:</p>
50
+ <div class="row -mx">
51
+ <div class="col m-span-12 px">
52
+ <k-card label="HTML">
53
+ <pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">k-context</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;myContext&quot;</span> <span class="hljs-attr">data</span>=<span class="hljs-string">&#x27;{&quot;count&quot;:0,&quot;theme&quot;:&quot;dark&quot;}&#x27;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-context</span>&gt;</span></code></pre>
54
+ </k-card>
55
+ </div>
56
+ </div>
57
+ <p>Use <code>set()</code> and <code>get()</code> to write and read data by key:</p>
58
+ <div class="row -mx">
59
+ <div class="col m-span-12 px">
60
+ <k-card label="JavaScript">
61
+ <pre><code class="hljs javascript"><span class="hljs-keyword">const</span> ctx = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&#x27;k-context&#x27;</span>);<br>ctx.<span class="hljs-title function_">set</span>(<span class="hljs-string">&#x27;count&#x27;</span>, <span class="hljs-number">0</span>);<br>ctx.<span class="hljs-title function_">set</span>(<span class="hljs-string">&#x27;theme&#x27;</span>, <span class="hljs-string">&#x27;dark&#x27;</span>);</code></pre>
62
+ </k-card>
63
+ </div>
64
+ </div>
65
+
66
+ <h3 id="events"><a href="#events" class="no-link">Listening to Events</a></h3>
67
+ <p><code>context:create</code> fires when a new key is set for the first time. <code>context:set</code> fires when an existing key's value changes. <code>context:delete</code> fires when a key is removed. All events bubble and are composed.</p>
68
+ <div class="row -mx">
69
+ <div class="col m-span-12 px">
70
+ <k-card label="JavaScript">
71
+ <pre><code class="hljs javascript"><span class="hljs-keyword">const</span> ctx = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&#x27;k-context&#x27;</span>);<br>ctx.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&#x27;context:create&#x27;</span>, <span class="hljs-function"><span class="hljs-params">e</span> =&gt;</span> {<br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;Created:&#x27;</span>, e.<span class="hljs-property">detail</span>.<span class="hljs-property">key</span>, e.<span class="hljs-property">detail</span>.<span class="hljs-property">value</span>);<br>});<br>ctx.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&#x27;context:set&#x27;</span>, <span class="hljs-function"><span class="hljs-params">e</span> =&gt;</span> {<br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;Updated:&#x27;</span>, e.<span class="hljs-property">detail</span>.<span class="hljs-property">key</span>, <span class="hljs-string">&#x27;from&#x27;</span>, e.<span class="hljs-property">detail</span>.<span class="hljs-property">oldValue</span>, <span class="hljs-string">&#x27;to&#x27;</span>, e.<span class="hljs-property">detail</span>.<span class="hljs-property">value</span>);<br>});<br>ctx.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&#x27;context:delete&#x27;</span>, <span class="hljs-function"><span class="hljs-params">e</span> =&gt;</span> {<br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;Deleted:&#x27;</span>, e.<span class="hljs-property">detail</span>.<span class="hljs-property">key</span>, <span class="hljs-string">&#x27;was&#x27;</span>, e.<span class="hljs-property">detail</span>.<span class="hljs-property">value</span>);<br>});</code></pre>
72
+ </k-card>
73
+ </div>
74
+ </div>
75
+
76
+ <h3 id="usingInComponents"><a href="#usingInComponents" class="no-link">Using in Components</a></h3>
77
+ <p>Child components locate the nearest ancestor context using <code>closest('k-context')</code>. This follows the same scoping pattern used by all Kempo UI parent–child relationships.</p>
78
+ <div class="row -mx">
79
+ <div class="col m-span-12 px">
80
+ <k-card label="JavaScript">
81
+ <pre><code class="hljs javascript"><span class="hljs-comment">// Inside a child component, find the nearest ancestor context:</span><br><span class="hljs-keyword">const</span> ctx = <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">closest</span>(<span class="hljs-string">&#x27;k-context&#x27;</span>);<br><span class="hljs-keyword">const</span> count = ctx?.<span class="hljs-title function_">get</span>(<span class="hljs-string">&#x27;count&#x27;</span>);</code></pre>
82
+ </k-card>
83
+ </div>
84
+ </div>
85
+ <p>To react to context changes from inside a shadow-DOM component, listen on the context element directly — events bubble and are <code>composed: true</code> so they also cross shadow boundaries.</p>
86
+
87
+ <h3 id="interactiveDemo"><a href="#interactiveDemo" class="no-link">Interactive Demo</a></h3>
88
+ <div class="row -mx">
89
+ <div class="col m-span-12 t-span-6 px">
90
+ <k-card label="HTML">
91
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-context</span> <span class="hljs-attr">data</span>=<span class="hljs-string">'{"count":0}'</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">button</span><br /> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"const ctx=this.closest('k-context'); ctx.set('count', (ctx.get('count') || 0) - 1)"</span><br /> &gt;</span>-<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">count-view</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">count-view</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">button</span><br /> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"const ctx=this.closest('k-context'); ctx.set('count', (ctx.get('count') || 0) + 1)"</span><br /> &gt;</span>+<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">button</span><br /> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"this.closest('k-context').clear()"</span><br /> &gt;</span>Clear<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">k-context</span>&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span>&gt;</span><span class="javascript"><br /> <span class="hljs-keyword">import</span> <span class="hljs-string">'../src/components/Context.js'</span>;<br /> <span class="hljs-keyword">import</span> ShadowComponent <span class="hljs-keyword">from</span> <span class="hljs-string">'../src/components/ShadowComponent.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-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CountView</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ShadowComponent</span> </span>{<br /> connectedCallback() {<br /> <span class="hljs-keyword">super</span>.connectedCallback();<br /> <span class="hljs-keyword">const</span> ctx = <span class="hljs-keyword">this</span>.closest(<span class="hljs-string">'k-context'</span>);<br /> ctx?.addEventListener(<span class="hljs-string">'context:create'</span>, () =&gt; <span class="hljs-keyword">this</span>.requestUpdate());<br /> ctx?.addEventListener(<span class="hljs-string">'context:set'</span>, () =&gt; <span class="hljs-keyword">this</span>.requestUpdate());<br /> ctx?.addEventListener(<span class="hljs-string">'context:delete'</span>, () =&gt; <span class="hljs-keyword">this</span>.requestUpdate());<br /> }<br /> render() {<br /> <span class="hljs-keyword">const</span> count = <span class="hljs-keyword">this</span>.closest(<span class="hljs-string">'k-context'</span>)?.get(<span class="hljs-string">'count'</span>);<br /> <span class="hljs-keyword">return</span> count !== <span class="hljs-literal">undefined</span> ? html`<span class="xml"></span><span class="hljs-subst">${count}</span><span class="xml">`</span> : html`<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">code</span>&gt;</span>undefined<span class="hljs-tag">&lt;/<span class="hljs-name">code</span>&gt;</span>`</span>;<br /> }<br /> }<br /> customElements.define(<span class="hljs-string">'count-view'</span>, CountView);<br /></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></code></pre>
92
+ </k-card>
93
+ </div>
94
+ <div class="col m-span-12 t-span-6 px">
95
+ <k-card label="Output">
96
+ <k-context data='{"count":0}'>
97
+ <button
98
+ onclick="const ctx=this.closest('k-context'); ctx.set('count', (ctx.get('count') || 0) - 1)"
99
+ >-</button>
100
+ <count-view></count-view>
101
+ <button
102
+ onclick="const ctx=this.closest('k-context'); ctx.set('count', (ctx.get('count') || 0) + 1)"
103
+ >+</button>
104
+ <button
105
+ onclick="this.closest('k-context').clear()"
106
+ >Clear</button>
107
+ </k-context>
108
+ <script type="module">
109
+ import '../src/components/Context.js';
110
+ import ShadowComponent from '../src/components/ShadowComponent.js';
111
+ import { html } from '../src/lit-all.min.js';
112
+ class CountView extends ShadowComponent {
113
+ connectedCallback() {
114
+ super.connectedCallback();
115
+ const ctx = this.closest('k-context');
116
+ ctx?.addEventListener('context:create', () => this.requestUpdate());
117
+ ctx?.addEventListener('context:set', () => this.requestUpdate());
118
+ ctx?.addEventListener('context:delete', () => this.requestUpdate());
119
+ }
120
+ render() {
121
+ const count = this.closest('k-context')?.get('count');
122
+ return count !== undefined ? html`${count}` : html`<code>undefined</code>`;
123
+ }
124
+ }
125
+ customElements.define('count-view', CountView);
126
+ </script>
127
+ </k-card>
128
+ </div>
129
+ </div>
130
+
131
+ <h2 id="jsRef"><a href="#jsRef" class="no-link">JavaScript Reference</a></h2>
132
+
133
+ <h3 id="constructor"><a href="#constructor" class="no-link">Constructor</a></h3>
134
+ <p><code>k-context</code> extends <code>LightComponent</code>. It renders nothing and sets <code>display: contents</code> on itself so its children remain in the normal document flow. It accepts an optional <code>data</code> attribute containing a JSON string to pre-populate the context state.</p>
135
+
136
+ <h3 id="attributes"><a href="#attributes" class="no-link">Attributes</a></h3>
137
+ <div class="table-wrapper mb">
138
+ <table>
139
+ <thead>
140
+ <tr>
141
+ <th>Attribute</th>
142
+ <th>Type</th>
143
+ <th>Description</th>
144
+ </tr>
145
+ </thead>
146
+ <tbody>
147
+ <tr>
148
+ <td><code>data</code></td>
149
+ <td><code>String</code> (JSON)</td>
150
+ <td>A JSON string representing the initial store state. Reflects back to the attribute as state changes via the component's methods. Defaults to <code>'{}'</code>.</td>
151
+ </tr>
152
+ </tbody>
153
+ </table>
154
+ </div>
155
+
156
+ <h3 id="methods"><a href="#methods" class="no-link">Methods</a></h3>
157
+ <div class="table-wrapper mb">
158
+ <table>
159
+ <thead>
160
+ <tr>
161
+ <th>Method</th>
162
+ <th>Description</th>
163
+ </tr>
164
+ </thead>
165
+ <tbody>
166
+ <tr>
167
+ <td><code>set(key, value)</code></td>
168
+ <td>Creates or updates a key. Fires <code>context:create</code> if the key is new, <code>context:set</code> if it already existed.</td>
169
+ </tr>
170
+ <tr>
171
+ <td><code>get(key)</code></td>
172
+ <td>Returns the stored value for <code>key</code>, or <code>undefined</code> if not set.</td>
173
+ </tr>
174
+ <tr>
175
+ <td><code>has(key)</code></td>
176
+ <td>Returns <code>true</code> if <code>key</code> exists in the store.</td>
177
+ </tr>
178
+ <tr>
179
+ <td><code>delete(key)</code></td>
180
+ <td>Removes a key and fires <code>context:delete</code>. No-op if the key does not exist.</td>
181
+ </tr>
182
+ <tr>
183
+ <td><code>clear()</code></td>
184
+ <td>Calls <code>delete()</code> on every key, firing <code>context:delete</code> for each.</td>
185
+ </tr>
186
+ <tr>
187
+ <td><code>getData()</code></td>
188
+ <td>Returns a shallow copy of the entire data store as a plain object.</td>
189
+ </tr>
190
+ </tbody>
191
+ </table>
192
+ </div>
193
+
194
+ <h3 id="eventsRef"><a href="#eventsRef" class="no-link">Events</a></h3>
195
+ <p>All events bubble and are <code>composed: true</code>, so they cross shadow DOM boundaries.</p>
196
+ <div class="table-wrapper mb">
197
+ <table>
198
+ <thead>
199
+ <tr>
200
+ <th>Event</th>
201
+ <th>Fired when</th>
202
+ <th><code>detail</code> shape</th>
203
+ </tr>
204
+ </thead>
205
+ <tbody>
206
+ <tr>
207
+ <td><code>context:create</code></td>
208
+ <td>A new key is set for the first time</td>
209
+ <td><code>{ key, value }</code></td>
210
+ </tr>
211
+ <tr>
212
+ <td><code>context:set</code></td>
213
+ <td>An existing key's value is changed</td>
214
+ <td><code>{ key, value, oldValue }</code></td>
215
+ </tr>
216
+ <tr>
217
+ <td><code>context:delete</code></td>
218
+ <td>A key is removed via <code>delete()</code> or <code>clear()</code></td>
219
+ <td><code>{ key, value }</code></td>
220
+ </tr>
221
+ </tbody>
222
+ </table>
223
+ </div>
224
+ </k-main>
225
+ <div style="height:33vh"></div>
226
+ <script type="module" src="../src/components/Import.js"></script>
227
+ <script type="module" src="../src/components/Accordion.js"></script>
228
+ <script type="module" src="../src/components/Card.js"></script>
229
+ <script type="module" src="../src/components/Main.js"></script>
230
+ </body>
231
+ </html>
@@ -68,7 +68,8 @@
68
68
  <pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">k-nav</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;bg-primary&quot;</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>Menu<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;/&quot;</span>&gt;</span>My App<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">&quot;flex: 1&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;/account&quot;</span>&gt;</span>Account<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">k-nav</span>&gt;</span></code></pre>
69
69
 
70
70
  <h2 id="attributes"><a href="#attributes" class="no-link">Attributes &amp; Properties</a></h2>
71
- <table class="mb">
71
+ <div class="table-wrapper mb">
72
+ <table>
72
73
  <thead>
73
74
  <tr>
74
75
  <th>Attribute / Property</th>
@@ -89,9 +90,11 @@
89
90
  </tr>
90
91
  </tbody>
91
92
  </table>
93
+ </div>
92
94
 
93
95
  <h2 id="slots"><a href="#slots" class="no-link">Slots</a></h2>
94
- <table class="mb">
96
+ <div class="table-wrapper mb">
97
+ <table>
95
98
  <thead>
96
99
  <tr>
97
100
  <th>Slot</th>
@@ -105,6 +108,7 @@
105
108
  </tr>
106
109
  </tbody>
107
110
  </table>
111
+ </div>
108
112
  </k-main>
109
113
 
110
114
  <script src="../src/components/Nav.js" type="module"></script>
@@ -112,6 +116,7 @@
112
116
  <script src="../src/components/Main.js" type="module"></script>
113
117
  <script src="../src/components/Accordion.js" type="module"></script>
114
118
  <script src="../src/components/Card.js" type="module"></script>
119
+ <div style="height:33vh"></div>
115
120
  <script src="../src/components/Icon.js" type="module"></script>
116
121
  </body>
117
122
  </html>
@@ -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>: &lt;Array>object</i></code></h5>
137
- <p>An array of objects containing a <code>name</code>, <code>label</code>, <code>renderer</code> function, and <code>calculator</code> function that represent the fields to be displayed. The <code>renderer</code> function can be used to customize the display of the field, and the <code>calculator</code> function can be used to calculate the value of the field based on other fields in the record.</p>
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>: &lt;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>
@@ -278,6 +289,7 @@
278
289
  <script type="module" src="../src/components/Table.js"></script>
279
290
  <script type="module" src="../src/components/Accordion.js"></script>
280
291
  <script type="module" src="../src/components/Card.js"></script>
292
+ <div style="height:33vh"></div>
281
293
  <script type="module" src="../src/components/Resize.js"></script>
282
294
 
283
295
  </body>
@@ -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="./components/table.html"><h5>Back to Table Component Documentation</h5></a>
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>
@@ -75,6 +75,7 @@
75
75
  <div style="height: 33.333vh"></div>
76
76
  <script type="module" src="../src/components/Import.js"></script>
77
77
  <script type="module" src="../src/components/Main.js"></script>
78
+ <div style="height:33vh"></div>
78
79
  <script type="module" src="../src/components/Table.js"></script>
79
80
  </body>
80
81
  </html>
@@ -54,9 +54,9 @@
54
54
  <h1 class="ta-center">Table - Custom Fields</h1>
55
55
  <k-main>
56
56
 
57
- <a href="./components/table.html"><h5>Back to Table Component Documentation</h5></a>
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 the labels, formatting the output, or creating a calculated field.</p>
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">&lt;<span class="hljs-name">k-table</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"customFieldsExample"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-table</span>&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span>&gt;</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>) =&gt;</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 &lt; <span class="hljs-number">0</span> || (monthDifference === <span class="hljs-number">0</span> &amp;&amp; today.getDate() &lt; 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>) =&gt;</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">&lt;/<span class="hljs-name">script</span>&gt;</span></code></pre>
62
62
 
@@ -106,10 +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">&lt;<span class="hljs-name">k-table</span> <span class="hljs-attr">id</span>=<span class="hljs-string">editableFieldExample</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-tc-edit</span> <span class="hljs-attr">slot</span>=<span class="hljs-string">after</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-tc-edit</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">k-table</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">module</span>&gt;</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">&#x27;k-table&#x27;</span>);<br> <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">&#x27;editableFieldExample&#x27;</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">&#x27;Alice&#x27;</span>,<br> <span class="hljs-attr">city</span>: <span class="hljs-string">&#x27;New York&#x27;</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">&#x27;Bob&#x27;</span>,<br> <span class="hljs-attr">city</span>: <span class="hljs-string">&#x27;Los Angeles&#x27;</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">&#x27;Charlie&#x27;</span>,<br> <span class="hljs-attr">city</span>: <span class="hljs-string">&#x27;Chicago&#x27;</span><br> }],<br> <span class="hljs-attr">fields</span>: [{<br> <span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;id&#x27;</span>,<br> <span class="hljs-attr">label</span>: <span class="hljs-string">&#x27;ID&#x27;</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">&#x27;name&#x27;</span>,<br> <span class="hljs-attr">label</span>: <span class="hljs-string">&#x27;Name&#x27;</span><br> }, {<br> <span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;city&#x27;</span>,<br> <span class="hljs-attr">label</span>: <span class="hljs-string">&#x27;City&#x27;</span><br> }]<br> });<br></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</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>
137
+ <div style="height:33vh"></div>
113
138
  <script type="module" src="../src/components/Table.js"></script>
139
+ <script type="module" src="../src/components/tableControls/Edit.js"></script>
114
140
  </body>
115
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="./components/table.html"><h5>Back to Table Component Documentation</h5></a>
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.
@@ -108,6 +108,7 @@
108
108
  </k-main>
109
109
  <div style="height: 33.333vh"></div>
110
110
  <script type="module" src="../src/components/Import.js"></script>
111
+ <div style="height:33vh"></div>
111
112
  <script type="module" src="../src/components/Main.js"></script>
112
113
  </body>
113
114
  </html>