@beeblock/svelar-datatable 0.1.3 → 0.1.5

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.
@@ -6,7 +6,7 @@ interface DatatablePluginConfig {
6
6
  }
7
7
  export declare class SvelarDatatablePlugin extends Plugin {
8
8
  readonly name = "svelar-datatable";
9
- readonly version = "0.1.2";
9
+ readonly version = "0.1.4";
10
10
  readonly description = "Full-featured DataTable plugin for Svelar \u2014 sorting, searching, pagination, inline editing, export, and server-side processing";
11
11
  private _config;
12
12
  constructor(config?: DatatablePluginConfig);
package/dist/index.d.ts CHANGED
@@ -1,6 +1,3 @@
1
- import { SvelarDatatablePlugin } from './SvelarDatatablePlugin.js';
2
- export default SvelarDatatablePlugin;
3
- export { SvelarDatatablePlugin } from './SvelarDatatablePlugin.js';
4
1
  export { DataTableStore } from './state/DataTableStore.js';
5
2
  export { ServerDataTableStore } from './state/ServerDataTableStore.js';
6
3
  export { ExportManager } from './export/ExportManager.js';
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
- import{Plugin as V}from"@beeblock/svelar/plugins";import{fileURLToPath as j}from"url";import{dirname as x,join as T}from"path";var $=x(j(import.meta.url)),K=x($),L=T(K,"src","publishable"),S={prefix:"/api",defaults:{perPage:15,perPageOptions:[10,15,25,50,100],searchDebounceMs:300,sortable:!0,searchable:!0,paginate:!0,selectable:"none",striped:!0,hover:!0,bordered:!1,compact:!1,responsive:!0,virtualScroll:!1,virtualRowHeight:48}},m=class extends V{name="svelar-datatable";version="0.1.2";description="Full-featured DataTable plugin for Svelar \u2014 sorting, searching, pagination, inline editing, export, and server-side processing";_config;constructor(t={}){super(),this._config={...S,...t,defaults:{...S.defaults,...t.defaults}}}get datatableConfig(){return this._config}static defaults(){return{...S.defaults}}publishables(){return{routes:[{source:T(L,"routes/datatable.ts"),dest:"src/routes/api/datatable/+server.ts",type:"asset"}]}}};var _=class{_state;_listeners=new Set;_columns=[];_rowIdFn;_stateSaveKey=null;_lastSelectedId=null;_paginate=!0;constructor(t){this._columns=t.columns,this._stateSaveKey=t.stateSaveKey??null,this._paginate=t.paginate!==!1,this._rowIdFn=typeof t.rowId=="function"?t.rowId:s=>s[t.rowId??"id"];let e=this._loadState();this._state={allRows:t.data??[],filteredRows:[],sortedRows:[],paginatedRows:[],sort:e?.sort??[],filters:e?.filters??[],globalSearch:e?.globalSearch??"",pagination:{page:e?.page??1,perPage:t.perPage??15,total:0,lastPage:1},selectedIds:new Set,columnVisibility:e?.columnVisibility??Object.fromEntries(t.columns.map(s=>[s.key,s.visible!==!1])),columnOrder:e?.columnOrder??t.columns.map(s=>s.key),loading:!1,error:null,editingRowId:null,editingColumn:null,editorMode:null,formData:{},validationErrors:{},draw:0,excelFocusedCell:null,excelEditingCell:null,excelEditValue:""},this._recompute()}subscribe(t){return this._listeners.add(t),()=>this._listeners.delete(t)}getState(){return this._state}_notify(){for(let t of this._listeners)t()}_saveState(){if(this._stateSaveKey)try{let t={sort:this._state.sort,filters:this._state.filters,globalSearch:this._state.globalSearch,page:this._state.pagination.page,columnVisibility:this._state.columnVisibility,columnOrder:this._state.columnOrder};localStorage.setItem(this._stateSaveKey,JSON.stringify(t))}catch{}}_loadState(){if(!this._stateSaveKey)return null;try{let t=localStorage.getItem(this._stateSaveKey);return t?JSON.parse(t):null}catch{return null}}setData(t){this._state={...this._state,allRows:t},this._recompute()}setSort(t){this._state={...this._state,sort:t,pagination:{...this._state.pagination,page:1}},this._recompute(),this._saveState()}toggleSort(t,e=!1){let s=this._state.sort.find(i=>i.column===t),a;s?s.direction==="asc"?a=e?this._state.sort.map(i=>i.column===t?{...i,direction:"desc"}:i):[{column:t,direction:"desc"}]:a=e?this._state.sort.filter(i=>i.column!==t):[]:a=e?[...this._state.sort,{column:t,direction:"asc"}]:[{column:t,direction:"asc"}],this.setSort(a)}setGlobalSearch(t){this._state={...this._state,globalSearch:t,pagination:{...this._state.pagination,page:1}},this._recompute(),this._saveState()}setFilters(t){this._state={...this._state,filters:t,pagination:{...this._state.pagination,page:1}},this._recompute(),this._saveState()}setColumnFilter(t,e,s="like"){let a=this._state.filters.filter(i=>i.column!==t);e!==""&&e!==null&&e!==void 0&&a.push({column:t,value:e,operator:s}),this.setFilters(a)}setPage(t){let e=this._state.pagination.lastPage,s=Math.max(1,Math.min(t,e));this._state={...this._state,pagination:{...this._state.pagination,page:s}},this._recompute(),this._saveState()}setPerPage(t){this._state={...this._state,pagination:{...this._state.pagination,perPage:t,page:1}},this._recompute(),this._saveState()}toggleColumnVisibility(t){let e={...this._state.columnVisibility};e[t]=!e[t],this._state={...this._state,columnVisibility:e},this._notify(),this._saveState()}setColumnVisibility(t){this._state={...this._state,columnVisibility:t},this._notify(),this._saveState()}reorderColumns(t){this._state={...this._state,columnOrder:t},this._notify(),this._saveState()}getRowId(t){return this._rowIdFn(t)}toggleSelect(t){let e=new Set(this._state.selectedIds);e.has(t)?e.delete(t):e.add(t),this._lastSelectedId=t,this._state={...this._state,selectedIds:e},this._notify()}getLastSelectedId(){return this._lastSelectedId}selectSingle(t){this._state={...this._state,selectedIds:new Set([t])},this._notify()}selectAll(){let t=this._state.filteredRows.map(e=>this.getRowId(e));this._state={...this._state,selectedIds:new Set(t)},this._notify()}deselectAll(){this._state={...this._state,selectedIds:new Set},this._notify()}selectRange(t,e){let s=this._state.sortedRows,a=s.findIndex(n=>this.getRowId(n)===t),i=s.findIndex(n=>this.getRowId(n)===e);if(a===-1||i===-1)return;let o=Math.min(a,i),u=Math.max(a,i),r=new Set(this._state.selectedIds);for(let n=o;n<=u;n++)r.add(this.getRowId(s[n]));this._state={...this._state,selectedIds:r},this._notify()}getSelectedRows(){return this._state.allRows.filter(t=>this._state.selectedIds.has(this.getRowId(t)))}openEditor(t,e,s){let a=t!==null?this._state.allRows.find(o=>this.getRowId(o)===t):null,i=a?{...a}:{};this._state={...this._state,editingRowId:t,editingColumn:e,editorMode:s,formData:i,validationErrors:{}},this._notify()}closeEditor(){this._state={...this._state,editingRowId:null,editingColumn:null,editorMode:null,formData:{},validationErrors:{}},this._notify()}setFormField(t,e){this._state={...this._state,formData:{...this._state.formData,[t]:e}},this._notify()}setValidationErrors(t){this._state={...this._state,validationErrors:t},this._notify()}setLoading(t){this._state={...this._state,loading:t},this._notify()}setError(t){this._state={...this._state,error:t},this._notify()}setServerResponse(t){this._state={...this._state,allRows:t.data,filteredRows:t.data,sortedRows:t.data,paginatedRows:t.data,loading:!1,draw:t.draw,pagination:{...this._state.pagination,total:t.recordsFiltered,lastPage:Math.ceil(t.recordsFiltered/this._state.pagination.perPage)||1}},this._notify()}resetState(){if(this._state={...this._state,sort:[],filters:[],globalSearch:"",pagination:{...this._state.pagination,page:1},selectedIds:new Set},this._recompute(),this._stateSaveKey)try{localStorage.removeItem(this._stateSaveKey)}catch{}}focusCell(t,e){this._state={...this._state,excelFocusedCell:{rowIndex:t,columnKey:e}},this._notify()}startCellEdit(){let t=this._state.excelFocusedCell;if(!t)return;let e=this._state.paginatedRows[t.rowIndex];if(!e)return;let s=this._columns.find(a=>a.key===t.columnKey);!s||s.editable===!1||(this._state={...this._state,excelEditingCell:{...t},excelEditValue:e[t.columnKey]!=null?String(e[t.columnKey]):""},this._notify())}setExcelEditValue(t){this._state={...this._state,excelEditValue:t}}commitCellEdit(){let t=this._state.excelEditingCell;if(!t)return null;let e=this._state.paginatedRows[t.rowIndex];if(!e)return this.cancelCellEdit(),null;let s=this._columns.find(o=>o.key===t.columnKey),a=e[t.columnKey],i=this._state.excelEditValue;return s?.type==="number"?i=i===""?null:Number(i):s?.type==="boolean"?i=i==="true"||i==="1":i===""&&(i=null),this._state={...this._state,excelEditingCell:null,excelEditValue:""},i===a||i===null&&a==null?(this._notify(),null):(e[t.columnKey]=i,this._notify(),{row:e,columnKey:t.columnKey,oldValue:a,newValue:i})}cancelCellEdit(){this._state={...this._state,excelEditingCell:null,excelEditValue:""},this._notify()}clearExcelFocus(){this._state={...this._state,excelFocusedCell:null,excelEditingCell:null,excelEditValue:""},this._notify()}getVisibleColumns(){return this._state.columnOrder.filter(t=>this._state.columnVisibility[t]!==!1).map(t=>this._columns.find(e=>e.key===t)).filter(Boolean)}navigateCell(t){let e=this._state.excelFocusedCell,s=this._state.paginatedRows,a=this.getVisibleColumns();if(a.length===0)return null;if(!e){let r=a.find(n=>n.editable!==!1);return r&&s.length>0&&this.focusCell(0,r.key),null}let{rowIndex:i,columnKey:o}=e,u=a.findIndex(r=>r.key===o);if(u===-1)return null;switch(t){case"left":{for(let r=u-1;r>=0;r--)if(a[r].editable!==!1)return this.focusCell(i,a[r].key),null;for(let r=a.length-1;r>=0;r--)if(a[r].editable!==!1)return i>0?(this.focusCell(i-1,a[r].key),null):"page-prev";return null}case"right":{for(let r=u+1;r<a.length;r++)if(a[r].editable!==!1)return this.focusCell(i,a[r].key),null;for(let r=0;r<a.length;r++)if(a[r].editable!==!1)return i<s.length-1?(this.focusCell(i+1,a[r].key),null):"page-next";return null}case"up":return i>0?(this.focusCell(i-1,o),null):"page-prev";case"down":return i<s.length-1?(this.focusCell(i+1,o),null):"page-next"}}_recompute(){let t=[...this._state.allRows];if(this._state.globalSearch){let n=this._state.globalSearch.toLowerCase(),d=this._columns.filter(l=>l.searchable!==!1);t=t.filter(l=>d.some(p=>{let h=l[p.key];return h!=null&&String(h).toLowerCase().includes(n)}))}for(let n of this._state.filters)t=t.filter(d=>{let l=d[n.column];switch(n.operator){case"=":return l==n.value;case"!=":return l!=n.value;case">":return l>n.value;case"<":return l<n.value;case">=":return l>=n.value;case"<=":return l<=n.value;case"like":return l!=null&&String(l).toLowerCase().includes(String(n.value).toLowerCase());case"not_like":return l==null||!String(l).toLowerCase().includes(String(n.value).toLowerCase());case"in":return Array.isArray(n.value)&&n.value.includes(l);case"between":return Array.isArray(n.value)&&l>=n.value[0]&&l<=n.value[1];case"null":return l==null;case"not_null":return l!=null;default:return!0}});let e=t;this._state.sort.length>0&&(t=[...t].sort((n,d)=>{for(let l of this._state.sort){let p=this._columns.find(I=>I.key===l.column),h=n[l.column],g=d[l.column],f=0;if(h==null?f=-1:g==null?f=1:p?.type==="number"?f=Number(h)-Number(g):p?.type==="date"?f=new Date(h).getTime()-new Date(g).getTime():p?.type==="boolean"?f=(h?1:0)-(g?1:0):f=String(h).localeCompare(String(g)),f!==0)return l.direction==="desc"?-f:f}return 0}));let s=t,a=e.length,i=this._state.pagination.perPage,o,u,r;if(this._paginate){r=Math.ceil(a/i)||1,u=Math.min(this._state.pagination.page,r);let n=(u-1)*i;o=s.slice(n,n+i)}else o=s,u=1,r=1;this._state={...this._state,filteredRows:e,sortedRows:s,paginatedRows:o,pagination:{page:u,perPage:i,total:a,lastPage:r}},this._notify()}};function O(c="XSRF-TOKEN"){if(typeof document>"u")return null;let t=c.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),e=document.cookie.match(new RegExp(`(?:^|;\\s*)${t}=([^;]*)`));return e?decodeURIComponent(e[1]):null}var v=class extends _{_serverUrl;_serverMethod;_drawCounter=0;_abortController=null;_fetchDebounceTimer=null;_serverColumns;_csrfCookieName;_csrfHeaderName;constructor(t){super(t),this._serverUrl=t.serverUrl,this._serverMethod=t.serverMethod??"GET",this._csrfCookieName=t.csrfCookieName??"XSRF-TOKEN",this._csrfHeaderName=t.csrfHeaderName??"X-CSRF-Token",this._serverColumns=t.columns.map(e=>({data:e.key,name:e.key,searchable:e.searchable!==!1,orderable:e.sortable!==!1}))}_buildHeaders(t={}){let e={...t},s=O(this._csrfCookieName);return s&&(e[this._csrfHeaderName]=s),e}setSort(t){let e=this.getState();this._state={...e,sort:t,pagination:{...e.pagination,page:1}},this._debouncedFetch()}setGlobalSearch(t){let e=this.getState();this._state={...e,globalSearch:t,pagination:{...e.pagination,page:1}},this._debouncedFetch()}setFilters(t){let e=this.getState();this._state={...e,filters:t,pagination:{...e.pagination,page:1}},this._debouncedFetch()}setPage(t){let e=this.getState();this._state={...e,pagination:{...e.pagination,page:t}},this._fetchFromServer()}setPerPage(t){let e=this.getState();this._state={...e,pagination:{...e.pagination,perPage:t,page:1}},this._fetchFromServer()}_debouncedFetch(){this._fetchDebounceTimer&&clearTimeout(this._fetchDebounceTimer),this._fetchDebounceTimer=setTimeout(()=>this._fetchFromServer(),300)}async _fetchFromServer(){this._abortController&&this._abortController.abort(),this._abortController=new AbortController;let t=this.getState();this.setLoading(!0),this.setError(null);let e=++this._drawCounter,s={draw:e,start:(t.pagination.page-1)*t.pagination.perPage,length:t.pagination.perPage,search:{value:t.globalSearch,regex:!1},order:t.sort.map(a=>({column:this._serverColumns.findIndex(i=>i.data===a.column),dir:a.direction})),columns:this._serverColumns.map(a=>({...a,search:{value:t.filters.find(i=>i.column===a.data)?.value??"",regex:!1}}))};try{let a;if(this._serverMethod==="POST")a=await fetch(this._serverUrl,{method:"POST",headers:this._buildHeaders({"Content-Type":"application/json"}),body:JSON.stringify(s),signal:this._abortController.signal});else{let o=new URLSearchParams;o.set("draw",String(s.draw)),o.set("start",String(s.start)),o.set("length",String(s.length)),o.set("search[value]",s.search.value),o.set("search[regex]",String(s.search.regex)),s.order.forEach((r,n)=>{o.set(`order[${n}][column]`,String(r.column)),o.set(`order[${n}][dir]`,r.dir)}),s.columns.forEach((r,n)=>{o.set(`columns[${n}][data]`,r.data),o.set(`columns[${n}][name]`,r.name),o.set(`columns[${n}][searchable]`,String(r.searchable)),o.set(`columns[${n}][orderable]`,String(r.orderable)),o.set(`columns[${n}][search][value]`,r.search.value),o.set(`columns[${n}][search][regex]`,String(r.search.regex))});let u=`${this._serverUrl}?${o.toString()}`;a=await fetch(u,{signal:this._abortController.signal})}if(!a.ok)throw new Error(`Server responded with ${a.status}`);let i=await a.json();i.draw===e&&this.setServerResponse(i)}catch(a){if(a.name==="AbortError")return;this.setLoading(!1),this.setError(a.message??"Failed to fetch data")}}async initialFetch(){await this._fetchFromServer()}destroy(){this._abortController&&this._abortController.abort(),this._fetchDebounceTimer&&clearTimeout(this._fetchDebounceTimer)}};function D(c){if(c==null)return"";let t=String(c);return t.includes(",")||t.includes('"')||t.includes(`
2
- `)||t.includes("\r")?'"'+t.replace(/"/g,'""')+'"':t}function R(c,t){let e=t.filter(i=>i.visible!==!1),s=e.map(i=>D(i.header)).join(","),a=c.map(i=>e.map(o=>D(i[o.key])).join(","));return"\uFEFF"+[s,...a].join(`\r
3
- `)}function E(c,t="export.csv"){let e=new Blob([c],{type:"text/csv;charset=utf-8;"});w(e,t)}function w(c,t){let e=URL.createObjectURL(c),s=document.createElement("a");s.href=e,s.download=t,document.body.appendChild(s),s.click(),document.body.removeChild(s),URL.revokeObjectURL(e)}async function F(c,t){let e=t.filter(o=>o.visible!==!1),s=e.map(o=>o.header).join(" "),a=c.map(o=>e.map(u=>{let r=o[u.key];return r==null?"":String(r)}).join(" ")),i=[s,...a].join(`
4
- `);await navigator.clipboard.writeText(i)}function y(c,t,e){let s=t.filter(r=>r.visible!==!1),a=s.map(r=>`<th style="border:1px solid #ddd;padding:8px 12px;text-align:left;background:#f5f5f5;font-weight:600;">${b(r.header)}</th>`).join(""),i=c.map(r=>`<tr>${s.map(d=>{let l=r[d.key];return`<td style="border:1px solid #ddd;padding:8px 12px;">${b(l==null?"":String(l))}</td>`}).join("")}</tr>`).join(""),o=`<!DOCTYPE html>
1
+ var m=class{_state;_listeners=new Set;_columns=[];_rowIdFn;_stateSaveKey=null;_lastSelectedId=null;_paginate=!0;constructor(t){this._columns=t.columns,this._stateSaveKey=t.stateSaveKey??null,this._paginate=t.paginate!==!1,this._rowIdFn=typeof t.rowId=="function"?t.rowId:s=>s[t.rowId??"id"];let e=this._loadState();this._state={allRows:t.data??[],filteredRows:[],sortedRows:[],paginatedRows:[],sort:e?.sort??[],filters:e?.filters??[],globalSearch:e?.globalSearch??"",pagination:{page:e?.page??1,perPage:t.perPage??15,total:0,lastPage:1},selectedIds:new Set,columnVisibility:e?.columnVisibility??Object.fromEntries(t.columns.map(s=>[s.key,s.visible!==!1])),columnOrder:e?.columnOrder??t.columns.map(s=>s.key),loading:!1,error:null,editingRowId:null,editingColumn:null,editorMode:null,formData:{},validationErrors:{},draw:0,excelFocusedCell:null,excelEditingCell:null,excelEditValue:""},this._recompute()}subscribe(t){return this._listeners.add(t),()=>this._listeners.delete(t)}getState(){return this._state}_notify(){for(let t of this._listeners)t()}_saveState(){if(this._stateSaveKey)try{let t={sort:this._state.sort,filters:this._state.filters,globalSearch:this._state.globalSearch,page:this._state.pagination.page,columnVisibility:this._state.columnVisibility,columnOrder:this._state.columnOrder};localStorage.setItem(this._stateSaveKey,JSON.stringify(t))}catch{}}_loadState(){if(!this._stateSaveKey)return null;try{let t=localStorage.getItem(this._stateSaveKey);return t?JSON.parse(t):null}catch{return null}}setData(t){this._state={...this._state,allRows:t},this._recompute()}setSort(t){this._state={...this._state,sort:t,pagination:{...this._state.pagination,page:1}},this._recompute(),this._saveState()}toggleSort(t,e=!1){let s=this._state.sort.find(i=>i.column===t),a;s?s.direction==="asc"?a=e?this._state.sort.map(i=>i.column===t?{...i,direction:"desc"}:i):[{column:t,direction:"desc"}]:a=e?this._state.sort.filter(i=>i.column!==t):[]:a=e?[...this._state.sort,{column:t,direction:"asc"}]:[{column:t,direction:"asc"}],this.setSort(a)}setGlobalSearch(t){this._state={...this._state,globalSearch:t,pagination:{...this._state.pagination,page:1}},this._recompute(),this._saveState()}setFilters(t){this._state={...this._state,filters:t,pagination:{...this._state.pagination,page:1}},this._recompute(),this._saveState()}setColumnFilter(t,e,s="like"){let a=this._state.filters.filter(i=>i.column!==t);e!==""&&e!==null&&e!==void 0&&a.push({column:t,value:e,operator:s}),this.setFilters(a)}setPage(t){let e=this._state.pagination.lastPage,s=Math.max(1,Math.min(t,e));this._state={...this._state,pagination:{...this._state.pagination,page:s}},this._recompute(),this._saveState()}setPerPage(t){this._state={...this._state,pagination:{...this._state.pagination,perPage:t,page:1}},this._recompute(),this._saveState()}toggleColumnVisibility(t){let e={...this._state.columnVisibility};e[t]=!e[t],this._state={...this._state,columnVisibility:e},this._notify(),this._saveState()}setColumnVisibility(t){this._state={...this._state,columnVisibility:t},this._notify(),this._saveState()}reorderColumns(t){this._state={...this._state,columnOrder:t},this._notify(),this._saveState()}getRowId(t){return this._rowIdFn(t)}toggleSelect(t){let e=new Set(this._state.selectedIds);e.has(t)?e.delete(t):e.add(t),this._lastSelectedId=t,this._state={...this._state,selectedIds:e},this._notify()}getLastSelectedId(){return this._lastSelectedId}selectSingle(t){this._state={...this._state,selectedIds:new Set([t])},this._notify()}selectAll(){let t=this._state.filteredRows.map(e=>this.getRowId(e));this._state={...this._state,selectedIds:new Set(t)},this._notify()}deselectAll(){this._state={...this._state,selectedIds:new Set},this._notify()}selectRange(t,e){let s=this._state.sortedRows,a=s.findIndex(n=>this.getRowId(n)===t),i=s.findIndex(n=>this.getRowId(n)===e);if(a===-1||i===-1)return;let o=Math.min(a,i),d=Math.max(a,i),r=new Set(this._state.selectedIds);for(let n=o;n<=d;n++)r.add(this.getRowId(s[n]));this._state={...this._state,selectedIds:r},this._notify()}getSelectedRows(){return this._state.allRows.filter(t=>this._state.selectedIds.has(this.getRowId(t)))}openEditor(t,e,s){let a=t!==null?this._state.allRows.find(o=>this.getRowId(o)===t):null,i=a?{...a}:{};this._state={...this._state,editingRowId:t,editingColumn:e,editorMode:s,formData:i,validationErrors:{}},this._notify()}closeEditor(){this._state={...this._state,editingRowId:null,editingColumn:null,editorMode:null,formData:{},validationErrors:{}},this._notify()}setFormField(t,e){this._state={...this._state,formData:{...this._state.formData,[t]:e}},this._notify()}setValidationErrors(t){this._state={...this._state,validationErrors:t},this._notify()}setLoading(t){this._state={...this._state,loading:t},this._notify()}setError(t){this._state={...this._state,error:t},this._notify()}setServerResponse(t){this._state={...this._state,allRows:t.data,filteredRows:t.data,sortedRows:t.data,paginatedRows:t.data,loading:!1,draw:t.draw,pagination:{...this._state.pagination,total:t.recordsFiltered,lastPage:Math.ceil(t.recordsFiltered/this._state.pagination.perPage)||1}},this._notify()}resetState(){if(this._state={...this._state,sort:[],filters:[],globalSearch:"",pagination:{...this._state.pagination,page:1},selectedIds:new Set},this._recompute(),this._stateSaveKey)try{localStorage.removeItem(this._stateSaveKey)}catch{}}focusCell(t,e){this._state={...this._state,excelFocusedCell:{rowIndex:t,columnKey:e}},this._notify()}startCellEdit(){let t=this._state.excelFocusedCell;if(!t)return;let e=this._state.paginatedRows[t.rowIndex];if(!e)return;let s=this._columns.find(a=>a.key===t.columnKey);!s||s.editable===!1||(this._state={...this._state,excelEditingCell:{...t},excelEditValue:e[t.columnKey]!=null?String(e[t.columnKey]):""},this._notify())}setExcelEditValue(t){this._state={...this._state,excelEditValue:t}}commitCellEdit(){let t=this._state.excelEditingCell;if(!t)return null;let e=this._state.paginatedRows[t.rowIndex];if(!e)return this.cancelCellEdit(),null;let s=this._columns.find(o=>o.key===t.columnKey),a=e[t.columnKey],i=this._state.excelEditValue;return s?.type==="number"?i=i===""?null:Number(i):s?.type==="boolean"?i=i==="true"||i==="1":i===""&&(i=null),this._state={...this._state,excelEditingCell:null,excelEditValue:""},i===a||i===null&&a==null?(this._notify(),null):(e[t.columnKey]=i,this._notify(),{row:e,columnKey:t.columnKey,oldValue:a,newValue:i})}cancelCellEdit(){this._state={...this._state,excelEditingCell:null,excelEditValue:""},this._notify()}clearExcelFocus(){this._state={...this._state,excelFocusedCell:null,excelEditingCell:null,excelEditValue:""},this._notify()}getVisibleColumns(){return this._state.columnOrder.filter(t=>this._state.columnVisibility[t]!==!1).map(t=>this._columns.find(e=>e.key===t)).filter(Boolean)}navigateCell(t){let e=this._state.excelFocusedCell,s=this._state.paginatedRows,a=this.getVisibleColumns();if(a.length===0)return null;if(!e){let r=a.find(n=>n.editable!==!1);return r&&s.length>0&&this.focusCell(0,r.key),null}let{rowIndex:i,columnKey:o}=e,d=a.findIndex(r=>r.key===o);if(d===-1)return null;switch(t){case"left":{for(let r=d-1;r>=0;r--)if(a[r].editable!==!1)return this.focusCell(i,a[r].key),null;for(let r=a.length-1;r>=0;r--)if(a[r].editable!==!1)return i>0?(this.focusCell(i-1,a[r].key),null):"page-prev";return null}case"right":{for(let r=d+1;r<a.length;r++)if(a[r].editable!==!1)return this.focusCell(i,a[r].key),null;for(let r=0;r<a.length;r++)if(a[r].editable!==!1)return i<s.length-1?(this.focusCell(i+1,a[r].key),null):"page-next";return null}case"up":return i>0?(this.focusCell(i-1,o),null):"page-prev";case"down":return i<s.length-1?(this.focusCell(i+1,o),null):"page-next"}}_recompute(){let t=[...this._state.allRows];if(this._state.globalSearch){let n=this._state.globalSearch.toLowerCase(),u=this._columns.filter(l=>l.searchable!==!1);t=t.filter(l=>u.some(p=>{let h=l[p.key];return h!=null&&String(h).toLowerCase().includes(n)}))}for(let n of this._state.filters)t=t.filter(u=>{let l=u[n.column];switch(n.operator){case"=":return l==n.value;case"!=":return l!=n.value;case">":return l>n.value;case"<":return l<n.value;case">=":return l>=n.value;case"<=":return l<=n.value;case"like":return l!=null&&String(l).toLowerCase().includes(String(n.value).toLowerCase());case"not_like":return l==null||!String(l).toLowerCase().includes(String(n.value).toLowerCase());case"in":return Array.isArray(n.value)&&n.value.includes(l);case"between":return Array.isArray(n.value)&&l>=n.value[0]&&l<=n.value[1];case"null":return l==null;case"not_null":return l!=null;default:return!0}});let e=t;this._state.sort.length>0&&(t=[...t].sort((n,u)=>{for(let l of this._state.sort){let p=this._columns.find(E=>E.key===l.column),h=n[l.column],g=u[l.column],f=0;if(h==null?f=-1:g==null?f=1:p?.type==="number"?f=Number(h)-Number(g):p?.type==="date"?f=new Date(h).getTime()-new Date(g).getTime():p?.type==="boolean"?f=(h?1:0)-(g?1:0):f=String(h).localeCompare(String(g)),f!==0)return l.direction==="desc"?-f:f}return 0}));let s=t,a=e.length,i=this._state.pagination.perPage,o,d,r;if(this._paginate){r=Math.ceil(a/i)||1,d=Math.min(this._state.pagination.page,r);let n=(d-1)*i;o=s.slice(n,n+i)}else o=s,d=1,r=1;this._state={...this._state,filteredRows:e,sortedRows:s,paginatedRows:o,pagination:{page:d,perPage:i,total:a,lastPage:r}},this._notify()}};function F(c="XSRF-TOKEN"){if(typeof document>"u")return null;let t=c.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),e=document.cookie.match(new RegExp(`(?:^|;\\s*)${t}=([^;]*)`));return e?decodeURIComponent(e[1]):null}var y=class extends m{_serverUrl;_serverMethod;_drawCounter=0;_abortController=null;_fetchDebounceTimer=null;_serverColumns;_csrfCookieName;_csrfHeaderName;constructor(t){super(t),this._serverUrl=t.serverUrl,this._serverMethod=t.serverMethod??"GET",this._csrfCookieName=t.csrfCookieName??"XSRF-TOKEN",this._csrfHeaderName=t.csrfHeaderName??"X-CSRF-Token",this._serverColumns=t.columns.map(e=>({data:e.key,name:e.key,searchable:e.searchable!==!1,orderable:e.sortable!==!1}))}_buildHeaders(t={}){let e={...t},s=F(this._csrfCookieName);return s&&(e[this._csrfHeaderName]=s),e}setSort(t){let e=this.getState();this._state={...e,sort:t,pagination:{...e.pagination,page:1}},this._debouncedFetch()}setGlobalSearch(t){let e=this.getState();this._state={...e,globalSearch:t,pagination:{...e.pagination,page:1}},this._debouncedFetch()}setFilters(t){let e=this.getState();this._state={...e,filters:t,pagination:{...e.pagination,page:1}},this._debouncedFetch()}setPage(t){let e=this.getState();this._state={...e,pagination:{...e.pagination,page:t}},this._fetchFromServer()}setPerPage(t){let e=this.getState();this._state={...e,pagination:{...e.pagination,perPage:t,page:1}},this._fetchFromServer()}_debouncedFetch(){this._fetchDebounceTimer&&clearTimeout(this._fetchDebounceTimer),this._fetchDebounceTimer=setTimeout(()=>this._fetchFromServer(),300)}async _fetchFromServer(){this._abortController&&this._abortController.abort(),this._abortController=new AbortController;let t=this.getState();this.setLoading(!0),this.setError(null);let e=++this._drawCounter,s={draw:e,start:(t.pagination.page-1)*t.pagination.perPage,length:t.pagination.perPage,search:{value:t.globalSearch,regex:!1},order:t.sort.map(a=>({column:this._serverColumns.findIndex(i=>i.data===a.column),dir:a.direction})),columns:this._serverColumns.map(a=>({...a,search:{value:t.filters.find(i=>i.column===a.data)?.value??"",regex:!1}}))};try{let a;if(this._serverMethod==="POST")a=await fetch(this._serverUrl,{method:"POST",headers:this._buildHeaders({"Content-Type":"application/json"}),body:JSON.stringify(s),signal:this._abortController.signal});else{let o=new URLSearchParams;o.set("draw",String(s.draw)),o.set("start",String(s.start)),o.set("length",String(s.length)),o.set("search[value]",s.search.value),o.set("search[regex]",String(s.search.regex)),s.order.forEach((r,n)=>{o.set(`order[${n}][column]`,String(r.column)),o.set(`order[${n}][dir]`,r.dir)}),s.columns.forEach((r,n)=>{o.set(`columns[${n}][data]`,r.data),o.set(`columns[${n}][name]`,r.name),o.set(`columns[${n}][searchable]`,String(r.searchable)),o.set(`columns[${n}][orderable]`,String(r.orderable)),o.set(`columns[${n}][search][value]`,r.search.value),o.set(`columns[${n}][search][regex]`,String(r.search.regex))});let d=`${this._serverUrl}?${o.toString()}`;a=await fetch(d,{signal:this._abortController.signal})}if(!a.ok)throw new Error(`Server responded with ${a.status}`);let i=await a.json();i.draw===e&&this.setServerResponse(i)}catch(a){if(a.name==="AbortError")return;this.setLoading(!1),this.setError(a.message??"Failed to fetch data")}}async initialFetch(){await this._fetchFromServer()}destroy(){this._abortController&&this._abortController.abort(),this._fetchDebounceTimer&&clearTimeout(this._fetchDebounceTimer)}};function v(c){if(c==null)return"";let t=String(c);return t.includes(",")||t.includes('"')||t.includes(`
2
+ `)||t.includes("\r")?'"'+t.replace(/"/g,'""')+'"':t}function C(c,t){let e=t.filter(i=>i.visible!==!1),s=e.map(i=>v(i.header)).join(","),a=c.map(i=>e.map(o=>v(i[o.key])).join(","));return"\uFEFF"+[s,...a].join(`\r
3
+ `)}function x(c,t="export.csv"){let e=new Blob([c],{type:"text/csv;charset=utf-8;"});S(e,t)}function S(c,t){let e=URL.createObjectURL(c),s=document.createElement("a");s.href=e,s.download=t,document.body.appendChild(s),s.click(),document.body.removeChild(s),URL.revokeObjectURL(e)}async function T(c,t){let e=t.filter(o=>o.visible!==!1),s=e.map(o=>o.header).join(" "),a=c.map(o=>e.map(d=>{let r=o[d.key];return r==null?"":String(r)}).join(" ")),i=[s,...a].join(`
4
+ `);await navigator.clipboard.writeText(i)}function b(c,t,e){let s=t.filter(r=>r.visible!==!1),a=s.map(r=>`<th style="border:1px solid #ddd;padding:8px 12px;text-align:left;background:#f5f5f5;font-weight:600;">${_(r.header)}</th>`).join(""),i=c.map(r=>`<tr>${s.map(u=>{let l=r[u.key];return`<td style="border:1px solid #ddd;padding:8px 12px;">${_(l==null?"":String(l))}</td>`}).join("")}</tr>`).join(""),o=`<!DOCTYPE html>
5
5
  <html><head>
6
- <title>${b(e??"Data Export")}</title>
6
+ <title>${_(e??"Data Export")}</title>
7
7
  <style>
8
8
  body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; margin: 20px; }
9
9
  table { border-collapse: collapse; width: 100%; font-size: 13px; }
@@ -11,9 +11,9 @@ import{Plugin as V}from"@beeblock/svelar/plugins";import{fileURLToPath as j}from
11
11
  @media print { h1 { margin-top: 0; } }
12
12
  </style>
13
13
  </head><body>
14
- ${e?`<h1>${b(e)}</h1>`:""}
14
+ ${e?`<h1>${_(e)}</h1>`:""}
15
15
  <table>
16
16
  <thead><tr>${a}</tr></thead>
17
17
  <tbody>${i}</tbody>
18
18
  </table>
19
- </body></html>`,u=window.open("","_blank");u&&(u.document.write(o),u.document.close(),u.focus(),setTimeout(()=>u.print(),250))}function b(c){return c.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function k(c,t,e="export.xlsx"){let s;try{s=await import("exceljs")}catch{throw new Error("exceljs is required for Excel export. Install it: npm install exceljs")}let a=new s.Workbook,i=a.addWorksheet("Data"),o=t.filter(d=>d.visible!==!1);i.columns=o.map(d=>({header:d.header,key:d.key,width:20}));let u=i.getRow(1);u.font={bold:!0},u.fill={type:"pattern",pattern:"solid",fgColor:{argb:"FFF0F0F0"}};for(let d of c){let l={};for(let p of o)l[p.key]=d[p.key]??"";i.addRow(l)}let r=await a.xlsx.writeBuffer(),n=new Blob([r],{type:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"});w(n,e)}function P(c,t,e="export.pdf"){y(c,t,e.replace(".pdf",""))}var C=class{async export(t,e,s,a){switch(t){case"csv":{let i=R(e,s);E(i,a??"export.csv");break}case"excel":await k(e,s,a??"export.xlsx");break;case"pdf":P(e,s,a??"export.pdf");break;case"clipboard":await F(e,s);break;case"print":y(e,s);break}}};var lt=m;export{_ as DataTableStore,C as ExportManager,v as ServerDataTableStore,m as SvelarDatatablePlugin,lt as default};
19
+ </body></html>`,d=window.open("","_blank");d&&(d.document.write(o),d.document.close(),d.focus(),setTimeout(()=>d.print(),250))}function _(c){return c.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function R(c,t,e="export.xlsx"){let s;try{s=await import("exceljs")}catch{throw new Error("exceljs is required for Excel export. Install it: npm install exceljs")}let a=new s.Workbook,i=a.addWorksheet("Data"),o=t.filter(u=>u.visible!==!1);i.columns=o.map(u=>({header:u.header,key:u.key,width:20}));let d=i.getRow(1);d.font={bold:!0},d.fill={type:"pattern",pattern:"solid",fgColor:{argb:"FFF0F0F0"}};for(let u of c){let l={};for(let p of o)l[p.key]=u[p.key]??"";i.addRow(l)}let r=await a.xlsx.writeBuffer(),n=new Blob([r],{type:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"});S(n,e)}function D(c,t,e="export.pdf"){b(c,t,e.replace(".pdf",""))}var w=class{async export(t,e,s,a){switch(t){case"csv":{let i=C(e,s);x(i,a??"export.csv");break}case"excel":await R(e,s,a??"export.xlsx");break;case"pdf":D(e,s,a??"export.pdf");break;case"clipboard":await T(e,s);break;case"print":b(e,s);break}}};export{m as DataTableStore,w as ExportManager,y as ServerDataTableStore};
@@ -0,0 +1,3 @@
1
+ import { SvelarDatatablePlugin } from './SvelarDatatablePlugin.js';
2
+ export default SvelarDatatablePlugin;
3
+ export { SvelarDatatablePlugin };
package/dist/plugin.js ADDED
@@ -0,0 +1 @@
1
+ import{Plugin as s}from"@beeblock/svelar/plugins";import{fileURLToPath as o}from"url";import{dirname as r,join as i}from"path";var a={prefix:"/api",defaults:{perPage:15,perPageOptions:[10,15,25,50,100],searchDebounceMs:300,sortable:!0,searchable:!0,paginate:!0,selectable:"none",striped:!0,hover:!0,bordered:!1,compact:!1,responsive:!0,virtualScroll:!1,virtualRowHeight:48}},l=r(o(import.meta.url)),n=r(l),u=i(n,"src","publishable"),e=class extends s{name="svelar-datatable";version="0.1.4";description="Full-featured DataTable plugin for Svelar \u2014 sorting, searching, pagination, inline editing, export, and server-side processing";_config;constructor(t={}){super(),this._config={...a,...t,defaults:{...a.defaults,...t.defaults}}}get datatableConfig(){return this._config}static defaults(){return{...a.defaults}}publishables(){return{routes:[{source:i(u,"routes/datatable.ts"),dest:"src/routes/api/datatable/+server.ts",type:"asset"}]}}};var m=e;export{e as SvelarDatatablePlugin,m as default};
package/package.json CHANGED
@@ -1,60 +1,72 @@
1
- {
2
- "name": "@beeblock/svelar-datatable",
3
- "version": "0.1.3",
4
- "description": "Full-featured DataTable plugin for Svelar — sorting, searching, pagination, inline editing, export, and server-side processing",
5
- "type": "module",
6
- "keywords": [
7
- "svelar-plugin",
8
- "datatable",
9
- "svelte",
10
- "sveltekit",
11
- "data-grid"
12
- ],
13
- "license": "MIT",
14
- "main": "dist/index.js",
15
- "types": "dist/index.d.ts",
16
- "exports": {
17
- "./package.json": "./package.json",
18
- ".": {
19
- "types": "./dist/index.d.ts",
20
- "default": "./dist/index.js"
21
- },
22
- "./server": {
23
- "types": "./dist/server/index.d.ts",
24
- "default": "./dist/server/index.js"
25
- },
26
- "./types": {
27
- "types": "./dist/types.d.ts",
28
- "default": "./dist/types.js"
29
- },
30
- "./ui": "./src/ui/index.ts",
31
- "./ui/*": "./src/ui/*"
32
- },
33
- "files": [
34
- "dist",
35
- "src/ui",
36
- "src/publishable",
37
- "LICENSE"
38
- ],
39
- "scripts": {
40
- "build": "tsup && (tsc --emitDeclarationOnly || echo 'Warning: tsc declaration emit had errors')",
41
- "dev": "tsup --watch"
42
- },
43
- "peerDependencies": {
44
- "@beeblock/svelar": ">=0.4.0",
45
- "svelte": "^5.0.0"
46
- },
47
- "peerDependenciesMeta": {
48
- "exceljs": {
49
- "optional": true
50
- },
51
- "@beeblock/svelar": {
52
- "optional": false
53
- }
54
- },
55
- "devDependencies": {
56
- "tsup": "^8.5.0",
57
- "typescript": "^5.7.0",
58
- "svelte": "^5.0.0"
59
- }
60
- }
1
+ {
2
+ "name": "@beeblock/svelar-datatable",
3
+ "version": "0.1.5",
4
+ "description": "Full-featured DataTable plugin for Svelar — sorting, searching, pagination, inline editing, export, and server-side processing",
5
+ "type": "module",
6
+ "keywords": [
7
+ "svelar-plugin",
8
+ "datatable",
9
+ "svelte",
10
+ "sveltekit",
11
+ "data-grid"
12
+ ],
13
+ "license": "MIT",
14
+ "main": "dist/index.js",
15
+ "types": "dist/index.d.ts",
16
+ "svelte": "./src/ui/index.ts",
17
+ "exports": {
18
+ "./package.json": "./package.json",
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "default": "./dist/index.js"
22
+ },
23
+ "./plugin": {
24
+ "types": "./dist/plugin.d.ts",
25
+ "default": "./dist/plugin.js"
26
+ },
27
+ "./server": {
28
+ "types": "./dist/server/index.d.ts",
29
+ "default": "./dist/server/index.js"
30
+ },
31
+ "./types": {
32
+ "types": "./dist/types.d.ts",
33
+ "default": "./dist/types.js"
34
+ },
35
+ "./ui": {
36
+ "types": "./src/ui/index.ts",
37
+ "svelte": "./src/ui/index.ts",
38
+ "import": "./src/ui/index.ts"
39
+ },
40
+ "./ui/*": {
41
+ "svelte": "./src/ui/*",
42
+ "import": "./src/ui/*"
43
+ }
44
+ },
45
+ "files": [
46
+ "dist",
47
+ "src/ui",
48
+ "src/publishable",
49
+ "LICENSE"
50
+ ],
51
+ "scripts": {
52
+ "build": "tsup && (tsc --emitDeclarationOnly || echo 'Warning: tsc declaration emit had errors')",
53
+ "dev": "tsup --watch"
54
+ },
55
+ "peerDependencies": {
56
+ "@beeblock/svelar": ">=0.4.0",
57
+ "svelte": "^5.0.0"
58
+ },
59
+ "peerDependenciesMeta": {
60
+ "exceljs": {
61
+ "optional": true
62
+ },
63
+ "@beeblock/svelar": {
64
+ "optional": false
65
+ }
66
+ },
67
+ "devDependencies": {
68
+ "tsup": "^8.5.0",
69
+ "typescript": "^5.7.0",
70
+ "svelte": "^5.0.0"
71
+ }
72
+ }