dataTable/dist/datatable.min.js

2 lines
9.2 KiB
JavaScript

import{Component as t,NewState as e,Util as i}from"@web/state";import{VirtualScroll as n}from"@web/base";t.register("DataTable",t=>{const i=n(),s=t.state;Object.assign(s,{list:[],fields:[],renderedList:[],prevHeight:0,postHeight:0,_listStartIndex:0,selStartR:-1,selStartF:-1,selEndR:-1,selEndF:-1,multiSelections:[],isSelecting:!1});const r=()=>{const n=t.querySelector(".dt-body");if(!n)return;const r=i.calc(n,s.list);r&&(r.renderedList.forEach((t,i)=>{if(!t.__watch){const n=e(t);r.renderedList[i]=n,s.list[r.listStartIndex+i]=n}}),Object.assign(s,{prevHeight:r.prevHeight,postHeight:r.postHeight,_listStartIndex:r.listStartIndex,renderedList:r.renderedList}))};t.refresh=r,s.__watch("list",e=>{s._listStartIndex=0;const n=t.querySelector(".dt-body");s.renderedList=i.reset(e,n||{clientHeight:800})||[],n&&(i.init(e,r),requestAnimationFrame(()=>r()))}),t.onItemUpdate=(t,e)=>i.update(t+s._listStartIndex,e),t.getOffset=(t,e,i)=>{const n=t.slice(0,e).filter(t=>"left"===t.pinned),s=t.slice(e+1).filter(t=>"right"===t.pinned);return("left"===i?n:s).reduce((t,e)=>t+(e.width||150),0)},t.isCellSelected=(t,e)=>{const i=Math.min(s.selStartR,s.selEndR),n=Math.max(s.selStartR,s.selEndR),r=Math.min(s.selStartF,s.selEndF),d=Math.max(s.selStartF,s.selEndF);return t>=i&&t<=n&&e>=r&&e<=d||s.multiSelections.some(i=>t>=i.r1&&t<=i.r2&&e>=i.f1&&e<=i.f2)},t.clearAllActive=(t=!1)=>{s.list.forEach(t=>{t&&t.__watch&&(null!==t._editingF&&(t._editingF=null),null!==t._activeF&&(t._activeF=null))}),t||(s.selStartR=-1,s.multiSelections=[])},t.startSelect=(e,i,n)=>{const r=t.isCellSelected(e,i);s.editingCell&&(s.editingCell=null),n.shiftKey&&-1!==s.selStartR?(s.selEndR=e,s.selEndF=i):(r||(n.ctrlKey||n.metaKey?-1!==s.selStartR&&s.multiSelections.push({r1:Math.min(s.selStartR,s.selEndR),r2:Math.max(s.selStartR,s.selEndR),f1:Math.min(s.selStartF,s.selEndF),f2:Math.max(s.selStartF,s.selEndF)}):t.clearAllActive(),s.selStartR=s.selEndR=e,s.selStartF=s.selEndF=i),s.isSelecting=!0,s.list[e]&&s.list[e].__watch&&(s.list[e]._activeF=i))},t.updateSelect=(t,e)=>s.isSelecting&&(s.selEndR=t,s.selEndF=e),t.endSelect=()=>s.isSelecting=!1,t.editCell=(i,n,r)=>{var d;if(!i.__watch)return;const l=Math.min(s.selStartR,s.selEndR),a=Math.max(s.selStartR,s.selEndR),o=Math.min(s.selStartF,s.selEndF),c=Math.max(s.selStartF,s.selEndF),h=s.list.indexOf(i),f=-1!==s.selStartR&&h>=l&&h<=a&&r>=o&&r<=c?a-l+1:0;if(f>1&&(null==(d=globalThis.UI)?void 0:d.toast)&&UI.toast(`{#Bulk Editing {num} rows... || ${f}#}`),t.clearAllActive(!0),i._editingF=n.id,i._activeF=r,f>1){const t=i.__watch(n.id,r=>{for(let t=l;t<=a;t++){const d=s.list[t];if(d!==i){const i=d.__watch?d:e(d);s.list[t]=i,i[n.id]=r}}t()})}};t.copy=async()=>{const t=Math.min(s.selStartR,s.selEndR),e=Math.max(s.selStartR,s.selEndR),i=Math.min(s.selStartF,s.selEndF),n=Math.max(s.selStartF,s.selEndF);if(-1===t)return;const r=s.list.slice(t,e+1).map(t=>s.fields.slice(i,n+1).map(e=>(t=>{const e=String(t??"");return e.includes("\t")||e.includes("\n")||e.includes('"')?'"'+e.replace(/"/g,'""')+'"':e})(t[e.id])).join("\t")).join("\n");await navigator.clipboard.writeText(r)},t.paste=async()=>{const t=(t=>{const e=[];let i=[],n="",s=!1;for(let r=0;r<t.length;r++){const d=t[r],l=t[r+1];s?'"'===d&&'"'===l?(n+='"',r++):'"'===d?s=!1:n+=d:'"'===d?s=!0:"\t"===d?(i.push(n),n=""):"\n"===d?(i.push(n),e.push(i),i=[],n=""):"\r"!==d&&(n+=d)}return i.push(n),e.push(i),e})(await navigator.clipboard.readText()),i=Math.min(s.selStartR,s.selEndR),n=Math.min(s.selStartF,s.selEndF);-1!==i&&t.forEach((t,r)=>{let d=s.list[i+r];d&&(d.__watch||(d=e(d),s.list[i+r]=d),t.forEach((t,e)=>{const i=s.fields[n+e];i&&("boolean"==typeof d[i.id]?d[i.id]="true"===t.toLowerCase():"number"==typeof d[i.id]?d[i.id]=Number(t):d[i.id]=t)}))})},t.addEventListener("keydown",e=>{(e.ctrlKey||e.metaKey)&&("c"===e.key&&(e.preventDefault(),t.copy()),"v"===e.key&&(e.preventDefault(),t.paste()))});const d=e=>!t.contains(e.target)&&t.clearAllActive();document.addEventListener("mousedown",d),window.addEventListener("mouseup",t.endSelect),t._onUnload=()=>{document.removeEventListener("mousedown",d),window.removeEventListener("mouseup",t.endSelect)}},i.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\" tabindex=\"0\" onunload=\"this._onUnload()\">\n\t<div class=\"dt-header d-flex flex-shrink-0 border-bottom bg-body-tertiary fw-bold text-muted small\" style=\"overflow:hidden; position:sticky; top:0; z-index:20; height:48px\">\n\t\t<div $each=\"this.state.fields\" as=\"f\" class=\"dt-col border-end d-flex align-items-center px-2 bg-body-tertiary\"\n\t\t\t$style=\"'width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px); min-width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px); ' + (f.pinned ? 'position:sticky; z-index:11;' : '') + (f.pinned === 'left' ? 'left:' + this.getOffset(this.state.fields, index, 'left') + 'px;' : '') + (f.pinned === 'right' ? 'right:' + this.getOffset(this.state.fields, index, 'right') + 'px;' : '')\">\n\t\t\t<span $text=\"f.name\" $class=\"'text-truncate flex-grow-1 ' + (f.type === 'number' ? 'text-end' : '')\"></span>\n\t\t\t<Resizer class=\"dt-resizer\" $.target=\"thisNode.parentNode\" min=\"50\" max=\"600\" $bind=\"f.width\" $onresizing=\"thisNode.closest('.dt-root').style.setProperty('--w-' + f.id, event.detail.newSize + 'px')\"/>\n\t\t</div>\n\t</div>\n\t<div class=\"dt-body flex-grow-1 overflow-auto bg-body\" $onscroll=\"requestAnimationFrame(() => this.refresh())\" style=\"overflow-anchor:none\">\n\t\t<div $if=\"this.state.prevHeight>0\" $style=\"'height:' + this.state.prevHeight + 'px;'\"></div>\n\t\t<div $each=\"this.state.renderedList\" as=\"row\" class=\"dt-row d-flex border-bottom\" $onupdate=\"this.onItemUpdate(rIdx, thisNode)\" index=\"rIdx\">\n\t\t\t<div $each=\"this.state.fields\" as=\"f\" class=\"dt-cell border-end d-flex align-items-center bg-body\"\n\t\t\t\t$class=\"'dt-cell border-end d-flex align-items-center bg-body' + (this.isCellSelected(rIdx + this.state._listStartIndex, index) ? ' bg-primary-subtle' : '') + (row._activeF === index ? ' dt-cell-active' : '')\"\n\t\t\t\t$style=\"'width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px); min-width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px); height:40px; ' + (f.pinned ? 'position:sticky; z-index:10;' : '') + (f.pinned === 'left' ? 'left:' + this.getOffset(this.state.fields, index, 'left') + 'px;' : '') + (f.pinned === 'right' ? 'right:' + this.getOffset(this.state.fields, index, 'right') + 'px;' : '')\"\n\t\t\t\t$onmousedown=\"this.startSelect(rIdx + this.state._listStartIndex, index, event)\"\n\t\t\t\t$onmouseenter=\"this.updateSelect(rIdx + this.state._listStartIndex, index)\"\n\t\t\t\t$ondblclick=\"this.editCell(row, f, index)\">\n\t\t\t\t\n\t\t\t\t<div $if=\"row._editingF !== f.id\" $class=\"'px-2 text-truncate w-100 h-100 d-flex align-items-center ' + (f.type === 'number' ? 'justify-content-end' : (f.type === 'switch' ? 'justify-content-center' : ''))\">\n\t\t\t\t\t<span $if=\"typeof row[f.id] !== 'boolean'\" $text=\"row[f.id] ?? ''\" class=\"text-truncate\"></span>\n\t\t\t\t\t<div $if=\"typeof row[f.id] === 'boolean'\" class=\"form-switch fs-5 m-0 d-flex align-items-center justify-content-center\" style=\"padding-left:0\">\n\t\t\t\t\t\t<input class=\"form-check-input m-0\" type=\"checkbox\" $checked=\"row[f.id]\" disabled>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t<div $if=\"row._editingF === f.id\" $class=\"'dt-editor-container' + (f.type === 'textarea' ? ' dt-editor-textarea' : '')\" $onclick=\"event.stopPropagation()\" $onmousedown=\"event.stopPropagation()\">\n\t\t\t\t\t<AutoForm inline class=\"dt-editor h-100 w-100\" $.state.schema=\"[{ ...f, name: f.id, label: f.name }]\" $.data=\"row\" \n\t\t\t\t\t\t$onkeydown=\"(event.key === 'Enter' && f.type !== 'textarea') && (row._editingF = null); event.key === 'Escape' && (row._editingF = null)\"/>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t\t<div $if=\"this.state.postHeight>0\" $style=\"'height:' + this.state.postHeight + 'px;'\"></div>\n\t</div>\n\t<style>\n\t\t.dt-root { font-size: 0.875rem; --dt-active-border: var(--bs-primary); outline: none; }\n\t\t.dt-row:hover .dt-cell { background-color: var(--bs-secondary-bg) !important; }\n\t\t.dt-cell { position: relative; transition: background 0.05s; border-color: var(--bs-border-color) !important; }\n\t\t.dt-cell.bg-primary-subtle { background-color: var(--bs-primary-bg-subtle) !important; }\n\t\t.dt-cell-active::after { content: ''; position: absolute; inset: 0; border: 2px solid var(--dt-active-border); pointer-events: none; z-index: 12; }\n\t\t.dt-col { position: relative; }\n\t\t.dt-editor-container { position: absolute; left: -1px; top: -1px; width: fit-content; min-width: calc(100% + 2px); height: auto; min-height: calc(100% + 2px); background: var(--bs-body-bg); z-index: 100; box-shadow: 0 4px 12px rgba(0,0,0,.3); border: 1px solid var(--dt-active-border); display: flex; align-items: center; }\n\t\t.dt-editor-textarea { height: auto; min-height: 100%; min-width: 260px; align-items: flex-start; }\n\t\t.dt-resizer { width: 4px !important; opacity: 0; transition: opacity 0.2s; position: absolute; right: 0; top: 0; height: 100%; background: var(--bs-primary); cursor: col-resize; z-index: 15; }\n\t\t.dt-col:hover .dt-resizer { opacity: 0.5; }\n\t\t.dt-resizer:hover { opacity: 1 !important; }\n\t</style>\n</div>\n"));