2 lines
18 KiB
JavaScript
2 lines
18 KiB
JavaScript
import{Component as t,NewState as e,RefreshState as n,Util as o}from"@web/state";import{VirtualScroll as i,State as r}from"@web/base";t.register("DataTable",t=>{t.state||(t.state=e({}));const o=t.state;Object.assign(o,{list:[],fields:[],_renderedList:[],prevHeight:0,postHeight:0,_listStartIndex:0,selectedRowCount:0,_originalList:[],sortConfig:{fieldId:null,direction:null},filterConfig:{},activeFieldId:null,_internalUpdate:!1,_appliedHash:"",_fieldsDirty:!1,_masterCellNodes:null});const l=(()=>{let t=!!window.__DT_PERF_MODE__;const e={refreshTime:0,refreshCount:0,scrollCount:0,totalNodes:0};return t&&!window.__statePerformanceTelemetry&&(window.__statePerformanceTelemetry={scanCount:0,reuseCount:0,moveCount:0}),{get stats(){return e},enable:()=>{t=!0},disable:()=>{t=!1},onScroll:()=>{t&&e.scrollCount++},startFrame:()=>{var e,n,o;return t?{start:performance.now(),scan:(null==(e=window.__statePerformanceTelemetry)?void 0:e.scanCount)||0,move:(null==(n=window.__statePerformanceTelemetry)?void 0:n.moveCount)||0,reuse:(null==(o=window.__statePerformanceTelemetry)?void 0:o.reuseCount)||0}:null},endFrame:(n,o)=>{if(!t||!n)return;e.refreshCount++,e.totalNodes+=o;const i=performance.now()-n.start;e.refreshTime+=i;const r=window.__statePerformanceTelemetry;if(r){const t=r.scanCount-n.scan,e=r.moveCount-n.move,l=r.reuseCount-n.reuse;(t>0||i>2)&&console.log(`[DataTable Frame] Time: ${i.toFixed(2)}ms, Scans: ${t}, Moves: ${e}, Reuses: ${l}, Rows: ${o}`)}}}})();o.perf=l.stats;const s=((t,e)=>{let n=null,o=null,i=[];const r=(t,e)=>!!(n&&t>=n.minRow&&t<=n.maxRow&&e>=n.minCol&&e<=n.maxCol)||i.some(n=>t>=n.minRow&&t<=n.maxRow&&e>=n.minCol&&e<=n.maxCol);let l=!1;const s=()=>{var o;if(window.__DT_FEATURES__&&!window.__DT_FEATURES__.selection)return;let s=1/0,a=-1/0;n&&(s=Math.min(s,n.minRow),a=Math.max(a,n.maxRow)),i.forEach(t=>{s=Math.min(s,t.minRow),a=Math.max(a,t.maxRow)});const d=s!==1/0;if(!d&&!l)return;l=d;const c=t.querySelector(".dt-body");if(!c)return;const m=c.children;for(let t=0;t<m.length;t++){const n=m[t];if(!n.classList.contains("dt-body-row"))continue;const i=((null==(o=n._ref)?void 0:o.rIdx)??-1)+e._listStartIndex;if(!d||i<s||i>a){const t=n.children;for(let e=0;e<t.length;e++)t[e].classList.remove("dt-cell-selected");continue}const l=n.children;for(let t=0;t<l.length;t++){const e=r(i,t),n=l[t];e?n.classList.add("dt-cell-selected"):n.classList.remove("dt-cell-selected")}}},a=()=>{let t=0;n&&(t+=n.maxRow-n.minRow+1),i.forEach(e=>t+=e.maxRow-e.minRow+1),e.selectedRowCount=t},d=(t=!1)=>{t||(n=null,o=null,i=[],s(),a())},c=()=>{if(!n)return null;let t=n.minRow,e=n.maxRow,o=n.minCol,r=n.maxCol;return i.forEach(n=>{t=Math.min(t,n.minRow),e=Math.max(e,n.maxRow),o=Math.min(o,n.minCol),r=Math.max(r,n.maxCol)}),{minRow:t,maxRow:e,minCol:o,maxCol:r}};return{applySelectionUI:s,clearAllActive:d,startSelect:(l,c,m)=>{const u=r(l,c),f=n&&(n.minRow!==n.maxRow||n.minCol!==n.maxCol)||i.length>0;m.shiftKey&&o?n={minRow:Math.min(o.row,l),maxRow:Math.max(o.row,l),minCol:Math.min(o.col,c),maxCol:Math.max(o.col,c)}:(!u||m.ctrlKey||m.metaKey?(m.ctrlKey||m.metaKey?n&&!u&&i.push(n):d(),o={row:l,col:c},n={minRow:l,maxRow:l,minCol:c,maxCol:c}):f||(t._potentialCancel={row:l,col:c}),e.isSelecting=!0),s(),a(),t.focus()},updateSelect:(i,r)=>{e.isSelecting&&o&&(n={minRow:Math.min(o.row,i),maxRow:Math.max(o.row,i),minCol:Math.min(o.col,r),maxCol:Math.max(o.col,r)},t._potentialCancel=null,s(),a())},endSelect:()=>{if(t._potentialCancel){const{row:e,col:n}=t._potentialCancel;r(e,n)&&d(),t._potentialCancel=null}e.isSelecting=!1},deleteSelected:()=>{const t=c();if(!t)return;const n=t.minRow,o=t.maxRow,i=e.list.filter((t,e)=>!(e>=n&&e<=o));e.list=i,d()},getSelectionBounds:c,copy:async()=>{const t=c();if(!t)return;const n=e.list.slice(t.minRow,t.maxRow+1).map(n=>e.fields.slice(t.minCol,t.maxCol+1).map(t=>{let e=String(n[t.id]??"");return(e.includes("\t")||e.includes("\n")||e.includes('"'))&&(e='"'+e.replace(/"/g,'""')+'"'),e}).join("\t")).join("\n");await navigator.clipboard.writeText(n)},paste:async()=>{try{const n=await navigator.clipboard.readText();if(!n)return;const o=c();if(!o)return;const i=n.split(/\r?\n/).filter(t=>t.length>0).map(t=>{const e=[];let n="",o=!1;for(let i=0;i<t.length;i++){const r=t[i];'"'===r?o&&'"'===t[i+1]?(n+='"',i++):o=!o:"\t"!==r||o?n+=r:(e.push(n),n="")}return e.push(n),e}),r=o.minRow,l=o.minCol,s=o.maxRow,a=o.maxCol,d=t.querySelector(".dt-body"),m=d?Array.from(d.childNodes).filter(t=>{var e;return null==(e=t.classList)?void 0:e.contains("dt-body-row")}):[];i.forEach((n,o)=>{const i=r+o;if(i>s||i>=e.list.length)return;const d=e.list[i];let c=!1;if(n.forEach((t,n)=>{const o=l+n;if(o>a||o>=e.fields.length)return;const i=e.fields[o];d[i.id]=t,c=!0}),c&&t.refreshNode){const n=m.find(t=>{var n;return((null==(n=t._ref)?void 0:n.rIdx)??-1)+e._listStartIndex===i});n&&t.refreshNode(n)}})}catch(t){console.error("Paste Error:",t)}}}})(t,o),a=((t,e,n)=>{const o=i({itemHeight:40});let r=null;const l=(t=!1)=>{if(!r)return;const i=o.calc(r,e.list);if(i){if(!t&&e.prevHeight===i.prevHeight&&e.postHeight===i.postHeight&&e._listStartIndex===i.listStartIndex&&e._renderedList.length===i.renderedList.length)return;Object.assign(e,{prevHeight:i.prevHeight,postHeight:i.postHeight,_listStartIndex:i.listStartIndex,_renderedList:i.renderedList}),null==n||n(i.renderedList.length,t)}};return{init:()=>{r=t.querySelector(".dt-main")},reset:n=>{e._listStartIndex=0,o.reset(n,r||t),e.list===n&&o.init(n,()=>l(!0))},refresh:l,onScroll:()=>l(!1)}})(t,o,(t,e)=>{s.applySelectionUI()});let d,c,m;t.format=(t,e)=>e.formatter?e.formatter(t,e):"string"==typeof t?t:null==t?"":"object"==typeof t?JSON.stringify(t):String(t),t.refreshNode=t=>n(t),t.refresh=()=>{const e=l.startFrame();if(a.refresh(),d||(d=t.querySelector(".dt-spacer-prev")),d){const t=o.prevHeight||0;d.style.height=t+"px",d.style.display=t>0?"block":"none"}if(c||(c=t.querySelector(".dt-spacer-post")),c){const t=o.postHeight||0;c.style.height=t+"px",c.style.display=t>0?"block":"none"}l.endFrame(e,o._renderedList.length)};let u=null;t.onScroll=()=>{l.onScroll(),t.refresh(),u||(u=requestAnimationFrame(()=>{u=null;const e=t.querySelector(".dt-column-menu");if(e&&"none"!==e.style.display&&o.activeFieldId){const n=t.querySelector(`.header-cell[data-id="${o.activeFieldId}"]`);if(n){const o=n.querySelector(".header-menu-btn").getBoundingClientRect(),i=t.getBoundingClientRect();e.style.left=o.right-i.left-180+"px",e.style.top=o.bottom-i.top+5+"px"}}}))},t.applySortFilter=(t={})=>{if(o._internalUpdate)return;const e={...o.filterConfig,...t.filters||{}},n=void 0!==t.sort?t.sort?{fieldId:o.activeFieldId,direction:t.sort}:{fieldId:null,direction:null}:o.sortConfig,i=JSON.stringify({s:n,f:e});if(o._appliedHash===i&&!t.force)return;o._internalUpdate=!0;const r=performance.now();let l=t.force?o.list:[...o._originalList||[]];t.force||(Object.keys(e).forEach(t=>{const n=e[t];if(n){const e=String(n).toLowerCase();l=l.filter(n=>String(n[t]??"").toLowerCase().includes(e))}}),n&&n.fieldId&&n.direction&&l.sort((t,e)=>{let o=t[n.fieldId],i=e[n.fieldId];if(o===i)return 0;if(null==o)return 1;if(null==i)return-1;const r=o>i?1:-1;return"asc"===n.direction?r:-r})),window.__perfTrace={evalCount:0,evalTotal:0},performance.now(),o._appliedHash=i,void 0!==t.sort&&(o.sortConfig=n),o.list=l,o._internalUpdate=!1;const s=performance.now()-r;console.log(`[DataTable Performance Profile] Sync Block: ${s.toFixed(2)}ms (Eval: ${window.__perfTrace.evalCount})`),requestAnimationFrame(()=>{setTimeout(()=>{console.log(`[DataTable Performance Profile] E2E Paint: ${(performance.now()-r).toFixed(2)}ms`)})}),window.__perfTrace=null},t.showColumnMenu=(e,i)=>{i.stopPropagation();const r=i.currentTarget,l=r.getBoundingClientRect(),s=t.getBoundingClientRect(),a=t.querySelector(".dt-column-menu");o.activeFieldId=e.id,a.style.display="block",a.style.left=l.right-s.left-180+"px",a.style.top=l.bottom-s.top+5+"px";const d=e=>{a.contains(e.target)||r.contains(e.target)||(t.hideColumnMenu(),t.applySortFilter(),document.removeEventListener("mousedown",d))};document.addEventListener("mousedown",d),n(a),setTimeout(()=>{const t=a.querySelector("input");t&&t.focus()},50)},t.hideColumnMenu=()=>{const e=t.querySelector(".dt-column-menu");e&&(e.style.display="none")},t.setSort=e=>{t.applySortFilter({sort:e})},t.clearColumnSettings=()=>{const e={...o.filterConfig};delete e[o.activeFieldId],o.filterConfig=e,t.applySortFilter({sort:null}),t.hideColumnMenu()},t._initRow=t=>{if(!t||!t.children)return;const e=t.children;for(let t=0;t<e.length;t++){const n=e[t],i=parseInt(n.dataset.fidx);isNaN(i)||(n._refExt={f:o.fields[i],fIdx:i})}},t.onMainMouseDown=e=>{var n,i;const r=e.target.closest(".dt-cell");if(!r)return;const l=r.closest(".dt-row");if(!l||l.classList.contains("dt-header-row"))return;const s=void 0!==r.dataset.fidx?parseInt(r.dataset.fidx):(null==(n=r._ref)?void 0:n.fIdx)??Array.from(l.children).indexOf(r),a=(null==(i=l._ref)?void 0:i.rIdx)??Array.from(t.querySelectorAll(".dt-body-row")).indexOf(l);t.startSelect(a+o._listStartIndex,s,e)},t.onMainMouseOver=e=>{var n,i;if(!o.isSelecting)return;const r=e.target.closest(".dt-cell");if(!r)return;const l=r.closest(".dt-row");if(!l||l.classList.contains("dt-header-row"))return;const s=void 0!==r.dataset.fidx?parseInt(r.dataset.fidx):(null==(n=r._ref)?void 0:n.fIdx)??Array.from(l.children).indexOf(r),a=(null==(i=l._ref)?void 0:i.rIdx)??Array.from(t.querySelectorAll(".dt-body-row")).indexOf(l);t.updateSelect(a+o._listStartIndex,s)},t.onMainDblClick=e=>{var n,i,r;const l=e.target.closest(".dt-cell");if(!l)return;const s=l.closest(".dt-row");if(!s||s.classList.contains("dt-header-row"))return;const a=null==(n=s._ref)?void 0:n.item,d=void 0!==l.dataset.fidx?parseInt(l.dataset.fidx):(null==(i=l._ref)?void 0:i.fIdx)??Array.from(s.children).indexOf(l),c=(null==(r=l._ref)?void 0:r.f)??o.fields[d];a&&c&&t.editCell(a,c,l)},o.__watch("fields",e=>{if(!e)return;o._fieldsDirty=!0;const n=e.map(t=>`var(--w-${t.id}, ${t.width||150}px)`).join(" "),i=e.reduce((t,e)=>t+(e.width||150),0);t.style.setProperty("--dt-grid-template",n),t.style.setProperty("--dt-row-width",i+"px")}),o.__watch("list",e=>{if(o._fieldsDirty){o._fieldsDirty=!1;const e=t.querySelector('.dt-body template[index="rIdx"]');if(e){const t=e.content.querySelector('template[as="f"]');t&&(o._masterCellNodes||(o._masterCellNodes=Array.from(t.content.childNodes).map(t=>t.cloneNode(!0))),t.removeAttribute("$each"),t.removeAttribute("as"),t.removeAttribute("index"),t.setAttribute("$if","true"),t.content.textContent="",o.fields.forEach((e,n)=>{o._masterCellNodes.forEach(e=>{const o=e.cloneNode(!0);1===o.nodeType&&(o.dataset.fidx=n),t.content.appendChild(o)})}))}}o._internalUpdate||(o._originalList=[...e||[]]),a.init(),a.reset(e)});let f=null;t.editCell=(e,o,i)=>{const l=t.querySelector(".dt-editor-overlay"),s=l.querySelector("AutoForm"),a=i.getBoundingClientRect(),d=t.getBoundingClientRect();f=i;let c=a.width;"textarea"===o.type||"TagsInput"===o.type?c=Math.max(a.width,300):"radio"===o.type&&(c=Math.max(a.width,240)),l.style.display="flex",l.style.left=a.left-d.left+"px",l.style.top=a.top-d.top+"px",l.style.width=c+"px",l.style.height="textarea"===o.type||"TagsInput"===o.type?"auto":a.height+"px",r.editingSchema=[{...o,name:o.id,label:""}],r.editingData=e,n(l),setTimeout(()=>{const t=s.querySelector(".form-control, .form-select, .form-check-input, input");t&&t.focus()},30)},t.hideEditor=(e=!0)=>{m||(m=t.querySelector(".dt-editor-overlay")),m&&"none"!==m.style.display&&(m.style.display="none",e&&f&&n(f),r.editingSchema=r.editingData=f=null,t.focus())},t.startSelect=s.startSelect,t.updateSelect=s.updateSelect,t.deleteSelected=s.deleteSelected;const p=e=>{const n=t.querySelector(".dt-editor-overlay");n&&"none"!==n.style.display&&!n.contains(e.target)&&t.hideEditor(!0),t.contains(e.target)||(null==n?void 0:n.contains(e.target))||s.clearAllActive()};window.addEventListener("mouseup",s.endSelect),document.addEventListener("mousedown",p),t._onUnload=()=>{document.removeEventListener("mousedown",p),window.removeEventListener("mouseup",s.endSelect)}},o.makeDom('\n<div class="dt-root d-flex flex-column h-100 border bg-body text-body overflow-hidden" style="position:relative; user-select:none; outline: none; min-height: 0" onunload="this._onUnload()" tabindex="0">\n\t<div class="dt-main flex-grow-1 overflow-auto" $onscroll="this.onScroll()" \n\t\t$onmousedown="this.onMainMouseDown(event)"\n\t\t$onmouseover="this.onMainMouseOver(event)"\n\t\t$ondblclick="this.onMainDblClick(event)"\n\t\tstyle="overflow-anchor:none; min-height: 0">\n\t\t<div class="dt-header border-bottom bg-light sticky-top" style="z-index:20">\n\t\t\t<div class="dt-row dt-header-row fw-bold text-muted small">\n\t\t\t\t<template $each="this.state?.fields || []">\n\t\t\t\t\t<div $data-id="item.id" class="dt-cell border-end px-2 d-flex align-items-center justify-content-between header-cell">\n\t\t\t\t\t\t<div class="d-flex align-items-center overflow-hidden">\n\t\t\t\t\t\t\t<i $if="this.state?.sortConfig?.fieldId === item.id" \n\t\t\t\t\t\t\t $class="\'bi bi-sort-\' + (this.state?.sortConfig?.direction === \'asc\' ? \'down\' : \'up-alt\') + \' me-1 text-primary\'"></i>\n\t\t\t\t\t\t\t<i $if="this.state?.filterConfig?.[item.id]" class="bi bi-filter me-1 text-primary"></i>\n\t\t\t\t\t\t\t<span $text="item.name" class="text-truncate"></span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<button class="btn btn-xs btn-link text-muted p-0 border-0 header-menu-btn" $onclick="this.showColumnMenu(item, event)">\n\t\t\t\t\t\t\t<i class="bi bi-chevron-down"></i>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</div>\n\t\t\t\t</template>\n\t\t\t</div>\n\t\t</div>\n\t\t<div class="dt-body" style="position:relative">\n\t\t\t<div class="dt-spacer-prev flex-shrink-0" style="display:none"></div>\n\t\t\t<template $each="this.state?._renderedList || []" key="id" index="rIdx">\n\t\t\t\t<div class="dt-row dt-body-row border-bottom bg-white" $.="this._initRow(thisNode)">\n\t\t\t\t\t<template $each="this.state?.fields || []" as="f" index="fIdx">\n\t\t\t\t\t\t<div class="dt-cell border-end px-2 d-flex align-items-center">\n\t\t\t\t\t\t\t<span $text="!f.formatter ? item[f.id] : this.format(item[f.id], f)" class="text-truncate"></span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</template>\n\t\t\t\t</div>\n\t\t\t</template>\n\t\t\t<div class="dt-spacer-post flex-shrink-0" style="display:none"></div>\n\t\t</div>\n\t</div>\n\n\t<div class="dt-column-menu border bg-body shadow-sm rounded" style="display:none; position:absolute; z-index:2000; min-width:180px" $onclick="event.stopPropagation()" $onmousedown="event.stopPropagation()">\n\t\t<div class="list-group list-group-flush">\n\t\t\t<button class="list-group-item list-group-item-action py-2 d-flex align-items-center" $onclick="this.setSort(\'asc\')">\n\t\t\t\t<i class="bi bi-sort-alpha-down me-2"></i> Sort Ascending\n\t\t\t</button>\n\t\t\t<button class="list-group-item list-group-item-action py-2 d-flex align-items-center" $onclick="this.setSort(\'desc\')">\n\t\t\t\t<i class="bi bi-sort-alpha-up-alt me-2"></i> Sort Descending\n\t\t\t</button>\n\t\t\t<div class="p-2 border-top bg-light-subtle">\n\t\t\t\t<div class="d-flex align-items-center mb-1 text-muted fw-bold" style="font-size: 10px">\n\t\t\t\t\t<i class="bi bi-filter me-1"></i> LOCAL FILTER\n\t\t\t\t</div>\n\t\t\t\t<input type="text" class="form-control form-control-sm" placeholder="Search..." \n\t\t\t\t\t$bind="this.state.filterConfig[this.state.activeFieldId]"\n\t\t\t\t\t$onkeydown="if(event.key === \'Enter\') this.applySortFilter()">\n\t\t\t</div>\n\t\t\t<button class="list-group-item list-group-item-action py-2 text-danger d-flex align-items-center border-top" $onclick="this.clearColumnSettings()">\n\t\t\t\t<i class="bi bi-x-circle me-2"></i> Clear Settings\n\t\t\t</button>\n\t\t</div>\n\t</div>\n\n\t<div class="dt-editor-overlay dt-editor-container" style="display: none" $onclick="event.stopPropagation()" $onmousedown="event.stopPropagation()">\n\t\t<AutoForm inline class="h-100 w-100" $onsubmit="thisNode.closest(\'DataTable\').hideEditor(true)"/>\n\t</div>\n\n\t<div class="dt-footer border-top bg-light d-flex align-items-center px-2 py-1 small text-muted" style="height:32px">\n\t\t<div class="d-flex align-items-center gap-2">\n\t\t\t<span $text="(this.state?.selectedRowCount || 0) + \' / \' + (this.state?.list?.length || 0)"></span>\n\t\t\t<button class="btn btn-xs btn-link text-danger p-0 border-0" title="Delete Selected" \n\t\t\t\t$if="this.state?.selectedRowCount" $onclick="this.deleteSelected()">\n\t\t\t\t<i class="bi bi-trash"></i>\n\t\t\t</button>\n\t\t</div>\n\t</div>\n</div>\n'),o.makeDom("\n<style>\n\tDataTable { display: block; }\n\t.dt-root { font-size: 0.875rem; }\n\t.dt-row {\n\t\tdisplay: grid;\n\t\tgrid-template-columns: var(--dt-grid-template);\n\t\twidth: var(--dt-row-width, max-content);\n\t\tmin-width: 100%;\n\t\theight: 40px;\n\t\tcontain: paint layout;\n\t}\n\t.dt-header-row { background-color: var(--bs-tertiary-bg); }\n\t.dt-cell { background: inherit; white-space: nowrap; flex-shrink: 0; contain: content; }\n\t.dt-body-row:hover { background-color: var(--bs-secondary-bg) !important; }\n\t.dt-cell-selected { background-color: var(--bs-primary-bg-subtle) !important; }\n\t.header-cell .header-menu-btn { opacity: 0; transition: opacity 0.2s; }\n\t.header-cell:hover .header-menu-btn { opacity: 1; }\n\t.dt-column-menu { box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important; }\n\t.dt-column-menu .list-group-item { border: none; cursor: pointer; }\n\t.dt-column-menu .list-group-item:hover { background-color: var(--bs-light); }\n\t.dt-editor-overlay {\n\t\tposition: absolute; z-index: 1000; background: var(--bs-body-bg);\n\t\tbox-shadow: 0 4px 16px rgba(0,0,0,0.25); border: 1px solid var(--bs-primary);\n\t\talign-items: center; overflow: visible;\n\t}\n\t.dt-editor-overlay .form-control { border: none !important; border-radius: 0 !important; height: 100% !important; padding: 0 8px !important; }\n\t.btn-xs { padding: 1px 5px; line-height: 1.5; }\n</style>\n")),"undefined"!=typeof document&&n(document.documentElement);
|