@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.
@@ -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.65",
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" }] },
@@ -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.65",
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
- <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`
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("empty-state")?u`
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}</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`
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubex/zinc",
3
- "version": "1.1.65",
3
+ "version": "1.1.67",
4
4
  "description": "A collection of web components for building web applications based off of @shoelace-style/Shoelace",
5
5
  "keywords": [
6
6
  "web components",
@@ -111,8 +111,8 @@ interface HeaderConfig {
111
111
  hideColumn?: boolean;
112
112
  secondary?: boolean;
113
113
  type?: string;
114
- cellTemplate ?: Cell;
115
- ifEmpty ?:Cell;
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
- if (this._initialLoad || !this._hasLoadedData) {
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
- <div>${this.renderTable(response)}</div>`;
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}</zn-alert>`;
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;