UNPKG

table-forge

Version:

A flexible, interactive data table generator with built-in sorting, filtering, and inline editing capabilities

1 lines 21.5 kB
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.TableBuilder=t():e.TableBuilder=t()}(this,(()=>(()=>{var e={659:e=>{function t(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,n)}return o}function o(e){for(var o=1;o<arguments.length;o++){var s=null!=arguments[o]?arguments[o]:{};o%2?t(Object(s),!0).forEach((function(t){n(e,t,s[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(s)):t(Object(s)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(s,t))}))}return e}function n(e,t,o){return(t=function(e){var t=function(e){if("object"!=typeof e||!e)return e;var t=e[Symbol.toPrimitive];if(void 0!==t){var o=t.call(e,"string");if("object"!=typeof o)return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"==typeof t?t:t+""}(t))in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}e.exports=class{constructor(e,t){let o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};this.container=document.getElementById(e),this.data=t,this.originalData=[...t],this.options={headerBgColor:o.headerBgColor||"#9ca3af",headerTextColor:o.headerTextColor||"#ffffff",headerFilteredBgColor:o.headerFilteredBgColor||"#60a5fa",bodyBgColor:o.bodyBgColor||"#ffffff",bodyTextColor:o.bodyTextColor||"#000000",columnOrder:o.columnOrder||[],columnDisplayNames:o.columnDisplayNames||{},inputColumns:o.inputColumns||[],dropdownColumns:o.dropdownColumns||{},requiredColumns:o.requiredColumns||[],frozenColumns:o.frozenColumns||[],calculatedColumns:o.calculatedColumns||{},enableSorting:o.enableSorting||!1,enableFiltering:o.enableFiltering||!1,enableAddRow:o.enableAddRow||!1,enableDeleteRow:o.enableDeleteRow||!1,enableSaveConfig:o.enableSaveConfig||!1,enableExcelExport:o.enableExcelExport||!1,enableJsonExport:o.enableJsonExport||!1,saveEndpoint:o.saveEndpoint||null,saveDataFormatter:o.saveDataFormatter||(e=>e),excelFileName:o.excelFileName||"table_export.xlsx",jsonFileName:o.jsonFileName||"table_export.json"},this.sortConfig={column:null,direction:"asc"},this.filters={},this.hasUnsavedChanges=!1,this.addCalculatedColumns(),this.originalDataString=JSON.stringify(t),console.log("TableCreator initialized with options:",this.options),console.log("TableCreator initialized with data:",this.data)}create(){const e=document.createElement("div");e.className="relative bg-white rounded-lg shadow-md border border-gray-200 overflow-hidden";const t=document.createElement("div");t.className="table-wrapper overflow-auto max-h-[70vh]",this.addFrozenColumnsStyles();const o=document.createElement("table");o.className="border-collapse table-auto relative w-full";const n=this.createHeader(),s=this.createBody();if(o.appendChild(n),o.appendChild(s),t.appendChild(o),e.appendChild(t),this.options.enableSaveConfig||this.options.enableAddRow||this.options.enableExcelExport||this.options.enableJsonExport){const e=document.createElement("div");e.className="sticky bottom-0 left-0 w-full bg-white border-t py-2 px-4 flex justify-between items-center z-10";const o=document.createElement("div");o.className="flex gap-2";const n=document.createElement("div");if(n.className="flex gap-2",this.options.enableJsonExport){const e=document.createElement("button");e.type="button",e.textContent="📄 Export to JSON",e.className="bg-yellow-600 hover:bg-yellow-700 text-white font-semibold py-2 px-4 rounded",e.addEventListener("click",(e=>{e.preventDefault(),this.exportToJson()})),o.appendChild(e)}if(this.options.enableExcelExport){const e=document.createElement("button");e.type="button",e.textContent="📊 Export to Excel",e.className="bg-green-700 hover:bg-green-800 text-white font-semibold py-2 px-4 rounded",e.addEventListener("click",(e=>{e.preventDefault(),this.exportToExcel()})),o.appendChild(e)}if(this.options.enableSaveConfig){const e=document.createElement("button");e.type="button",e.textContent="Save Configuration",e.className="bg-green-500 text-white font-semibold py-2 px-4 rounded opacity-50 cursor-not-allowed",e.disabled=!0,e.id="saveConfigBtn",e.title="No changes to save";const t=document.createElement("div");t.className="relative inline-block";const o=document.createElement("div");o.className="absolute hidden bg-gray-800 text-white text-sm rounded px-2 py-1 bottom-full left-1/2 transform -translate-x-1/2 mb-2 whitespace-nowrap",t.appendChild(o),e.addEventListener("mouseenter",(()=>{this.hasActiveFilters()?(o.textContent="Cannot save while filters are active. Clear filters first.",o.classList.remove("hidden")):this.hasUnsavedChanges||(o.textContent="No changes to save",o.classList.remove("hidden"))})),e.addEventListener("mouseleave",(()=>{o.classList.add("hidden")})),e.addEventListener("click",(async()=>{e.disabled=!0,e.textContent="Saving...",await this.saveConfiguration()?(e.className="bg-green-500 text-white font-semibold py-2 px-4 rounded opacity-50 cursor-not-allowed",e.textContent="Save Configuration",e.disabled=!0,o.textContent="Changes saved successfully"):(e.className="bg-green-500 hover:bg-green-600 text-white font-semibold py-2 px-4 rounded",e.textContent="Save Configuration",e.disabled=!1,o.textContent="Error saving changes. Try again.")})),t.appendChild(e),n.appendChild(t)}if(this.options.enableAddRow){const e=document.createElement("button");e.textContent="+ Add New Row",e.className="bg-blue-500 hover:bg-blue-600 text-white font-semibold py-2 px-4 rounded",e.addEventListener("click",(()=>{this.addNewRow(),this.checkForChanges()})),n.appendChild(e)}e.appendChild(o),e.appendChild(n),t.appendChild(e)}this.container.appendChild(e),this.addCopyHandler(o)}createHeader(){const e=document.createElement("thead"),t=document.createElement("tr");if(0===this.data.length)return e;if(this.getOrderedColumns().forEach((e=>{const o=document.createElement("th");o.className="sticky top-0 px-6 py-3 text-sm font-semibold tracking-wider relative",o.style.backgroundColor=this.filters[e]?this.options.headerFilteredBgColor:this.options.headerBgColor,o.style.color=this.options.headerTextColor;const n=document.createElement("div");n.className="absolute bottom-0 left-0 right-0",n.style.height="3px",n.style.backgroundColor="#d1d5db",n.style.boxShadow="0 1px 1px rgba(0,0,0,0.1)",o.appendChild(n),this.options.frozenColumns.includes(e)&&o.setAttribute("data-frozen-column",e);const s=document.createElement("div");s.className=this.options.enableSorting||this.options.enableFiltering?"flex items-center justify-between space-x-4":"flex items-center justify-center";const i=document.createElement("span");if(i.className="flex items-center whitespace-nowrap",i.innerHTML='\n <span class="font-medium">\n '.concat(this.options.columnDisplayNames[e]||e,"\n </span>\n ").concat(this.options.requiredColumns.includes(e)?'<span class="text-red-500 ml-1">*</span>':"","\n "),i.title=e,s.appendChild(i),this.options.enableSorting||this.options.enableFiltering){const t=document.createElement("div");if(t.className="flex items-center space-x-2",this.options.enableSorting){const o=document.createElement("button");o.innerHTML="↕️",o.className="hover:bg-opacity-20 hover:bg-white rounded p-1 transition-colors",o.title="Sort by ".concat(this.options.columnDisplayNames[e]||e),o.addEventListener("click",(()=>this.handleSort(e))),t.appendChild(o)}if(this.options.enableFiltering){const o=document.createElement("button");o.type="button",o.innerHTML=this.filters[e]?"🎛️":"🔍",o.className="hover:bg-opacity-20 hover:bg-white rounded p-1 transition-colors\n ".concat(this.filters[e]?"bg-opacity-20 bg-white":""),o.title="Filter ".concat(this.options.columnDisplayNames[e]||e),o.addEventListener("click",(t=>{t.preventDefault(),this.showFilterDropdown(t,e)})),t.appendChild(o)}s.appendChild(t)}o.appendChild(s),t.appendChild(o)})),this.options.enableDeleteRow){const e=document.createElement("th");e.className="sticky top-0 px-6 py-3 text-sm font-semibold",e.style.backgroundColor=this.options.headerBgColor,e.style.color=this.options.headerTextColor,e.innerHTML="";const o=document.createElement("div");o.className="absolute bottom-0 left-0 right-0",o.style.height="3px",o.style.backgroundColor="#d1d5db",o.style.boxShadow="0 1px 1px rgba(0,0,0,0.1)",e.appendChild(o),t.appendChild(e)}return e.appendChild(t),e}createBody(){const e=document.createElement("tbody"),t=this.getOrderedColumns();return this.data.forEach(((o,n)=>{const s=document.createElement("tr");if(s.className="hover:bg-gray-50",t.forEach((e=>{const t=o[e],i=document.createElement("td");if(i.className="border px-4 py-2 text-xs text-center",i.style.backgroundColor=this.options.bodyBgColor,i.style.color=this.options.bodyTextColor,this.options.frozenColumns.includes(e)&&i.setAttribute("data-frozen-column",e),this.options.dropdownColumns[e]){const o=document.createElement("select");o.className="p-1 border rounded w-full text-center ".concat(this.options.requiredColumns.includes(e)?"required":""),o.style.backgroundColor=this.options.bodyBgColor,o.style.color=this.options.bodyTextColor,this.options.requiredColumns.includes(e)&&(o.required=!0),o.style.width="100%";const s=[...this.options.dropdownColumns[e]];s.includes(t)||s.push(t),s.forEach((e=>{const n=document.createElement("option");n.value=e,n.textContent=e,n.style.textAlign="center",e===t&&(n.selected=!0),o.appendChild(n)})),o.addEventListener("change",(t=>{this.data[n][e]=String(t.target.value),this.checkForChanges()})),i.appendChild(o)}else if(this.options.inputColumns.includes(e)){const o=document.createElement("input");o.type="text",o.value=String(t||""),o.className="p-1 border rounded text-center ".concat(this.options.requiredColumns.includes(e)?"required":""),o.style.backgroundColor=this.options.bodyBgColor,o.style.color=this.options.bodyTextColor,this.options.requiredColumns.includes(e)&&(o.required=!0),o.style.minWidth="max-content",o.style.width="100%",o.addEventListener("change",(t=>{this.data[n][e]=String(t.target.value),this.checkForChanges()})),i.appendChild(o)}else i.textContent=t;s.appendChild(i)})),this.options.enableDeleteRow){const e=document.createElement("td");e.className="border px-4 py-2 text-xs bg-gray-50";const t=document.createElement("button");t.type="button",t.innerHTML="⛌",t.className="text-red-500 hover:text-red-700 focus:outline-none",t.title="Delete row",t.addEventListener("click",(e=>{e.preventDefault(),this.deleteRow(n)})),e.appendChild(t),s.appendChild(e)}e.appendChild(s)})),e}getOrderedColumns(){if(!this.data.length)return[];const e=Object.keys(this.data[0]);if(!this.options.columnOrder)return e;const t=[...this.options.columnOrder];return e.forEach((e=>{t.includes(e)||t.push(e)})),t}addFrozenColumnsStyles(){var e;if(null===(e=this.options.frozenColumns)||void 0===e||!e.length)return;const t=document.createElement("style");let o=0;const n=this.options.frozenColumns.map(((e,t)=>{const n='[data-frozen-column="'.concat(e,'"]'),s="\n ".concat(n," {\n position: sticky;\n left: ").concat(o,"px;\n z-index: 3;\n background-color: ").concat(this.options.bodyBgColor,";\n color: ").concat(this.options.bodyTextColor,";\n }\n ").concat(n,":after {\n content: '';\n position: absolute;\n right: 0;\n top: 0;\n bottom: 0;\n width: 2px;\n background-color: #d1d5db;\n box-shadow: 2px 0 4px rgba(0,0,0,0.05);\n }\n thead ").concat(n," {\n z-index: 4;\n background-color: ").concat(this.options.headerBgColor," !important;\n color: ").concat(this.options.headerTextColor," !important;\n }\n thead ").concat(n,":after {\n background-color: #d1d5db;\n box-shadow: 2px 0 4px rgba(0,0,0,0.1);\n }\n .table-wrapper {\n position: relative;\n z-index: 1;\n }\n ");return o+=200,s})).join("\n");t.textContent=n,document.head.appendChild(t)}addCalculatedColumns(){this.options.calculatedColumns&&0!==Object.keys(this.options.calculatedColumns).length&&(this.data=this.data.map((e=>{const t=o({},e);return Object.entries(this.options.calculatedColumns).forEach((o=>{let[n,s]=o;t[n]=s(e)})),t})),this.originalData=this.originalData.map((e=>{const t=o({},e);return Object.entries(this.options.calculatedColumns).forEach((o=>{let[n,s]=o;t[n]=s(e)})),t})))}handleSort(e){this.sortConfig.column===e?this.sortConfig.direction="asc"===this.sortConfig.direction?"desc":"asc":(this.sortConfig.column=e,this.sortConfig.direction="asc"),this.data.sort(((t,o)=>{const n=t[e],s=o[e];return isNaN(n)||isNaN(s)?"asc"===this.sortConfig.direction?String(n).localeCompare(String(s)):String(s).localeCompare(String(n)):"asc"===this.sortConfig.direction?n-s:s-n})),this.container.innerHTML="",this.create()}showFilterDropdown(e,t){e.stopPropagation();const o=document.getElementById("filter-dropdown");let n;o&&o.remove(),n=this.filters[t]?[...new Set(this.originalData.map((e=>e[t])))]:[...new Set(this.data.map((e=>e[t])))];const s=document.createElement("div");s.id="filter-dropdown",s.className="absolute bg-white border rounded shadow-lg z-50 max-h-64 overflow-y-auto";const i=e.target.getBoundingClientRect();s.style.top="".concat(i.bottom+window.scrollY,"px"),s.style.left="".concat(i.left+window.scrollX,"px");const r=document.createElement("input");r.type="text",r.placeholder="Search...",r.className="w-full p-2 border-b";const a=document.createElement("label");a.className="block p-2 hover:bg-gray-100";const l=document.createElement("input");l.type="checkbox",l.checked=!1,l.className="mr-2",a.appendChild(l),a.appendChild(document.createTextNode("Select All")),s.appendChild(r),s.appendChild(a);const c=document.createElement("div"),d=e=>{c.innerHTML="",e.forEach((e=>{const o=document.createElement("label");o.className="block p-2 hover:bg-gray-100";const n=document.createElement("input");n.type="checkbox",n.value=e,n.className="mr-2",n.checked=!this.filters[t]||this.filters[t].includes(e),o.appendChild(n),o.appendChild(document.createTextNode(e||"(Blank)")),c.appendChild(o)}))};d(n),s.appendChild(c);const h=document.createElement("div");h.className="flex justify-between p-2 border-t";const p=document.createElement("button");p.textContent="Apply",p.className="px-3 py-1 bg-blue-500 text-white rounded";const u=document.createElement("button");u.textContent="Clear",u.className="px-3 py-1 bg-gray-300 rounded",h.appendChild(u),h.appendChild(p),s.appendChild(h),r.addEventListener("input",(e=>{const t=e.target.value.toLowerCase(),o=n.filter((e=>String(e).toLowerCase().includes(t)));d(o)})),l.addEventListener("change",(e=>{c.querySelectorAll('input[type="checkbox"]').forEach((t=>t.checked=e.target.checked))})),p.addEventListener("click",(()=>{const o=Array.from(c.querySelectorAll('input[type="checkbox"]:checked')).map((e=>e.value));o.length===n.length?delete this.filters[t]:this.filters[t]=o,this.applyFilters();const i=e.target;i.innerHTML=this.filters[t]?"🎛️":"🔍",i.className="ml-2 focus:outline-none ".concat(this.filters[t]?"text-blue-500":""),s.remove()})),u.addEventListener("click",(()=>{delete this.filters[t],this.applyFilters();const o=e.target;o.innerHTML="🔍",o.className="ml-2 focus:outline-none",s.remove()})),document.addEventListener("click",(function e(t){s.contains(t.target)||(s.remove(),document.removeEventListener("click",e))})),document.body.appendChild(s)}applyFilters(){this.data=this.originalData.filter((e=>Object.entries(this.filters).every((t=>{let[o,n]=t;return n.includes(e[o])})))),this.checkForChanges(),this.container.innerHTML="",this.create()}hasActiveFilters(){return Object.keys(this.filters).length>0}addNewRow(){const e=Object.keys(this.data[0]).reduce(((e,t)=>{if(this.options.dropdownColumns[t])e[t]="";else{const o=this.data[0][t];e[t]="number"==typeof o?0:""}return e}),{});this.data.push(e),this.originalData.push(o({},e)),this.container.innerHTML="",this.create();const t=this.container.querySelector(".table-wrapper");t.scrollTop=t.scrollHeight}deleteRow(e){confirm("Are you sure you want to delete this row?")&&(this.data.splice(e,1),this.originalData=this.originalData.filter(((t,o)=>o!==e)),0!==this.data.length?(this.container.innerHTML="",this.create(),this.showNotification("Row deleted successfully","success"),this.hasUnsavedChanges=!0,this.checkForChanges()):this.showNotification("Cannot delete last row","error"))}checkForChanges(){const e=JSON.stringify(this.data)!==this.originalDataString,t=this.hasActiveFilters(),o=document.getElementById("saveConfigBtn");o&&(o.disabled=t||!e,t?(o.className="bg-green-500 text-white font-semibold py-2 px-4 rounded opacity-50 cursor-not-allowed",o.title="Cannot save while filters are active"):(o.className=e?"bg-green-500 hover:bg-green-600 text-white font-semibold py-2 px-4 rounded":"bg-green-500 text-white font-semibold py-2 px-4 rounded opacity-50 cursor-not-allowed",o.title=e?"Save changes":"No changes to save")),this.hasUnsavedChanges=e}async saveConfiguration(){if(!this.options.saveEndpoint)return console.error("Save endpoint not configured"),!1;const e=this.validateData();if(e.length>0){const t=e.join("\n");return this.showNotification(t,"error"),console.error("Validation errors:",e),!1}try{const e=this.data.map((e=>this.stringifyValues(e))),t=this.options.saveDataFormatter(e),o=new URLSearchParams;"object"==typeof t&&null!==t&&Object.entries(t).forEach((e=>{let[t,n]=e;"object"==typeof n?o.append(t,JSON.stringify(n)):o.append(t,String(n))}));const n=await fetch(this.options.saveEndpoint,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:o});if(!n.ok)throw new Error("HTTP error! status: ".concat(n.status));return this.originalData=e,this.originalDataString=JSON.stringify(e),this.hasUnsavedChanges=!1,this.showNotification("Configuration saved successfully","success"),!0}catch(e){return console.error("Error saving configuration:",e),this.showNotification("Error saving configuration","error"),!1}}validateData(){const e=[];return this.data.forEach(((t,o)=>{this.options.requiredColumns.forEach((n=>{if(!t[n]||""===t[n].trim()){const t=this.options.columnDisplayNames[n]||n;e.push("Row ".concat(o+1,": ").concat(t," is required \n"))}}))})),e}stringifyValues(e){const t=o({},e);return Object.keys(t).forEach((e=>{t[e]=null!=t[e]?String(t[e]):""})),t}showNotification(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"success";const o=document.createElement("div");o.className="fixed top-4 right-4 px-4 py-2 rounded shadow-lg z-50 ".concat("success"===t?"bg-green-500":"bg-red-500"," text-white"),o.textContent=e,document.body.appendChild(o),setTimeout((()=>o.remove()),3e3)}exportToExcel(){try{const e=this.getOrderedColumns(),t=[e.map((e=>this.options.columnDisplayNames[e]||e))];this.data.forEach((o=>{const n=e.map((e=>o[e]));t.push(n)}));const o=XLSX.utils.aoa_to_sheet(t),n=XLSX.utils.book_new();XLSX.utils.book_append_sheet(n,o,"Sheet1"),XLSX.writeFile(n,this.options.excelFileName),this.showNotification("Table exported successfully","success")}catch(e){console.error("Error exporting to Excel:",e),this.showNotification("Error exporting to Excel","error")}}exportToJson(){try{const e=JSON.stringify(this.data,null,2),t=new Blob([e],{type:"application/json"}),o=window.URL.createObjectURL(t),n=document.createElement("a");n.href=o,n.download=this.options.jsonFileName,document.body.appendChild(n),n.click(),window.URL.revokeObjectURL(o),document.body.removeChild(n),this.showNotification("JSON exported successfully","success")}catch(e){console.error("Error exporting to JSON:",e),this.showNotification("Error exporting to JSON","error")}}addCopyHandler(e){e.addEventListener("copy",(t=>{t.preventDefault();const o=window.getSelection().getRangeAt(0),n=[],s=o.cloneContents().querySelectorAll("th, td"),i=e.rows[0].cells.length;let r=[];s.forEach(((e,t)=>{let o;const a=e.querySelector("input"),l=e.querySelector("select");o=a?a.value:l?l.value:e.textContent.replace("↕️","").replace("🎛️","").replace("🔍","").trim(),r.push(o),r.length!==i&&t!==s.length-1||(n.push(r.join("\t")),r=[])}));const a=n.join("\n");t.clipboardData.setData("text/plain",a)}))}addRequiredFieldStyles(){const e=document.createElement("style");e.textContent="\n .required {\n border-color: #ef4444;\n }\n .required:focus {\n border-color: #ef4444;\n box-shadow: 0 0 0 1px #ef4444;\n }\n ",document.head.appendChild(e)}}}},t={};function o(n){var s=t[n];if(void 0!==s)return s.exports;var i=t[n]={exports:{}};return e[n](i,i.exports,o),i.exports}o.d=(e,t)=>{for(var n in t)o.o(t,n)&&!o.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},o.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var n={};return(()=>{"use strict";o.d(n,{default:()=>e});const e=o(659)})(),n.default})()));