@kubex/zinc 1.1.65 → 1.1.67
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/custom-elements.json +16 -3
- package/dist/vscode.html-custom-data.json +1 -1
- package/dist/web-types.json +6 -2
- package/dist/zn.d.ts +2 -0
- package/dist/zn.min.js +16 -7
- package/package.json +1 -1
- package/src/components/data-table/data-table.component.ts +35 -11
- package/src/components/data-table/data-table.test.ts +70 -0
|
@@ -7860,6 +7860,10 @@
|
|
|
7860
7860
|
{
|
|
7861
7861
|
"description": "Slot for custom empty state.",
|
|
7862
7862
|
"name": "empty-state"
|
|
7863
|
+
},
|
|
7864
|
+
{
|
|
7865
|
+
"description": "Slot for a custom no-results state, shown when a search on a no-initial-load table returns no rows.",
|
|
7866
|
+
"name": "no-results"
|
|
7863
7867
|
}
|
|
7864
7868
|
],
|
|
7865
7869
|
"members": [
|
|
@@ -8132,6 +8136,15 @@
|
|
|
8132
8136
|
"privacy": "private",
|
|
8133
8137
|
"default": "false"
|
|
8134
8138
|
},
|
|
8139
|
+
{
|
|
8140
|
+
"kind": "field",
|
|
8141
|
+
"name": "_lastLoadHadRows",
|
|
8142
|
+
"type": {
|
|
8143
|
+
"text": "boolean"
|
|
8144
|
+
},
|
|
8145
|
+
"privacy": "private",
|
|
8146
|
+
"default": "false"
|
|
8147
|
+
},
|
|
8135
8148
|
{
|
|
8136
8149
|
"kind": "field",
|
|
8137
8150
|
"name": "_lastTableContent",
|
|
@@ -8204,7 +8217,7 @@
|
|
|
8204
8217
|
"kind": "field",
|
|
8205
8218
|
"name": "hasSlotController",
|
|
8206
8219
|
"privacy": "private",
|
|
8207
|
-
"default": "new HasSlotController( this, '[default]', ActionSlots.search.valueOf(), ActionSlots.delete.valueOf(), ActionSlots.modify.valueOf(), ActionSlots.create.valueOf(), ActionSlots.filter.valueOf(), ActionSlots.sort.valueOf(), ActionSlots.inputs.valueOf(), 'empty-state' )"
|
|
8220
|
+
"default": "new HasSlotController( this, '[default]', ActionSlots.search.valueOf(), ActionSlots.delete.valueOf(), ActionSlots.modify.valueOf(), ActionSlots.create.valueOf(), ActionSlots.filter.valueOf(), ActionSlots.sort.valueOf(), ActionSlots.inputs.valueOf(), 'empty-state', 'no-results' )"
|
|
8208
8221
|
},
|
|
8209
8222
|
{
|
|
8210
8223
|
"kind": "field",
|
|
@@ -8985,7 +8998,7 @@
|
|
|
8985
8998
|
"tagNameWithoutPrefix": "data-table",
|
|
8986
8999
|
"tagName": "zn-data-table",
|
|
8987
9000
|
"customElement": true,
|
|
8988
|
-
"jsDoc": "/**\n * @summary Short summary of the component's intended use.\n * @documentation https://zinc.style/components/data-table\n * @status experimental\n * @since 1.0\n *\n * @dependency zn-alert\n * @dependency zn-button\n * @dependency zn-empty-state\n * @dependency zn-chip\n * @dependency zn-hover-container\n * @dependency zn-dropdown\n * @dependency zn-menu\n * @dependency zn-menu-item\n * @dependency zn-button-group\n * @dependency zn-confirm\n * @dependency zn-skeleton\n * @dependency zn-data-table-search\n *\n * @slot - The default slot.\n * @slot search - Slot for search component.\n * @slot sort - Slot for sort component.\n * @slot filter - Slot for filter component.\n * @slot filter-top - Slot for top-level filter component.\n * @slot delete-action - Slot for delete action button.\n * @slot modify-action - Slot for modify action button.\n * @slot create-action - Slot for create action button.\n * @slot inputs - Slot for additional input controls.\n * @slot empty-state - Slot for custom empty state.\n *\n * @csspart base - The component's base wrapper.\n *\n * @cssproperty --example - An example CSS custom property.\n */",
|
|
9001
|
+
"jsDoc": "/**\n * @summary Short summary of the component's intended use.\n * @documentation https://zinc.style/components/data-table\n * @status experimental\n * @since 1.0\n *\n * @dependency zn-alert\n * @dependency zn-button\n * @dependency zn-empty-state\n * @dependency zn-chip\n * @dependency zn-hover-container\n * @dependency zn-dropdown\n * @dependency zn-menu\n * @dependency zn-menu-item\n * @dependency zn-button-group\n * @dependency zn-confirm\n * @dependency zn-skeleton\n * @dependency zn-data-table-search\n *\n * @slot - The default slot.\n * @slot search - Slot for search component.\n * @slot sort - Slot for sort component.\n * @slot filter - Slot for filter component.\n * @slot filter-top - Slot for top-level filter component.\n * @slot delete-action - Slot for delete action button.\n * @slot modify-action - Slot for modify action button.\n * @slot create-action - Slot for create action button.\n * @slot inputs - Slot for additional input controls.\n * @slot empty-state - Slot for custom empty state.\n * @slot no-results - Slot for a custom no-results state, shown when a search on a no-initial-load table returns no rows.\n *\n * @csspart base - The component's base wrapper.\n *\n * @cssproperty --example - An example CSS custom property.\n */",
|
|
8989
9002
|
"documentation": "https://zinc.style/components/data-table",
|
|
8990
9003
|
"status": "experimental",
|
|
8991
9004
|
"since": "1.0",
|
|
@@ -42301,7 +42314,7 @@
|
|
|
42301
42314
|
"package": {
|
|
42302
42315
|
"name": "@kubex/zinc",
|
|
42303
42316
|
"description": "A collection of web components for building web applications based off of @shoelace-style/Shoelace",
|
|
42304
|
-
"version": "1.1.
|
|
42317
|
+
"version": "1.1.67",
|
|
42305
42318
|
"author": "",
|
|
42306
42319
|
"license": "MIT"
|
|
42307
42320
|
}
|
|
@@ -1079,7 +1079,7 @@
|
|
|
1079
1079
|
},
|
|
1080
1080
|
{
|
|
1081
1081
|
"name": "zn-data-table",
|
|
1082
|
-
"description": "Short summary of the component's intended use.\n---\n\n\n### **Slots:**\n - _default_ - The default slot.\n- **search** - Slot for search component.\n- **sort** - Slot for sort component.\n- **filter** - Slot for filter component.\n- **filter-top** - Slot for top-level filter component.\n- **delete-action** - Slot for delete action button.\n- **modify-action** - Slot for modify action button.\n- **create-action** - Slot for create action button.\n- **inputs** - Slot for additional input controls.\n- **empty-state** - Slot for custom empty state.\n\n### **CSS Properties:**\n - **--example** - An example CSS custom property. _(default: undefined)_\n\n### **CSS Parts:**\n - **base** - The component's base wrapper.",
|
|
1082
|
+
"description": "Short summary of the component's intended use.\n---\n\n\n### **Slots:**\n - _default_ - The default slot.\n- **search** - Slot for search component.\n- **sort** - Slot for sort component.\n- **filter** - Slot for filter component.\n- **filter-top** - Slot for top-level filter component.\n- **delete-action** - Slot for delete action button.\n- **modify-action** - Slot for modify action button.\n- **create-action** - Slot for create action button.\n- **inputs** - Slot for additional input controls.\n- **empty-state** - Slot for custom empty state.\n- **no-results** - Slot for a custom no-results state, shown when a search on a no-initial-load table returns no rows.\n\n### **CSS Properties:**\n - **--example** - An example CSS custom property. _(default: undefined)_\n\n### **CSS Parts:**\n - **base** - The component's base wrapper.",
|
|
1083
1083
|
"attributes": [
|
|
1084
1084
|
{ "name": "data-uri", "values": [] },
|
|
1085
1085
|
{ "name": "data", "values": [{ "name": "Row[]" }, { "name": "Row" }] },
|
package/dist/web-types.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json",
|
|
3
3
|
"name": "@kubex/zinc",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.67",
|
|
5
5
|
"description-markup": "markdown",
|
|
6
6
|
"contributions": {
|
|
7
7
|
"html": {
|
|
@@ -2411,7 +2411,7 @@
|
|
|
2411
2411
|
},
|
|
2412
2412
|
{
|
|
2413
2413
|
"name": "zn-data-table",
|
|
2414
|
-
"description": "Short summary of the component's intended use.\n---\n\n\n### **Slots:**\n - _default_ - The default slot.\n- **search** - Slot for search component.\n- **sort** - Slot for sort component.\n- **filter** - Slot for filter component.\n- **filter-top** - Slot for top-level filter component.\n- **delete-action** - Slot for delete action button.\n- **modify-action** - Slot for modify action button.\n- **create-action** - Slot for create action button.\n- **inputs** - Slot for additional input controls.\n- **empty-state** - Slot for custom empty state.\n\n### **CSS Properties:**\n - **--example** - An example CSS custom property. _(default: undefined)_\n\n### **CSS Parts:**\n - **base** - The component's base wrapper.",
|
|
2414
|
+
"description": "Short summary of the component's intended use.\n---\n\n\n### **Slots:**\n - _default_ - The default slot.\n- **search** - Slot for search component.\n- **sort** - Slot for sort component.\n- **filter** - Slot for filter component.\n- **filter-top** - Slot for top-level filter component.\n- **delete-action** - Slot for delete action button.\n- **modify-action** - Slot for modify action button.\n- **create-action** - Slot for create action button.\n- **inputs** - Slot for additional input controls.\n- **empty-state** - Slot for custom empty state.\n- **no-results** - Slot for a custom no-results state, shown when a search on a no-initial-load table returns no rows.\n\n### **CSS Properties:**\n - **--example** - An example CSS custom property. _(default: undefined)_\n\n### **CSS Parts:**\n - **base** - The component's base wrapper.",
|
|
2415
2415
|
"doc-url": "",
|
|
2416
2416
|
"attributes": [
|
|
2417
2417
|
{ "name": "data-uri", "value": { "type": "string" } },
|
|
@@ -2523,6 +2523,10 @@
|
|
|
2523
2523
|
{
|
|
2524
2524
|
"name": "empty-state",
|
|
2525
2525
|
"description": "Slot for custom empty state."
|
|
2526
|
+
},
|
|
2527
|
+
{
|
|
2528
|
+
"name": "no-results",
|
|
2529
|
+
"description": "Slot for a custom no-results state, shown when a search on a no-initial-load table returns no rows."
|
|
2526
2530
|
}
|
|
2527
2531
|
],
|
|
2528
2532
|
"events": [],
|
package/dist/zn.d.ts
CHANGED
|
@@ -3526,6 +3526,7 @@ declare module "components/data-table/data-table.component" {
|
|
|
3526
3526
|
* @slot create-action - Slot for create action button.
|
|
3527
3527
|
* @slot inputs - Slot for additional input controls.
|
|
3528
3528
|
* @slot empty-state - Slot for custom empty state.
|
|
3529
|
+
* @slot no-results - Slot for a custom no-results state, shown when a search on a no-initial-load table returns no rows.
|
|
3529
3530
|
*
|
|
3530
3531
|
* @csspart base - The component's base wrapper.
|
|
3531
3532
|
*
|
|
@@ -3578,6 +3579,7 @@ declare module "components/data-table/data-table.component" {
|
|
|
3578
3579
|
selectAllButton: ZnButton;
|
|
3579
3580
|
private _initialLoad;
|
|
3580
3581
|
private _hasLoadedData;
|
|
3582
|
+
private _lastLoadHadRows;
|
|
3581
3583
|
private _lastTableContent;
|
|
3582
3584
|
private readonly resizeObserver;
|
|
3583
3585
|
private page;
|
package/dist/zn.min.js
CHANGED
|
@@ -653,23 +653,31 @@ import{a as Cn,b as i_,c as sr,d as c,e as F}from"./chunks/zn.UBLZO6CI.js";var Q
|
|
|
653
653
|
<div class="table__collum--datetime">
|
|
654
654
|
<span><strong>${o.slice(0,10)}</strong></span>
|
|
655
655
|
<zn-style size="s" muted>${o.slice(11)}</zn-style>
|
|
656
|
-
</div>`})},yt=class extends C{constructor(){super(...arguments);this.data=[];this.sortDirection="asc";this.localSort=!1;this.filter="";this.search="";this.key="id";this.headers={};this.displayTemplates={};this.hiddenHeaders="{}";this.hiddenColumns="{}";this.unsortableHeaders="{}";this.unsortable=!1;this.standalone=!1;this.emptyStateIcon="data_alert";this.filters=[];this.method="POST";this.noInitialLoad=!1;this.groupBy="";this.groups="";this.itemsPerPage=Hh;this._initialLoad=!0;this._hasLoadedData=!1;this._lastTableContent=u``;this.resizeObserver=new Jt(this,{target:null,callback:()=>{this.tableContainer&&this.tableContainer.scrollIntoView({behavior:"smooth",block:"nearest"})}});this.page=Xb;this._rows=[];this.numberOfRowsSelected=0;this.selectedRows=[];this.hasSlotController=new Q(this,"[default]","search".valueOf(),"delete-action".valueOf(),"modify-action".valueOf(),"create-action".valueOf(),"filter".valueOf(),"sort".valueOf(),"inputs".valueOf(),"empty-state");this._dataTask=new al(this,{task:async([t,r],{signal:n})=>{if(t===void 0||this.noInitialLoad&&this._initialLoad)return{rows:[],page:1,perPage:this.itemsPerPage,total:0};this.groupBy&&(this.itemsPerPage=1e3);let a={page:this.page,perPage:this.itemsPerPage,sortColumn:this.sortColumn,sortDirection:this.sortDirection,filter:this.filter,search:this.search},s=this.hasSlotController.getSlots("inputs".valueOf()),l={};s&&(s.forEach(h=>{if(["zn-input","zn-select","zn-query-builder","zn-multiselect","zn-params-select","zn-datepicker","input","select","textarea"].includes(h.tagName.toLowerCase())){let f=h.value||h.getAttribute("value"),b=h.name||h.getAttribute("name");b&&(l[b]=f)}}),Object.assign(a,l)),r&&typeof r=="object"&&Object.assign(a,r);let d=await fetch(t,{method:this.method,headers:{"x-kx-fetch-style":"zn-data-table"},signal:n,credentials:"same-origin",body:this.method==="POST"?JSON.stringify(a):void 0});if(!d.ok)throw new Error(d.statusText);return d.json()},args:()=>[this.dataUri,this.requestParams]});this.rowHasActions=!1;this._expandedRows=new Set;this._hiddenCells=new Map;this._formatTemplates=Qk;this.requestParams={};this.filterChangeListener=t=>{t.target instanceof Lh&&(this.filter=t.target.value,this._dataTask.run().then(r=>r))};this.searchChangeListener=t=>{let r=t.target;if(r&&r.tagName==="ZN-DATA-TABLE-SEARCH"){if(this.search=r.value,t.detail){let{formData:n,searchUri:a}=t.detail;if(n&&typeof n=="object"&&(this.requestParams={...this.requestParams,...n}),a){let s=this.dataUri;this.dataUri=a,this._dataTask.run().then(()=>{this.dataUri=s});return}}this._dataTask.run().then(n=>n)}}}refresh(){this._initialLoad=!1,this._dataTask.run().then(t=>t)}render(){let t=u``;if(this.noInitialLoad&&this._initialLoad)t=u`
|
|
657
|
-
<slot name="empty-state"></slot>`;else if(this.dataUri)t=this._dataTask.render({pending:()=>this._initialLoad||!this._hasLoadedData?u`
|
|
656
|
+
</div>`})},yt=class extends C{constructor(){super(...arguments);this.data=[];this.sortDirection="asc";this.localSort=!1;this.filter="";this.search="";this.key="id";this.headers={};this.displayTemplates={};this.hiddenHeaders="{}";this.hiddenColumns="{}";this.unsortableHeaders="{}";this.unsortable=!1;this.standalone=!1;this.emptyStateIcon="data_alert";this.filters=[];this.method="POST";this.noInitialLoad=!1;this.groupBy="";this.groups="";this.itemsPerPage=Hh;this._initialLoad=!0;this._hasLoadedData=!1;this._lastLoadHadRows=!1;this._lastTableContent=u``;this.resizeObserver=new Jt(this,{target:null,callback:()=>{this.tableContainer&&this.tableContainer.scrollIntoView({behavior:"smooth",block:"nearest"})}});this.page=Xb;this._rows=[];this.numberOfRowsSelected=0;this.selectedRows=[];this.hasSlotController=new Q(this,"[default]","search".valueOf(),"delete-action".valueOf(),"modify-action".valueOf(),"create-action".valueOf(),"filter".valueOf(),"sort".valueOf(),"inputs".valueOf(),"empty-state","no-results");this._dataTask=new al(this,{task:async([t,r],{signal:n})=>{if(t===void 0||this.noInitialLoad&&this._initialLoad)return{rows:[],page:1,perPage:this.itemsPerPage,total:0};this.groupBy&&(this.itemsPerPage=1e3);let a={page:this.page,perPage:this.itemsPerPage,sortColumn:this.sortColumn,sortDirection:this.sortDirection,filter:this.filter,search:this.search},s=this.hasSlotController.getSlots("inputs".valueOf()),l={};s&&(s.forEach(h=>{if(["zn-input","zn-select","zn-query-builder","zn-multiselect","zn-params-select","zn-datepicker","input","select","textarea"].includes(h.tagName.toLowerCase())){let f=h.value||h.getAttribute("value"),b=h.name||h.getAttribute("name");b&&(l[b]=f)}}),Object.assign(a,l)),r&&typeof r=="object"&&Object.assign(a,r);let d=await fetch(t,{method:this.method,headers:{"x-kx-fetch-style":"zn-data-table"},signal:n,credentials:"same-origin",body:this.method==="POST"?JSON.stringify(a):void 0});if(!d.ok)throw new Error(d.statusText);return d.json()},args:()=>[this.dataUri,this.requestParams]});this.rowHasActions=!1;this._expandedRows=new Set;this._hiddenCells=new Map;this._formatTemplates=Qk;this.requestParams={};this.filterChangeListener=t=>{t.target instanceof Lh&&(this.filter=t.target.value,this._dataTask.run().then(r=>r))};this.searchChangeListener=t=>{let r=t.target;if(r&&r.tagName==="ZN-DATA-TABLE-SEARCH"){if(this.search=r.value,t.detail){let{formData:n,searchUri:a}=t.detail;if(n&&typeof n=="object"&&(this.requestParams={...this.requestParams,...n}),a){let s=this.dataUri;this.dataUri=a,this._dataTask.run().then(()=>{this.dataUri=s});return}}this._dataTask.run().then(n=>n)}}}refresh(){this._initialLoad=!1,this._dataTask.run().then(t=>t)}render(){let t=u``;if(this.noInitialLoad&&this._initialLoad)t=u`
|
|
657
|
+
<slot name="empty-state"></slot>`;else if(this.dataUri)t=this._dataTask.render({pending:()=>this._initialLoad||!this._hasLoadedData||!this._lastLoadHadRows?u`
|
|
658
658
|
<div>${this.loadingTable()}</div>`:u`
|
|
659
|
-
<div class="reduced-opacity">${this._lastTableContent}</div>`,complete:a=>(this._initialLoad=!1,this._hasLoadedData=!0,this._lastTableContent=u`
|
|
659
|
+
<div class="reduced-opacity">${this._lastTableContent}</div>`,complete:a=>(this._initialLoad=!1,this._hasLoadedData=!0,this._lastLoadHadRows=(a?.rows?.length??0)>0,this._lastTableContent=u`
|
|
660
660
|
<div>${this.renderTable(a)}</div>`,this._lastTableContent),error:a=>{if(a instanceof Error){if(a.name==="SyntaxError")return console.debug(a.message),u`
|
|
661
661
|
<zn-sp>
|
|
662
662
|
<zn-alert level="error">Unable to load data</zn-alert>
|
|
663
663
|
</zn-sp>`;if(a.message==="Not Found")return this.emptyState()}return u`
|
|
664
|
-
<div>${a}</div>`}});else if(Array.isArray(this.data)?this.data.length>0:this.data){let a=Array.isArray(this.data)?this.data:[this.data],s={rows:a,perPage:a.length,total:a.length,page:1};this._initialLoad=!1,this._hasLoadedData=!0,this._lastTableContent=u`
|
|
665
|
-
|
|
664
|
+
<div>${a}</div>`}});else if(Array.isArray(this.data)?this.data.length>0:this.data){let a=Array.isArray(this.data)?this.data:[this.data],s={rows:a,perPage:a.length,total:a.length,page:1};this._initialLoad=!1,this._hasLoadedData=!0,this._lastLoadHadRows=a.length>0,this._lastTableContent=u`
|
|
665
|
+
<div>${this.renderTable(s)}</div>`,t=this._lastTableContent}let r=this.hasSlotController.test("delete-action".valueOf())||this.hasSlotController.test("modify-action".valueOf())||this.hasSlotController.test("create-action".valueOf())||this.hasSlotController.test("sort".valueOf())||this.hasSlotController.test("filter".valueOf())||this.hasSlotController.test("filter-top".valueOf())||this.hasSlotController.test("search".valueOf()),n=this.hasSlotController.test("inputs".valueOf());return u`
|
|
666
666
|
<div class="table-container" ${Fa(a=>this.tableContainer=a)}>
|
|
667
667
|
${n?u`
|
|
668
668
|
<slot name="${"inputs".valueOf()}" style="display: none"></slot>`:null}
|
|
669
669
|
${r?this.getTableHeader():u``}
|
|
670
670
|
${t}
|
|
671
671
|
</div>
|
|
672
|
-
`}connectedCallback(){super.connectedCallback(),this.addEventListener("zn-filter-change",this.filterChangeListener),this.addEventListener("zn-search-change",this.searchChangeListener)}getTemplate(t){return this.displayTemplates[t]??this._formatTemplates[t]}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener("zn-filter-change",this.filterChangeListener),this.removeEventListener("zn-search-change",this.searchChangeListener)}emptyState(){return this.hasSlotController.test("
|
|
672
|
+
`}connectedCallback(){super.connectedCallback(),this.addEventListener("zn-filter-change",this.filterChangeListener),this.addEventListener("zn-search-change",this.searchChangeListener)}getTemplate(t){return this.displayTemplates[t]??this._formatTemplates[t]}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener("zn-filter-change",this.filterChangeListener),this.removeEventListener("zn-search-change",this.searchChangeListener)}emptyState(){return this.noInitialLoad&&!this._initialLoad?this.hasSlotController.test("no-results")?u`
|
|
673
|
+
<div class="table--empty">
|
|
674
|
+
<slot name="no-results"></slot>
|
|
675
|
+
</div>`:u`
|
|
676
|
+
<div class="table--empty">
|
|
677
|
+
<zn-empty-state caption="No Results Found" icon="search">
|
|
678
|
+
<p>Nothing matched your search. Try adjusting your filters.</p>
|
|
679
|
+
</zn-empty-state>
|
|
680
|
+
</div>`:this.hasSlotController.test("empty-state")?u`
|
|
673
681
|
<div class="table--empty">
|
|
674
682
|
<slot name="empty-state"></slot>
|
|
675
683
|
</div>`:u`
|
|
@@ -691,7 +699,8 @@ import{a as Cn,b as i_,c as sr,d as c,e as F}from"./chunks/zn.UBLZO6CI.js";var Q
|
|
|
691
699
|
<zn-alert level="${t.level??"error"}"
|
|
692
700
|
icon="${H(t.icon)}"
|
|
693
701
|
size="small"
|
|
694
|
-
center>${t.text}
|
|
702
|
+
center>${t.text}
|
|
703
|
+
</zn-alert>`}humanize(t){return t.charAt(0).toUpperCase()+t.slice(1)}renderTableData(t,r){let n=Object.values(this.headers).filter(h=>h.hideHeader||h.hideColumn||Object.values(this.hiddenColumns).includes(h.key)?!1:!h.secondary);this._secondaryHeaders=Object.values(this.headers).filter(h=>h.hideColumn||Object.values(this.hiddenColumns).includes(h.key)?!1:h.secondary===!0),this.rowHasActions=this._rows.some(h=>h.actions&&h.actions.length>0);let a=new Set(this._secondaryHeaders.map(h=>h.key));this._hiddenCells.clear();let s=new Map;this._rows.forEach(h=>{let m=Array.isArray(h.cells)?h.cells:[],f=[],b=m.filter(g=>a.has(g.column));this._hiddenCells.set(h.id,b),n.forEach(g=>{let v=m.find(x=>x.column===g.key);v?f.push(v):f.push({text:"",column:g.key})}),s.set(h.id,f)});let l=this.hasHiddenColumns(),d=n.length+(this.rowHasActions?1:0)+(l?1:0);return u`
|
|
695
704
|
<div style="overflow-x: auto">
|
|
696
705
|
<table class="${D({table:!0,"table--standalone":this.standalone,"with-hover":!this.unsortable&&!this.hideCheckboxes})}">
|
|
697
706
|
<thead>
|
package/package.json
CHANGED
|
@@ -111,8 +111,8 @@ interface HeaderConfig {
|
|
|
111
111
|
hideColumn?: boolean;
|
|
112
112
|
secondary?: boolean;
|
|
113
113
|
type?: string;
|
|
114
|
-
cellTemplate
|
|
115
|
-
ifEmpty
|
|
114
|
+
cellTemplate?: Cell;
|
|
115
|
+
ifEmpty?: Cell;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
type DisplayTemplate = (cell: Cell, row: Row, header: HeaderConfig) =>
|
|
@@ -178,6 +178,7 @@ type AllowedInputElement =
|
|
|
178
178
|
* @slot create-action - Slot for create action button.
|
|
179
179
|
* @slot inputs - Slot for additional input controls.
|
|
180
180
|
* @slot empty-state - Slot for custom empty state.
|
|
181
|
+
* @slot no-results - Slot for a custom no-results state, shown when a search on a no-initial-load table returns no rows.
|
|
181
182
|
*
|
|
182
183
|
* @csspart base - The component's base wrapper.
|
|
183
184
|
*
|
|
@@ -250,12 +251,13 @@ export default class ZnDataTable extends ZincElement {
|
|
|
250
251
|
|
|
251
252
|
@property() groups = '';
|
|
252
253
|
|
|
253
|
-
@property({attribute: 'per-page-size', type:Number}) itemsPerPage:number = DEFAULT_PER_PAGE;
|
|
254
|
+
@property({attribute: 'per-page-size', type: Number}) itemsPerPage: number = DEFAULT_PER_PAGE;
|
|
254
255
|
@query('#select-all-rows') selectAllButton: ZnButton;
|
|
255
256
|
|
|
256
257
|
// Data Table Properties
|
|
257
258
|
private _initialLoad = true;
|
|
258
259
|
private _hasLoadedData = false;
|
|
260
|
+
private _lastLoadHadRows = false;
|
|
259
261
|
private _lastTableContent: TemplateResult = html``;
|
|
260
262
|
|
|
261
263
|
private readonly resizeObserver = new ResizeController(this, {
|
|
@@ -287,7 +289,8 @@ export default class ZnDataTable extends ZincElement {
|
|
|
287
289
|
ActionSlots.filter.valueOf(),
|
|
288
290
|
ActionSlots.sort.valueOf(),
|
|
289
291
|
ActionSlots.inputs.valueOf(),
|
|
290
|
-
'empty-state'
|
|
292
|
+
'empty-state',
|
|
293
|
+
'no-results'
|
|
291
294
|
);
|
|
292
295
|
|
|
293
296
|
private _dataTask = new Task(this, {
|
|
@@ -375,8 +378,10 @@ export default class ZnDataTable extends ZincElement {
|
|
|
375
378
|
tableBody = this._dataTask.render({
|
|
376
379
|
pending: () => {
|
|
377
380
|
// Show the skeleton until data has rendered at least once, so the
|
|
378
|
-
// first fetch on a no-initial-load table doesn't render blank space
|
|
379
|
-
|
|
381
|
+
// first fetch on a no-initial-load table doesn't render blank space.
|
|
382
|
+
// Also show it when the last load had no rows - a reduced-opacity
|
|
383
|
+
// empty state gives no visible indication that a search is running
|
|
384
|
+
if (this._initialLoad || !this._hasLoadedData || !this._lastLoadHadRows) {
|
|
380
385
|
return html`
|
|
381
386
|
<div>${this.loadingTable()}</div>`;
|
|
382
387
|
}
|
|
@@ -386,6 +391,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
386
391
|
complete: (data) => {
|
|
387
392
|
this._initialLoad = false;
|
|
388
393
|
this._hasLoadedData = true;
|
|
394
|
+
this._lastLoadHadRows = ((data as Response)?.rows?.length ?? 0) > 0;
|
|
389
395
|
this._lastTableContent = html`
|
|
390
396
|
<div>${this.renderTable(data as Response)}</div>`;
|
|
391
397
|
return this._lastTableContent;
|
|
@@ -416,8 +422,9 @@ export default class ZnDataTable extends ZincElement {
|
|
|
416
422
|
};
|
|
417
423
|
this._initialLoad = false;
|
|
418
424
|
this._hasLoadedData = true;
|
|
425
|
+
this._lastLoadHadRows = rows.length > 0;
|
|
419
426
|
this._lastTableContent = html`
|
|
420
|
-
|
|
427
|
+
<div>${this.renderTable(response)}</div>`;
|
|
421
428
|
tableBody = this._lastTableContent;
|
|
422
429
|
}
|
|
423
430
|
|
|
@@ -496,6 +503,22 @@ export default class ZnDataTable extends ZincElement {
|
|
|
496
503
|
}
|
|
497
504
|
|
|
498
505
|
emptyState() {
|
|
506
|
+
if (this.noInitialLoad && !this._initialLoad) {
|
|
507
|
+
if (this.hasSlotController.test('no-results')) {
|
|
508
|
+
return html`
|
|
509
|
+
<div class="table--empty">
|
|
510
|
+
<slot name="no-results"></slot>
|
|
511
|
+
</div>`;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return html`
|
|
515
|
+
<div class="table--empty">
|
|
516
|
+
<zn-empty-state caption="No Results Found" icon="search">
|
|
517
|
+
<p>Nothing matched your search. Try adjusting your filters.</p>
|
|
518
|
+
</zn-empty-state>
|
|
519
|
+
</div>`;
|
|
520
|
+
}
|
|
521
|
+
|
|
499
522
|
if (this.hasSlotController.test('empty-state')) {
|
|
500
523
|
return html`
|
|
501
524
|
<div class="table--empty">
|
|
@@ -605,7 +628,8 @@ export default class ZnDataTable extends ZincElement {
|
|
|
605
628
|
<zn-alert level="${error.level ?? 'error'}"
|
|
606
629
|
icon="${ifDefined(error.icon)}"
|
|
607
630
|
size="small"
|
|
608
|
-
center>${error.text}
|
|
631
|
+
center>${error.text}
|
|
632
|
+
</zn-alert>`;
|
|
609
633
|
}
|
|
610
634
|
|
|
611
635
|
public humanize(str: string) {
|
|
@@ -996,7 +1020,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
996
1020
|
renderCell(data: Cell, row?: Row, header?: HeaderConfig): TemplateResult | ZincElement {
|
|
997
1021
|
const fn = (header?.type ? this.getTemplate(header.type) : undefined);
|
|
998
1022
|
|
|
999
|
-
if((!data.text && !data.iconSrc) && header?.ifEmpty !== undefined) {
|
|
1023
|
+
if ((!data.text && !data.iconSrc) && header?.ifEmpty !== undefined) {
|
|
1000
1024
|
const header2 = {...header, type: undefined, render: undefined, renderTemplate: undefined};
|
|
1001
1025
|
return this.renderCell({...data, ...header.ifEmpty}, row, header2)
|
|
1002
1026
|
}
|
|
@@ -1006,11 +1030,11 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1006
1030
|
return typeof out === 'string' ? html`${unsafeHTML(out)}` : out;
|
|
1007
1031
|
}
|
|
1008
1032
|
|
|
1009
|
-
if(!data || typeof data !== 'object') {
|
|
1033
|
+
if (!data || typeof data !== 'object') {
|
|
1010
1034
|
return data;
|
|
1011
1035
|
}
|
|
1012
1036
|
|
|
1013
|
-
if(header?.cellTemplate !== undefined) {
|
|
1037
|
+
if (header?.cellTemplate !== undefined) {
|
|
1014
1038
|
data = {...header.cellTemplate, ...data}
|
|
1015
1039
|
}
|
|
1016
1040
|
|
|
@@ -49,6 +49,76 @@ describe('<zn-data-table>', () => {
|
|
|
49
49
|
}
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
+
it('shows the loading skeleton on reload when the previous load returned no rows', async () => {
|
|
53
|
+
const originalFetch = window.fetch;
|
|
54
|
+
const emptyResponse = () => new Response(JSON.stringify({rows: [], page: 1, perPage: 10, total: 0}),
|
|
55
|
+
{status: 200, headers: {'Content-Type': 'application/json'}});
|
|
56
|
+
window.fetch = () => Promise.resolve(emptyResponse());
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const el = await fixture<ZnDataTable>(html`
|
|
60
|
+
<zn-data-table data-uri="/test-data" headers='{"name": {"key": "name", "label": "Name"}}'></zn-data-table>`);
|
|
61
|
+
await waitUntil(() => el.shadowRoot?.querySelector('zn-empty-state'));
|
|
62
|
+
|
|
63
|
+
let release: (value: Response) => void;
|
|
64
|
+
window.fetch = () => new Promise<Response>(resolve => (release = resolve));
|
|
65
|
+
el.refresh();
|
|
66
|
+
await waitUntil(() => el.shadowRoot?.querySelector('zn-skeleton'));
|
|
67
|
+
expect(el.shadowRoot!.querySelector('zn-skeleton')).to.exist;
|
|
68
|
+
|
|
69
|
+
release!(emptyResponse());
|
|
70
|
+
await waitUntil(() => el.shadowRoot?.querySelector('zn-empty-state'));
|
|
71
|
+
} finally {
|
|
72
|
+
window.fetch = originalFetch;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('shows the empty-state slot before any load on a no-initial-load table, then a no-results state after an empty search', async () => {
|
|
77
|
+
const originalFetch = window.fetch;
|
|
78
|
+
window.fetch = () => Promise.resolve(new Response(JSON.stringify({rows: [], page: 1, perPage: 10, total: 0}),
|
|
79
|
+
{status: 200, headers: {'Content-Type': 'application/json'}}));
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
const el = await fixture<ZnDataTable>(html`
|
|
83
|
+
<zn-data-table data-uri="/test-data" no-initial-load headers='{"name": {"key": "name", "label": "Name"}}'>
|
|
84
|
+
<div slot="empty-state" id="filters-prompt">No Filters Selected</div>
|
|
85
|
+
</zn-data-table>`);
|
|
86
|
+
|
|
87
|
+
expect(el.shadowRoot!.querySelector('slot[name="empty-state"]')).to.exist;
|
|
88
|
+
|
|
89
|
+
el.refresh();
|
|
90
|
+
await waitUntil(() => el.shadowRoot?.querySelector('zn-empty-state'));
|
|
91
|
+
|
|
92
|
+
expect(el.shadowRoot!.querySelector('slot[name="empty-state"]')).to.not.exist;
|
|
93
|
+
const emptyState = el.shadowRoot!.querySelector('zn-empty-state')!;
|
|
94
|
+
expect(emptyState.getAttribute('caption')).to.equal('No Results Found');
|
|
95
|
+
} finally {
|
|
96
|
+
window.fetch = originalFetch;
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('prefers the no-results slot after an empty search on a no-initial-load table', async () => {
|
|
101
|
+
const originalFetch = window.fetch;
|
|
102
|
+
window.fetch = () => Promise.resolve(new Response(JSON.stringify({rows: [], page: 1, perPage: 10, total: 0}),
|
|
103
|
+
{status: 200, headers: {'Content-Type': 'application/json'}}));
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const el = await fixture<ZnDataTable>(html`
|
|
107
|
+
<zn-data-table data-uri="/test-data" no-initial-load headers='{"name": {"key": "name", "label": "Name"}}'>
|
|
108
|
+
<div slot="empty-state">No Filters Selected</div>
|
|
109
|
+
<div slot="no-results">No Customers Found</div>
|
|
110
|
+
</zn-data-table>`);
|
|
111
|
+
|
|
112
|
+
el.refresh();
|
|
113
|
+
await waitUntil(() => el.shadowRoot?.querySelector('slot[name="no-results"]'));
|
|
114
|
+
|
|
115
|
+
expect(el.shadowRoot!.querySelector('slot[name="no-results"]')).to.exist;
|
|
116
|
+
expect(el.shadowRoot!.querySelector('slot[name="empty-state"]')).to.not.exist;
|
|
117
|
+
} finally {
|
|
118
|
+
window.fetch = originalFetch;
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
52
122
|
it('exposes displayTemplates as an object property', async () => {
|
|
53
123
|
const el = await fixture<ZnDataTable>(html` <zn-data-table></zn-data-table> `);
|
|
54
124
|
const templates = (el as unknown as {displayTemplates: Record<string, unknown>}).displayTemplates;
|